diff options
Diffstat (limited to 'django/db')
-rw-r--r-- | django/db/backends/ado_mssql/base.py | 9 | ||||
-rw-r--r-- | django/db/backends/mysql/base.py | 9 | ||||
-rw-r--r-- | django/db/backends/oracle/base.py | 24 | ||||
-rw-r--r-- | django/db/backends/postgresql/base.py | 9 | ||||
-rw-r--r-- | django/db/backends/postgresql_psycopg2/base.py | 9 | ||||
-rw-r--r-- | django/db/backends/sqlite3/base.py | 9 | ||||
-rw-r--r-- | django/db/models/query.py | 51 |
7 files changed, 90 insertions, 30 deletions
diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index ad1f6dd60f..33800980a8 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -87,6 +87,9 @@ class DatabaseWrapper(local): self.connection.close() self.connection = None +allows_group_by_ordinal = True +allows_unique_and_pk = True +returns_dates_as_strings = False supports_constraints = True uses_case_insensitive_names = False @@ -116,6 +119,9 @@ def get_date_trunc_sql(lookup_type, field_name): if lookup_type=='day': return "Convert(datetime, Convert(varchar(12), %s))" % field_name +def get_datetime_cast_sql(): + return None + def get_limit_offset_sql(limit, offset=None): # TODO: This is a guess. Make sure this is correct. sql = "LIMIT %s" % limit @@ -138,6 +144,9 @@ def get_pk_default_value(): def get_max_name_length(): return None +def get_autoinc_sql(table): + return None + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 0dd73ac51a..e5756dc4b0 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -130,6 +130,9 @@ class DatabaseWrapper(local): self.server_version = tuple([int(x) for x in m.groups()]) return self.server_version +allows_group_by_ordinal = True +allows_unique_and_pk = True +returns_dates_as_strings = True # MySQLdb requires a typecast for dates supports_constraints = True uses_case_insensitive_names = False @@ -164,6 +167,9 @@ def get_date_trunc_sql(lookup_type, field_name): sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) return sql +def get_datetime_cast_sql(): + return None + def get_limit_offset_sql(limit, offset=None): sql = "LIMIT " if offset and offset != 0: @@ -185,6 +191,9 @@ def get_pk_default_value(): def get_max_name_length(): return 64; +def get_autoinc_sql(table): + return None + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'LIKE %s', diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 5831ca47da..4e6314d32e 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -59,6 +59,9 @@ class DatabaseWrapper(local): self.connection.close() self.connection = None +allows_group_by_ordinal = False +allows_unique_and_pk = False # Suppress UNIQUE/PK for Oracle (ORA-02259) +returns_dates_as_strings = False supports_constraints = True uses_case_insensitive_names = True @@ -118,6 +121,9 @@ def get_date_trunc_sql(lookup_type, field_name): sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) return sql +def get_datetime_cast_sql(): + return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')" + def get_limit_offset_sql(limit, offset=None): # Limits and offset are too complicated to be handled here. # Instead, they are handled in django/db/backends/oracle/query.py. @@ -138,11 +144,27 @@ def get_pk_default_value(): def get_max_name_length(): return 30 +def get_autoinc_sql(table): + # To simulate auto-incrementing primary keys in Oracle, we have to + # create a sequence and a trigger. + name_length = get_max_name_length() - 3 + sq_name = '%s_sq' % util.truncate_name(table, name_length) + tr_name = '%s_tr' % util.truncate_name(table, name_length) + sequence_sql = 'CREATE SEQUENCE %s;' % sq_name + trigger_sql = """CREATE OR REPLACE TRIGGER %s + BEFORE INSERT ON %s + FOR EACH ROW + WHEN (new.id IS NULL) + BEGIN + SELECT %s.nextval INTO :new.id FROM dual; + END;\n""" % (tr_name, quote_name(table), sq_name) + return sequence_sql, trigger_sql + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': "LIKE %s ESCAPE '\\'", 'contains': "LIKE %s ESCAPE '\\'", - 'icontains': "LIKE %s ESCAPE '\\'", + 'icontains': "LIKE LOWER(%s) ESCAPE '\\'", 'gt': '> %s', 'gte': '>= %s', 'lt': '< %s', diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 1fba77a550..fd3ef87c45 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -61,6 +61,9 @@ class DatabaseWrapper(local): self.connection.close() self.connection = None +allows_group_by_ordinal = True +allows_unique_and_pk = True +returns_dates_as_strings = False supports_constraints = True uses_case_insensitive_names = False @@ -95,6 +98,9 @@ def get_date_trunc_sql(lookup_type, field_name): # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) +def get_datetime_cast_sql(): + return None + def get_limit_offset_sql(limit, offset=None): sql = "LIMIT %s" % limit if offset and offset != 0: @@ -116,6 +122,9 @@ def get_pk_default_value(): def get_max_name_length(): return None +def get_autoinc_sql(table): + return None + # Register these custom typecasts, because Django expects dates/times to be # in Python's native (standard-library) datetime/time format, whereas psycopg # use mx.DateTime by default. diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index bfbb7616db..b2722d8d99 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -62,6 +62,9 @@ class DatabaseWrapper(local): self.connection.close() self.connection = None +allows_group_by_ordinal = True +allows_unique_and_pk = True +returns_dates_as_strings = False supports_constraints = True uses_case_insensitive_names = True @@ -88,6 +91,9 @@ def get_date_trunc_sql(lookup_type, field_name): # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) +def get_datetime_cast_sql(): + return None + def get_limit_offset_sql(limit, offset=None): sql = "LIMIT %s" % limit if offset and offset != 0: @@ -109,6 +115,9 @@ def get_pk_default_value(): def get_max_name_length(): return None +def get_autoinc_sql(table): + return None + OPERATOR_MAPPING = { 'exact': '= %s', 'iexact': 'ILIKE %s', diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index cc27bb0a1c..c92e83a6ad 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -98,6 +98,9 @@ class SQLiteCursorWrapper(Database.Cursor): def convert_query(self, query, num_params): return query % tuple("?" * num_params) +allows_group_by_ordinal = True +allows_unique_and_pk = True +returns_dates_as_strings = True supports_constraints = False uses_case_insensitive_names = False @@ -131,6 +134,9 @@ def get_date_trunc_sql(lookup_type, field_name): # sqlite doesn't support DATE_TRUNC, so we fake it as above. return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) +def get_datetime_cast_sql(): + return None + def get_limit_offset_sql(limit, offset=None): sql = "LIMIT %s" % limit if offset and offset != 0: @@ -152,6 +158,9 @@ def get_pk_default_value(): def get_max_name_length(): return None +def get_autoinc_sql(table): + return None + def _sqlite_date_trunc(lookup_type, dt): try: dt = util.typecast_timestamp(dt) diff --git a/django/db/models/query.py b/django/db/models/query.py index 847906fd26..53a17c23d7 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1,5 +1,4 @@ -from django import db -from django.db import backend, connection, transaction +from django.db import backend, connection, get_query_module, transaction from django.db.models.fields import DateField, FieldDoesNotExist from django.db.models import signals from django.dispatch import dispatcher @@ -509,13 +508,12 @@ class _QuerySet(object): else: assert self._offset is None, "'offset' is not allowed without 'limit'" - return select, " ".join(sql), params + return select, " ".join(sql), params, None -# Check to see if the DB backend would like to define its own QuerySet class -# and otherwise use the default. -backend_query_module = db.get_query_module() -if hasattr(backend_query_module, "get_query_set_class"): - QuerySet = db.get_query_module().get_query_set_class(_QuerySet) +# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. +backend_query_module = get_query_module() +if hasattr(backend_query_module, 'get_query_set_class'): + QuerySet = backend_query_module.get_query_set_class(_QuerySet) else: QuerySet = _QuerySet @@ -561,21 +559,18 @@ class DateQuerySet(QuerySet): field_name = backend.quote_name(self._field.column) date_trunc_sql = backend.get_date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name)) - fmt = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' - - if settings.DATABASE_ENGINE == 'oracle': - sql = fmt % (date_trunc_sql, sql, date_trunc_sql, self._order) - cursor = connection.cursor() - cursor.execute(sql, params) - return [row[0] for row in cursor.fetchall()] + if backend.allows_group_by_ordinal: + group_by = '1' else: - sql = fmt % (date_trunc_sql, sql, 1, self._order_by) - cursor = connection.cursor() - cursor.execute(sql, params) - # We have to manually run typecast_timestamp(str()) on the results, because - # MySQL doesn't automatically cast the result of date functions as datetime - # objects -- MySQL returns the values as strings, instead. + group_by = date_trunc_sql + fmt = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' + stmt = fmt % (date_trunc_sql, sql, group_by, self._order) + cursor = connection.cursor() + cursor.execute(stmt, params) + if backend.returns_dates_as_strings: return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] + else: + return [row[0] for row in cursor.fetchall()] def _clone(self, klass=None, **kwargs): c = super(DateQuerySet, self)._clone(klass, **kwargs) @@ -657,15 +652,13 @@ def get_where_clause(lookup_type, table_prefix, field_name, value): if table_prefix.endswith('.'): table_prefix = backend.quote_name(table_prefix[:-1])+'.' field_name = backend.quote_name(field_name) - # TODO: move this into django.db.backends.oracle somehow - if settings.DATABASE_ENGINE == 'oracle': - if lookup_type == 'icontains': - return 'lower(%s%s) %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) - elif type(value) == datetime.datetime: - return "%s%s %s" % (table_prefix, field_name, - (backend.OPERATOR_MAPPING[lookup_type] % "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')")) + if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): + cast_sql = backend.get_datetime_cast_sql() + else: + cast_sql = '%s' try: - return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) + return '%s%s %s' % (table_prefix, field_name, + backend.OPERATOR_MAPPING[lookup_type] % cast_sql) except KeyError: pass if lookup_type == 'in': |