diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 76788bb84b..5a9560aac3 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -53,3 +53,10 @@ class BaseDatabaseOperations(object): This SQL is executed when a table is created. """ return None + + def date_extract_sql(self, lookup_type, field_name): + """ + Given a lookup_type of 'year', 'month' or 'day', returns the SQL that + extracts a value from the given date field field_name. + """ + raise NotImplementedError() diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index b9d1bf1ed6..561cd337a6 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -49,7 +49,8 @@ def variantToPython(variant, adType): Database.convertVariantToPython = variantToPython class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + return "DATEPART(%s, %s)" % (lookup_type, field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -88,10 +89,6 @@ def get_last_insert_id(cursor, table_name, pk_name): cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name)) return cursor.fetchone()[0] -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - return "DATEPART(%s, %s)" % (lookup_type, table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' if lookup_type=='year': diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index 4f4a3caa43..245ab9b002 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -44,7 +44,6 @@ dictfetchone = complain dictfetchmany = complain dictfetchall = complain get_last_insert_id = complain -get_date_extract_sql = complain get_date_trunc_sql = complain get_datetime_cast_sql = complain get_limit_offset_sql = complain diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 0051fe2072..e257b39bb8 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -54,7 +54,9 @@ server_version_re = re.compile(r'(\d{1,2})\.(\d{1,2})\.(\d{1,2})') # TRADITIONAL will automatically cause most warnings to be treated as errors. class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html + return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -137,11 +139,6 @@ dictfetchall = util.dictfetchall def get_last_insert_id(cursor, table_name, pk_name): return cursor.lastrowid -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py index cc19782e7d..457b6ef777 100644 --- a/django/db/backends/mysql_old/base.py +++ b/django/db/backends/mysql_old/base.py @@ -64,7 +64,9 @@ class MysqlDebugWrapper: return getattr(self.cursor, attr) class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html + return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -156,11 +158,6 @@ dictfetchall = util.dictfetchall def get_last_insert_id(cursor, table_name, pk_name): return cursor.lastrowid -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html - return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' fields = ['year', 'month', 'day', 'hour', 'minute', 'second'] diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 90efc4578a..7ce5d4485e 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -38,6 +38,10 @@ class DatabaseOperations(BaseDatabaseOperations): END;/""" % (tr_name, quote_name(table), sq_name) return sequence_sql, trigger_sql + def date_extract_sql(self, lookup_type, field_name): + # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 + return "EXTRACT(%s FROM %s)" % (lookup_type, field_name) + class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -153,11 +157,6 @@ def get_last_insert_id(cursor, table_name, pk_name): cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name) return cursor.fetchone()[0] -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 - return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' # Oracle uses TRUNC() for both dates and numbers. diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 1c6f5ec978..fa659b60c0 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -58,7 +58,9 @@ class UnicodeCursorWrapper(object): postgres_version = None class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT + return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -122,11 +124,6 @@ def get_last_insert_id(cursor, table_name, pk_name): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT - return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index cf1d33bd35..9bedfbf077 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -20,7 +20,9 @@ psycopg2.extensions.register_type(psycopg2.extensions.UNICODE) postgres_version = None class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT + return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -76,11 +78,6 @@ def get_last_insert_id(cursor, table_name, pk_name): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT - return "EXTRACT('%s' FROM %s)" % (lookup_type, table_name) - def get_date_trunc_sql(lookup_type, field_name): # lookup_type is 'year', 'month', 'day' # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index 5579d9d9ee..c7e84d7946 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -35,7 +35,10 @@ Database.register_converter("decimal", util.typecast_decimal) Database.register_adapter(decimal.Decimal, util.rev_typecast_decimal) class DatabaseOperations(BaseDatabaseOperations): - pass + def date_extract_sql(self, lookup_type, field_name): + # sqlite doesn't support extract, so we fake it with the user-defined + # function _sqlite_extract that's registered in connect(). + return 'django_extract("%s", %s)' % (lookup_type.lower(), field_name) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -100,12 +103,6 @@ dictfetchall = util.dictfetchall def get_last_insert_id(cursor, table_name, pk_name): return cursor.lastrowid -def get_date_extract_sql(lookup_type, table_name): - # lookup_type is 'year', 'month', 'day' - # sqlite doesn't support extract, so we fake it with the user-defined - # function _sqlite_extract that's registered in connect(), above. - return 'django_extract("%s", %s)' % (lookup_type.lower(), table_name) - def _sqlite_extract(lookup_type, dt): try: dt = util.typecast_timestamp(dt) diff --git a/django/db/models/query.py b/django/db/models/query.py index 4ad3ee5cbb..e576540e53 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -808,7 +808,7 @@ def get_where_clause(lookup_type, table_prefix, field_name, value, db_type): elif lookup_type in ('range', 'year'): return '%s BETWEEN %%s AND %%s' % field_sql elif lookup_type in ('month', 'day'): - return "%s = %%s" % backend.get_date_extract_sql(lookup_type, field_sql) + return "%s = %%s" % connection.ops.date_extract_sql(lookup_type, field_sql) elif lookup_type == 'isnull': return "%s IS %sNULL" % (field_sql, (not value and 'NOT ' or '')) elif lookup_type == 'search':