From 221f99ed5831b71b7ddb810ec8808a884773ef04 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Mon, 20 Aug 2007 01:03:33 +0000 Subject: [PATCH] Refactored quote_name() to DatabaseOperations.quote_name(). Refs #5106 git-svn-id: http://code.djangoproject.com/svn/django/trunk@5967 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/auth/models.py | 17 +++--- django/contrib/contenttypes/generic.py | 10 ++-- .../management/commands/createcachetable.py | 11 ++-- django/core/management/sql.py | 55 ++++++++++-------- django/db/backends/__init__.py | 7 +++ django/db/backends/ado_mssql/base.py | 12 ++-- django/db/backends/dummy/base.py | 1 - django/db/backends/mysql/base.py | 14 ++--- django/db/backends/mysql/introspection.py | 3 +- django/db/backends/mysql_old/base.py | 14 ++--- django/db/backends/mysql_old/introspection.py | 3 +- django/db/backends/oracle/base.py | 44 +++++++------- django/db/backends/oracle/introspection.py | 4 +- django/db/backends/postgresql/base.py | 33 +++++------ .../db/backends/postgresql/introspection.py | 4 +- .../db/backends/postgresql_psycopg2/base.py | 34 +++++------ .../postgresql_psycopg2/introspection.py | 4 +- django/db/backends/sqlite3/base.py | 12 ++-- django/db/backends/sqlite3/introspection.py | 4 +- django/db/models/base.py | 49 +++++++++------- django/db/models/fields/related.py | 9 +-- django/db/models/query.py | 57 +++++++++++-------- django/test/utils.py | 10 ++-- 23 files changed, 224 insertions(+), 187 deletions(-) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index a70227e448..3584cea533 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,6 +1,6 @@ from django.core import validators from django.core.exceptions import ImproperlyConfigured -from django.db import backend, connection, models +from django.db import connection, models from django.contrib.contenttypes.models import ContentType from django.utils.encoding import smart_str from django.utils.translation import ugettext_lazy as _ @@ -203,6 +203,7 @@ class User(models.Model): # AND gp."group_id" = ug."group_id" # AND ct."id" = p."content_type_id" # AND ug."user_id" = %s, [self.id]) + qn = connection.ops.quote_name sql = """ SELECT ct.%s, p.%s FROM %s p, %s gp, %s ug, %s ct @@ -210,13 +211,13 @@ class User(models.Model): AND gp.%s = ug.%s AND ct.%s = p.%s AND ug.%s = %%s""" % ( - backend.quote_name('app_label'), backend.quote_name('codename'), - backend.quote_name('auth_permission'), backend.quote_name('auth_group_permissions'), - backend.quote_name('auth_user_groups'), backend.quote_name('django_content_type'), - backend.quote_name('id'), backend.quote_name('permission_id'), - backend.quote_name('group_id'), backend.quote_name('group_id'), - backend.quote_name('id'), backend.quote_name('content_type_id'), - backend.quote_name('user_id'),) + qn('app_label'), qn('codename'), + qn('auth_permission'), qn('auth_group_permissions'), + qn('auth_user_groups'), qn('django_content_type'), + qn('id'), qn('permission_id'), + qn('group_id'), qn('group_id'), + qn('id'), qn('content_type_id'), + qn('user_id'),) cursor.execute(sql, [self.id]) self._group_perm_cache = set(["%s.%s" % (row[0], row[1]) for row in cursor.fetchall()]) return self._group_perm_cache diff --git a/django/contrib/contenttypes/generic.py b/django/contrib/contenttypes/generic.py index efa49edf73..b738a269ec 100644 --- a/django/contrib/contenttypes/generic.py +++ b/django/contrib/contenttypes/generic.py @@ -4,7 +4,7 @@ Classes allowing "generic" relations through ContentType and object-id fields. from django import oldforms from django.core.exceptions import ObjectDoesNotExist -from django.db import backend +from django.db import connection from django.db.models import signals from django.db.models.fields.related import RelatedField, Field, ManyToManyRel from django.db.models.loading import get_model @@ -163,13 +163,15 @@ class ReverseGenericRelatedObjectsDescriptor(object): superclass = rel_model._default_manager.__class__ RelatedManager = create_generic_related_manager(superclass) + qn = connection.ops.quote_name + manager = RelatedManager( model = rel_model, instance = instance, symmetrical = (self.field.rel.symmetrical and instance.__class__ == rel_model), - join_table = backend.quote_name(self.field.m2m_db_table()), - source_col_name = backend.quote_name(self.field.m2m_column_name()), - target_col_name = backend.quote_name(self.field.m2m_reverse_name()), + join_table = qn(self.field.m2m_db_table()), + source_col_name = qn(self.field.m2m_column_name()), + target_col_name = qn(self.field.m2m_reverse_name()), content_type = ContentType.objects.get_for_model(self.field.model), content_type_field_name = self.field.content_type_field_name, object_id_field_name = self.field.object_id_field_name diff --git a/django/core/management/commands/createcachetable.py b/django/core/management/commands/createcachetable.py index dc8b1b7854..c1fa1a26a5 100644 --- a/django/core/management/commands/createcachetable.py +++ b/django/core/management/commands/createcachetable.py @@ -8,7 +8,7 @@ class Command(LabelCommand): requires_model_validation = False def handle_label(self, tablename, **options): - from django.db import backend, connection, transaction, models + from django.db import connection, transaction, models fields = ( # "key" is a reserved word in MySQL, so use "cache_key" instead. models.CharField(name='cache_key', max_length=255, unique=True, primary_key=True), @@ -17,8 +17,9 @@ class Command(LabelCommand): ) table_output = [] index_output = [] + qn = connection.ops.quote_name for f in fields: - field_output = [backend.quote_name(f.name), f.db_type()] + field_output = [qn(f.name), f.db_type()] field_output.append("%sNULL" % (not f.null and "NOT " or "")) if f.unique: field_output.append("UNIQUE") @@ -27,10 +28,10 @@ class Command(LabelCommand): if f.db_index: unique = f.unique and "UNIQUE " or "" index_output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \ - (unique, tablename, f.name, backend.quote_name(tablename), - backend.quote_name(f.name))) + (unique, tablename, f.name, qn(tablename), + qn(f.name))) table_output.append(" ".join(field_output)) - full_statement = ["CREATE TABLE %s (" % backend.quote_name(tablename)] + full_statement = ["CREATE TABLE %s (" % qn(tablename)] for i, line in enumerate(table_output): full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) full_statement.append(');') diff --git a/django/core/management/sql.py b/django/core/management/sql.py index fc73e6a933..add4632920 100644 --- a/django/core/management/sql.py +++ b/django/core/management/sql.py @@ -116,6 +116,7 @@ def sql_delete(app, style): table_name_converter = lambda x: x output = [] + qn = connection.ops.quote_name # Output DROP TABLE statements for standard application tables. to_delete = set() @@ -136,7 +137,7 @@ def sql_delete(app, style): if cursor and table_name_converter(model._meta.db_table) in table_names: # Drop the table now output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), - style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) + style.SQL_TABLE(qn(model._meta.db_table)))) if backend.supports_constraints and model in references_to_delete: for rel_class, f in references_to_delete[model]: table = rel_class._meta.db_table @@ -146,7 +147,7 @@ def sql_delete(app, style): r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) output.append('%s %s %s %s;' % \ (style.SQL_KEYWORD('ALTER TABLE'), - style.SQL_TABLE(backend.quote_name(table)), + style.SQL_TABLE(qn(table)), style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()), style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length())))) del references_to_delete[model] @@ -159,7 +160,7 @@ def sql_delete(app, style): for f in opts.many_to_many: if cursor and table_name_converter(f.m2m_db_table()) in table_names: output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), - style.SQL_TABLE(backend.quote_name(f.m2m_db_table())))) + style.SQL_TABLE(qn(f.m2m_db_table())))) if hasattr(backend, 'get_drop_sequence'): output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column))) @@ -219,6 +220,7 @@ def sql_model_create(model, style, known_models=set()): final_output = [] table_output = [] pending_references = {} + qn = connection.ops.quote_name for f in opts.fields: col_type = f.db_type() tablespace = f.db_tablespace or opts.db_tablespace @@ -227,7 +229,7 @@ def sql_model_create(model, style, known_models=set()): # database columns in this table. continue # Make the definition (e.g. 'foo VARCHAR(30)') for this field. - field_output = [style.SQL_FIELD(backend.quote_name(f.column)), + field_output = [style.SQL_FIELD(qn(f.column)), style.SQL_COLTYPE(col_type)] field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) if f.unique and (not f.primary_key or backend.allows_unique_and_pk): @@ -241,8 +243,8 @@ def sql_model_create(model, style, known_models=set()): if f.rel: if f.rel.to in known_models: field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ - style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)) + ' (' + \ - style.SQL_FIELD(backend.quote_name(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + + style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \ + style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + connection.ops.deferrable_sql() ) else: @@ -251,14 +253,14 @@ def sql_model_create(model, style, known_models=set()): pr = pending_references.setdefault(f.rel.to, []).append((model, f)) table_output.append(' '.join(field_output)) if opts.order_with_respect_to: - table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \ + table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \ style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ style.SQL_KEYWORD('NULL')) for field_constraints in opts.unique_together: table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ - ", ".join([backend.quote_name(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints])) + ", ".join([qn(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints])) - full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' ('] + full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' ('] for i, line in enumerate(table_output): # Combine and add commas. full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or '')) full_statement.append(')') @@ -283,6 +285,7 @@ def sql_for_pending_references(model, style, pending_references): from django.db import backend, connection from django.db.backends.util import truncate_name + qn = connection.ops.quote_name final_output = [] if backend.supports_constraints: opts = model._meta @@ -297,8 +300,8 @@ def sql_for_pending_references(model, style, pending_references): # So we are careful with character usage here. r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ - (backend.quote_name(r_table), truncate_name(r_name, connection.ops.max_name_length()), - backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), + (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()), + qn(r_col), qn(table), qn(col), connection.ops.deferrable_sql())) del pending_references[model] return final_output @@ -309,6 +312,7 @@ def many_to_many_sql_for_model(model, style): opts = model._meta final_output = [] + qn = connection.ops.quote_name for f in opts.many_to_many: if not isinstance(f.rel, generic.GenericRel): tablespace = f.db_tablespace or opts.db_tablespace @@ -317,30 +321,30 @@ def many_to_many_sql_for_model(model, style): else: tablespace_sql = '' table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ - style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] + style.SQL_TABLE(qn(f.m2m_db_table())) + ' ('] table_output.append(' %s %s %s%s,' % \ - (style.SQL_FIELD(backend.quote_name('id')), + (style.SQL_FIELD(qn('id')), style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), tablespace_sql)) table_output.append(' %s %s %s %s (%s)%s,' % \ - (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), + (style.SQL_FIELD(qn(f.m2m_column_name())), style.SQL_COLTYPE(models.ForeignKey(model).db_type()), style.SQL_KEYWORD('NOT NULL REFERENCES'), - style.SQL_TABLE(backend.quote_name(opts.db_table)), - style.SQL_FIELD(backend.quote_name(opts.pk.column)), + style.SQL_TABLE(qn(opts.db_table)), + style.SQL_FIELD(qn(opts.pk.column)), connection.ops.deferrable_sql())) table_output.append(' %s %s %s %s (%s)%s,' % \ - (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), + (style.SQL_FIELD(qn(f.m2m_reverse_name())), style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), style.SQL_KEYWORD('NOT NULL REFERENCES'), - style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), - style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), + style.SQL_TABLE(qn(f.rel.to._meta.db_table)), + style.SQL_FIELD(qn(f.rel.to._meta.pk.column)), connection.ops.deferrable_sql())) table_output.append(' %s (%s, %s)%s' % \ (style.SQL_KEYWORD('UNIQUE'), - style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), - style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), + style.SQL_FIELD(qn(f.m2m_column_name())), + style.SQL_FIELD(qn(f.m2m_reverse_name())), tablespace_sql)) table_output.append(')') if opts.db_tablespace and backend.supports_tablespaces: @@ -350,7 +354,7 @@ def many_to_many_sql_for_model(model, style): final_output.append('\n'.join(table_output)) # Add any extra SQL needed to support auto-incrementing PKs - autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table()) + autoinc_sql = connection.ops.autoinc_sql(f.m2m_db_table()) if autoinc_sql: for stmt in autoinc_sql: final_output.append(stmt) @@ -389,6 +393,7 @@ def sql_indexes_for_model(model, style): from django.db import backend, connection output = [] + qn = connection.ops.quote_name for f in model._meta.fields: if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys): unique = f.unique and 'UNIQUE ' or '' @@ -399,10 +404,10 @@ def sql_indexes_for_model(model, style): tablespace_sql = '' output.append( style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ - style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ + style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ style.SQL_KEYWORD('ON') + ' ' + \ - style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ - "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ + style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \ + "(%s)" % style.SQL_FIELD(qn(f.column)) + \ "%s;" % tablespace_sql ) return output diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 78dc506add..632dd36639 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -134,6 +134,13 @@ class BaseDatabaseOperations(object): """ return 'DEFAULT' + def quote_name(self, name): + """ + Returns a quoted version of the given table, index or column name. Does + not quote the given name if it's already been quoted. + """ + raise NotImplementedError() + def random_function_sql(self): """ Returns a SQL expression that returns a random value. diff --git a/django/db/backends/ado_mssql/base.py b/django/db/backends/ado_mssql/base.py index 4898d38341..2c6149bf38 100644 --- a/django/db/backends/ado_mssql/base.py +++ b/django/db/backends/ado_mssql/base.py @@ -67,11 +67,16 @@ class DatabaseOperations(BaseDatabaseOperations): cursor.execute("SELECT %s FROM %s WHERE %s = @@IDENTITY" % (pk_name, table_name, pk_name)) return cursor.fetchone()[0] + def quote_name(self, name): + if name.startswith('[') and name.endswith(']'): + return name # Quoting once is enough. + return '[%s]' % name + def random_function_sql(self): return 'RAND()' def tablespace_sql(self, tablespace, inline=False): - return "ON %s" % quote_name(tablespace) + return "ON %s" % self.quote_name(tablespace) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -97,11 +102,6 @@ supports_constraints = True supports_tablespaces = True uses_case_insensitive_names = False -def quote_name(name): - if name.startswith('[') and name.endswith(']'): - return name # Quoting once is enough. - return '[%s]' % name - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall diff --git a/django/db/backends/dummy/base.py b/django/db/backends/dummy/base.py index 9339d87bf3..7c1341b6d6 100644 --- a/django/db/backends/dummy/base.py +++ b/django/db/backends/dummy/base.py @@ -39,7 +39,6 @@ class DatabaseWrapper(object): supports_constraints = False supports_tablespaces = False -quote_name = complain dictfetchone = complain dictfetchmany = complain dictfetchall = complain diff --git a/django/db/backends/mysql/base.py b/django/db/backends/mysql/base.py index 3af055ba8d..124c3c8a33 100644 --- a/django/db/backends/mysql/base.py +++ b/django/db/backends/mysql/base.py @@ -84,6 +84,11 @@ class DatabaseOperations(BaseDatabaseOperations): sql += "%s," % offset return sql + str(limit) + def quote_name(self, name): + if name.startswith("`") and name.endswith("`"): + return name # Quoting once is enough. + return "`%s`" % name + def random_function_sql(self): return 'RAND()' @@ -94,7 +99,7 @@ class DatabaseOperations(BaseDatabaseOperations): if tables: sql = ['SET FOREIGN_KEY_CHECKS = 0;'] for table in tables: - sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(quote_name(table)))) + sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table)))) sql.append('SET FOREIGN_KEY_CHECKS = 1;') # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements @@ -102,7 +107,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.extend(["%s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('TABLE'), - style.SQL_TABLE(quote_name(sequence['table'])), + style.SQL_TABLE(self.quote_name(sequence['table'])), style.SQL_KEYWORD('AUTO_INCREMENT'), style.SQL_FIELD('= 1'), ) for sequence in sequences]) @@ -179,11 +184,6 @@ supports_constraints = True supports_tablespaces = False uses_case_insensitive_names = False -def quote_name(name): - if name.startswith("`") and name.endswith("`"): - return name # Quoting once is enough. - return "`%s`" % name - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall diff --git a/django/db/backends/mysql/introspection.py b/django/db/backends/mysql/introspection.py index 39733311c5..68fe5d99e6 100644 --- a/django/db/backends/mysql/introspection.py +++ b/django/db/backends/mysql/introspection.py @@ -1,8 +1,9 @@ -from django.db.backends.mysql.base import quote_name +from django.db.backends.mysql.base import DatabaseOperations from MySQLdb import ProgrammingError, OperationalError from MySQLdb.constants import FIELD_TYPE import re +quote_name = DatabaseOperations().quote_name foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") def get_table_list(cursor): diff --git a/django/db/backends/mysql_old/base.py b/django/db/backends/mysql_old/base.py index 935f92fcaf..85c1beb558 100644 --- a/django/db/backends/mysql_old/base.py +++ b/django/db/backends/mysql_old/base.py @@ -94,6 +94,11 @@ class DatabaseOperations(BaseDatabaseOperations): sql += "%s," % offset return sql + str(limit) + def quote_name(self, name): + if name.startswith("`") and name.endswith("`"): + return name # Quoting once is enough. + return "`%s`" % name + def random_function_sql(self): return 'RAND()' @@ -104,7 +109,7 @@ class DatabaseOperations(BaseDatabaseOperations): if tables: sql = ['SET FOREIGN_KEY_CHECKS = 0;'] for table in tables: - sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(quote_name(table)))) + sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table)))) sql.append('SET FOREIGN_KEY_CHECKS = 1;') # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements @@ -112,7 +117,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.extend(["%s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('TABLE'), - style.SQL_TABLE(quote_name(sequence['table'])), + style.SQL_TABLE(self.quote_name(sequence['table'])), style.SQL_KEYWORD('AUTO_INCREMENT'), style.SQL_FIELD('= 1'), ) for sequence in sequences]) @@ -198,11 +203,6 @@ supports_constraints = True supports_tablespaces = False uses_case_insensitive_names = False -def quote_name(name): - if name.startswith("`") and name.endswith("`"): - return name # Quoting once is enough. - return "`%s`" % name - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall diff --git a/django/db/backends/mysql_old/introspection.py b/django/db/backends/mysql_old/introspection.py index cb5b8320d9..b910774b95 100644 --- a/django/db/backends/mysql_old/introspection.py +++ b/django/db/backends/mysql_old/introspection.py @@ -1,8 +1,9 @@ -from django.db.backends.mysql_old.base import quote_name +from django.db.backends.mysql_old.base import DatabaseOperations from MySQLdb import ProgrammingError, OperationalError from MySQLdb.constants import FIELD_TYPE import re +quote_name = DatabaseOperations().quote_name foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") def get_table_list(cursor): diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index 0c9a0a55b0..0ad289c2c2 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -35,7 +35,7 @@ class DatabaseOperations(BaseDatabaseOperations): WHEN (new.id IS NULL) BEGIN SELECT %s.nextval INTO :new.id FROM dual; - END;/""" % (tr_name, quote_name(table), sq_name) + END;/""" % (tr_name, self.quote_name(table), sq_name) return sequence_sql, trigger_sql def date_extract_sql(self, lookup_type, field_name): @@ -70,6 +70,15 @@ class DatabaseOperations(BaseDatabaseOperations): def max_name_length(self): return 30 + def quote_name(self, name): + # SQL92 requires delimited (quoted) names to be case-sensitive. When + # not quoted, Oracle has case-insensitive behavior for identifiers, but + # always defaults to uppercase. + # We simplify things by making Oracle identifiers always uppercase. + if not name.startswith('"') and not name.endswith('"'): + name = '"%s"' % util.truncate_name(name.upper(), DatabaseOperations().max_name_length()) + return name.upper() + def random_function_sql(self): return "DBMS_RANDOM.RANDOM" @@ -82,14 +91,14 @@ class DatabaseOperations(BaseDatabaseOperations): sql = ['%s %s %s;' % \ (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) + style.SQL_FIELD(self.quote_name(table)) ) for table in tables] # Since we've just deleted all the rows, running our sequence # ALTER code will reset the sequence to 0. for sequence_info in sequences: table_name = sequence_info['table'] seq_name = get_sequence_name(table_name) - query = _get_sequence_reset_sql() % {'sequence':seq_name, 'table':quote_name(table_name)} + query = _get_sequence_reset_sql() % {'sequence': seq_name, 'table': self.quote_name(table_name)} sql.append(query) return sql else: @@ -116,7 +125,7 @@ class DatabaseOperations(BaseDatabaseOperations): return '' def tablespace_sql(self, tablespace, inline=False): - return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) + return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace)) class DatabaseWrapper(BaseDatabaseWrapper): ops = DatabaseOperations() @@ -215,15 +224,6 @@ def to_unicode(s): return force_unicode(s) return s -def quote_name(name): - # SQL92 requires delimited (quoted) names to be case-sensitive. When - # not quoted, Oracle has case-insensitive behavior for identifiers, but - # always defaults to uppercase. - # We simplify things by making Oracle identifiers always uppercase. - if not name.startswith('"') and not name.endswith('"'): - name = '"%s"' % util.truncate_name(name.upper(), DatabaseOperations().max_name_length()) - return name.upper() - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall @@ -235,7 +235,7 @@ def get_field_cast_sql(db_type): return "%s%s" def get_drop_sequence(table): - return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) + return "DROP SEQUENCE %s;" % DatabaseOperations().quote_name(get_sequence_name(table)) def _get_sequence_reset_sql(): # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. @@ -328,9 +328,10 @@ def get_query_set_class(DefaultQuerySet): handle_legacy_orderlist, orderfield2column opts = self.model._meta + qn = connection.ops.quote_name # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. - select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] + select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] tables = [quote_only_if_word(t) for t in self._tables] joins = SortedDict() where = self._where[:] @@ -348,10 +349,10 @@ def get_query_set_class(DefaultQuerySet): # Add any additional SELECTs. if self._select: - select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) + select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()]) # Start composing the body of the SQL statement. - sql = [" FROM", backend.quote_name(opts.db_table)] + sql = [" FROM", qn(opts.db_table)] # Compose the join dictionary into SQL describing the joins. if joins: @@ -384,15 +385,15 @@ def get_query_set_class(DefaultQuerySet): order = "ASC" if "." in col_name: table_prefix, col_name = col_name.split('.', 1) - table_prefix = backend.quote_name(table_prefix) + '.' + table_prefix = qn(table_prefix) + '.' else: # Use the database table as a column prefix if it wasn't given, # and if the requested column isn't a custom SELECT. if "." not in col_name and col_name not in (self._select or ()): - table_prefix = backend.quote_name(opts.db_table) + '.' + table_prefix = qn(opts.db_table) + '.' else: table_prefix = '' - order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order)) + order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order)) if order_by: sql.append("ORDER BY " + ", ".join(order_by)) @@ -417,8 +418,7 @@ def get_query_set_class(DefaultQuerySet): #Oracle's row_number() function always requires an order-by clause. #So we need to define a default order-by, since none was provided. order_by_clause = " OVER (ORDER BY %s.%s)" % \ - (backend.quote_name(opts.db_table), - backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) + (qn(opts.db_table), qn(opts.fields[0].db_column or opts.fields[0].column)) # limit_and_offset_clause if self._limit is None: assert self._offset is None, "'offset' is not allowed without 'limit'" diff --git a/django/db/backends/oracle/introspection.py b/django/db/backends/oracle/introspection.py index 44430a0029..6f800c8bb6 100644 --- a/django/db/backends/oracle/introspection.py +++ b/django/db/backends/oracle/introspection.py @@ -1,8 +1,8 @@ -from django.db.backends.oracle.base import quote_name +from django.db.backends.oracle.base import DatabaseOperations import re import cx_Oracle - +quote_name = DatabaseOperations().quote_name foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") def get_table_list(cursor): diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py index 9ce5890728..3d7e7515d3 100644 --- a/django/db/backends/postgresql/base.py +++ b/django/db/backends/postgresql/base.py @@ -73,6 +73,11 @@ class DatabaseOperations(BaseDatabaseOperations): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] + def quote_name(self, name): + if name.startswith('"') and name.endswith('"'): + return name # Quoting once is enough. + return '"%s"' % name + def sql_flush(self, style, tables, sequences): if tables: if postgres_version[0] >= 8 and postgres_version[1] >= 1: @@ -82,7 +87,7 @@ class DatabaseOperations(BaseDatabaseOperations): # statement. sql = ['%s %s;' % \ (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) + style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables])) )] else: # Older versions of Postgres can't do TRUNCATE in a single call, so @@ -90,7 +95,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql = ['%s %s %s;' % \ (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) + style.SQL_FIELD(self.quote_name(table)) ) for table in tables] # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements @@ -103,7 +108,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.append("%s %s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), + style.SQL_FIELD(self.quote_name('%s_%s_seq' % (table_name, column_name))), style.SQL_KEYWORD('RESTART'), style.SQL_KEYWORD('WITH'), style.SQL_FIELD('1') @@ -114,7 +119,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.append("%s %s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), + style.SQL_FIELD(self.quote_name('%s_id_seq' % table_name)), style.SQL_KEYWORD('RESTART'), style.SQL_KEYWORD('WITH'), style.SQL_FIELD('1') @@ -127,6 +132,7 @@ class DatabaseOperations(BaseDatabaseOperations): def sequence_reset_sql(self, style, model_list): from django.db import models output = [] + qn = self.quote_name for model in model_list: # Use `coalesce` to set the sequence for each model to the max pk value if there are records, # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true @@ -135,19 +141,19 @@ class DatabaseOperations(BaseDatabaseOperations): if isinstance(f, models.AutoField): output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), - style.SQL_FIELD(quote_name(f.column)), - style.SQL_FIELD(quote_name(f.column)), + style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), + style.SQL_FIELD(qn(f.column)), + style.SQL_FIELD(qn(f.column)), style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('FROM'), - style.SQL_TABLE(quote_name(model._meta.db_table)))) + style.SQL_TABLE(qn(model._meta.db_table)))) break # Only one AutoField is allowed per model, so don't bother continuing. for f in model._meta.many_to_many: output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), - style.SQL_FIELD(quote_name('id')), - style.SQL_FIELD(quote_name('id')), + style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), + style.SQL_FIELD(qn('id')), + style.SQL_FIELD(qn('id')), style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('FROM'), style.SQL_TABLE(f.m2m_db_table()))) @@ -194,11 +200,6 @@ supports_constraints = True supports_tablespaces = False uses_case_insensitive_names = False -def quote_name(name): - if name.startswith('"') and name.endswith('"'): - return name # Quoting once is enough. - return '"%s"' % name - def dictfetchone(cursor): "Returns a row from the cursor as a dict" return cursor.dictfetchone() diff --git a/django/db/backends/postgresql/introspection.py b/django/db/backends/postgresql/introspection.py index 2605490afd..982c004569 100644 --- a/django/db/backends/postgresql/introspection.py +++ b/django/db/backends/postgresql/introspection.py @@ -1,4 +1,6 @@ -from django.db.backends.postgresql.base import quote_name +from django.db.backends.postgresql.base import DatabaseOperations + +quote_name = DatabaseOperations().quote_name def get_table_list(cursor): "Returns a list of table names in the current database." diff --git a/django/db/backends/postgresql_psycopg2/base.py b/django/db/backends/postgresql_psycopg2/base.py index 4bd64cda78..152f31d88c 100644 --- a/django/db/backends/postgresql_psycopg2/base.py +++ b/django/db/backends/postgresql_psycopg2/base.py @@ -35,7 +35,13 @@ class DatabaseOperations(BaseDatabaseOperations): cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name)) return cursor.fetchone()[0] + def quote_name(self, name): + if name.startswith('"') and name.endswith('"'): + return name # Quoting once is enough. + return '"%s"' % name + def sql_flush(self, style, tables, sequences): + qn = self.quote_name if tables: if postgres_version[0] >= 8 and postgres_version[1] >= 1: # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* @@ -44,7 +50,7 @@ class DatabaseOperations(BaseDatabaseOperations): # statement. sql = ['%s %s;' % \ (style.SQL_KEYWORD('TRUNCATE'), - style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) + style.SQL_FIELD(', '.join([qn(table) for table in tables])) )] else: # Older versions of Postgres can't do TRUNCATE in a single call, so @@ -52,7 +58,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql = ['%s %s %s;' % \ (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) + style.SQL_FIELD(qn(table)) ) for table in tables] # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements @@ -65,7 +71,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.append("%s %s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (table_name, column_name))), + style.SQL_FIELD(qn('%s_%s_seq' % (table_name, column_name))), style.SQL_KEYWORD('RESTART'), style.SQL_KEYWORD('WITH'), style.SQL_FIELD('1') @@ -76,7 +82,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql.append("%s %s %s %s %s %s;" % \ (style.SQL_KEYWORD('ALTER'), style.SQL_KEYWORD('SEQUENCE'), - style.SQL_FIELD(quote_name('%s_id_seq' % table_name)), + style.SQL_FIELD(qn('%s_id_seq' % table_name)), style.SQL_KEYWORD('RESTART'), style.SQL_KEYWORD('WITH'), style.SQL_FIELD('1') @@ -88,6 +94,7 @@ class DatabaseOperations(BaseDatabaseOperations): def sequence_reset_sql(self, style, model_list): from django.db import models + qn = self.quote_name output = [] for model in model_list: # Use `coalesce` to set the sequence for each model to the max pk value if there are records, @@ -97,19 +104,19 @@ class DatabaseOperations(BaseDatabaseOperations): if isinstance(f, models.AutoField): output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), - style.SQL_FIELD(quote_name(f.column)), - style.SQL_FIELD(quote_name(f.column)), + style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))), + style.SQL_FIELD(qn(f.column)), + style.SQL_FIELD(qn(f.column)), style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('FROM'), - style.SQL_TABLE(quote_name(model._meta.db_table)))) + style.SQL_TABLE(qn(model._meta.db_table)))) break # Only one AutoField is allowed per model, so don't bother continuing. for f in model._meta.many_to_many: output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ (style.SQL_KEYWORD('SELECT'), - style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), - style.SQL_FIELD(quote_name('id')), - style.SQL_FIELD(quote_name('id')), + style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())), + style.SQL_FIELD(qn('id')), + style.SQL_FIELD(qn('id')), style.SQL_KEYWORD('IS NOT'), style.SQL_KEYWORD('FROM'), style.SQL_TABLE(f.m2m_db_table()))) @@ -156,11 +163,6 @@ supports_constraints = True supports_tablespaces = False uses_case_insensitive_names = False -def quote_name(name): - if name.startswith('"') and name.endswith('"'): - return name # Quoting once is enough. - return '"%s"' % name - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall diff --git a/django/db/backends/postgresql_psycopg2/introspection.py b/django/db/backends/postgresql_psycopg2/introspection.py index a953368991..bf839c3e95 100644 --- a/django/db/backends/postgresql_psycopg2/introspection.py +++ b/django/db/backends/postgresql_psycopg2/introspection.py @@ -1,4 +1,6 @@ -from django.db.backends.postgresql_psycopg2.base import quote_name +from django.db.backends.postgresql_psycopg2.base import DatabaseOperations + +quote_name = DatabaseOperations().quote_name def get_table_list(cursor): "Returns a list of table names in the current database." diff --git a/django/db/backends/sqlite3/base.py b/django/db/backends/sqlite3/base.py index a48ff3a93c..7d291480a5 100644 --- a/django/db/backends/sqlite3/base.py +++ b/django/db/backends/sqlite3/base.py @@ -51,6 +51,11 @@ class DatabaseOperations(BaseDatabaseOperations): def pk_default_value(self): return 'NULL' + def quote_name(self, name): + if name.startswith('"') and name.endswith('"'): + return name # Quoting once is enough. + return '"%s"' % name + def sql_flush(self, style, tables, sequences): # NB: The generated SQL below is specific to SQLite # Note: The DELETE FROM... SQL generated below works for SQLite databases @@ -58,7 +63,7 @@ class DatabaseOperations(BaseDatabaseOperations): sql = ['%s %s %s;' % \ (style.SQL_KEYWORD('DELETE'), style.SQL_KEYWORD('FROM'), - style.SQL_FIELD(quote_name(table)) + style.SQL_FIELD(self.quote_name(table)) ) for table in tables] # Note: No requirement for reset of auto-incremented indices (cf. other # sql_flush() implementations). Just return SQL at this point @@ -115,11 +120,6 @@ supports_constraints = False supports_tablespaces = False uses_case_insensitive_names = False -def quote_name(name): - if name.startswith('"') and name.endswith('"'): - return name # Quoting once is enough. - return '"%s"' % name - dictfetchone = util.dictfetchone dictfetchmany = util.dictfetchmany dictfetchall = util.dictfetchall diff --git a/django/db/backends/sqlite3/introspection.py b/django/db/backends/sqlite3/introspection.py index cb2fbb8ee0..52b880aac2 100644 --- a/django/db/backends/sqlite3/introspection.py +++ b/django/db/backends/sqlite3/introspection.py @@ -1,4 +1,6 @@ -from django.db.backends.sqlite3.base import quote_name +from django.db.backends.sqlite3.base import DatabaseOperations + +quote_name = DatabaseOperations().quote_name def get_table_list(cursor): "Returns a list of table names in the current database." diff --git a/django/db/models/base.py b/django/db/models/base.py index d328bda31e..670eeb4a14 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -207,6 +207,8 @@ class Model(object): non_pks = [f for f in self._meta.fields if not f.primary_key] cursor = connection.cursor() + qn = connection.ops.quote_name + # First, try an UPDATE. If that doesn't update anything, do an INSERT. pk_val = self._get_pk_val() # Note: the comparison with '' is required for compatibility with @@ -216,21 +218,21 @@ class Model(object): if pk_set: # Determine whether a record with the primary key already exists. cursor.execute("SELECT 1 FROM %s WHERE %s=%%s" % \ - (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), + (qn(self._meta.db_table), qn(self._meta.pk.column)), self._meta.pk.get_db_prep_lookup('exact', pk_val)) # If it does already exist, do an UPDATE. if cursor.fetchone(): db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, False)) for f in non_pks] if db_values: cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ - (backend.quote_name(self._meta.db_table), - ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), - backend.quote_name(self._meta.pk.column)), + (qn(self._meta.db_table), + ','.join(['%s=%%s' % qn(f.column) for f in non_pks]), + qn(self._meta.pk.column)), db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)) else: record_exists = False if not pk_set or not record_exists: - field_names = [backend.quote_name(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] + field_names = [qn(f.column) for f in self._meta.fields if not isinstance(f, AutoField)] db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)] # If the PK has been manually set, respect that. if pk_set: @@ -238,20 +240,19 @@ class Model(object): db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)] placeholders = ['%s'] * len(field_names) if self._meta.order_with_respect_to: - field_names.append(backend.quote_name('_order')) + field_names.append(qn('_order')) # TODO: This assumes the database supports subqueries. placeholders.append('(SELECT COUNT(*) FROM %s WHERE %s = %%s)' % \ - (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.order_with_respect_to.column))) + (qn(self._meta.db_table), qn(self._meta.order_with_respect_to.column))) db_values.append(getattr(self, self._meta.order_with_respect_to.attname)) if db_values: cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % \ - (backend.quote_name(self._meta.db_table), ','.join(field_names), + (qn(self._meta.db_table), ','.join(field_names), ','.join(placeholders)), db_values) else: # Create a new record with defaults for everything. cursor.execute("INSERT INTO %s (%s) VALUES (%s)" % - (backend.quote_name(self._meta.db_table), - backend.quote_name(self._meta.pk.column), + (qn(self._meta.db_table), qn(self._meta.pk.column), connection.ops.pk_default_value())) if self._meta.has_auto_field and not pk_set: setattr(self, self._meta.pk.attname, connection.ops.last_insert_id(cursor, self._meta.db_table, self._meta.pk.column)) @@ -326,10 +327,11 @@ class Model(object): return force_unicode(dict(field.choices).get(value, value), strings_only=True) def _get_next_or_previous_by_FIELD(self, field, is_next, **kwargs): + qn = connection.ops.quote_name op = is_next and '>' or '<' where = '(%s %s %%s OR (%s = %%s AND %s.%s %s %%s))' % \ - (backend.quote_name(field.column), op, backend.quote_name(field.column), - backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), op) + (qn(field.column), op, qn(field.column), + qn(self._meta.db_table), qn(self._meta.pk.column), op) param = smart_str(getattr(self, field.attname)) q = self.__class__._default_manager.filter(**kwargs).order_by((not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name) q._where.append(where) @@ -340,14 +342,15 @@ class Model(object): raise self.DoesNotExist, "%s matching query does not exist." % self.__class__._meta.object_name def _get_next_or_previous_in_order(self, is_next): + qn = connection.ops.quote_name cachename = "__%s_order_cache" % is_next if not hasattr(self, cachename): op = is_next and '>' or '<' order_field = self._meta.order_with_respect_to where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \ - (backend.quote_name('_order'), op, backend.quote_name('_order'), - backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), - '%s=%%s' % backend.quote_name(order_field.column)] + (qn('_order'), op, qn('_order'), + qn(self._meta.db_table), qn(self._meta.pk.column)), + '%s=%%s' % qn(order_field.column)] params = [self._get_pk_val(), getattr(self, order_field.attname)] obj = self._default_manager.order_by('_order').extra(where=where, params=params)[:1].get() setattr(self, cachename, obj) @@ -429,24 +432,26 @@ class Model(object): # ORDERING METHODS ######################### def method_set_order(ordered_obj, self, id_list): + qn = connection.ops.quote_name cursor = connection.cursor() # Example: "UPDATE poll_choices SET _order = %s WHERE poll_id = %s AND id = %s" sql = "UPDATE %s SET %s = %%s WHERE %s = %%s AND %s = %%s" % \ - (backend.quote_name(ordered_obj._meta.db_table), backend.quote_name('_order'), - backend.quote_name(ordered_obj._meta.order_with_respect_to.column), - backend.quote_name(ordered_obj._meta.pk.column)) + (qn(ordered_obj._meta.db_table), qn('_order'), + qn(ordered_obj._meta.order_with_respect_to.column), + qn(ordered_obj._meta.pk.column)) rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name) cursor.executemany(sql, [(i, rel_val, j) for i, j in enumerate(id_list)]) transaction.commit_unless_managed() def method_get_order(ordered_obj, self): + qn = connection.ops.quote_name cursor = connection.cursor() # Example: "SELECT id FROM poll_choices WHERE poll_id = %s ORDER BY _order" sql = "SELECT %s FROM %s WHERE %s = %%s ORDER BY %s" % \ - (backend.quote_name(ordered_obj._meta.pk.column), - backend.quote_name(ordered_obj._meta.db_table), - backend.quote_name(ordered_obj._meta.order_with_respect_to.column), - backend.quote_name('_order')) + (qn(ordered_obj._meta.pk.column), + qn(ordered_obj._meta.db_table), + qn(ordered_obj._meta.order_with_respect_to.column), + qn('_order')) rel_val = getattr(self, ordered_obj._meta.order_with_respect_to.rel.field_name) cursor.execute(sql, [rel_val]) return [r[0] for r in cursor.fetchall()] diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index d3603b0016..8d541bd078 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -1,4 +1,4 @@ -from django.db import backend, transaction +from django.db import backend, connection, transaction from django.db.models import signals, get_model from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class from django.db.models.related import RelatedObject @@ -319,7 +319,6 @@ def create_many_related_manager(superclass): # source_col_name: the PK colname in join_table for the source object # target_col_name: the PK colname in join_table for the target object # *objs - objects to add. Either object instances, or primary keys of object instances. - from django.db import connection # If there aren't any objects, there is nothing to do. if objs: @@ -350,7 +349,6 @@ def create_many_related_manager(superclass): # source_col_name: the PK colname in join_table for the source object # target_col_name: the PK colname in join_table for the target object # *objs - objects to remove - from django.db import connection # If there aren't any objects, there is nothing to do. if objs: @@ -371,7 +369,6 @@ def create_many_related_manager(superclass): def _clear_items(self, source_col_name): # source_col_name: the PK colname in join_table for the source object - from django.db import connection cursor = connection.cursor() cursor.execute("DELETE FROM %s WHERE %s = %%s" % \ (self.join_table, source_col_name), @@ -400,7 +397,7 @@ class ManyRelatedObjectsDescriptor(object): superclass = rel_model._default_manager.__class__ RelatedManager = create_many_related_manager(superclass) - qn = backend.quote_name + qn = connection.ops.quote_name manager = RelatedManager( model=rel_model, core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()}, @@ -441,7 +438,7 @@ class ReverseManyRelatedObjectsDescriptor(object): superclass = rel_model._default_manager.__class__ RelatedManager = create_many_related_manager(superclass) - qn = backend.quote_name + qn = connection.ops.quote_name manager = RelatedManager( model=rel_model, core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()}, diff --git a/django/db/models/query.py b/django/db/models/query.py index 151915dd39..8cf4510622 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -64,23 +64,24 @@ def orderfield2column(f, opts): return f def orderlist2sql(order_list, opts, prefix=''): + qn = connection.ops.quote_name if prefix.endswith('.'): - prefix = backend.quote_name(prefix[:-1]) + '.' + prefix = qn(prefix[:-1]) + '.' output = [] for f in handle_legacy_orderlist(order_list): if f.startswith('-'): - output.append('%s%s DESC' % (prefix, backend.quote_name(orderfield2column(f[1:], opts)))) + output.append('%s%s DESC' % (prefix, qn(orderfield2column(f[1:], opts)))) elif f == '?': output.append(connection.ops.random_function_sql()) else: - output.append('%s%s ASC' % (prefix, backend.quote_name(orderfield2column(f, opts)))) + output.append('%s%s ASC' % (prefix, qn(orderfield2column(f, opts)))) return ', '.join(output) def quote_only_if_word(word): if re.search('\W', word): # Don't quote if there are spaces or non-word chars. return word else: - return backend.quote_name(word) + return connection.ops.quote_name(word) class _QuerySet(object): "Represents a lazy database lookup for a set of objects" @@ -235,8 +236,8 @@ class _QuerySet(object): cursor = connection.cursor() if self._distinct: - id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), - backend.quote_name(self.model._meta.pk.column)) + id_col = "%s.%s" % (connection.ops.quote_name(self.model._meta.db_table), + connection.ops.quote_name(self.model._meta.pk.column)) cursor.execute("SELECT COUNT(DISTINCT(%s))" % id_col + sql, params) else: cursor.execute("SELECT COUNT(*)" + sql, params) @@ -308,11 +309,12 @@ class _QuerySet(object): assert self._limit is None and self._offset is None, \ "Cannot use 'limit' or 'offset' with in_bulk" assert isinstance(id_list, (tuple, list)), "in_bulk() must be provided with a list of IDs." + qn = connection.ops.quote_name id_list = list(id_list) if id_list == []: return {} qs = self._clone() - qs._where.append("%s.%s IN (%s)" % (backend.quote_name(self.model._meta.db_table), backend.quote_name(self.model._meta.pk.column), ",".join(['%s'] * len(id_list)))) + qs._where.append("%s.%s IN (%s)" % (qn(self.model._meta.db_table), qn(self.model._meta.pk.column), ",".join(['%s'] * len(id_list)))) qs._params.extend(id_list) return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()]) @@ -481,10 +483,11 @@ class _QuerySet(object): return self._result_cache def _get_sql_clause(self): + qn = connection.ops.quote_name opts = self.model._meta # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. - select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] + select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] tables = [quote_only_if_word(t) for t in self._tables] joins = SortedDict() where = self._where[:] @@ -505,10 +508,10 @@ class _QuerySet(object): # Add any additional SELECTs. if self._select: - select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) + select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in self._select.items()]) # Start composing the body of the SQL statement. - sql = [" FROM", backend.quote_name(opts.db_table)] + sql = [" FROM", qn(opts.db_table)] # Compose the join dictionary into SQL describing the joins. if joins: @@ -541,15 +544,15 @@ class _QuerySet(object): order = "ASC" if "." in col_name: table_prefix, col_name = col_name.split('.', 1) - table_prefix = backend.quote_name(table_prefix) + '.' + table_prefix = qn(table_prefix) + '.' else: # Use the database table as a column prefix if it wasn't given, # and if the requested column isn't a custom SELECT. if "." not in col_name and col_name not in (self._select or ()): - table_prefix = backend.quote_name(opts.db_table) + '.' + table_prefix = qn(opts.db_table) + '.' else: table_prefix = '' - order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order)) + order_by.append('%s%s %s' % (table_prefix, qn(orderfield2column(col_name, opts)), order)) if order_by: sql.append("ORDER BY " + ", ".join(order_by)) @@ -579,6 +582,8 @@ class ValuesQuerySet(QuerySet): except EmptyResultSet: raise StopIteration + qn = connection.ops.quote_name + # self._select is a dictionary, and dictionaries' key order is # undefined, so we convert it to a list of tuples. extra_select = self._select.items() @@ -605,9 +610,9 @@ class ValuesQuerySet(QuerySet): field_names = [f.attname for f in fields] columns = [f.column for f in fields] - select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] + select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns] if extra_select: - select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in extra_select]) + select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select]) field_names.extend([f[0] for f in extra_select]) cursor = connection.cursor() @@ -632,17 +637,19 @@ class DateQuerySet(QuerySet): def iterator(self): from django.db.backends.util import typecast_timestamp from django.db.models.fields import DateTimeField + + qn = connection.ops.quote_name self._order_by = () # Clear this because it'll mess things up otherwise. if self._field.null: self._where.append('%s.%s IS NOT NULL' % \ - (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) + (qn(self.model._meta.db_table), qn(self._field.column))) try: select, sql, params = self._get_sql_clause() except EmptyResultSet: raise StopIteration - table_name = backend.quote_name(self.model._meta.db_table) - field_name = backend.quote_name(self._field.column) + table_name = qn(self.model._meta.db_table) + field_name = qn(self._field.column) if backend.allows_group_by_ordinal: group_by = '1' @@ -650,8 +657,8 @@ class DateQuerySet(QuerySet): group_by = connection.ops.date_trunc_sql(self._kind, '%s.%s' % (table_name, field_name)) sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ - (connection.ops.date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), - backend.quote_name(self._field.column))), sql, group_by, self._order) + (connection.ops.date_trunc_sql(self._kind, '%s.%s' % (qn(self.model._meta.db_table), + qn(self._field.column))), sql, group_by, self._order) cursor = connection.cursor() cursor.execute(sql, params) @@ -778,8 +785,8 @@ class QNot(Q): def get_where_clause(lookup_type, table_prefix, field_name, value, db_type): if table_prefix.endswith('.'): - table_prefix = backend.quote_name(table_prefix[:-1])+'.' - field_name = backend.quote_name(field_name) + table_prefix = connection.ops.quote_name(table_prefix[:-1])+'.' + field_name = connection.ops.quote_name(field_name) if type(value) == datetime.datetime and connection.ops.datetime_cast_sql(): cast_sql = connection.ops.datetime_cast_sql() else: @@ -849,7 +856,7 @@ def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen, if max_depth and cur_depth > max_depth: return None - qn = backend.quote_name + qn = connection.ops.quote_name for f in opts.fields: if f.rel and not f.null: db_table = f.rel.to._meta.db_table @@ -947,7 +954,7 @@ def field_choices(field_list, related_query): return choices def lookup_inner(path, lookup_type, value, opts, table, column): - qn = backend.quote_name + qn = connection.ops.quote_name joins, where, params = SortedDict(), [], [] current_opts = opts current_table = table @@ -1113,7 +1120,7 @@ def lookup_inner(path, lookup_type, value, opts, table, column): def delete_objects(seen_objs): "Iterate through a list of seen classes, and remove any instances that are referred to" - qn = backend.quote_name + qn = connection.ops.quote_name ordered_classes = seen_objs.keys() ordered_classes.reverse() diff --git a/django/test/utils.py b/django/test/utils.py index bc53ef4bfb..79aaa6d2c4 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -118,13 +118,15 @@ def create_test_db(verbosity=1, autoclobber=False): else: TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + qn = connection.ops.quote_name + # Create the test database and connect to it. We need to autocommit # if the database supports it because PostgreSQL doesn't allow # CREATE/DROP DATABASE statements within transactions. cursor = connection.cursor() _set_autocommit(connection) try: - cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix)) + cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix)) except Exception, e: sys.stderr.write("Got an error creating the test database: %s\n" % e) if not autoclobber: @@ -133,10 +135,10 @@ def create_test_db(verbosity=1, autoclobber=False): try: if verbosity >= 1: print "Destroying old test database..." - cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) + cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME)) if verbosity >= 1: print "Creating test database..." - cursor.execute("CREATE DATABASE %s %s" % (backend.quote_name(TEST_DATABASE_NAME), suffix)) + cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix)) except Exception, e: sys.stderr.write("Got an error recreating the test database: %s\n" % e) sys.exit(2) @@ -180,5 +182,5 @@ def destroy_test_db(old_database_name, verbosity=1): cursor = connection.cursor() _set_autocommit(connection) time.sleep(1) # To avoid "database is being accessed by other users" errors. - cursor.execute("DROP DATABASE %s" % backend.quote_name(TEST_DATABASE_NAME)) + cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME)) connection.close()