mirror of
https://github.com/django/django.git
synced 2025-09-06 10:40:49 +00:00
Merge branch 'master' into schema-alteration
Conflicts: django/db/backends/__init__.py django/db/backends/mysql/base.py django/db/backends/oracle/base.py django/db/backends/oracle/creation.py django/db/backends/postgresql_psycopg2/base.py django/db/backends/sqlite3/base.py django/db/models/fields/related.py
This commit is contained in:
commit
7f3678dc4c
4298 changed files with 130294 additions and 339961 deletions
|
@ -30,13 +30,14 @@ if (version < (1, 2, 1) or (version[:3] == (1, 2, 1) and
|
|||
from MySQLdb.converters import conversions, Thing2Literal
|
||||
from MySQLdb.constants import FIELD_TYPE, CLIENT
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import utils
|
||||
from django.db.backends import *
|
||||
from django.db.backends.signals import connection_created
|
||||
from django.db.backends.mysql.client import DatabaseClient
|
||||
from django.db.backends.mysql.creation import DatabaseCreation
|
||||
from django.db.backends.mysql.introspection import DatabaseIntrospection
|
||||
from django.db.backends.mysql.validation import DatabaseValidation
|
||||
from django.utils.encoding import force_str, force_text
|
||||
from django.db.backends.mysql.schema import DatabaseSchemaEditor
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.functional import cached_property
|
||||
|
@ -45,7 +46,6 @@ from django.utils import six
|
|||
from django.utils import timezone
|
||||
|
||||
# Raise exceptions for database warnings if DEBUG is on
|
||||
from django.conf import settings
|
||||
if settings.DEBUG:
|
||||
warnings.filterwarnings("error", category=Database.Warning)
|
||||
|
||||
|
@ -118,31 +118,24 @@ class CursorWrapper(object):
|
|||
|
||||
def execute(self, query, args=None):
|
||||
try:
|
||||
# args is None means no string interpolation
|
||||
return self.cursor.execute(query, args)
|
||||
except Database.IntegrityError as e:
|
||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||
except Database.OperationalError as e:
|
||||
# Map some error codes to IntegrityError, since they seem to be
|
||||
# misclassified and Django would prefer the more logical place.
|
||||
if e[0] in self.codes_for_integrityerror:
|
||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
||||
except Database.DatabaseError as e:
|
||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
||||
raise
|
||||
|
||||
def executemany(self, query, args):
|
||||
try:
|
||||
return self.cursor.executemany(query, args)
|
||||
except Database.IntegrityError as e:
|
||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||
except Database.OperationalError as e:
|
||||
# Map some error codes to IntegrityError, since they seem to be
|
||||
# misclassified and Django would prefer the more logical place.
|
||||
if e[0] in self.codes_for_integrityerror:
|
||||
six.reraise(utils.IntegrityError, utils.IntegrityError(*tuple(e.args)), sys.exc_info()[2])
|
||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
||||
except Database.DatabaseError as e:
|
||||
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
|
||||
raise
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr in self.__dict__:
|
||||
|
@ -195,6 +188,12 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
|||
"Confirm support for introspected foreign keys"
|
||||
return self._mysql_storage_engine != 'MyISAM'
|
||||
|
||||
@cached_property
|
||||
def has_zoneinfo_database(self):
|
||||
cursor = self.connection.cursor()
|
||||
cursor.execute("SELECT 1 FROM mysql.time_zone LIMIT 1")
|
||||
return cursor.fetchone() is not None
|
||||
|
||||
class DatabaseOperations(BaseDatabaseOperations):
|
||||
compiler_module = "django.db.backends.mysql.compiler"
|
||||
|
||||
|
@ -220,6 +219,39 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||
return sql
|
||||
|
||||
def datetime_extract_sql(self, lookup_type, field_name, tzname):
|
||||
if settings.USE_TZ:
|
||||
field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
|
||||
params = [tzname]
|
||||
else:
|
||||
params = []
|
||||
# http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html
|
||||
if lookup_type == 'week_day':
|
||||
# DAYOFWEEK() returns an integer, 1-7, Sunday=1.
|
||||
# Note: WEEKDAY() returns 0-6, Monday=0.
|
||||
sql = "DAYOFWEEK(%s)" % field_name
|
||||
else:
|
||||
sql = "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
|
||||
return sql, params
|
||||
|
||||
def datetime_trunc_sql(self, lookup_type, field_name, tzname):
|
||||
if settings.USE_TZ:
|
||||
field_name = "CONVERT_TZ(%s, 'UTC', %%s)" % field_name
|
||||
params = [tzname]
|
||||
else:
|
||||
params = []
|
||||
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
||||
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
||||
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
||||
try:
|
||||
i = fields.index(lookup_type) + 1
|
||||
except ValueError:
|
||||
sql = field_name
|
||||
else:
|
||||
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
||||
return sql, params
|
||||
|
||||
def date_interval_sql(self, sql, connector, timedelta):
|
||||
return "(%s %s INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND)" % (sql, connector,
|
||||
timedelta.days, timedelta.seconds, timedelta.microseconds)
|
||||
|
@ -242,7 +274,7 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
# With MySQLdb, cursor objects have an (undocumented) "_last_executed"
|
||||
# attribute where the exact query sent to the database is saved.
|
||||
# See MySQLdb/cursors.py in the source distribution.
|
||||
return cursor._last_executed.decode('utf-8')
|
||||
return force_text(cursor._last_executed, errors='replace')
|
||||
|
||||
def no_limit_value(self):
|
||||
# 2**64 - 1, as recommended by the MySQL documentation
|
||||
|
@ -316,11 +348,10 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
# MySQL doesn't support microseconds
|
||||
return six.text_type(value.replace(microsecond=0))
|
||||
|
||||
def year_lookup_bounds(self, value):
|
||||
def year_lookup_bounds_for_datetime_field(self, value):
|
||||
# Again, no microseconds
|
||||
first = '%s-01-01 00:00:00'
|
||||
second = '%s-12-31 23:59:59.99'
|
||||
return [first % value, second % value]
|
||||
first, second = super(DatabaseOperations, self).year_lookup_bounds_for_datetime_field(value)
|
||||
return [first.replace(microsecond=0), second.replace(microsecond=0)]
|
||||
|
||||
def max_name_length(self):
|
||||
return 64
|
||||
|
@ -329,15 +360,6 @@ class DatabaseOperations(BaseDatabaseOperations):
|
|||
items_sql = "(%s)" % ", ".join(["%s"] * len(fields))
|
||||
return "VALUES " + ", ".join([items_sql] * num_values)
|
||||
|
||||
def savepoint_create_sql(self, sid):
|
||||
return "SAVEPOINT %s" % sid
|
||||
|
||||
def savepoint_commit_sql(self, sid):
|
||||
return "RELEASE SAVEPOINT %s" % sid
|
||||
|
||||
def savepoint_rollback_sql(self, sid):
|
||||
return "ROLLBACK TO SAVEPOINT %s" % sid
|
||||
|
||||
class DatabaseWrapper(BaseDatabaseWrapper):
|
||||
vendor = 'mysql'
|
||||
operators = {
|
||||
|
@ -357,10 +379,11 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
'iendswith': 'LIKE %s',
|
||||
}
|
||||
|
||||
Database = Database
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(DatabaseWrapper, self).__init__(*args, **kwargs)
|
||||
|
||||
self.server_version = None
|
||||
self.features = DatabaseFeatures(self)
|
||||
self.ops = DatabaseOperations(self)
|
||||
self.client = DatabaseClient(self)
|
||||
|
@ -368,15 +391,6 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
self.introspection = DatabaseIntrospection(self)
|
||||
self.validation = DatabaseValidation(self)
|
||||
|
||||
def _valid_connection(self):
|
||||
if self.connection is not None:
|
||||
try:
|
||||
self.connection.ping()
|
||||
return True
|
||||
except DatabaseError:
|
||||
self.close()
|
||||
return False
|
||||
|
||||
def get_connection_params(self):
|
||||
kwargs = {
|
||||
'conv': django_conversions,
|
||||
|
@ -417,12 +431,7 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
cursor.execute('SET SQL_AUTO_IS_NULL = 0')
|
||||
cursor.close()
|
||||
|
||||
def _cursor(self):
|
||||
if not self._valid_connection():
|
||||
conn_params = self.get_connection_params()
|
||||
self.connection = self.get_new_connection(conn_params)
|
||||
self.init_connection_state()
|
||||
connection_created.send(sender=self.__class__, connection=self)
|
||||
def create_cursor(self):
|
||||
cursor = self.connection.cursor()
|
||||
return CursorWrapper(cursor)
|
||||
|
||||
|
@ -432,24 +441,8 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
except Database.NotSupportedError:
|
||||
pass
|
||||
|
||||
@cached_property
|
||||
def mysql_version(self):
|
||||
if not self.server_version:
|
||||
new_connection = False
|
||||
if not self._valid_connection():
|
||||
# Ensure we have a connection with the DB by using a temporary
|
||||
# cursor
|
||||
new_connection = True
|
||||
self.cursor().close()
|
||||
server_info = self.connection.get_server_info()
|
||||
if new_connection:
|
||||
# Make sure we close the connection
|
||||
self.close()
|
||||
m = server_version_re.match(server_info)
|
||||
if not m:
|
||||
raise Exception('Unable to determine MySQL version from version string %r' % server_info)
|
||||
self.server_version = tuple([int(x) for x in m.groups()])
|
||||
return self.server_version
|
||||
def _set_autocommit(self, autocommit):
|
||||
self.connection.autocommit(autocommit)
|
||||
|
||||
def disable_constraint_checking(self):
|
||||
"""
|
||||
|
@ -503,3 +496,20 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
|||
def schema_editor(self):
|
||||
"Returns a new instance of this backend's SchemaEditor"
|
||||
return DatabaseSchemaEditor(self)
|
||||
|
||||
def is_usable(self):
|
||||
try:
|
||||
self.connection.ping()
|
||||
except DatabaseError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@cached_property
|
||||
def mysql_version(self):
|
||||
with self.temporary_connection():
|
||||
server_info = self.connection.get_server_info()
|
||||
match = server_version_re.match(server_info)
|
||||
if not match:
|
||||
raise Exception('Unable to determine MySQL version from version string %r' % server_info)
|
||||
return tuple([int(x) for x in match.groups()])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue