Ticket #6148: 6148_django1.5.diff

File 6148_django1.5.diff, 194.6 KB (added by Anssi Kääriäinen, 12 years ago)
  • django/conf/global_settings.py

     
    407407DEFAULT_TABLESPACE = ''
    408408DEFAULT_INDEX_TABLESPACE = ''
    409409
     410# The default database schema to use for tables
     411DEFAULT_SCHEMA = None
     412
    410413# Default X-Frame-Options header value
    411414X_FRAME_OPTIONS = 'SAMEORIGIN'
    412415
  • django/db/models/fields/related.py

     
    11from operator import attrgetter
    22
    3 from django.db import connection, router
     3from django.db import connection, router, QName
    44from django.db.backends import util
    55from django.db.models import signals, get_model
    66from django.db.models.fields import (AutoField, Field, IntegerField,
     
    568568            # dealing with PK values.
    569569            fk = self.through._meta.get_field(self.source_field_name)
    570570            source_col = fk.column
    571             join_table = self.through._meta.db_table
    572571            connection = connections[db]
    573572            qn = connection.ops.quote_name
     573            join_alias = connection.qname(self.through)
    574574            qs = qs.extra(select={'_prefetch_related_val':
    575                                       '%s.%s' % (qn(join_table), qn(source_col))})
     575                                      '%s.%s' % (join_alias, qn(source_col))})
    576576            select_attname = fk.rel.get_related_field().get_attname()
    577577            return (qs,
    578578                    attrgetter('_prefetch_related_val'),
     
    10851085        to = to.lower()
    10861086    meta = type('Meta', (object,), {
    10871087        'db_table': field._get_m2m_db_table(klass._meta),
     1088        'db_schema': field._get_m2m_db_schema(klass._meta),
    10881089        'managed': managed,
    10891090        'auto_created': klass,
    10901091        'app_label': klass._meta.app_label,
     
    11211122            through=kwargs.pop('through', None))
    11221123
    11231124        self.db_table = kwargs.pop('db_table', None)
     1125        self.db_schema = kwargs.pop('db_schema', None)
    11241126        if kwargs['rel'].through is not None:
    11251127            assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
     1128            assert self.db_schema is None, "Cannot specify a db_schema if an intermediary model is used."
     1129        # TODO: by default use the containing model's db_schema
     1130           
    11261131
    11271132        Field.__init__(self, **kwargs)
    11281133
     
    11421147            return util.truncate_name('%s_%s' % (opts.db_table, self.name),
    11431148                                      connection.ops.max_name_length())
    11441149
     1150    def _get_m2m_db_schema(self, opts):
     1151        "Function that can be curried to provide the m2m schema name for this relation"
     1152        if self.rel.through is not None and self.rel.through._meta.db_schema:
     1153            return self.rel.through._meta.db_schema
     1154        elif self.db_schema:
     1155            return self.db_schema
     1156        else:
     1157            return opts.db_schema
     1158
     1159
     1160    def _get_m2m_qualified_name(self, opts):
     1161        "Function that can be curried to provide the qualified m2m table name for this relation"
     1162        schema = self._get_m2m_db_schema(opts)
     1163        table = self._get_m2m_db_table(opts)
     1164        return QName(schema, table, False)
     1165
    11451166    def _get_m2m_attr(self, related, attr):
    11461167        "Function that can be curried to provide the source accessor or DB column name for the m2m table"
    11471168        cache_attr = '_m2m_%s_cache' % attr
     
    12121233
    12131234        # Set up the accessor for the m2m table name for the relation
    12141235        self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
     1236        self.m2m_db_schema = curry(self._get_m2m_db_schema, cls._meta)
     1237        self.m2m_qualified_name = curry(self._get_m2m_qualified_name,
     1238                                        cls._meta)
    12151239
    12161240        # Populate some necessary rel arguments so that cross-app relations
    12171241        # work correctly.
     
    12231247        if isinstance(self.rel.to, basestring):
    12241248            target = self.rel.to
    12251249        else:
    1226             target = self.rel.to._meta.db_table
     1250            target = self.rel.to._meta.qualified_name
    12271251        cls._meta.duplicate_targets[self.column] = (target, "m2m")
    12281252
    12291253    def contribute_to_related_class(self, cls, related):
  • django/db/models/query.py

     
    15581558        A dict mapping column names to model field names.
    15591559        """
    15601560        if not hasattr(self, '_model_fields'):
    1561             converter = connections[self.db].introspection.table_name_converter
     1561            converter = connections[self.db].introspection.identifier_converter
    15621562            self._model_fields = {}
    15631563            for field in self.model._meta.fields:
    15641564                name, column = field.get_attname_column()
  • django/db/models/sql/query.py

     
    1616from django.db.models import signals
    1717from django.db.models.expressions import ExpressionNode
    1818from django.db.models.fields import FieldDoesNotExist
    19 from django.db.models.query_utils import InvalidQuery
    2019from django.db.models.sql import aggregates as base_aggregates_module
    2120from django.db.models.sql.constants import *
    2221from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
     
    5958    def get_columns(self):
    6059        if self.cursor is None:
    6160            self._execute_query()
    62         converter = connections[self.using].introspection.table_name_converter
     61        converter = connections[self.using].introspection.identifier_converter
    6362        return [converter(column_meta[0])
    6463                for column_meta in self.cursor.description]
    6564
     
    638637        Callback used by deferred_to_columns(). The "target" parameter should
    639638        be a set instance.
    640639        """
    641         table = model._meta.db_table
     640        table = model._meta.qualified_name
    642641        if table not in target:
    643642            target[table] = set()
    644643        for field in fields:
     
    659658            self.alias_refcount[alias] += 1
    660659            return alias, False
    661660
    662         # Create a new alias for this table.
    663         if current:
     661        # Create a new alias for this table if this table is already used
     662        # in the query, or if there is the same table name used in the query
     663        # under different schema qualified name.
     664        if current or table_name[1] in [t[1] for t in self.tables]:
    664665            alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1)
    665             current.append(alias)
     666            if current:
     667                current.append(alias)
    666668        else:
    667669            # The first occurence of a table uses the table name directly.
    668670            alias = table_name
     
    832834            alias = self.tables[0]
    833835            self.ref_alias(alias)
    834836        else:
    835             alias = self.join((None, self.model._meta.db_table, None, None))
     837            alias = self.join((None, self.model._meta.qualified_name, None, None))
    836838        return alias
    837839
    838840    def count_active_tables(self):
     
    945947                    seen[model] = root_alias
    946948                else:
    947949                    link_field = opts.get_ancestor_link(model)
    948                     seen[model] = self.join((root_alias, model._meta.db_table,
     950                    seen[model] = self.join((root_alias, model._meta.qualified_name,
    949951                            link_field.column, model._meta.pk.column))
    950952        self.included_inherited_models = seen
    951953
     
    13291331                                    (id(opts), lhs_col), ()))
    13301332                            dupe_set.add((opts, lhs_col))
    13311333                        opts = int_model._meta
    1332                         alias = self.join((alias, opts.db_table, lhs_col,
     1334                        alias = self.join((alias, opts.qualified_name, lhs_col,
    13331335                                opts.pk.column), exclusions=exclusions)
    13341336                        joins.append(alias)
    13351337                        exclusions.add(alias)
     
    13551357                        (table1, from_col1, to_col1, table2, from_col2,
    13561358                                to_col2, opts, target) = cached_data
    13571359                    else:
    1358                         table1 = field.m2m_db_table()
     1360                        table1 = field.m2m_qualified_name()
    13591361                        from_col1 = opts.get_field_by_name(
    13601362                            field.m2m_target_field_name())[0].column
    13611363                        to_col1 = field.m2m_column_name()
    13621364                        opts = field.rel.to._meta
    1363                         table2 = opts.db_table
     1365                        table2 = opts.qualified_name
    13641366                        from_col2 = field.m2m_reverse_name()
    13651367                        to_col2 = opts.get_field_by_name(
    13661368                            field.m2m_reverse_target_field_name())[0].column
     
    13881390                    else:
    13891391                        opts = field.rel.to._meta
    13901392                        target = field.rel.get_related_field()
    1391                         table = opts.db_table
     1393                        table = opts.qualified_name
    13921394                        from_col = field.column
    13931395                        to_col = target.column
    13941396                        orig_opts._join_cache[name] = (table, from_col, to_col,
     
    14101412                        (table1, from_col1, to_col1, table2, from_col2,
    14111413                                to_col2, opts, target) = cached_data
    14121414                    else:
    1413                         table1 = field.m2m_db_table()
     1415                        table1 = field.m2m_qualified_name()
    14141416                        from_col1 = opts.get_field_by_name(
    14151417                            field.m2m_reverse_target_field_name())[0].column
    14161418                        to_col1 = field.m2m_reverse_name()
    14171419                        opts = orig_field.opts
    1418                         table2 = opts.db_table
     1420                        table2 = opts.qualified_name
    14191421                        from_col2 = field.m2m_column_name()
    14201422                        to_col2 = opts.get_field_by_name(
    14211423                            field.m2m_target_field_name())[0].column
     
    14391441                        local_field = opts.get_field_by_name(
    14401442                                field.rel.field_name)[0]
    14411443                        opts = orig_field.opts
    1442                         table = opts.db_table
     1444                        table = opts.qualified_name
    14431445                        from_col = local_field.column
    14441446                        to_col = field.column
    14451447                        # In case of a recursive FK, use the to_field for
     
    17221724        else:
    17231725            opts = self.model._meta
    17241726            if not self.select:
    1725                 count = self.aggregates_module.Count((self.join((None, opts.db_table, None, None)), opts.pk.column),
     1727                count = self.aggregates_module.Count((self.join((None, opts.qualified_name, None, None)), opts.pk.column),
    17261728                                         is_summary=True, distinct=True)
    17271729            else:
    17281730                # Because of SQL portability issues, multi-column, distinct
  • django/db/models/sql/subqueries.py

     
    4141            where = self.where_class()
    4242            where.add((Constraint(None, field.column, field), 'in',
    4343                    pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]), AND)
    44             self.do_query(self.model._meta.db_table, where, using=using)
     44            self.do_query(self.model._meta.qualified_name, where, using=using)
    4545
    4646class UpdateQuery(Query):
    4747    """
  • django/db/models/sql/compiler.py

     
    11from itertools import izip
    22
     3from django.conf import settings
    34from django.core.exceptions import FieldError
    4 from django.db import transaction
     5from django.db import transaction, QName
    56from django.db.backends.util import truncate_name
    67from django.db.models.query_utils import select_related_descend
    78from django.db.models.sql.constants import *
     
    2728        # cleaned. We are not using a clone() of the query here.
    2829        """
    2930        if not self.query.tables:
    30             self.query.join((None, self.query.model._meta.db_table, None, None))
     31            self.query.join((None, self.query.model._meta.qualified_name, None, None))
    3132        if (not self.query.select and self.query.default_cols and not
    3233                self.query.included_inherited_models):
    3334            self.query.setup_inherited_models()
     
    4243        """
    4344        if name in self.quote_cache:
    4445            return self.quote_cache[name]
    45         if ((name in self.query.alias_map and name not in self.query.table_map) or
    46                 name in self.query.extra_select):
     46        if name in self.query.alias_map and not isinstance(name, tuple):
    4747            self.quote_cache[name] = name
    4848            return name
    49         r = self.connection.ops.quote_name(name)
     49        if isinstance(name, tuple):
     50            r = self.connection.ops.qualified_name(name)
     51        else:
     52            r = self.connection.ops.quote_name(name)
    5053        self.quote_cache[name] = r
    5154        return r
    5255
     
    281284                        alias = start_alias
    282285                    else:
    283286                        link_field = opts.get_ancestor_link(model)
    284                         alias = self.query.join((start_alias, model._meta.db_table,
    285                                 link_field.column, model._meta.pk.column))
     287                        alias = self.query.join((start_alias,
     288                                                 model._meta.qualified_name,
     289                                                 link_field.column,
     290                                                 model._meta.pk.column))
    286291                    seen[model] = alias
    287292            else:
    288293                # If we're starting from the base model of the queryset, the
     
    508513        qn2 = self.connection.ops.quote_name
    509514        first = True
    510515        for alias in self.query.tables:
    511             if not self.query.alias_refcount[alias]:
     516            # Extra tables can end up in self.tables, but not in the
     517            # alias_map if they aren't in a join. That's OK. We skip them.
     518            if not self.query.alias_refcount[alias] or alias not in self.query.alias_map:
    512519                continue
    513             try:
    514                 name, alias, join_type, lhs, lhs_col, col, nullable = self.query.alias_map[alias]
    515             except KeyError:
    516                 # Extra tables can end up in self.tables, but not in the
    517                 # alias_map if they aren't in a join. That's OK. We skip them.
    518                 continue
    519             alias_str = (alias != name and ' %s' % alias or '')
     520            name, alias, join_type, lhs, lhs_col, col, nullable = self.query.alias_map[alias]
     521            alias_str = alias != name and ' %s' % qn(alias) or ''
    520522            if join_type and not first:
    521523                result.append('%s %s%s ON (%s.%s = %s.%s)'
    522524                        % (join_type, qn(name), alias_str, qn(lhs),
     
    526528                result.append('%s%s%s' % (connector, qn(name), alias_str))
    527529            first = False
    528530        for t in self.query.extra_tables:
    529             alias, unused = self.query.table_alias(t)
    530             # Only add the alias if it's not already present (the table_alias()
    531             # calls increments the refcount, so an alias refcount of one means
    532             # this is the only reference.
    533             if alias not in self.query.alias_map or self.query.alias_refcount[alias] == 1:
     531            # Plain string table names are assumed to be in default schema
     532            if not isinstance(t, QName):
     533                t = QName(self.connection.schema, t, False)
     534            # Only add the table if it is not already in the query.
     535            if t not in self.query.table_map or self.query.alias_refcount[t] == 0:
     536                # This will add the table into the query properly, however we
     537                # are not interested in the alias it gets, we add it as a
     538                # plain table.
     539                self.query.table_alias(t)
    534540                connector = not first and ', ' or ''
    535                 result.append('%s%s' % (connector, qn(alias)))
     541                result.append('%s%s' % (connector, qn(t)))
    536542                first = False
    537543        return result, []
    538544
     
    546552            if (len(self.query.model._meta.fields) == len(self.query.select) and
    547553                self.connection.features.allows_group_by_pk):
    548554                self.query.group_by = [
    549                     (self.query.model._meta.db_table, self.query.model._meta.pk.column)
     555                    (self.query.model._meta.qualified_name, self.query.model._meta.pk.column)
    550556                ]
    551557
    552558            group_by = self.query.group_by or []
     
    614620            # what "used" specifies).
    615621            avoid = avoid_set.copy()
    616622            dupe_set = orig_dupe_set.copy()
    617             table = f.rel.to._meta.db_table
     623            table = f.rel.to._meta.qualified_name
    618624            promote = nullable or f.null
    619625            if model:
    620626                int_opts = opts
     
    635641                                ()))
    636642                        dupe_set.add((opts, lhs_col))
    637643                    int_opts = int_model._meta
    638                     alias = self.query.join((alias, int_opts.db_table, lhs_col,
     644                    alias = self.query.join((alias, int_opts.qualified_name, lhs_col,
    639645                            int_opts.pk.column), exclusions=used,
    640646                            promote=promote)
    641647                    alias_chain.append(alias)
     
    687693                # what "used" specifies).
    688694                avoid = avoid_set.copy()
    689695                dupe_set = orig_dupe_set.copy()
    690                 table = model._meta.db_table
     696                table = model._meta.qualified_name
    691697
    692698                int_opts = opts
    693699                alias = root_alias
     
    710716                            dupe_set.add((opts, lhs_col))
    711717                        int_opts = int_model._meta
    712718                        alias = self.query.join(
    713                             (alias, int_opts.db_table, lhs_col, int_opts.pk.column),
     719                            (alias, int_opts.qualified_name, lhs_col, int_opts.pk.column),
    714720                            exclusions=used, promote=True, reuse=used
    715721                        )
    716722                        alias_chain.append(alias)
     
    775781                        # into `resolve_columns` because it wasn't selected.
    776782                        only_load = self.deferred_to_columns()
    777783                        if only_load:
    778                             db_table = self.query.model._meta.db_table
     784                            db_table = self.query.model._meta.qualified_name
    779785                            fields = [f for f in fields if db_table in only_load and
    780786                                      f.column in only_load[db_table]]
    781787                    row = self.resolve_columns(row, fields)
     
    856862        # We don't need quote_name_unless_alias() here, since these are all
    857863        # going to be column names (so we can avoid the extra overhead).
    858864        qn = self.connection.ops.quote_name
     865        qn3 = self.connection.ops.qualified_name
    859866        opts = self.query.model._meta
    860         result = ['INSERT INTO %s' % qn(opts.db_table)]
     867        result = ['INSERT INTO %s' % qn3(opts.qualified_name)]
    861868
    862869        has_fields = bool(self.query.fields)
    863870        fields = self.query.fields if has_fields else [opts.pk]
     
    887894            ]
    888895        if self.return_id and self.connection.features.can_return_id_from_insert:
    889896            params = params[0]
    890             col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
     897            col = "%s.%s" % (qn3(opts.qualified_name), qn(opts.pk.column))
    891898            result.append("VALUES (%s)" % ", ".join(placeholders[0]))
    892899            r_fmt, r_params = self.connection.ops.return_insert_id()
    893900            result.append(r_fmt % col)
     
    913920        if self.connection.features.can_return_id_from_insert:
    914921            return self.connection.ops.fetch_returned_insert_id(cursor)
    915922        return self.connection.ops.last_insert_id(cursor,
    916                 self.query.model._meta.db_table, self.query.model._meta.pk.column)
     923                self.query.model._meta.qualified_name, self.query.model._meta.pk.column)
    917924
    918925
    919926class SQLDeleteCompiler(SQLCompiler):
     
    939946        self.pre_sql_setup()
    940947        if not self.query.values:
    941948            return '', ()
    942         table = self.query.tables[0]
     949        opts = self.query.model._meta
     950        table = self.connection.ops.qualified_name(opts.qualified_name)
    943951        qn = self.quote_name_unless_alias
    944         result = ['UPDATE %s' % qn(table)]
     952        alias = qn(self.query.tables[0])
     953        if table == alias:
     954            alias = ''
     955        else:
     956            alias = ' %s' % alias
     957        result = ['UPDATE %s%s' % (table, alias)]
    945958        result.append('SET')
    946959        values, update_params = [], []
    947960        for field, model, val in self.query.values:
  • django/db/models/options.py

     
    22from bisect import bisect
    33
    44from django.conf import settings
     5from django.db import QName
    56from django.db.models.related import RelatedObject
    67from django.db.models.fields.related import ManyToManyRel
    78from django.db.models.fields import AutoField, FieldDoesNotExist
     
    1718DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
    1819                 'unique_together', 'permissions', 'get_latest_by',
    1920                 'order_with_respect_to', 'app_label', 'db_tablespace',
    20                  'abstract', 'managed', 'proxy', 'auto_created')
     21                 'abstract', 'managed', 'proxy', 'auto_created', 'db_schema')
    2122
    2223class Options(object):
    2324    def __init__(self, meta, app_label=None):
     
    2627        self.module_name, self.verbose_name = None, None
    2728        self.verbose_name_plural = None
    2829        self.db_table = ''
     30        self.db_schema = ''
     31        self.qualified_name = QName(schema=None, table='', db_format=False)
    2932        self.ordering = []
    3033        self.unique_together =  []
    3134        self.permissions =  []
     
    112115        # If the db_table wasn't provided, use the app_label + module_name.
    113116        if not self.db_table:
    114117            self.db_table = "%s_%s" % (self.app_label, self.module_name)
     118            # TODO: Using connection.ops is wrong: in multidb setup this doesn't work
     119            # correctly except if different connections happen to have the same
     120            # max_name_length.
    115121            self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
     122        self.qualified_name = QName(schema=self.db_schema, table=self.db_table,
     123                                    db_format=False)
    116124
    117125    def _prepare(self, model):
    118126        if self.order_with_respect_to:
     
    193201        self.pk = target._meta.pk
    194202        self.proxy_for_model = target
    195203        self.db_table = target._meta.db_table
     204        self.db_schema = target._meta.db_schema
     205        self.qualified_name = target._meta.qualified_name
    196206
    197207    def __repr__(self):
    198208        return '<Options for %s>' % self.object_name
  • django/db/__init__.py

     
    33from django.core.exceptions import ImproperlyConfigured
    44from django.db.utils import (ConnectionHandler, ConnectionRouter,
    55    load_backend, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError)
     6from collections import namedtuple
    67
     8# All table names must be QNames. The db_format argument
     9# tells if the qualified name is in a format that is ready
     10# to be used in the database or if the qname needs to be
     11# converted first.
     12QName = namedtuple("QName", "schema table db_format")
     13
    714__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
    815    'IntegrityError', 'DEFAULT_DB_ALIAS')
    916
    10 
    1117if DEFAULT_DB_ALIAS not in settings.DATABASES:
    1218    raise ImproperlyConfigured("You must define a '%s' database" % DEFAULT_DB_ALIAS)
    1319
  • django/db/utils.py

     
    7878            conn['ENGINE'] = 'django.db.backends.dummy'
    7979        conn.setdefault('OPTIONS', {})
    8080        conn.setdefault('TIME_ZONE', 'UTC' if settings.USE_TZ else settings.TIME_ZONE)
    81         for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']:
     81        conn.setdefault('SCHEMA', settings.DEFAULT_SCHEMA)
     82        for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT', 'SCHEMA']:
    8283            conn.setdefault(setting, '')
    8384        for setting in ['TEST_CHARSET', 'TEST_COLLATION', 'TEST_NAME', 'TEST_MIRROR']:
    8485            conn.setdefault(setting, None)
     86        # Some databases need a unique prefix for schemas to avoid name
     87        # collisions between production database or another testing database
     88        # in the same instance. This needs to be a string unlike the other
     89        # TEST_ settings above.
     90        conn.setdefault('TEST_SCHEMA_PREFIX', '')
     91        conn.setdefault('TEST_SCHEMAS', [])
    8592
    8693    def __getitem__(self, alias):
    8794        if hasattr(self._connections, alias):
  • django/db/backends/postgresql_psycopg2/introspection.py

     
     1from django.db import QName
    12from django.db.backends import BaseDatabaseIntrospection
    23
    34
     
    2122        1266: 'TimeField',
    2223        1700: 'DecimalField',
    2324    }
     25
     26    def get_schema_list(self, cursor):
     27        cursor.execute("""
     28            SELECT n.nspname
     29            FROM pg_catalog.pg_namespace n
     30            WHERE n.nspname != 'information_schema' AND n.nspname not like 'pg_%%'""")
     31        return [row[0] for row in cursor.fetchall()]
    2432       
    25     def get_table_list(self, cursor):
    26         "Returns a list of table names in the current database."
     33    def get_visible_tables_list(self, cursor):
     34        """
     35        Returns a list of all by-default visible table names in the current
     36        database.
     37        """
     38        sql = """
     39            SELECT n.nspname, c.relname
     40            FROM pg_catalog.pg_class c
     41            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
     42            WHERE c.relkind IN ('r', 'v', '')
     43                AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
     44                AND (pg_catalog.pg_table_is_visible(c.oid)"""
     45        # We must add the default schema to always visible schemas to make
     46        # things work nicely.
     47        if self.connection.schema:
     48            sql += " OR n.nspname = %s)"
     49            cursor.execute(sql, (self.connection.schema,))
     50        else:
     51            cursor.execute(sql + ')')
     52        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
     53   
     54    def get_qualified_tables_list(self, cursor, schemas):
     55        """
     56        Returns schema qualified names of all tables in the current database.
     57        """
     58        if not schemas:
     59            return []
    2760        cursor.execute("""
    28             SELECT c.relname
     61            SELECT n.nspname, c.relname
    2962            FROM pg_catalog.pg_class c
    3063            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    3164            WHERE c.relkind IN ('r', 'v', '')
    32                 AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
    33                 AND pg_catalog.pg_table_is_visible(c.oid)""")
    34         return [row[0] for row in cursor.fetchall()]
     65                AND n.nspname IN %s""", (tuple(schemas),))
     66        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
    3567
    36     def get_table_description(self, cursor, table_name):
     68    def get_table_description(self, cursor, qname):
    3769        "Returns a description of the table, with the DB-API cursor.description interface."
    3870        # As cursor.description does not return reliably the nullable property,
    3971        # we have to query the information_schema (#7783)
    40         cursor.execute("""
    41             SELECT column_name, is_nullable
    42             FROM information_schema.columns
    43             WHERE table_name = %s""", [table_name])
     72        qname = self.qname_converter(qname)
     73        if not qname.schema:
     74            cursor.execute("""
     75                SELECT column_name, is_nullable
     76                FROM information_schema.columns
     77                WHERE table_name = %s""",
     78                [qname.table])
     79        else:
     80            cursor.execute("""
     81                SELECT column_name, is_nullable
     82                FROM information_schema.columns
     83                WHERE table_schema = %s and table_name = %s""",
     84                [qname.schema, qname.table])
    4485        null_map = dict(cursor.fetchall())
    45         cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
     86        cursor.execute(
     87            "SELECT * FROM %s LIMIT 1" % self.connection.ops.qualified_name(qname))
    4688        return [tuple([item for item in line[:6]] + [null_map[line[0]]==u'YES'])
    47             for line in cursor.description]
     89                for line in cursor.description]
    4890
    49     def get_relations(self, cursor, table_name):
     91    def get_relations(self, cursor, qname):
    5092        """
    5193        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    52         representing all relationships to the given table. Indexes are 0-based.
     94        representing all relationships to the given table. Indexes are 0-based. The
     95        other_table will be in qualified format.
    5396        """
    54         cursor.execute("""
    55             SELECT con.conkey, con.confkey, c2.relname
    56             FROM pg_constraint con, pg_class c1, pg_class c2
    57             WHERE c1.oid = con.conrelid
    58                 AND c2.oid = con.confrelid
    59                 AND c1.relname = %s
    60                 AND con.contype = 'f'""", [table_name])
     97        qname = self.qname_converter(qname)
     98        if not qname.schema:
     99            cursor.execute("""
     100                SELECT con.conkey, con.confkey, nsp2.nspname, c2.relname
     101                FROM pg_constraint con, pg_class c1, pg_class c2,
     102                     pg_namespace nsp2
     103                WHERE c1.oid = con.conrelid
     104                    AND c2.oid = con.confrelid
     105                    AND nsp2.oid = c2.relnamespace
     106                    AND c1.relname = %s
     107                    AND con.contype = 'f'""",
     108                [qname.table])
     109        else:
     110            cursor.execute("""
     111                SELECT con.conkey, con.confkey, nsp2.nspname, c2.relname
     112                FROM pg_constraint con, pg_class c1, pg_class c2,
     113                     pg_namespace nsp1, pg_namespace nsp2
     114                WHERE c1.oid = con.conrelid
     115                    AND nsp1.oid = c1.relnamespace
     116                    AND c2.oid = con.confrelid
     117                    AND nsp2.oid = c2.relnamespace
     118                    AND nsp1.nspname = %s
     119                    AND c1.relname = %s
     120                    AND con.contype = 'f'""",
     121                [qname.schema, qname.table])
    61122        relations = {}
    62123        for row in cursor.fetchall():
    63124            # row[0] and row[1] are single-item lists, so grab the single item.
    64             relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
     125            relations[row[0][0] - 1] = (row[1][0] - 1,
     126                                        QName(row[2], row[3], True))
    65127        return relations
    66128
    67     def get_indexes(self, cursor, table_name):
     129    def get_indexes(self, cursor, qname):
    68130        """
    69131        Returns a dictionary of fieldname -> infodict for the given table,
    70132        where each infodict is in the format:
     
    73135        """
    74136        # This query retrieves each index on the given table, including the
    75137        # first associated field name
    76         cursor.execute("""
    77             SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
    78             FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
    79                 pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
    80             WHERE c.oid = idx.indrelid
    81                 AND idx.indexrelid = c2.oid
    82                 AND attr.attrelid = c.oid
    83                 AND attr.attnum = idx.indkey[0]
    84                 AND c.relname = %s""", [table_name])
     138        qname = self.qname_converter(qname)
     139        if not qname.schema:
     140            cursor.execute("""
     141                SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
     142                FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
     143                    pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
     144                WHERE c.oid = idx.indrelid
     145                    AND idx.indexrelid = c2.oid
     146                    AND attr.attrelid = c.oid
     147                    AND attr.attnum = idx.indkey[0]
     148                    AND c.relname = %s""",
     149                [qname.table])
     150        else:
     151            cursor.execute("""
     152                SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
     153                FROM pg_catalog.pg_class c, pg_catalog.pg_namespace nsp,
     154                    pg_catalog.pg_class c2, pg_catalog.pg_index idx,
     155                    pg_catalog.pg_attribute attr
     156                WHERE c.oid = idx.indrelid
     157                    AND nsp.oid = c.relnamespace
     158                    AND idx.indexrelid = c2.oid
     159                    AND attr.attrelid = c.oid
     160                    AND attr.attnum = idx.indkey[0]
     161                    AND nsp.nspname = %s AND c.relname = %s""",
     162                [qname.schema, qname.table])
    85163        indexes = {}
    86164        for row in cursor.fetchall():
    87165            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
     
    92170                continue
    93171            indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
    94172        return indexes
     173
     174    def qname_converter(self, qname, force_schema=False):
     175        """
     176        On postgresql it is impossible to force usage of schema -
     177        we do not know what the default schema is. In fact, there can be
     178        multiple schemas.
     179        """
     180        assert isinstance(qname, QName)
     181        if qname.db_format:
     182            return qname
     183        return QName((qname.schema or self.connection.schema), qname.table,
     184                     True)
  • django/db/backends/postgresql_psycopg2/operations.py

     
     1from django.db import QName
    12from django.db.backends import BaseDatabaseOperations
    23
    34
     
    5657            return 'HOST(%s)'
    5758        return '%s'
    5859
    59     def last_insert_id(self, cursor, table_name, pk_name):
     60    def last_insert_id(self, cursor, qualified_name, pk_name):
    6061        # Use pg_get_serial_sequence to get the underlying sequence name
    6162        # from the table name and column name (available since PostgreSQL 8)
    6263        cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (
    63             self.quote_name(table_name), pk_name))
     64            self.qualified_name(qualified_name), pk_name))
    6465        return cursor.fetchone()[0]
    6566
    6667    def no_limit_value(self):
     
    7172            return name # Quoting once is enough.
    7273        return '"%s"' % name
    7374
     75    def qualified_name(self, qname):
     76        """
     77        Return the table's name in fully qualified format
     78        (schema_name.tbl_name) if there is a schema_name, else returns just
     79        the tbl_name in quoted format.
     80
     81        convert_name has no effect on PostgreSQL, as there is no need
     82        to do anything else than quoting for the name even in testing.
     83        """
     84        assert isinstance(qname, QName)
     85        schema = qname.schema
     86        if not qname.db_format and not schema:
     87            schema = self.connection.schema
     88        if schema:
     89            return "%s.%s" % (self.quote_name(schema),
     90                              self.quote_name(qname.table))
     91        else:
     92            return self.quote_name(qname.table)
     93
    7494    def set_time_zone_sql(self):
    7595        return "SET TIME ZONE %s"
    7696
     
    81101            # table.
    82102            sql = ['%s %s;' % \
    83103                (style.SQL_KEYWORD('TRUNCATE'),
    84                     style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
     104                    style.SQL_FIELD(', '.join([self.qualified_name(table)
     105                                               for table in tables]))
    85106            )]
    86107
    87108            # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
    88109            # to reset sequence indices
    89110            for sequence_info in sequences:
    90                 table_name = sequence_info['table']
     111                qname = sequence_info['qname']
     112                qualified_name = self.qualified_name(qname)
    91113                column_name = sequence_info['column']
    92114                if not (column_name and len(column_name) > 0):
    93115                    # This will be the case if it's an m2m using an autogenerated
     
    95117                    column_name = 'id'
    96118                sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
    97119                    (style.SQL_KEYWORD('SELECT'),
    98                     style.SQL_TABLE(self.quote_name(table_name)),
     120                    style.SQL_TABLE(qualified_name),
    99121                    style.SQL_FIELD(column_name))
    100122                )
    101123            return sql
     
    121143
    122144            for f in model._meta.local_fields:
    123145                if isinstance(f, models.AutoField):
     146                    qualified_name = self.qualified_name(model._meta.qualified_name)
    124147                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    125148                        (style.SQL_KEYWORD('SELECT'),
    126                         style.SQL_TABLE(qn(model._meta.db_table)),
     149                        style.SQL_TABLE(qualified_name),
    127150                        style.SQL_FIELD(f.column),
    128151                        style.SQL_FIELD(qn(f.column)),
    129152                        style.SQL_FIELD(qn(f.column)),
    130153                        style.SQL_KEYWORD('IS NOT'),
    131154                        style.SQL_KEYWORD('FROM'),
    132                         style.SQL_TABLE(qn(model._meta.db_table))))
     155                        style.SQL_TABLE(qualified_name)))
    133156                    break # Only one AutoField is allowed per model, so don't bother continuing.
    134157            for f in model._meta.many_to_many:
    135158                if not f.rel.through:
     159                    qualified_name = self.qualified_name(f.m2m_qualified_name())
    136160                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    137161                        (style.SQL_KEYWORD('SELECT'),
    138                         style.SQL_TABLE(qn(f.m2m_db_table())),
     162                        style.SQL_TABLE(qualified_name),
    139163                        style.SQL_FIELD('id'),
    140164                        style.SQL_FIELD(qn('id')),
    141165                        style.SQL_FIELD(qn('id')),
    142166                        style.SQL_KEYWORD('IS NOT'),
    143167                        style.SQL_KEYWORD('FROM'),
    144                         style.SQL_TABLE(qn(f.m2m_db_table()))))
     168                        style.SQL_TABLE(qualified_name)))
    145169        return output
    146170
    147171    def savepoint_create_sql(self, sid):
  • django/db/backends/postgresql_psycopg2/creation.py

     
    4343    def sql_indexes_for_field(self, model, f, style):
    4444        if f.db_index and not f.unique:
    4545            qn = self.connection.ops.quote_name
    46             db_table = model._meta.db_table
     46            qn3 = self.connection.ops.qualified_name
     47            qualified_name = model._meta.qualified_name
     48            db_table = qualified_name[1]
    4749            tablespace = f.db_tablespace or model._meta.db_tablespace
    4850            if tablespace:
    4951                tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
     
    5658                return (style.SQL_KEYWORD('CREATE INDEX') + ' ' +
    5759                        style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' +
    5860                        style.SQL_KEYWORD('ON') + ' ' +
    59                         style.SQL_TABLE(qn(db_table)) + ' ' +
     61                        style.SQL_TABLE(qn3(qualified_name)) + ' ' +
    6062                        "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) +
    6163                        "%s;" % tablespace_sql)
    6264
     
    8587        self.connection.connection.rollback()
    8688        self.connection.connection.set_isolation_level(
    8789                psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
     90
     91    def sql_destroy_schema(self, schema, style):
     92        qn = self.connection.ops.quote_name
     93        return "%s %s CASCADE;" % (style.SQL_KEYWORD('DROP SCHEMA'), qn(schema))
  • django/db/backends/postgresql_psycopg2/base.py

     
    8383    has_bulk_insert = True
    8484    supports_tablespaces = True
    8585    can_distinct_on_fields = True
     86    namespaced_schemas = True
    8687
    8788class DatabaseWrapper(BaseDatabaseWrapper):
    8889    vendor = 'postgresql'
     
    121122        self.validation = BaseDatabaseValidation(self)
    122123        self._pg_version = None
    123124
     125    def _get_test_schema_prefix(self):
     126        return ''
     127    test_schema_prefix = property(_get_test_schema_prefix)
     128
    124129    def check_constraints(self, table_names=None):
    125130        """
    126131        To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they
  • django/db/backends/creation.py

     
    22import time
    33
    44from django.conf import settings
     5from django.db import QName
    56from django.db.utils import load_backend
     7from django.core.management.color import no_style
    68
    79# The prefix to put on the default database name when creating
    810# the test database.
     
    2830        """
    2931        return '%x' % (abs(hash(args)) % 4294967296L)  # 2**32
    3032
     33    def sql_create_schema(self, schema, style):
     34        """
     35        Returns the SQL required to create a single schema
     36        """
     37        qn = self.connection.ops.quote_name
     38        output = "%s %s;" % (style.SQL_KEYWORD('CREATE SCHEMA'), qn(schema))
     39        return output
     40
    3141    def sql_create_model(self, model, style, known_models=set()):
    3242        """
    3343        Returns the SQL required to create a single model, as a tuple of:
     
    4050        table_output = []
    4151        pending_references = {}
    4252        qn = self.connection.ops.quote_name
     53        qn3 = self.connection.ops.qualified_name
    4354        for f in opts.local_fields:
    4455            col_type = f.db_type(connection=self.connection)
    4556            tablespace = f.db_tablespace or opts.db_tablespace
     
    7990                     for f in field_constraints]))
    8091
    8192        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' +
    82                           style.SQL_TABLE(qn(opts.db_table)) + ' (']
     93                          style.SQL_TABLE(qn3(opts.qualified_name)) + ' (']
    8394        for i, line in enumerate(table_output): # Combine and add commas.
    8495            full_statement.append(
    8596                '    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     
    96107            # Add any extra SQL needed to support auto-incrementing primary
    97108            # keys.
    98109            auto_column = opts.auto_field.db_column or opts.auto_field.name
    99             autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table,
     110            autoinc_sql = self.connection.ops.autoinc_sql(opts.qualified_name,
    100111                                                          auto_column)
    101112            if autoinc_sql:
    102113                for stmt in autoinc_sql:
     
    109120        Return the SQL snippet defining the foreign key reference for a field.
    110121        """
    111122        qn = self.connection.ops.quote_name
     123        from_qname = field.model._meta.qualified_name
     124        to_qname = field.rel.to._meta.qualified_name
     125        qname = self.qualified_name_for_ref(from_qname, to_qname)
    112126        if field.rel.to in known_models:
    113127            output = [style.SQL_KEYWORD('REFERENCES') + ' ' +
    114                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' +
     128                style.SQL_TABLE(qname) + ' (' +
    115129                style.SQL_FIELD(qn(field.rel.to._meta.get_field(
    116130                    field.rel.field_name).column)) + ')' +
    117131                self.connection.ops.deferrable_sql()
     
    132146        from django.db.backends.util import truncate_name
    133147
    134148        if not model._meta.managed or model._meta.proxy:
     149            # So, we have a reference to either unmanaged model or to
     150            # a proxy model. Lets just clear the pending_references
     151            # for now.
     152            if model in pending_references:
     153                del pending_references[model]
    135154            return []
    136155        qn = self.connection.ops.quote_name
     156        qn3 = self.connection.ops.qualified_name
    137157        final_output = []
    138158        opts = model._meta
    139159        if model in pending_references:
    140160            for rel_class, f in pending_references[model]:
    141161                rel_opts = rel_class._meta
    142162                r_table = rel_opts.db_table
     163                r_qname = rel_opts.qualified_name
    143164                r_col = f.column
    144165                table = opts.db_table
     166                qname = self.qualified_name_for_ref(r_qname, opts.qualified_name)
    145167                col = opts.get_field(f.rel.field_name).column
    146168                # For MySQL, r_name must be unique in the first 64 characters.
    147169                # So we are careful with character usage here.
     
    149171                    r_col, col, self._digest(r_table, table))
    150172                final_output.append(style.SQL_KEYWORD('ALTER TABLE') +
    151173                    ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
    152                     (qn(r_table), qn(truncate_name(
     174                    (qn3(r_qname), qn(truncate_name(
    153175                        r_name, self.connection.ops.max_name_length())),
    154                     qn(r_col), qn(table), qn(col),
     176                    qn(r_col), qname, qn(col),
    155177                    self.connection.ops.deferrable_sql()))
    156178            del pending_references[model]
    157179        return final_output
    158180
     181    def qualified_name_for_ref(self, from_table, ref_table):
     182        """
     183        In certain databases if the from_table is in qualified format and
     184        ref_table is not, it is assumed the ref_table references a table
     185        in the same schema as from_table is from. However, we want the
     186        reference to be to default schema, not the same schema the from_table
     187        is. This method will fix this issue where that is a problem.
     188        """
     189        return self.connection.ops.qualified_name(ref_table)
     190
    159191    def sql_indexes_for_model(self, model, style):
    160192        """
    161193        Returns the CREATE INDEX SQL statements for a single model.
     
    171203        """
    172204        Return the CREATE INDEX SQL statements for a single model field.
    173205        """
    174         from django.db.backends.util import truncate_name
    175 
    176206        if f.db_index and not f.unique:
    177207            qn = self.connection.ops.quote_name
     208            qn3 = self.connection.ops.qualified_name
    178209            tablespace = f.db_tablespace or model._meta.db_tablespace
    179210            if tablespace:
    180211                tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
     
    182213                    tablespace_sql = ' ' + tablespace_sql
    183214            else:
    184215                tablespace_sql = ''
    185             i_name = '%s_%s' % (model._meta.db_table, self._digest(f.column))
     216            qualified_name = self.qualified_index_name(model, f.column)
    186217            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
    187                 style.SQL_TABLE(qn(truncate_name(
    188                     i_name, self.connection.ops.max_name_length()))) + ' ' +
     218                style.SQL_TABLE(qualified_name) + ' ' +
    189219                style.SQL_KEYWORD('ON') + ' ' +
    190                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
     220                style.SQL_TABLE(qn3(model._meta.qualified_name)) + ' ' +
    191221                "(%s)" % style.SQL_FIELD(qn(f.column)) +
    192222                "%s;" % tablespace_sql]
    193223        else:
    194224            output = []
    195225        return output
    196226
     227    def qualified_index_name(self, model, col):
     228        """
     229        Some databases do support schemas, but indexes can not be placed in a
     230        different schema. So, to support those databases, we need to be able
     231        to return the index name in different qualified format than the rest
     232        of the database identifiers.
     233        """
     234        from django.db.backends.util import truncate_name
     235        i_name = '%s_%s' % (model._meta.db_table, self._digest(col))
     236        i_name = truncate_name(i_name, self.connection.ops.max_name_length())
     237        return self.connection.ops.qualified_name(
     238            QName(model._meta.db_schema, i_name, False))
     239
     240    def sql_destroy_schema(self, schema, style):
     241        """
     242        Returns the SQL required to destroy a single schema.
     243        """
     244        return ""
     245
    197246    def sql_destroy_model(self, model, references_to_delete, style):
    198247        """
    199248        Return the DROP TABLE and restraint dropping statements for a single
     
    202251        if not model._meta.managed or model._meta.proxy:
    203252            return []
    204253        # Drop the table now
    205         qn = self.connection.ops.quote_name
     254        qn3 = self.connection.ops.qualified_name
    206255        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
    207                               style.SQL_TABLE(qn(model._meta.db_table)))]
     256                              style.SQL_TABLE(qn3(model._meta.qualified_name)))]
    208257        if model in references_to_delete:
    209258            output.extend(self.sql_remove_table_constraints(
    210259                model, references_to_delete, style))
    211260        if model._meta.has_auto_field:
    212             ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
     261            ds = self.connection.ops.drop_sequence_sql(model._meta.qualified_name)
    213262            if ds:
    214263                output.append(ds)
    215264        return output
     
    220269            return []
    221270        output = []
    222271        qn = self.connection.ops.quote_name
     272        qn3 = self.connection.ops.qualified_name
    223273        for rel_class, f in references_to_delete[model]:
    224274            table = rel_class._meta.db_table
     275            qname = rel_class._meta.qualified_name
    225276            col = f.column
    226277            r_table = model._meta.db_table
    227278            r_col = model._meta.get_field(f.rel.field_name).column
     
    229280                col, r_col, self._digest(table, r_table))
    230281            output.append('%s %s %s %s;' % \
    231282                (style.SQL_KEYWORD('ALTER TABLE'),
    232                 style.SQL_TABLE(qn(table)),
     283                style.SQL_TABLE(qn3(qname)),
    233284                style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
    234285                style.SQL_FIELD(qn(truncate_name(
    235286                    r_name, self.connection.ops.max_name_length())))))
     
    240291        """
    241292        Creates a test database, prompting the user for confirmation if the
    242293        database already exists. Returns the name of the test database created.
     294
     295        Also creates needed schemas, which on some backends live in the same
     296        namespace than databases. If there are schema name clashes, prompts
     297        the user for confirmation.
    243298        """
    244299        # Don't import django.core.management if it isn't needed.
    245300        from django.core.management import call_command
     
    253308            print "Creating test database for alias '%s'%s..." % (
    254309                self.connection.alias, test_db_repr)
    255310
    256         self._create_test_db(verbosity, autoclobber)
     311        schemas = self.get_schemas()
     312        self._create_test_db(verbosity, autoclobber, schemas)
    257313
     314        # Create the test schemas.
     315        self.connection.settings_dict["NAME"] = test_database_name
    258316        self.connection.close()
    259         self.connection.settings_dict["NAME"] = test_database_name
     317        schemas = ['%s%s' % (self.connection.test_schema_prefix, s) for s in schemas]
     318        created_schemas = self._create_test_schemas(verbosity, schemas, autoclobber)
    260319
    261320        # Confirm the feature set of the test database
    262321        self.connection.features.confirm()
     
    292351        # the side effect of initializing the test database.
    293352        self.connection.cursor()
    294353
    295         return test_database_name
     354        return test_database_name, created_schemas
    296355
     356    def _create_test_schemas(self, verbosity, schemas, autoclobber):
     357        style = no_style()
     358        cursor = self.connection.cursor()
     359        existing_schemas = self.connection.introspection.get_schema_list(cursor)
     360        if not self.connection.features.namespaced_schemas:
     361            conflicts = [s for s in existing_schemas if s in schemas]
     362        else:
     363            conflicts = []
     364        if conflicts:
     365            print 'The following schemas already exists: %s' % ', '.join(conflicts)
     366            if not autoclobber:
     367                confirm = raw_input(
     368                    "Type 'yes' if you would like to try deleting these schemas "
     369                    "or 'no' to cancel: ")
     370            if autoclobber or confirm == 'yes':
     371                try:
     372                    # Some databases (well, MySQL) complain about foreign keys when
     373                    # dropping a database. So, disable the constraints temporarily.
     374                    self.connection.disable_constraint_checking()
     375                    for schema in conflicts:
     376                        if verbosity >= 1:
     377                            print "Destroying schema %s" % schema
     378                        cursor.execute(self.sql_destroy_schema(schema, style))
     379                        existing_schemas.remove(schema)
     380                finally:
     381                    self.connection.enable_constraint_checking()
     382            else:
     383                print "Tests cancelled."
     384                sys.exit(1)
     385           
     386        to_create = [s for s in schemas if s not in existing_schemas]
     387        for schema in to_create:
     388            if verbosity >= 1:
     389                print "Creating schema %s" % schema
     390            cursor.execute(self.sql_create_schema(schema, style))
     391            self.connection.settings_dict['TEST_SCHEMAS'].append(schema)
     392        return to_create
     393
     394    def get_schemas(self):
     395        from django.db import models
     396        apps = models.get_apps()
     397        schemas = set()
     398        for app in apps:
     399            app_models = models.get_models(app, include_auto_created=True)
     400            for model in app_models:
     401                schema = model._meta.db_schema
     402                if schema:
     403                    schemas.add(schema)
     404        conn_default_schema = self.connection.settings_dict['SCHEMA']
     405        if conn_default_schema:
     406            schemas.add(conn_default_schema)
     407        return schemas
     408
    297409    def _get_test_db_name(self):
    298410        """
    299411        Internal implementation - returns the name of the test DB that will be
     
    305417            return self.connection.settings_dict['TEST_NAME']
    306418        return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
    307419
    308     def _create_test_db(self, verbosity, autoclobber):
     420    def _create_test_db(self, verbosity, autoclobber, schemas):
    309421        """
    310422        Internal implementation - creates the test db tables.
    311423        """
     
    335447                    if verbosity >= 1:
    336448                        print ("Destroying old test database '%s'..."
    337449                               % self.connection.alias)
     450                    # MySQL nicely doesn't have a drop-cascade option, nor
     451                    # does it allow dropping a database having foreign key
     452                    # references pointing to it. So, we just disable foreign
     453                    # key checks and then immediately enable them. MySQL is
     454                    # happy after this hack, and other databases simply do
     455                    # not care.
     456                    try:
     457                        self.connection.disable_constraint_checking()
     458                        cursor.execute(
     459                            "DROP DATABASE %s" % qn(test_database_name))
     460                    finally:
     461                        self.connection.enable_constraint_checking()
    338462                    cursor.execute(
    339                         "DROP DATABASE %s" % qn(test_database_name))
    340                     cursor.execute(
    341463                        "CREATE DATABASE %s %s" % (qn(test_database_name),
    342464                                                   suffix))
    343465                except Exception, e:
     
    348470                print "Tests cancelled."
    349471                sys.exit(1)
    350472
     473        self.connection.settings_dict['TEST_SCHEMAS'].append(test_database_name)
    351474        return test_database_name
    352475
    353     def destroy_test_db(self, old_database_name, verbosity=1):
     476    def destroy_test_db(self, old_database_name, created_schemas, verbosity=1):
    354477        """
    355478        Destroy a test database, prompting the user for confirmation if the
    356479        database already exists.
    357480        """
     481        # On databases where there is no support for multiple databases
     482        # with multiple schemas we need to destroy the created schemas
     483        # manually.
     484        cursor = self.connection.cursor()
     485        style = no_style()
     486        if not self.connection.features.namespaced_schemas:
     487            try:
     488                self.connection.disable_constraint_checking()
     489                for schema in created_schemas:
     490                    if verbosity >= 1:
     491                        print "Destroying schema '%s'..." % schema
     492                    cursor.execute(self.sql_destroy_schema(schema, style))
     493            finally:
     494                self.connection.enable_constraint_checking()
    358495        self.connection.close()
    359496        test_database_name = self.connection.settings_dict['NAME']
     497                   
     498
    360499        if verbosity >= 1:
    361500            test_db_repr = ''
    362501            if verbosity >= 2:
     
    429568            settings_dict['ENGINE'],
    430569            settings_dict['NAME']
    431570        )
     571
     572    def post_create_pending_references(self, pending_references, as_sql=False):
     573        """
     574        Create any pending references which need special handling (for example
     575        different connections). The as_sql flag tells us if we should return
     576        the raw SQL used. This is needed for the "sql" management commands.
     577        """
     578        raise NotImplementedError
  • django/db/backends/sqlite3/base.py

     
    1111import re
    1212import sys
    1313
    14 from django.db import utils
     14from django.db import utils, QName
    1515from django.db.backends import *
    1616from django.db.backends.signals import connection_created
    1717from django.db.backends.sqlite3.client import DatabaseClient
     
    8484    supports_mixed_date_datetime_comparisons = False
    8585    has_bulk_insert = True
    8686    can_combine_inserts_with_and_without_auto_increment_pk = True
     87    supports_foreign_keys = False
     88    # SQLite doesn't support schemas at all, but our hack of appending
     89    # the schema name to table name creates namespaced schemas from
     90    # Django's perspective
     91    namespaced_schemas = True
    8792
    8893    def _supports_stddev(self):
    8994        """Confirm support for STDDEV and related stats functions
     
    139144            return name # Quoting once is enough.
    140145        return '"%s"' % name
    141146
     147    def qualified_name(self, qname):
     148        # Fake schema support by using the schema as a prefix to the
     149        # table name. Keep record of what names are already qualified
     150        # to avoid double-qualifying.
     151        assert isinstance(qname, QName)
     152        if qname.db_format:
     153            # A name from DB must not have a schema (no schema support)
     154            assert not qname.schema
     155            schema = None
     156        else:
     157            schema = qname.schema or self.connection.schema
     158        if schema:
     159            return self.quote_name('%s_%s' % (schema, qname.table))
     160        else:
     161            return self.quote_name(qname.table)
     162
    142163    def no_limit_value(self):
    143164        return -1
    144165
     
    146167        # NB: The generated SQL below is specific to SQLite
    147168        # Note: The DELETE FROM... SQL generated below works for SQLite databases
    148169        # because constraints don't exist
    149         sql = ['%s %s %s;' % \
     170        sql = []
     171        for table in tables:
     172            sql.append('%s %s %s;' % \
    150173                (style.SQL_KEYWORD('DELETE'),
    151174                 style.SQL_KEYWORD('FROM'),
    152                  style.SQL_FIELD(self.quote_name(table))
    153                  ) for table in tables]
     175                 style.SQL_FIELD(self.qualified_name(table))
     176                 ))
    154177        # Note: No requirement for reset of auto-incremented indices (cf. other
    155178        # sql_flush() implementations). Just return SQL at this point
    156179        return sql
     
    243266        self.introspection = DatabaseIntrospection(self)
    244267        self.validation = BaseDatabaseValidation(self)
    245268
     269    def convert_schema(self, schema):
     270        # No real schema support.
     271        return None
     272
    246273    def _sqlite_create_connection(self):
    247274        settings_dict = self.settings_dict
    248275        if not settings_dict['NAME']:
     
    295322        """
    296323        cursor = self.cursor()
    297324        if table_names is None:
    298             table_names = self.introspection.get_table_list(cursor)
     325            table_names = self.introspection.get_visible_tables_list(cursor)
     326        else:
     327            table_names = [self.introspection.qname_converter(t) for t in table_names]
    299328        for table_name in table_names:
    300329            primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
    301330            if not primary_key_column_name:
     
    307336                    LEFT JOIN `%s` as REFERRED
    308337                    ON (REFERRING.`%s` = REFERRED.`%s`)
    309338                    WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL"""
    310                     % (primary_key_column_name, column_name, table_name, referenced_table_name,
     339                    % (primary_key_column_name, column_name, table_name[1], referenced_table_name[1],
    311340                    column_name, referenced_column_name, column_name, referenced_column_name))
    312341                for bad_row in cursor.fetchall():
    313342                    raise utils.IntegrityError("The row in table '%s' with primary key '%s' has an invalid "
    314343                        "foreign key: %s.%s contains a value '%s' that does not have a corresponding value in %s.%s."
    315                         % (table_name, bad_row[0], table_name, column_name, bad_row[1],
     344                        % (table_name, bad_row[0], table_name[1], column_name, bad_row[1],
    316345                        referenced_table_name, referenced_column_name))
    317346
    318347    def close(self):
  • django/db/backends/sqlite3/introspection.py

     
    11import re
     2from django.db import QName
    23from django.db.backends import BaseDatabaseIntrospection
    34
    45# This light wrapper "fakes" a dictionary interface, because some SQLite data
     
    4142class DatabaseIntrospection(BaseDatabaseIntrospection):
    4243    data_types_reverse = FlexibleFieldLookupDict()
    4344
    44     def get_table_list(self, cursor):
    45         "Returns a list of table names in the current database."
     45    def get_visible_tables_list(self, cursor):
     46        "Returns a list of table names in the current database"
    4647        # Skip the sqlite_sequence system table used for autoincrement key
    4748        # generation.
    4849        cursor.execute("""
    4950            SELECT name FROM sqlite_master
    5051            WHERE type='table' AND NOT name='sqlite_sequence'
    5152            ORDER BY name""")
    52         return [row[0] for row in cursor.fetchall()]
     53        return [QName(None, row[0], True) for row in cursor.fetchall()]
    5354
    54     def get_table_description(self, cursor, table_name):
     55    def get_table_description(self, cursor, qualified_name):
    5556        "Returns a description of the table, with the DB-API cursor.description interface."
    5657        return [(info['name'], info['type'], None, None, None, None,
    57                  info['null_ok']) for info in self._table_info(cursor, table_name)]
     58                 info['null_ok']) for info in self._table_info(cursor, qualified_name[1])]
    5859
    59     def get_relations(self, cursor, table_name):
     60    def get_relations(self, cursor, qualified_name):
    6061        """
    6162        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    6263        representing all relationships to the given table. Indexes are 0-based.
    6364        """
     65        table_name = qualified_name[1]
    6466
    6567        # Dictionary of relations to return
    6668        relations = {}
     
    98100
    99101                name = other_desc.split(' ', 1)[0].strip('"')
    100102                if name == column:
    101                     relations[field_index] = (other_index, table)
     103                    relations[field_index] = (other_index,
     104                                              QName(None, table, True))
    102105                    break
    103106
    104107        return relations
    105108
    106     def get_key_columns(self, cursor, table_name):
     109    def get_key_columns(self, cursor, qname):
    107110        """
    108111        Returns a list of (column_name, referenced_table_name, referenced_column_name) for all
    109112        key columns in given table.
    110113        """
     114        table_name = self.qname_converter(qname)[1]
    111115        key_columns = []
    112116
    113117        # Schema for this table
     
    128132                continue
    129133
    130134            # This will append (column_name, referenced_table_name, referenced_column_name) to key_columns
    131             key_columns.append(tuple([s.strip('"') for s in m.groups()]))
     135            add = tuple([s.strip('"') for s in m.groups()])
     136            add = add[0], (None, add[1], True), add[2]
     137            key_columns.append(add)
    132138
    133139        return key_columns
    134140
    135     def get_indexes(self, cursor, table_name):
     141    def get_indexes(self, cursor, qualified_name):
    136142        """
    137143        Returns a dictionary of fieldname -> infodict for the given table,
    138144        where each infodict is in the format:
    139145            {'primary_key': boolean representing whether it's the primary key,
    140146             'unique': boolean representing whether it's a unique index}
    141147        """
     148        table_name = qualified_name[1]
    142149        indexes = {}
    143150        for info in self._table_info(cursor, table_name):
    144151            indexes[info['name']] = {'primary_key': info['pk'] != 0,
     
    157164            indexes[name]['unique'] = True
    158165        return indexes
    159166
    160     def get_primary_key_column(self, cursor, table_name):
     167    def get_primary_key_column(self, cursor, qname):
    161168        """
    162169        Get the column name of the primary key for the given table.
    163170        """
     171        qname = self.qname_converter(qname)
     172        table_name = qname[1]
    164173        # Don't use PRAGMA because that causes issues with some transactions
    165174        cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"])
    166175        results = cursor.fetchone()[0].strip()
     
    180189                 'null_ok': not field[3],
    181190                 'pk': field[5]     # undocumented
    182191                 } for field in cursor.fetchall()]
     192
     193    def identifier_converter(self, identifier):
     194        return identifier
     195
     196    def qname_converter(self, qname, force_schema=False):
     197        # For SQLite force_schema does nothing, as the default schema is
     198        # None.
     199        assert isinstance(qname, QName)
     200        assert not (qname.schema and qname.db_format)
     201        return QName(None, self.connection.ops.qualified_name(qname)[1:-1],
     202                     True)
  • django/db/backends/sqlite3/creation.py

     
    3333
    3434    def sql_for_pending_references(self, model, style, pending_references):
    3535        "SQLite3 doesn't support constraints"
     36        if model in pending_references:
     37            del pending_references[model]
    3638        return []
    3739
    3840    def sql_remove_table_constraints(self, model, references_to_delete, style):
    3941        "SQLite3 doesn't support constraints"
    4042        return []
    4143
     44    def sql_create_schema(self, schema, verbosity):
     45        "SQLite3 doesn't support schemas"
     46        return
     47
    4248    def _get_test_db_name(self):
    4349        test_database_name = self.connection.settings_dict['TEST_NAME']
    4450        if test_database_name and test_database_name != ':memory:':
    4551            return test_database_name
    4652        return ':memory:'
    4753
    48     def _create_test_db(self, verbosity, autoclobber):
     54    def _create_test_db(self, verbosity, autoclobber, schemas):
    4955        test_database_name = self._get_test_db_name()
    5056        if test_database_name != ':memory:':
    5157            # Erase the old test database
     
    6571                    sys.exit(1)
    6672        return test_database_name
    6773
     74    def _create_test_schemas(self, verbosity, schemas, cursor):
     75        return []
     76
    6877    def _destroy_test_db(self, test_database_name, verbosity):
    6978        if test_database_name and test_database_name != ":memory:":
    7079            # Remove the SQLite database file
     
    8190        SQLite since the databases will be distinct despite having the same
    8291        TEST_NAME. See http://www.sqlite.org/inmemorydb.html
    8392        """
    84         settings_dict = self.connection.settings_dict
    8593        test_dbname = self._get_test_db_name()
    8694        sig = [self.connection.settings_dict['NAME']]
    8795        if test_dbname == ':memory:':
  • django/db/backends/mysql/introspection.py

     
     1from django.db import QName
    12from django.db.backends import BaseDatabaseIntrospection
    23from MySQLdb import ProgrammingError, OperationalError
    34from MySQLdb.constants import FIELD_TYPE
     
    2829        FIELD_TYPE.VAR_STRING: 'CharField',
    2930    }
    3031
    31     def get_table_list(self, cursor):
    32         "Returns a list of table names in the current database."
    33         cursor.execute("SHOW TABLES")
    34         return [row[0] for row in cursor.fetchall()]
     32    def get_visible_tables_list(self, cursor):
     33        "Returns a list of visible tables"
     34        return self.get_qualified_tables_list(cursor, [self.connection.settings_dict['NAME']])
    3535
    36     def get_table_description(self, cursor, table_name):
     36    def get_qualified_tables_list(self, cursor, schemas):
     37        default_schema = self.connection.convert_schema(None)
     38        if default_schema:
     39            schemas.append(default_schema)
     40        if not schemas:
     41            return []
     42        param_list = ', '.join(['%s']*len(schemas))
     43        cursor.execute("""
     44            SELECT table_schema, table_name
     45              FROM information_schema.tables
     46             WHERE table_schema in (%s)""" % param_list, schemas)
     47        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
     48
     49    def get_table_description(self, cursor, qname):
    3750        "Returns a description of the table, with the DB-API cursor.description interface."
    38         cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
     51        qname = self.qname_converter(qname)
     52        cursor.execute("SELECT * FROM %s LIMIT 1"
     53                       % self.connection.ops.qualified_name(qname))
    3954        return cursor.description
    4055
    41     def _name_to_index(self, cursor, table_name):
     56    def _name_to_index(self, cursor, qname):
    4257        """
    4358        Returns a dictionary of {field_name: field_index} for the given table.
    4459        Indexes are 0-based.
    4560        """
    46         return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
     61        return dict((d[0], i) for i, d in enumerate(self.get_table_description(cursor, qname)))
    4762
    48     def get_relations(self, cursor, table_name):
     63    def get_relations(self, cursor, qname):
    4964        """
    5065        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    5166        representing all relationships to the given table. Indexes are 0-based.
    5267        """
    53         my_field_dict = self._name_to_index(cursor, table_name)
    54         constraints = self.get_key_columns(cursor, table_name)
     68        my_field_dict = self._name_to_index(cursor, qname)
     69        constraints = self.get_key_columns(cursor, qname)
    5570        relations = {}
    5671        for my_fieldname, other_table, other_field in constraints:
    5772            other_field_index = self._name_to_index(cursor, other_table)[other_field]
     
    5974            relations[my_field_index] = (other_field_index, other_table)
    6075        return relations
    6176
    62     def get_key_columns(self, cursor, table_name):
     77    def get_key_columns(self, cursor, qname):
    6378        """
    64         Returns a list of (column_name, referenced_table_name, referenced_column_name) for all
    65         key columns in given table.
     79        Returns a list of
     80            (column_name,
     81            (reference_table_schema, referenced_table_name),
     82            referenced_column_name)
     83        for all key columns in given table.
    6684        """
    6785        key_columns = []
     86        qname = self.qname_converter(qname, force_schema=True)
    6887        try:
    6988            cursor.execute("""
    70                 SELECT column_name, referenced_table_name, referenced_column_name
     89                SELECT column_name, referenced_table_schema, referenced_table_name, referenced_column_name
    7190                FROM information_schema.key_column_usage
    72                 WHERE table_name = %s
    73                     AND table_schema = DATABASE()
     91                WHERE table_schema = %s
     92                    AND table_name = %s
    7493                    AND referenced_table_name IS NOT NULL
    75                     AND referenced_column_name IS NOT NULL""", [table_name])
    76             key_columns.extend(cursor.fetchall())
     94                    AND referenced_column_name IS NOT NULL""",
     95                           [qname.schema, qname.table])
     96            for row in cursor.fetchall():
     97                key_columns.append((row[0], QName(row[1], row[2], True),
     98                                    row[3]))
    7799        except (ProgrammingError, OperationalError):
    78100            # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
    79101            # Go through all constraints and save the equal matches.
    80             cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.quote_name(table_name))
     102            cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.qualified_name(qname))
    81103            for row in cursor.fetchall():
    82104                pos = 0
    83105                while True:
     
    85107                    if match == None:
    86108                        break
    87109                    pos = match.end()
    88                     key_columns.append(match.groups())
     110                    groups = match.groups()
     111                    tblname = groups[1]
     112                    if '.' in tblname:
     113                        tblname = tblname.split('.')
     114                    else:
     115                        tblname = None, tblname
     116                    key_columns.append((groups[0], tblname, groups[2]))
    89117        return key_columns
    90118
    91     def get_primary_key_column(self, cursor, table_name):
     119    def get_primary_key_column(self, cursor, qname):
    92120        """
    93121        Returns the name of the primary key column for the given table
    94122        """
    95         for column in self.get_indexes(cursor, table_name).iteritems():
     123        for column in self.get_indexes(cursor, qname).iteritems():
    96124            if column[1]['primary_key']:
    97125                return column[0]
    98126        return None
    99127
    100     def get_indexes(self, cursor, table_name):
     128    def get_indexes(self, cursor, qname):
    101129        """
    102130        Returns a dictionary of fieldname -> infodict for the given table,
    103131        where each infodict is in the format:
    104132            {'primary_key': boolean representing whether it's the primary key,
    105133             'unique': boolean representing whether it's a unique index}
    106134        """
    107         cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
     135        qname = self.qname_converter(qname)
     136        cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.qualified_name(qname))
    108137        indexes = {}
    109138        for row in cursor.fetchall():
    110139            indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
    111140        return indexes
    112141
     142    def get_schema_list(self, cursor):
     143        cursor.execute("SHOW DATABASES")
     144        return [r[0] for r in cursor.fetchall()]
     145
     146    def qname_converter(self, qname, force_schema=False):
     147        assert isinstance(qname, QName)
     148        if qname.db_format and (qname.schema or not force_schema):
     149            return qname
     150        schema = self.connection.convert_schema(qname.schema)
     151        if not schema and force_schema:
     152            schema = self.connection.settings_dict['NAME']
     153        return QName(schema, qname.table, True)
  • django/db/backends/mysql/creation.py

     
     1from django.db import QName
    12from django.db.backends.creation import BaseDatabaseCreation
    23
    34class DatabaseCreation(BaseDatabaseCreation):
     
    5859            style.SQL_KEYWORD('NOT NULL'))
    5960        ]
    6061        deferred = [
    61             (field.m2m_db_table(), field.m2m_column_name(), opts.db_table,
     62            (field.m2m_qualified_table(), field.m2m_column_name(), opts.qualified_name,
    6263                opts.pk.column),
    63             (field.m2m_db_table(), field.m2m_reverse_name(),
    64                 field.rel.to._meta.db_table, field.rel.to._meta.pk.column)
     64            (field.m2m_qualified_table(), field.m2m_reverse_name(),
     65                field.rel.to._meta.qualified_name, field.rel.to._meta.pk.column)
    6566            ]
    6667        return table_output, deferred
     68
     69    def sql_destroy_schema(self, schema, style):
     70        qn = self.connection.ops.quote_name
     71        return "%s %s;" % (style.SQL_KEYWORD('DROP DATABASE'), qn(schema))
     72   
     73    def qualified_index_name(self, model, col):
     74        """
     75        On MySQL we must use the db_schema prefixed to the index name as
     76        indexes can not be placed into different schemas.
     77        """
     78        from django.db.backends.util import truncate_name
     79        schema = model._meta.db_schema or self.connection.schema
     80        max_len = self.connection.ops.max_name_length()
     81        schema_prefix = ''
     82        if schema:
     83             schema = self.connection.convert_schema(schema)
     84             schema_prefix = truncate_name(schema, max_len / 2) + '_'
     85        i_name = '%s%s_%s' % (schema_prefix, model._meta.db_table, self._digest(col))
     86        i_name = self.connection.ops.quote_name(truncate_name(i_name, max_len))
     87        return i_name
     88
     89    def qualified_name_for_ref(self, from_table, ref_table):
     90        """
     91        MySQL does not have qualified name format for indexes, so make sure to
     92        use qualified names if needed.
     93        """
     94        from_qn = self.connection.introspection.qname_converter(from_table)
     95        to_qn = self.connection.introspection.qname_converter(ref_table)
     96        if to_qn.schema is None:
     97            to_qn = QName(self.connection.settings_dict['NAME'],
     98                          to_qn.table, to_qn.db_format)
     99        return super(DatabaseCreation, self).qualified_name_for_ref(from_qn, to_qn)
  • django/db/backends/mysql/base.py

     
    1515    from django.core.exceptions import ImproperlyConfigured
    1616    raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
    1717
     18from django.db.backends.util import truncate_name
     19
    1820# We want version (1, 2, 1, 'final', 2) or later. We can't just use
    1921# lexicographic ordering in this check because then (1, 2, 1, 'gamma')
    2022# inadvertently passes the version test.
     
    187189        "Confirm support for introspected foreign keys"
    188190        return self._mysql_storage_engine() != 'MyISAM'
    189191
     192    def confirm(self):
     193        super(DatabaseFeatures, self).confirm()
     194        self.supports_foreign_keys != self.can_introspect_foreign_keys
     195
    190196class DatabaseOperations(BaseDatabaseOperations):
    191197    compiler_module = "django.db.backends.mysql.compiler"
    192198
     
    245251            return name # Quoting once is enough.
    246252        return "`%s`" % name
    247253
     254    def qualified_name(self, qname):
     255        schema = qname.schema
     256        if not qname.db_format:
     257            schema = self.connection.convert_schema(schema)
     258        if schema:
     259            return "%s.%s" % (self.quote_name(schema),
     260                              self.quote_name(qname.table))
     261        else:
     262            return self.quote_name(qname.table)
     263
    248264    def random_function_sql(self):
    249265        return 'RAND()'
    250266
     
    255271        if tables:
    256272            sql = ['SET FOREIGN_KEY_CHECKS = 0;']
    257273            for table in tables:
    258                 sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
     274                sql.append('%s %s;'
     275                           % (style.SQL_KEYWORD('TRUNCATE'),
     276                              style.SQL_FIELD(self.qualified_name(table))))
    259277            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
    260278
    261279            # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
     
    263281            sql.extend(["%s %s %s %s %s;" % \
    264282                (style.SQL_KEYWORD('ALTER'),
    265283                 style.SQL_KEYWORD('TABLE'),
    266                  style.SQL_TABLE(self.quote_name(sequence['table'])),
     284                 style.SQL_TABLE(self.qualified_name((sequence['qname']))),
    267285                 style.SQL_KEYWORD('AUTO_INCREMENT'),
    268286                 style.SQL_FIELD('= 1'),
    269287                ) for sequence in sequences])
     
    347365        self.creation = DatabaseCreation(self)
    348366        self.introspection = DatabaseIntrospection(self)
    349367        self.validation = DatabaseValidation(self)
     368   
     369    def convert_schema(self, schema):
     370        schema = schema or self.schema
     371        if schema and self.test_schema_prefix:
     372            return truncate_name('%s%s' % (self.test_schema_prefix, schema),
     373                                 self.ops.max_name_length())
     374        return schema
    350375
    351376    def _valid_connection(self):
    352377        if self.connection is not None:
     
    442467        ALL IMMEDIATE")
    443468        """
    444469        cursor = self.cursor()
     470        qn3 = self.ops.qualified_name
    445471        if table_names is None:
    446             table_names = self.introspection.get_table_list(cursor)
     472            table_names = self.introspection.get_visible_tables_list(cursor)
    447473        for table_name in table_names:
    448474            primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
    449475            if not primary_key_column_name:
     
    451477            key_columns = self.introspection.get_key_columns(cursor, table_name)
    452478            for column_name, referenced_table_name, referenced_column_name in key_columns:
    453479                cursor.execute("""
    454                     SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
    455                     LEFT JOIN `%s` as REFERRED
     480                    SELECT REFERRING.`%s`, REFERRING.`%s` FROM %s as REFERRING
     481                    LEFT JOIN %s as REFERRED
    456482                    ON (REFERRING.`%s` = REFERRED.`%s`)
    457483                    WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL"""
    458                     % (primary_key_column_name, column_name, table_name, referenced_table_name,
    459                     column_name, referenced_column_name, column_name, referenced_column_name))
     484                    % (primary_key_column_name, column_name, qn3(table_name),
     485                       qn3(referenced_table_name), column_name,
     486                       referenced_column_name, column_name,
     487                       referenced_column_name))
    460488                for bad_row in cursor.fetchall():
    461489                    raise utils.IntegrityError("The row in table '%s' with primary key '%s' has an invalid "
    462490                        "foreign key: %s.%s contains a value '%s' that does not have a corresponding value in %s.%s."
    463                         % (table_name, bad_row[0],
    464                         table_name, column_name, bad_row[1],
    465                         referenced_table_name, referenced_column_name))
     491                        % (table_name[1], bad_row[0],
     492                        table_name[1], column_name, bad_row[1],
     493                        referenced_table_name[1], referenced_column_name))
  • django/db/backends/oracle/base.py

     
    44Requires cx_Oracle: http://cx-oracle.sourceforge.net/
    55"""
    66
    7 
    87import datetime
    98import decimal
    109import sys
    1110import warnings
    1211
     12from django.db import QName
    1313
    1414def _setup_environment(environ):
    1515    import platform
     
    4848from django.conf import settings
    4949from django.db import utils
    5050from django.db.backends import *
     51from django.db.backends.util import truncate_name
    5152from django.db.backends.signals import connection_created
    5253from django.db.backends.oracle.client import DatabaseClient
    5354from django.db.backends.oracle.creation import DatabaseCreation
     
    8788class DatabaseOperations(BaseDatabaseOperations):
    8889    compiler_module = "django.db.backends.oracle.compiler"
    8990
    90     def autoinc_sql(self, table, column):
     91    def autoinc_sql(self, qname, column):
    9192        # To simulate auto-incrementing primary keys in Oracle, we have to
    9293        # create a sequence and a trigger.
    93         sq_name = self._get_sequence_name(table)
    94         tr_name = self._get_trigger_name(table)
    95         tbl_name = self.quote_name(table)
    96         col_name = self.quote_name(column)
     94        seq_name = self._get_sequence_name(qname)
     95        schema = seq_name.schema.upper()
     96        sname = self.connection.ops.qualified_name(seq_name).upper()
     97        params = {
     98            'sq_name': seq_name.table,
     99            'schema': schema,
     100            'qualified_sq_name': sname,
     101            'tr_name': self._get_trigger_name(qname),
     102            'tbl_name': self.qualified_name(qname),
     103            'col_name' : self.quote_name(column),
     104        }
    97105        sequence_sql = """
    98106DECLARE
    99107    i INTEGER;
    100108BEGIN
    101     SELECT COUNT(*) INTO i FROM USER_CATALOG
    102         WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
     109    SELECT COUNT(*) INTO i FROM ALL_CATALOG
     110        WHERE TABLE_NAME = '%(sq_name)s' AND OWNER = '%(schema)s'
     111              AND TABLE_TYPE = 'SEQUENCE';
    103112    IF i = 0 THEN
    104         EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
     113        EXECUTE IMMEDIATE 'CREATE SEQUENCE %(qualified_sq_name)s';
    105114    END IF;
    106115END;
    107 /""" % locals()
     116/""" % params
    108117        trigger_sql = """
    109 CREATE OR REPLACE TRIGGER "%(tr_name)s"
     118CREATE OR REPLACE TRIGGER %(tr_name)s
    110119BEFORE INSERT ON %(tbl_name)s
    111120FOR EACH ROW
    112121WHEN (new.%(col_name)s IS NULL)
    113122    BEGIN
    114         SELECT "%(sq_name)s".nextval
     123        SELECT %(qualified_sq_name)s.nextval
    115124        INTO :new.%(col_name)s FROM dual;
    116125    END;
    117 /""" % locals()
     126/""" % params
    118127        return sequence_sql, trigger_sql
    119128
    120129    def date_extract_sql(self, lookup_type, field_name):
     
    196205    def deferrable_sql(self):
    197206        return " DEFERRABLE INITIALLY DEFERRED"
    198207
    199     def drop_sequence_sql(self, table):
    200         return "DROP SEQUENCE %s;" % self.quote_name(self._get_sequence_name(table))
     208    def drop_sequence_sql(self, qname):
     209        seq_name = self._get_sequence_name(qname)
     210        qname = self.connection.ops.qualified_name(seq_name)
     211        return "DROP SEQUENCE %s;" % qname
    201212
    202213    def fetch_returned_insert_id(self, cursor):
    203214        return long(cursor._insert_id_var.getvalue())
     
    213224        # The DB API definition does not define this attribute.
    214225        return cursor.statement
    215226
    216     def last_insert_id(self, cursor, table_name, pk_name):
    217         sq_name = self._get_sequence_name(table_name)
    218         cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
     227    def last_insert_id(self, cursor, qualified_name, pk_name):
     228        sq_name = self._get_sequence_name(qualified_name)
     229        cursor.execute('SELECT %s.currval FROM dual' % sq_name)
    219230        return cursor.fetchone()[0]
    220231
    221232    def lookup_cast(self, lookup_type):
     
    247258                                               self.max_name_length())
    248259        return name.upper()
    249260
     261    def qualified_name(self, qname):
     262        assert isinstance(qname, QName)
     263        schema = qname.schema
     264        if not qname.db_format:
     265            if not schema:
     266                schema = self.connection.schema
     267            schema = self.connection.convert_schema(schema)
     268        if schema:
     269            return "%s.%s" % (self.quote_name(schema),
     270                              self.quote_name(qname.table))
     271        else:
     272            return self.quote_name(qname.table)
     273
    250274    def random_function_sql(self):
    251275        return "DBMS_RANDOM.RANDOM"
    252276
     277
    253278    def regex_lookup_9(self, lookup_type):
    254279        raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
    255280
     
    284309            sql = ['%s %s %s;' % \
    285310                    (style.SQL_KEYWORD('DELETE'),
    286311                     style.SQL_KEYWORD('FROM'),
    287                      style.SQL_FIELD(self.quote_name(table)))
     312                     style.SQL_FIELD(self.qualified_name(table)))
    288313                    for table in tables]
    289314            # Since we've just deleted all the rows, running our sequence
    290315            # ALTER code will reset the sequence to 0.
    291316            for sequence_info in sequences:
    292                 sequence_name = self._get_sequence_name(sequence_info['table'])
    293                 table_name = self.quote_name(sequence_info['table'])
    294                 column_name = self.quote_name(sequence_info['column'] or 'id')
    295                 query = _get_sequence_reset_sql() % {'sequence': sequence_name,
    296                                                      'table': table_name,
    297                                                      'column': column_name}
    298                 sql.append(query)
     317                q = self._sequence_reset_sql_for_col(
     318                    sequence_info['qname'], sequence_info['column'] or 'id')
     319                sql.append(q)
    299320            return sql
    300321        else:
    301322            return []
    302323
     324    def _sequence_reset_sql_for_col(self, qname, column):
     325        qname = self.connection.introspection.qname_converter(qname)
     326        qn = self.connection.ops.qualified_name
     327        table = qn(qname)
     328        sequence_name = self._get_sequence_name(qname)
     329        column_name = self.quote_name(column)
     330        params = {'sequence': sequence_name.table, 'table': table,
     331                  'schema': sequence_name.schema.upper(), 'column': column_name}
     332        query = _get_sequence_reset_sql(bool(params['schema']))
     333        return query % params
     334
    303335    def sequence_reset_sql(self, style, model_list):
    304336        from django.db import models
    305337        output = []
    306         query = _get_sequence_reset_sql()
    307338        for model in model_list:
    308339            for f in model._meta.local_fields:
    309340                if isinstance(f, models.AutoField):
    310                     table_name = self.quote_name(model._meta.db_table)
    311                     sequence_name = self._get_sequence_name(model._meta.db_table)
    312                     column_name = self.quote_name(f.column)
    313                     output.append(query % {'sequence': sequence_name,
    314                                            'table': table_name,
    315                                            'column': column_name})
     341                    q = self._sequence_reset_sql_for_col(
     342                        model._meta.qualified_name, f.column)
     343                    output.append(q)
    316344                    # Only one AutoField is allowed per model, so don't
    317345                    # continue to loop
    318346                    break
    319347            for f in model._meta.many_to_many:
    320348                if not f.rel.through:
    321                     table_name = self.quote_name(f.m2m_db_table())
    322                     sequence_name = self._get_sequence_name(f.m2m_db_table())
    323                     column_name = self.quote_name('id')
    324                     output.append(query % {'sequence': sequence_name,
    325                                            'table': table_name,
    326                                            'column': column_name})
     349                    q = self._sequence_reset_sql_for_col(
     350                        f.m2m_qualified_name(), 'id')
     351                    output.append(q)
    327352        return output
    328353
    329354    def start_transaction_sql(self):
     
    377402            raise NotImplementedError("Bit-wise or is not supported in Oracle.")
    378403        return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
    379404
    380     def _get_sequence_name(self, table):
     405    def _get_sequence_name(self, qname):
     406        assert isinstance(qname, QName)
     407        qname = self.connection.introspection.qname_converter(qname,
     408                                                              force_schema=True)
    381409        name_length = self.max_name_length() - 3
    382         return '%s_SQ' % util.truncate_name(table, name_length).upper()
     410        seq_name = '%s_SQ' % util.truncate_name(qname.table,
     411                                                name_length).upper()
     412        return QName(qname.schema, seq_name, True)
     413        #return '%s_SQ' % util.truncate_name(name, name_length).upper()
    383414
    384     def _get_trigger_name(self, table):
     415    def _get_trigger_name(self, qname):
     416        assert isinstance(qname, QName)
     417        qname = self.connection.introspection.qname_converter(qname)
    385418        name_length = self.max_name_length() - 3
    386         return '%s_TR' % util.truncate_name(table, name_length).upper()
     419        trig_name = '%s_TR' % util.truncate_name(qname.table,
     420                                                 name_length).upper()
     421        return self.qualified_name(QName(qname.schema, trig_name, True))
    387422
    388423    def bulk_insert_sql(self, fields, num_values):
    389424        items_sql = "SELECT %s FROM DUAL" % ", ".join(["%s"] * len(fields))
     
    444479        self.creation = DatabaseCreation(self)
    445480        self.introspection = DatabaseIntrospection(self)
    446481        self.validation = BaseDatabaseValidation(self)
     482   
     483    def convert_schema(self, schema):
     484        schema = schema or self.schema
     485        if schema and self.test_schema_prefix:
     486            return truncate_name('%s%s' % (self.test_schema_prefix, schema),
     487                                 self.ops.max_name_length())
     488        return schema
    447489
    448490    def check_constraints(self, table_names=None):
    449491        """
     
    476518            conn_params = self.settings_dict['OPTIONS'].copy()
    477519            if 'use_returning_into' in conn_params:
    478520                del conn_params['use_returning_into']
    479             self.connection = Database.connect(conn_string, **conn_params)
     521            try:
     522                self.connection = Database.connect(conn_string, **conn_params)
     523            except:
     524                print conn_string
     525                raise
    480526            cursor = FormatStylePlaceholderCursor(self.connection)
    481527            # Set oracle date to ansi date format.  This only needs to execute
    482528            # once when we create a new connection. We also set the Territory
     
    814860    return s
    815861
    816862
    817 def _get_sequence_reset_sql():
     863def _get_sequence_reset_sql(with_schema=False):
    818864    # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
    819     return """
     865    if with_schema:
     866        return """
    820867DECLARE
    821868    table_value integer;
    822869    seq_value integer;
    823870BEGIN
    824871    SELECT NVL(MAX(%(column)s), 0) INTO table_value FROM %(table)s;
     872    SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM all_sequences
     873           WHERE sequence_name = '%(sequence)s' AND sequence_owner = '%(schema)s';
     874    WHILE table_value > seq_value LOOP
     875        SELECT "%(schema)s"."%(sequence)s".nextval INTO seq_value FROM dual;
     876    END LOOP;
     877END;
     878/"""
     879    else:
     880        return """
     881DECLARE
     882    table_value integer;
     883    seq_value integer;
     884BEGIN
     885    SELECT NVL(MAX(%(column)s), 0) INTO table_value FROM %(table)s;
    825886    SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences
    826887           WHERE sequence_name = '%(sequence)s';
    827888    WHILE table_value > seq_value LOOP
  • django/db/backends/oracle/introspection.py

     
     1from django.db import QName
    12from django.db.backends import BaseDatabaseIntrospection
    23import cx_Oracle
    34import re
     
    3738            return super(DatabaseIntrospection, self).get_field_type(
    3839                data_type, description)
    3940
    40     def get_table_list(self, cursor):
    41         "Returns a list of table names in the current database."
    42         cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
    43         return [row[0].lower() for row in cursor.fetchall()]
     41    def get_visible_tables_list(self, cursor):
     42        "Returns a list of visible tables"
     43        return self.get_qualified_tables_list(cursor, [self.connection.settings_dict['USER']])
    4444
    45     def get_table_description(self, cursor, table_name):
     45    def get_qualified_tables_list(self, cursor, schemas):
     46        "Returns a list of table names in the given schemas list."
     47        default_schema = self.connection.convert_schema(None)
     48        if default_schema:
     49            schemas.append(default_schema)
     50        if not schemas:
     51            return []
     52        param_list = ', '.join(['%s']*len(schemas))
     53        schemas = [s.upper() for s in schemas]
     54        cursor.execute("""
     55            SELECT OWNER, TABLE_NAME
     56              FROM ALL_TABLES WHERE OWNER in (%s)""" % param_list, schemas)
     57        return [QName(row[0].lower(), row[1].lower(), True)
     58                for row in cursor.fetchall()]
     59
     60    def get_table_description(self, cursor, qname):
    4661        "Returns a description of the table, with the DB-API cursor.description interface."
    47         cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
     62        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2"
     63                       % self.connection.ops.qualified_name(qname))
    4864        description = []
    4965        for desc in cursor.description:
    5066            description.append((desc[0].lower(),) + desc[1:])
    5167        return description
    5268
    53     def table_name_converter(self, name):
    54         "Table name comparison is case insensitive under Oracle"
     69    def identifier_converter(self, name):
    5570        return name.lower()
    5671
    57     def _name_to_index(self, cursor, table_name):
     72    def qname_converter(self, qname, force_schema=False):
     73        assert isinstance(qname, QName)
     74        if qname.db_format and (qname.schema or not force_schema):
     75            return qname
     76        schema = self.connection.convert_schema(qname.schema)
     77        if not schema and force_schema:
     78            schema = self.connection.settings_dict['USER']
     79        return QName(schema, qname.table, True)
     80
     81    def _name_to_index(self, cursor, qname):
    5882        """
    5983        Returns a dictionary of {field_name: field_index} for the given table.
    6084        Indexes are 0-based.
    6185        """
    62         return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
     86        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, qname))])
    6387
    64     def get_relations(self, cursor, table_name):
     88    def get_relations(self, cursor, qname):
    6589        """
    6690        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    6791        representing all relationships to the given table. Indexes are 0-based.
    6892        """
    69         table_name = table_name.upper()
     93        qname = self.qname_converter(qname, force_schema=True)
     94        schema, table = qname.schema.upper(), qname.table.upper()
    7095        cursor.execute("""
    71     SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
    72     FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
    73            user_tab_cols ta, user_tab_cols tb
    74     WHERE  user_constraints.table_name = %s AND
     96    SELECT ta.column_id - 1, tb.table_name, tb.owner, tb.column_id - 1
     97    FROM   all_constraints, ALL_CONS_COLUMNS ca, ALL_CONS_COLUMNS cb,
     98           all_tab_cols ta, all_tab_cols tb
     99    WHERE  all_constraints.table_name = %s AND
     100           all_constraints.owner = %s AND
    75101           ta.table_name = %s AND
     102           ta.owner = %s AND
    76103           ta.column_name = ca.column_name AND
    77104           ca.table_name = %s AND
    78            user_constraints.constraint_name = ca.constraint_name AND
    79            user_constraints.r_constraint_name = cb.constraint_name AND
     105           ca.owner = %s AND
     106           all_constraints.constraint_name = ca.constraint_name AND
     107           all_constraints.r_constraint_name = cb.constraint_name AND
    80108           cb.table_name = tb.table_name AND
    81109           cb.column_name = tb.column_name AND
    82            ca.position = cb.position""", [table_name, table_name, table_name])
     110           ca.position = cb.position""", [table, schema, table, schema,
     111                                          table, schema])
    83112
    84113        relations = {}
    85114        for row in cursor.fetchall():
    86             relations[row[0]] = (row[2], row[1].lower())
     115            relations[row[0]] = (
     116                row[3], QName(row[2].lower(), row[1].lower(), True))
    87117        return relations
    88118
    89     def get_indexes(self, cursor, table_name):
     119    def get_indexes(self, cursor, qname):
    90120        """
    91121        Returns a dictionary of fieldname -> infodict for the given table,
    92122        where each infodict is in the format:
     
    96126        # This query retrieves each index on the given table, including the
    97127        # first associated field name
    98128        # "We were in the nick of time; you were in great peril!"
     129        qname = self.qname_converter(qname, force_schema=True)
     130        schema, table = qname.schema.upper(), qname.table.upper()
     131        # There can be multiple constraints for a given column, and we
     132        # are interested if _any_ of them is unique or primary key, hence
     133        # the group by + max.
    99134        sql = """\
    100 SELECT LOWER(all_tab_cols.column_name) AS column_name,
    101        CASE user_constraints.constraint_type
    102            WHEN 'P' THEN 1 ELSE 0
    103        END AS is_primary_key,
    104        CASE user_indexes.uniqueness
    105            WHEN 'UNIQUE' THEN 1 ELSE 0
    106        END AS is_unique
    107 FROM   all_tab_cols, user_cons_columns, user_constraints, user_ind_columns, user_indexes
    108 WHERE  all_tab_cols.column_name = user_cons_columns.column_name (+)
    109   AND  all_tab_cols.table_name = user_cons_columns.table_name (+)
    110   AND  user_cons_columns.constraint_name = user_constraints.constraint_name (+)
    111   AND  user_constraints.constraint_type (+) = 'P'
    112   AND  user_ind_columns.column_name (+) = all_tab_cols.column_name
    113   AND  user_ind_columns.table_name (+) = all_tab_cols.table_name
    114   AND  user_indexes.uniqueness (+) = 'UNIQUE'
    115   AND  user_indexes.index_name (+) = user_ind_columns.index_name
    116   AND  all_tab_cols.table_name = UPPER(%s)
     135SELECT column_name, max(is_primary_key), max(is_unique)
     136  FROM (
     137    SELECT LOWER(all_tab_cols.column_name) AS column_name,
     138           CASE all_constraints.constraint_type
     139               WHEN 'P' THEN 1 ELSE 0
     140           END AS is_primary_key,
     141           CASE all_indexes.uniqueness
     142               WHEN 'UNIQUE' THEN 1 ELSE 0
     143           END AS is_unique
     144    FROM   all_tab_cols, all_cons_columns, all_constraints, all_ind_columns, all_indexes
     145    WHERE  all_tab_cols.column_name = all_cons_columns.column_name (+)
     146      AND  all_tab_cols.table_name = all_cons_columns.table_name (+)
     147      AND  all_tab_cols.owner = all_cons_columns.owner (+)
     148      AND  all_cons_columns.constraint_name = all_constraints.constraint_name (+)
     149      AND  all_cons_columns.owner = all_constraints.owner (+)
     150      AND  all_constraints.constraint_type (+) = 'P'
     151      AND  all_ind_columns.column_name (+) = all_tab_cols.column_name
     152      AND  all_ind_columns.table_name (+) = all_tab_cols.table_name
     153      AND  all_ind_columns.index_owner (+) = all_tab_cols.owner
     154      AND  all_indexes.uniqueness (+) = 'UNIQUE'
     155      AND  all_indexes.index_name (+) = all_ind_columns.index_name
     156      AND  all_indexes.table_owner (+) = all_ind_columns.index_owner
     157      AND  all_tab_cols.table_name = %s
     158      AND  all_tab_cols.owner = %s
     159    )
     160GROUP BY column_name
    117161"""
    118         cursor.execute(sql, [table_name])
     162        cursor.execute(sql, [table, schema])
    119163        indexes = {}
    120164        for row in cursor.fetchall():
    121165            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
    122166        return indexes
     167
     168    def get_schema_list(self, cursor):
     169        cursor.execute("SELECT USERNAME FROM ALL_USERS")
     170        return [r[0] for r in cursor.fetchall()]
  • django/db/backends/oracle/creation.py

     
    11import sys
    22import time
     3from django.db import QName
    34from django.db.backends.creation import BaseDatabaseCreation
     5from django.core.management.color import no_style
    46
    57TEST_DATABASE_PREFIX = 'test_'
    68PASSWORD = 'Im_a_lumberjack'
     
    4345    def __init__(self, connection):
    4446        super(DatabaseCreation, self).__init__(connection)
    4547
    46     def _create_test_db(self, verbosity=1, autoclobber=False):
     48    def _get_ddl_parameters(self):
    4749        TEST_NAME = self._test_database_name()
    4850        TEST_USER = self._test_database_user()
    4951        TEST_PASSWD = self._test_database_passwd()
    5052        TEST_TBLSPACE = self._test_database_tblspace()
    5153        TEST_TBLSPACE_TMP = self._test_database_tblspace_tmp()
    52 
    53         parameters = {
     54        return {
    5455            'dbname': TEST_NAME,
    5556            'user': TEST_USER,
    5657            'password': TEST_PASSWD,
     
    5859            'tblspace_temp': TEST_TBLSPACE_TMP,
    5960        }
    6061
     62    def _create_test_db(self, verbosity=1, autoclobber=False, schemas=[]):
     63        parameters = self._get_ddl_parameters()
    6164        cursor = self.connection.cursor()
    6265        if self._test_database_create():
    6366            try:
     
    6568            except Exception, e:
    6669                sys.stderr.write("Got an error creating the test database: %s\n" % e)
    6770                if not autoclobber:
    68                     confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_NAME)
     71                    confirm = raw_input("It appears the test database, %(dbname)s, already "
     72                                        "exists. Type 'yes' to delete it, or 'no' to cancel: "
     73                                        % parameters)
    6974                if autoclobber or confirm == 'yes':
    7075                    try:
    7176                        if verbosity >= 1:
     
    8388            if verbosity >= 1:
    8489                print "Creating test user..."
    8590            try:
    86                 self._create_test_user(cursor, parameters, verbosity)
     91                self._create_test_user(cursor, parameters, verbosity, dba=bool(schemas))
    8792            except Exception, e:
    8893                sys.stderr.write("Got an error creating the test user: %s\n" % e)
    8994                if not autoclobber:
    90                     confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_USER)
     95                    confirm = raw_input("It appears the test user, %(user)s, already exists. "
     96                                        "Type 'yes' to delete it, or 'no' to cancel: "
     97                                        % parameters)
    9198                if autoclobber or confirm == 'yes':
    9299                    try:
    93100                        if verbosity >= 1:
     
    95102                        self._destroy_test_user(cursor, parameters, verbosity)
    96103                        if verbosity >= 1:
    97104                            print "Creating test user..."
    98                         self._create_test_user(cursor, parameters, verbosity)
     105                        self._create_test_user(cursor, parameters, verbosity, dba=bool(schemas))
    99106                    except Exception, e:
    100107                        sys.stderr.write("Got an error recreating the test user: %s\n" % e)
    101108                        sys.exit(2)
     
    105112
    106113        self.connection.settings_dict['SAVED_USER'] = self.connection.settings_dict['USER']
    107114        self.connection.settings_dict['SAVED_PASSWORD'] = self.connection.settings_dict['PASSWORD']
    108         self.connection.settings_dict['TEST_USER'] = self.connection.settings_dict['USER'] = TEST_USER
    109         self.connection.settings_dict['PASSWORD'] = TEST_PASSWD
     115        self.connection.settings_dict['TEST_USER'] = self.connection.settings_dict['USER'] = parameters['user']
     116        self.connection.settings_dict['PASSWORD'] = parameters['password']
    110117
    111118        return self.connection.settings_dict['NAME']
    112119
     
    115122        Destroy a test database, prompting the user for confirmation if the
    116123        database already exists. Returns the name of the test database created.
    117124        """
    118         TEST_NAME = self._test_database_name()
    119         TEST_USER = self._test_database_user()
    120         TEST_PASSWD = self._test_database_passwd()
    121         TEST_TBLSPACE = self._test_database_tblspace()
    122         TEST_TBLSPACE_TMP = self._test_database_tblspace_tmp()
     125        parameters = self._get_ddl_parameters()
    123126
    124127        self.connection.settings_dict['USER'] = self.connection.settings_dict['SAVED_USER']
    125128        self.connection.settings_dict['PASSWORD'] = self.connection.settings_dict['SAVED_PASSWORD']
    126129
    127         parameters = {
    128             'dbname': TEST_NAME,
    129             'user': TEST_USER,
    130             'password': TEST_PASSWD,
    131             'tblspace': TEST_TBLSPACE,
    132             'tblspace_temp': TEST_TBLSPACE_TMP,
    133         }
    134 
    135130        cursor = self.connection.cursor()
    136131        time.sleep(1) # To avoid "database is being accessed by other users" errors.
     132        if self._test_database_create():
     133            if verbosity >= 1:
     134                print 'Destroying test database tables...'
     135            self._execute_test_db_destruction(cursor, parameters, verbosity)
    137136        if self._test_user_create():
    138137            if verbosity >= 1:
    139138                print 'Destroying test user...'
    140139            self._destroy_test_user(cursor, parameters, verbosity)
    141         if self._test_database_create():
    142             if verbosity >= 1:
    143                 print 'Destroying test database tables...'
    144             self._execute_test_db_destruction(cursor, parameters, verbosity)
    145140        self.connection.close()
    146141
    147142    def _execute_test_db_creation(self, cursor, parameters, verbosity):
     
    159154        ]
    160155        self._execute_statements(cursor, statements, parameters, verbosity)
    161156
    162     def _create_test_user(self, cursor, parameters, verbosity):
     157    def _create_test_user(self, cursor, parameters, verbosity, dba=False):
    163158        if verbosity >= 2:
    164159            print "_create_test_user(): username = %s" % parameters['user']
     160        parameters = parameters.copy()
     161        parameters['dba'] = ', DBA' if dba else ''
     162
    165163        statements = [
    166164            """CREATE USER %(user)s
    167165               IDENTIFIED BY %(password)s
    168166               DEFAULT TABLESPACE %(tblspace)s
    169167               TEMPORARY TABLESPACE %(tblspace_temp)s
    170168            """,
    171             """GRANT CONNECT, RESOURCE TO %(user)s""",
     169            """GRANT CONNECT, RESOURCE %(dba)s TO %(user)s""",
    172170        ]
    173171        self._execute_statements(cursor, statements, parameters, verbosity)
    174172
     
    186184            print "_destroy_test_user(): user=%s" % parameters['user']
    187185            print "Be patient.  This can take some time..."
    188186        statements = [
    189             'DROP USER %(user)s CASCADE',
     187            self.sql_destroy_schema(parameters['user'], style=None)
    190188        ]
    191189        self._execute_statements(cursor, statements, parameters, verbosity)
    192190
     191    def sql_destroy_schema(self, schema, style):
     192        return "DROP USER %s CASCADE" % schema
     193
     194
    193195    def _execute_statements(self, cursor, statements, parameters, verbosity):
    194196        for template in statements:
    195197            stmt = template % parameters
     
    272274
    273275    def set_autocommit(self):
    274276        self.connection.connection.autocommit = True
     277
     278    def _create_test_schemas(self, verbosity, schemas, autoclobber):
     279        if not self._test_user_create():
     280            return []
     281        cursor = self.connection.cursor()
     282        parameters = self._get_ddl_parameters()
     283        parameters['authorization'] = parameters['user']
     284        conv = self.connection.introspection.identifier_converter
     285        existing_schemas = [conv(s) for s in self.connection.introspection.get_schema_list(cursor)]
     286        conflicts = [conv(s) for s in existing_schemas if conv(s) in schemas]
     287        if conflicts:
     288            print 'The following users already exists: %s' % ', '.join(conflicts)
     289            if not autoclobber:
     290                confirm = raw_input(
     291                    "Type 'yes' if you would like to try deleting these users "
     292                    "or 'no' to cancel: ")
     293            if autoclobber or confirm == 'yes':
     294                for schema in conflicts:
     295                    parameters['user'] = schema
     296                    if verbosity >= 1:
     297                        print "Destroying user %s" % schema
     298                    self._destroy_test_user(cursor, parameters, verbosity)
     299                    existing_schemas.remove(schema)
     300            else:
     301                print "Tests cancelled."
     302                sys.exit(1)
     303           
     304        to_create = [s for s in schemas if s not in existing_schemas]
     305        for schema in to_create:
     306            parameters['user'] = schema
     307            if verbosity >= 1:
     308                print "Creating user %s" % schema
     309            self._create_test_user(cursor, parameters, verbosity)
     310        return to_create
     311
     312    def needs_separate_conn(self, from_qname, to_qname):
     313        conv = self.connection.introspection.qname_converter
     314        def_schema = conv(QName(None, None, False), force_schema=True).schema
     315        from_qname = conv(from_qname, force_schema=True)
     316        to_qname = conv(to_qname, force_schema=True)
     317        return (def_schema != from_qname.schema
     318                or to_qname.schema != from_qname.schema)
     319
     320    def sql_for_inline_foreign_key_references(self, field, known_models, style):
     321        """
     322        Return the SQL snippet defining the foreign key reference for a field.
     323
     324        Oracle doesn't let you do cross-schema foreign keys, except if you
     325        are connected to the "from" schema. Don't ask why.
     326        """
     327        if self.needs_separate_conn(field.model._meta.qualified_name,
     328                                    field.rel.to._meta.qualified_name):
     329            return [], True
     330        return super(DatabaseCreation, self).sql_for_inline_foreign_key_references(field, known_models, style)
     331
     332    def sql_for_pending_references(self, model, style, pending_references,
     333                                   second_pass=False):
     334        """
     335        Sad fact of life: On Oracle it is impossible to do cross-schema
     336        references unless you explisitly grant REFERENCES on the referenced
     337        table, and in addition the reference is made from the schema
     338        containing the altered table (the one getting the new constraint).
     339        To make this even nicer, it is impossible to do the GRANT using the
     340        same user we are giving the REFERENCES right, as you can't GRANT
     341        yourself.
     342
     343        The solution we are using is to do the pending cross-schema references
     344        in two stages after all tables have been created:
     345            1) Connect as the foreign key's target table owner, and grant
     346               REFERENCES to all users needing to do foreign keys.
     347            2) Connect as the source table's owner, and create the foreign
     348               keys.
     349        To support this arrangement, we will create only non-cross-schema
     350        references unless we are explicitly told by the second_pass flag
     351        that it is safe to do the cross schema references.
     352
     353        It is possible to grant REFERENCES to public (but it seems other roles
     354        will not work), but as we need to anyways do this multi-connection
     355        dance it seems better to do the grants explicitly only when needed.
     356        """
     357        if second_pass:
     358            return super(DatabaseCreation, self).sql_for_pending_references(
     359                model, style, pending_references)
     360        # Split the "safe" and "unsafe" references apart, and call
     361        # the super() method for the safe set.
     362        cross_schema_refs = []
     363        single_schema_refs = []
     364        if model in pending_references:
     365            for rel_class, f in pending_references[model]:
     366                if self.needs_separate_conn(rel_class._meta.qualified_name,
     367                                            model._meta.qualified_name):
     368                    cross_schema_refs.append((rel_class, f))
     369                else:
     370                    single_schema_refs.append((rel_class, f))
     371        sql = []
     372        if single_schema_refs:
     373            pending_references[model] = single_schema_refs
     374            sql = super(DatabaseCreation, self).sql_for_pending_references(
     375                model, style, pending_references)
     376        if cross_schema_refs:
     377            pending_references[model] = cross_schema_refs
     378        return sql
     379
     380    def post_create_pending_references(self, pending_references, as_sql=False):
     381        # Build a dictionary: from_schema -> [(model, refs)...]
     382        references_to_schema = {}
     383        sql = []
     384        conv = self.connection.introspection.qname_converter
     385        for model, refs in pending_references.items():
     386            schema = conv(model._meta.qualified_name, force_schema=True).schema
     387            if schema not in references_to_schema:
     388                references_to_schema[schema] = []
     389            references_to_schema[schema].append((model, refs))
     390        # Pass 1: give grants.
     391        for schema, all_refs in references_to_schema.items():
     392            grant_to = set()
     393            for model, refs in all_refs:
     394                for ref in refs:
     395                    to_user = conv(ref[0]._meta.qualified_name,
     396                                   force_schema=True).schema
     397                    if to_user != schema:
     398                        grant_to.add((model, to_user))
     399            sql.extend(self._grant_references(schema, grant_to, as_sql))
     400        # Prepare for pass 2. This time we must connect as the user
     401        # of the altered table's schema. So, first build a dictionary of
     402        # from_schema -> [{model: [refs]}], that is, build a
     403        # pending_references for each schema separately.
     404        references_from_schema = {}
     405        for model, refs in pending_references.items():
     406            for ref in refs:
     407                schema = conv(ref[0]._meta.qualified_name,
     408                              force_schema=True).schema
     409                if schema not in references_from_schema:
     410                    references_from_schema[schema] = {}
     411                if model not in references_from_schema[schema]:
     412                    references_from_schema[schema][model] = []
     413                references_from_schema[schema][model].append(ref)
     414        # Pass 2: create the actual references
     415        for schema, ref_dict in references_from_schema.items():
     416            per_schema_sql = ['-- Connect as user "%s"' % schema] if as_sql else []
     417            for model, refs in ref_dict.items():
     418                ref_sql = self.sql_for_pending_references(model, no_style(),
     419                                                          ref_dict, second_pass=True)
     420                per_schema_sql.extend(ref_sql)
     421            if not as_sql:
     422                self._run_sql_as_user(schema, per_schema_sql)
     423            sql.extend(per_schema_sql)
     424        return sql
     425
     426    def _grant_references(self, schema, grant_to, as_sql):
     427        sql = ['-- Connect as user "%s"' % schema] if as_sql else []
     428        qn = self.connection.ops.quote_name
     429        for model, user in grant_to:
     430            sql.append('GRANT REFERENCES ON %s TO %s'
     431                       % (qn(model._meta.db_table), qn(user)))
     432        if not as_sql:
     433            self._run_sql_as_user(schema, sql)
     434        return sql
     435
     436    def _run_sql_as_user(self, user, sql):
     437        if not sql:
     438            return
     439        self.connection.close()
     440        try:
     441            old_settings = self.connection.settings_dict.copy()
     442            self.connection.settings_dict['USER'] = user
     443            cursor = self.connection.cursor()
     444            for q in sql:
     445                cursor.execute(q)
     446        finally:
     447            self.connection.close()
     448            self.connection.settings_dict = old_settings
  • django/db/backends/__init__.py

     
    77from contextlib import contextmanager
    88
    99from django.conf import settings
    10 from django.db import DEFAULT_DB_ALIAS
     10from django.db import DEFAULT_DB_ALIAS, QName
    1111from django.db.backends import util
    1212from django.db.transaction import TransactionManagementError
    1313from django.utils.importlib import import_module
     
    4444
    4545    def __ne__(self, other):
    4646        return not self == other
     47   
     48    def _get_schema(self):
     49        return self.settings_dict['SCHEMA']
     50    schema = property(_get_schema)
    4751
     52    def _get_test_schema_prefix(self):
     53        return self.settings_dict['TEST_SCHEMA_PREFIX']
     54    test_schema_prefix = property(_get_test_schema_prefix)
     55
     56    def convert_schema(self, schema):
     57        return schema or self.schema
     58
    4859    def _commit(self):
    4960        if self.connection is not None:
    5061            return self.connection.commit()
     
    311322    def make_debug_cursor(self, cursor):
    312323        return util.CursorDebugWrapper(cursor, self)
    313324
     325    def qname(self, model):
     326        """
     327        Given a model class or instance, returns its current database table
     328        name in schema qualified format.
     329        """
     330        return self.ops.qualified_name(model._meta.qualified_name)
     331
    314332class BaseDatabaseFeatures(object):
    315333    allows_group_by_pk = False
    316334    # True if django.db.backend.utils.typecast_timestamp is used on values
     
    404422    _confirmed = False
    405423    supports_transactions = None
    406424    supports_stddev = None
     425    supports_foreign_keys = True
    407426    can_introspect_foreign_keys = None
    408427
    409428    # Support for the DISTINCT ON clause
    410429    can_distinct_on_fields = False
    411430
     431    # If the database has databases and schemas as different concepts
     432    # or plain fakes schemas, it is safe to skip conflicts checking in
     433    # testing on that database.
     434    namespaced_schemas = False
     435
    412436    def __init__(self, connection):
    413437        self.connection = connection
    414438
     
    461485        self.connection = connection
    462486        self._cache = None
    463487
    464     def autoinc_sql(self, table, column):
     488    def autoinc_sql(self, qualified_name, column):
    465489        """
    466490        Returns any SQL needed to support auto-incrementing primary keys, or
    467491        None if no SQL is necessary.
     
    513537        """
    514538        return "DROP CONSTRAINT"
    515539
    516     def drop_sequence_sql(self, table):
     540    def drop_sequence_sql(self, qualified_name):
    517541        """
    518542        Returns any SQL necessary to drop the sequence for the given table.
    519543        Returns None if no SQL is necessary.
     
    594618
    595619        return smart_unicode(sql) % u_params
    596620
    597     def last_insert_id(self, cursor, table_name, pk_name):
     621    def last_insert_id(self, cursor, qualified_name, pk_name):
    598622        """
    599623        Given a cursor object that has just performed an INSERT statement into
    600624        a table that has an auto-incrementing ID, returns the newly created ID.
     
    673697        """
    674698        raise NotImplementedError()
    675699
     700    def qualified_name(self, qualified_name, from_model=False):
     701        """
     702        Formats the given schema, table_name tuple into database's
     703        qualified and quoted name format. The schema can be None.
     704
     705        We need to know if the name if from an existing database
     706        table, or from a model. The reason is that some backends
     707        do modifications to the name (schema-prefix the table name)
     708        at runtime, and we must not do that repeatedly. Hence, if
     709        the name comes from the DB and is already schema-prefixed,
     710        then we must not schema-prefix it again.
     711        """
     712        raise NotImplementedError
     713
    676714    def random_function_sql(self):
    677715        """
    678716        Returns a SQL expression that returns a random value.
     
    718756        """
    719757        return ''
    720758
    721     def sql_flush(self, style, tables, sequences):
     759    def sql_flush(self, style, tables, sequences, from_db):
    722760        """
    723761        Returns a list of SQL statements required to remove all data from
    724762        the given database tables (without actually removing the tables
     
    726764
    727765        The `style` argument is a Style object as returned by either
    728766        color_style() or no_style() in django.core.management.color.
     767
     768        The from_db argument tells if the names are coming from database
     769        or from model._meta, needed for schema support.
    729770        """
    730771        raise NotImplementedError()
    731772
     
    883924        distinguish between a FloatField and IntegerField, for example."""
    884925        return self.data_types_reverse[data_type]
    885926
    886     def table_name_converter(self, name):
    887         """Apply a conversion to the name for the purposes of comparison.
     927    def qname_converter(self, qname, force_schema=False):
     928        """
     929        Apply a conversion to the name for the purposes of comparison.
    888930
    889931        The default table name converter is for case sensitive comparison.
     932
     933        The given name must be a QName. If force_schema is set, then backends
     934        should try to append a default schema name to the given name if
     935        applicable for the backend.
    890936        """
    891         return name
     937        return qname
    892938
     939    def identifier_converter(self, identifier):
     940        """
     941        On some backends we need to do a little acrobaty to convert the names
     942        from the DB and names from Models into consistent format. For example,
     943        the return types from DB might be upper-case, but we need them
     944        lower-case for comparisons. This method can be used to convert
     945        identifiers into consistent format.
     946        """
     947        return identifier
     948
     949
    893950    def table_names(self):
    894         "Returns a list of names of all tables that exist in the database."
     951        "Returns a list of names of all tables that are visible in the database."
    895952        cursor = self.connection.cursor()
    896         return self.get_table_list(cursor)
     953        return self.get_visible_tables_list(cursor)
    897954
     955    def get_visible_tables_list(self, cursoe):
     956        """
     957        Returns all visible (in "search path") tables from the database.
     958        """
     959        return []
     960
     961    def qualified_names(self, schemas=None):
     962        """
     963        Returns qualified table names for all schemas Django is using.
     964        """
     965        cursor = self.connection.cursor()
     966        if schemas is None:
     967            return self.get_visible_tables_list(cursor)
     968        else:
     969            return
     970       
     971    def all_qualified_names(self):
     972        """
     973        Gets all table names from the database. Note that it is intentional
     974        that visible tables appear both as unqualified and qualified tables.
     975        """
     976        cursor = self.connection.cursor()
     977        nonqualified_tables = self.get_visible_tables_list(cursor)
     978        schemas = self.connection.creation.get_schemas()
     979        schemas = [self.connection.convert_schema(s) for s in schemas]
     980        qualified_tables = self.get_qualified_tables_list(cursor, schemas)
     981        return set([QName(None, t, from_db) for _, t, from_db
     982                    in nonqualified_tables]
     983                   + qualified_tables)
     984   
     985    def get_qualified_tables_list(self, cursor, schemas):
     986        """
     987        Returns schema qualified names (as pair schema, tbl_name) of all
     988        tables in the given schemas.
     989        """
     990        return []
     991
     992    def get_schema_list(self, cursor):
     993        "Returns a list of schemas that exist in the database"
     994        return []
     995   
     996
     997    def schema_names(self):
     998        "Returns a list of schemas that exist in the database"
     999        cursor = self.connection.cursor()
     1000        return self.get_schema_list(cursor)
     1001
    8981002    def django_table_names(self, only_existing=False):
    8991003        """
    900         Returns a list of all table names that have associated Django models and
    901         are in INSTALLED_APPS.
     1004        Returns a list of all table's qualified names that have associated
     1005        Django models and are in INSTALLED_APPS.
    9021006
    903         If only_existing is True, the resulting list will only include the tables
    904         that actually exist in the database.
     1007        If only_existing is True, the resulting list will only include the
     1008        tables that actually exist in the database.
    9051009        """
    9061010        from django.db import models, router
    9071011        tables = set()
     
    9111015                    continue
    9121016                if not router.allow_syncdb(self.connection.alias, model):
    9131017                    continue
    914                 tables.add(model._meta.db_table)
    915                 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
     1018                tables.add(model._meta.qualified_name)
     1019                tables.update([f.m2m_qualified_name()
     1020                               for f in model._meta.local_many_to_many])
    9161021        tables = list(tables)
    9171022        if only_existing:
    918             existing_tables = self.table_names()
    919             tables = [
    920                 t
    921                 for t in tables
    922                 if self.table_name_converter(t) in existing_tables
    923             ]
    924         return tables
     1023            found_tables = []
     1024            existing_tables = self.all_qualified_names()
     1025            found_tables.extend([
     1026                t for t in tables
     1027                if self.qname_converter(t) in existing_tables
     1028            ])
     1029            return found_tables
     1030        else:
     1031            return tables
    9251032
    9261033    def installed_models(self, tables):
    927         "Returns a set of all models represented by the provided list of table names."
     1034        """
     1035        Returns a set of all models represented by the provided list of table names.
     1036
     1037        The given tables are assumed to be pre-converted.
     1038        """
    9281039        from django.db import models, router
    9291040        all_models = []
    9301041        for app in models.get_apps():
    9311042            for model in models.get_models(app):
    9321043                if router.allow_syncdb(self.connection.alias, model):
    9331044                    all_models.append(model)
    934         tables = map(self.table_name_converter, tables)
    9351045        return set([
    9361046            m for m in all_models
    937             if self.table_name_converter(m._meta.db_table) in tables
     1047            if self.qname_converter(m._meta.qualified_name) in tables
    9381048        ])
    9391049
    9401050    def sequence_list(self):
    941         "Returns a list of information about all DB sequences for all models in all apps."
     1051        """
     1052        Returns a list of information about all DB sequences for all models
     1053        in all apps.
     1054        """
    9421055        from django.db import models, router
    9431056
    9441057        apps = models.get_apps()
     
    9521065                    continue
    9531066                for f in model._meta.local_fields:
    9541067                    if isinstance(f, models.AutoField):
    955                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
     1068                        qname = self.qname_converter(model._meta.qualified_name)
     1069                        sequence_list.append({'qname': qname, 'column': f.column})
    9561070                        break # Only one AutoField is allowed per model, so don't bother continuing.
    9571071
    9581072                for f in model._meta.local_many_to_many:
    9591073                    # If this is an m2m using an intermediate table,
    9601074                    # we don't need to reset the sequence.
    9611075                    if f.rel.through is None:
    962                         sequence_list.append({'table': f.m2m_db_table(), 'column': None})
     1076                        qname = self.qname_converter(f.m2m_qualified_name())
     1077                        sequence_list.append({'qname': qname,
     1078                                              'column': None})
    9631079
    9641080        return sequence_list
    9651081
  • django/core/management/commands/syncdb.py

     
    5555        db = options.get('database')
    5656        connection = connections[db]
    5757        cursor = connection.cursor()
    58 
     58        converter = connection.introspection.qname_converter
     59        # We might fetch the same table multiple times - once as qualified and
     60        # once as visible table (None, t). That is wanted, so that we can easily
     61        # see if a model with schema = None is installed, as well as if model with
     62        # locked schema is installed.
     63        tables = connection.introspection.all_qualified_names()
     64       
    5965        # Get a list of already installed *models* so that references work right.
    60         tables = connection.introspection.table_names()
    6166        seen_models = connection.introspection.installed_models(tables)
    6267        created_models = set()
    6368        pending_references = {}
     
    7176        ]
    7277        def model_installed(model):
    7378            opts = model._meta
    74             converter = connection.introspection.table_name_converter
    75             return not ((converter(opts.db_table) in tables) or
    76                 (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
     79            return not ((converter(opts.qualified_name) in tables) or
     80                (opts.auto_created and converter(opts.auto_created._meta.qualified_name) in tables))
    7781
    7882        manifest = SortedDict(
    7983            (app_name, filter(model_installed, model_list))
     
    8387        # Create the tables for each model
    8488        if verbosity >= 1:
    8589            print "Creating tables ..."
     90        seen_schemas = connection.introspection.schema_names()
     91        seen_schemas = set([connection.introspection.identifier_converter(s)
     92                            for s in seen_schemas])
     93
    8694        for app_name, model_list in manifest.items():
    8795            for model in model_list:
    8896                # Create the model's database table, if it doesn't already exist.
    8997                if verbosity >= 3:
    9098                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
    91                 sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
     99                sql = []
     100                schema = connection.convert_schema(model._meta.qualified_name[0])
     101                if schema and schema not in seen_schemas:
     102                    q = connection.creation.sql_create_schema(schema, self.style)
     103                    if q:
     104                        sql.append(q)
     105                    seen_schemas.add(schema)
     106                table_sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
     107                sql.extend(table_sql)
    92108                seen_models.add(model)
    93109                created_models.add(model)
    94110                for refto, refs in references.items():
    95111                    pending_references.setdefault(refto, []).extend(refs)
    96112                    if refto in seen_models:
    97                         sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
    98                 sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
     113                        ref_sql = connection.creation.sql_for_pending_references(
     114                            refto, self.style, pending_references)
     115                        if ref_sql:
     116                            sql.extend(ref_sql)
     117                ref_sql = sql.extend(connection.creation.sql_for_pending_references(
     118                    model, self.style, pending_references))
     119                if ref_sql:
     120                    sql.extend(ref_sql)
    99121                if verbosity >= 1 and sql:
    100                     print "Creating table %s" % model._meta.db_table
     122                    if model._meta.db_schema:
     123                        print "Creating table %s.%s" % model._meta.qualified_name
     124                    else:
     125                        print "Creating table %s" % model._meta.db_table
    101126                for statement in sql:
    102127                    cursor.execute(statement)
    103                 tables.append(connection.introspection.table_name_converter(model._meta.db_table))
     128                tables.add(connection.introspection.qname_converter(model._meta.qualified_name))
    104129
    105 
    106130        transaction.commit_unless_managed(using=db)
     131        # We need to see if there are still some pending references left: this
     132        # is possible on backends where we must do cross-schema references
     133        # using different connections (hence also outside the above
     134        # transaction)
     135        if pending_references:
     136            # Pass the references to connection-specific handler.
     137            connection.creation.post_create_pending_references(pending_references)
    107138
    108         # Send the post_syncdb signal, so individual apps can do whatever they need
    109         # to do at this point.
     139        # Send the post_syncdb signal, so individual apps can do whatever they
     140        # need to do at this point.
    110141        emit_post_sync_signal(created_models, verbosity, interactive, db)
    111142
    112143        # The connection may have been closed by a syncdb handler.
  • django/core/management/commands/loaddata.py

     
    218218
    219219            # Since we disabled constraint checks, we must manually check for
    220220            # any invalid keys that might have been added
    221             table_names = [model._meta.db_table for model in models]
    222             connection.check_constraints(table_names=table_names)
     221            qualified_names = [model._meta.qualified_name for model in models]
     222            connection.check_constraints(table_names=qualified_names)
    223223
    224224        except (SystemExit, KeyboardInterrupt):
    225225            raise
  • django/core/management/commands/inspectdb.py

     
    2727    def handle_inspection(self, options):
    2828        connection = connections[options.get('database')]
    2929
    30         table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
     30        table2model = lambda qname: qname[1].title().replace('_', '').replace(' ', '').replace('-', '')
    3131
    3232        cursor = connection.cursor()
    3333        yield "# This is an auto-generated Django model module."
     
    4141        yield ''
    4242        yield 'from %s import models' % self.db_module
    4343        yield ''
    44         for table_name in connection.introspection.get_table_list(cursor):
    45             yield 'class %s(models.Model):' % table2model(table_name)
     44        inspect = connection.introspection
     45        for qname in inspect.get_visible_tables_list(cursor):
     46            yield 'class %s(models.Model):' % table2model(qname)
    4647            try:
    47                 relations = connection.introspection.get_relations(cursor, table_name)
     48                relations = inspect.get_relations(cursor, qname)
    4849            except NotImplementedError:
    4950                relations = {}
    5051            try:
    51                 indexes = connection.introspection.get_indexes(cursor, table_name)
     52                indexes = inspect.get_indexes(cursor, qname)
    5253            except NotImplementedError:
    5354                indexes = {}
    54             for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
     55            for i, row in enumerate(inspect.get_table_description(cursor, qname)):
    5556                column_name = row[0]
    5657                att_name = column_name.lower()
    5758                comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
     
    8283                    comment_notes.append('Field name made lowercase.')
    8384
    8485                if i in relations:
    85                     rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
     86                    rel_to = relations[i][1] == qname and "'self'" or table2model(relations[i][1])
    8687                    field_type = 'ForeignKey(%s' % rel_to
    8788                    if att_name.endswith('_id'):
    8889                        att_name = att_name[:-3]
     
    9192                else:
    9293                    # Calling `get_field_type` to get the field type string and any
    9394                    # additional paramters and notes.
    94                     field_type, field_params, field_notes = self.get_field_type(connection, table_name, row)
     95                    field_type, field_params, field_notes = self.get_field_type(connection, qname, row)
    9596                    extra_params.update(field_params)
    9697                    comment_notes.extend(field_notes)
    9798
     
    118119                    extra_params['blank'] = True
    119120                    if not field_type in ('TextField(', 'CharField('):
    120121                        extra_params['null'] = True
    121 
    122122                field_desc = '%s = models.%s' % (att_name, field_type)
    123123                if extra_params:
    124124                    if not field_desc.endswith('('):
     
    128128                if comment_notes:
    129129                    field_desc += ' # ' + ' '.join(comment_notes)
    130130                yield '    %s' % field_desc
    131             for meta_line in self.get_meta(table_name):
     131            for meta_line in self.get_meta(qname):
    132132                yield meta_line
    133133
    134     def get_field_type(self, connection, table_name, row):
     134    def get_field_type(self, connection, qname, row):
    135135        """
    136136        Given the database connection, the table name, and the cursor row
    137137        description, this routine will return the given field type name, as
     
    162162
    163163        return field_type, field_params, field_notes
    164164
    165     def get_meta(self, table_name):
     165    def get_meta(self, qname):
    166166        """
    167167        Return a sequence comprising the lines of code necessary
    168168        to construct the inner Meta class for the model corresponding
    169169        to the given database table name.
    170170        """
    171171        return ['    class Meta:',
    172                 '        db_table = %r' % table_name,
     172                '        db_table = %r' % qname[1],
     173                '        db_schema = %r' % qname[0] or 'None',
    173174                '']
  • django/core/management/validation.py

     
    1919    validates all models of all installed apps. Writes errors, if any, to outfile.
    2020    Returns number of errors.
    2121    """
    22     from django.conf import settings
    2322    from django.db import models, connection
    2423    from django.db.models.loading import get_app_errors
    2524    from django.db.models.fields.related import RelatedObject
     
    3231
    3332    for cls in models.get_models(app):
    3433        opts = cls._meta
    35 
     34       
    3635        # Do field-specific validation.
    3736        for f in opts.local_fields:
    3837            if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
  • django/core/management/sql.py

     
    33
    44from django.conf import settings
    55from django.core.management.base import CommandError
    6 from django.db import models
     6from django.db import models, QName
    77from django.db.models import get_models
    88
    99def sql_create(app, style, connection):
    1010    "Returns a list of the CREATE TABLE SQL statements for the given app."
    11 
    1211    if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
    1312        # This must be the "dummy" database backend, which means the user
    1413        # hasn't set ENGINE for the database.
     
    2322    # we can be conservative).
    2423    app_models = models.get_models(app, include_auto_created=True)
    2524    final_output = []
    26     tables = connection.introspection.table_names()
     25    tables = connection.introspection.all_qualified_names()
    2726    known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
    2827    pending_references = {}
    2928
    3029    for model in app_models:
     30        schema = connection.convert_schema(model._meta.db_schema)
     31        if schema:
     32            output = connection.creation.sql_create_schema(schema, style)
     33            if output:
     34                final_output.append(output)
    3135        output, references = connection.creation.sql_create_model(model, style, known_models)
    3236        final_output.extend(output)
    3337        for refto, refs in references.items():
     
    6367
    6468    # Figure out which tables already exist
    6569    if cursor:
    66         table_names = connection.introspection.get_table_list(cursor)
     70        table_names = connection.introspection.all_qualified_names(converted=True)
    6771    else:
    6872        table_names = []
    6973
     
    7579    references_to_delete = {}
    7680    app_models = models.get_models(app, include_auto_created=True)
    7781    for model in app_models:
    78         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     82        if cursor and connection.introspection.qname_converter(model._meta.qualified_name) in table_names:
    7983            # The table exists, so it needs to be dropped
    8084            opts = model._meta
    8185            for f in opts.local_fields:
     
    8589            to_delete.add(model)
    8690
    8791    for model in app_models:
    88         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     92        if connection.introspection.qname_converter(model._meta.qualified_name) in table_names:
    8993            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    9094
    9195    # Close database connection explicitly, in case this output is being piped
     
    106110    if only_django:
    107111        tables = connection.introspection.django_table_names(only_existing=True)
    108112    else:
    109         tables = connection.introspection.table_names()
     113        tables = connection.introspection.all_qualified_names()
     114    if [t for t in tables if not isinstance(t, QName)]:
     115        import ipdb; ipdb.set_trace()
    110116    statements = connection.ops.sql_flush(
    111117        style, tables, connection.introspection.sequence_list()
    112118    )
     
    145151    if opts.managed:
    146152        post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
    147153        for f in post_sql_fields:
    148             output.extend(f.post_create_sql(style, model._meta.db_table))
     154            output.extend(f.post_create_sql(style, model._meta.qualified_name))
    149155
    150156    # Some backends can't execute more than one SQL statement at a time,
    151157    # so split into separate statements.
  • django/contrib/contenttypes/generic.py

     
    173173    def m2m_db_table(self):
    174174        return self.rel.to._meta.db_table
    175175
     176    def m2m_qualified_name(self):
     177        return self.rel.to._meta.qualified_name
     178
     179    def m2m_db_schema(self):
     180        return self.rel.to._meta.db_schema
     181
    176182    def m2m_column_name(self):
    177183        return self.object_id_field_name
    178184
  • django/test/simple.py

     
    271271        dependencies = {}
    272272        for alias in connections:
    273273            connection = connections[alias]
     274            if not connection.settings_dict['TEST_SCHEMA_PREFIX'] is None:
     275                connection.settings_dict['TEST_SCHEMA_PREFIX'] = '%s_' % alias
    274276            if connection.settings_dict['TEST_MIRROR']:
    275277                # If the database is marked as a test mirror, save
    276278                # the alias.
     
    301303            test_databases.items(), dependencies):
    302304            # Actually create the database for the first connection
    303305            connection = connections[aliases[0]]
    304             old_names.append((connection, db_name, True))
    305             test_db_name = connection.creation.create_test_db(
     306            test_db_name, created_schemas = connection.creation.create_test_db(
    306307                self.verbosity, autoclobber=not self.interactive)
     308            old_names.append((connection, db_name, True, created_schemas))
    307309            for alias in aliases[1:]:
    308310                connection = connections[alias]
    309311                if db_name:
    310                     old_names.append((connection, db_name, False))
     312                    old_names.append((connection, db_name, False, []))
    311313                    connection.settings_dict['NAME'] = test_db_name
    312314                else:
    313315                    # If settings_dict['NAME'] isn't defined, we have a backend
    314316                    # where the name isn't important -- e.g., SQLite, which
    315317                    # uses :memory:. Force create the database instead of
    316318                    # assuming it's a duplicate.
    317                     old_names.append((connection, db_name, True))
     319                    old_names.append((connection, db_name, True, []))
    318320                    connection.creation.create_test_db(
    319321                        self.verbosity, autoclobber=not self.interactive)
    320322
     
    335337        Destroys all the non-mirror databases.
    336338        """
    337339        old_names, mirrors = old_config
    338         for connection, old_name, destroy in old_names:
     340        for connection, old_name, destroy, created_schemas in old_names:
    339341            if destroy:
    340                 connection.creation.destroy_test_db(old_name, self.verbosity)
     342                connection.creation.destroy_test_db(old_name, created_schemas, self.verbosity)
    341343
    342344    def teardown_test_environment(self, **kwargs):
    343345        unittest.removeHandler()
  • tests/modeltests/raw_query/tests.py

     
    22
    33from datetime import date
    44
    5 from django.db.models.sql.query import InvalidQuery
     5from django.db.models.query_utils import InvalidQuery
     6from django.db import connection
    67from django.test import TestCase
    78
    89from .models import Author, Book, Coffee, Reviewer, FriendlyAuthor
     
    5960        """
    6061        Basic test of raw query with a simple database query
    6162        """
    62         query = "SELECT * FROM raw_query_author"
     63        query = "SELECT * FROM %s" % connection.qname(Author)
    6364        authors = Author.objects.all()
    6465        self.assertSuccessfulRawQuery(Author, query, authors)
    6566
     
    6869        Raw queries are lazy: they aren't actually executed until they're
    6970        iterated over.
    7071        """
    71         q = Author.objects.raw('SELECT * FROM raw_query_author')
     72        q = Author.objects.raw('SELECT * FROM %s' % connection.qname(Author))
    7273        self.assertTrue(q.query.cursor is None)
    7374        list(q)
    7475        self.assertTrue(q.query.cursor is not None)
     
    7778        """
    7879        Test of a simple raw query against a model containing a foreign key
    7980        """
    80         query = "SELECT * FROM raw_query_book"
     81        query = "SELECT * FROM %s" % connection.qname(Book)
    8182        books = Book.objects.all()
    8283        self.assertSuccessfulRawQuery(Book, query, books)
    8384
     
    8687        Test of a simple raw query against a model containing a field with
    8788        db_column defined.
    8889        """
    89         query = "SELECT * FROM raw_query_coffee"
     90        query = "SELECT * FROM %s" % connection.qname(Coffee)
    9091        coffees = Coffee.objects.all()
    9192        self.assertSuccessfulRawQuery(Coffee, query, coffees)
    9293
     
    102103        )
    103104
    104105        for select in selects:
    105             query = "SELECT %s FROM raw_query_author" % select
     106            query = "SELECT %s FROM %s" % (select, connection.qname(Author))
    106107            authors = Author.objects.all()
    107108            self.assertSuccessfulRawQuery(Author, query, authors)
    108109
     
    111112        Test of raw query's optional ability to translate unexpected result
    112113        column names to specific model fields
    113114        """
    114         query = "SELECT first_name AS first, last_name AS last, dob, id FROM raw_query_author"
     115        query = ("SELECT first_name AS first, last_name AS last, dob, id FROM %s"
     116                 % connection.qname(Author))
    115117        translations = {'first': 'first_name', 'last': 'last_name'}
    116118        authors = Author.objects.all()
    117119        self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
     
    120122        """
    121123        Test passing optional query parameters
    122124        """
    123         query = "SELECT * FROM raw_query_author WHERE first_name = %s"
     125        query = "SELECT * FROM %s WHERE first_name = %%s" % connection.qname(Author)
    124126        author = Author.objects.all()[2]
    125127        params = [author.first_name]
    126128        results = list(Author.objects.raw(query, params=params))
     
    132134        """
    133135        Test of a simple raw query against a model containing a m2m field
    134136        """
    135         query = "SELECT * FROM raw_query_reviewer"
     137        query = "SELECT * FROM %s" % connection.qname(Reviewer)
    136138        reviewers = Reviewer.objects.all()
    137139        self.assertSuccessfulRawQuery(Reviewer, query, reviewers)
    138140
     
    140142        """
    141143        Test to insure that extra translations are ignored.
    142144        """
    143         query = "SELECT * FROM raw_query_author"
     145        query = "SELECT * FROM %s" % connection.qname(Author)
    144146        translations = {'something': 'else'}
    145147        authors = Author.objects.all()
    146148        self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
    147149
    148150    def testMissingFields(self):
    149         query = "SELECT id, first_name, dob FROM raw_query_author"
     151        query = "SELECT id, first_name, dob FROM %s" % connection.qname(Author)
    150152        for author in Author.objects.raw(query):
    151153            self.assertNotEqual(author.first_name, None)
    152154            # last_name isn't given, but it will be retrieved on demand
    153155            self.assertNotEqual(author.last_name, None)
    154156
    155157    def testMissingFieldsWithoutPK(self):
    156         query = "SELECT first_name, dob FROM raw_query_author"
     158        query = "SELECT first_name, dob FROM %s" % connection.qname(Author)
    157159        try:
    158160            list(Author.objects.raw(query))
    159161            self.fail('Query without primary key should fail')
     
    161163            pass
    162164
    163165    def testAnnotations(self):
    164         query = "SELECT a.*, count(b.id) as book_count FROM raw_query_author a LEFT JOIN raw_query_book b ON a.id = b.author_id GROUP BY a.id, a.first_name, a.last_name, a.dob ORDER BY a.id"
     166        query = "SELECT a.*, count(b.id) as book_count FROM %s a LEFT JOIN %s b ON a.id = b.author_id GROUP BY a.id, a.first_name, a.last_name, a.dob ORDER BY a.id" % (connection.qname(Author), connection.qname(Book))
    165167        expected_annotations = (
    166168            ('book_count', 3),
    167169            ('book_count', 0),
     
    172174        self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
    173175
    174176    def testWhiteSpaceQuery(self):
    175         query = "    SELECT * FROM raw_query_author"
     177        query = "    SELECT * FROM %s" % connection.qname(Author)
    176178        authors = Author.objects.all()
    177179        self.assertSuccessfulRawQuery(Author, query, authors)
    178180
    179181    def testMultipleIterations(self):
    180         query = "SELECT * FROM raw_query_author"
     182        query = "SELECT * FROM %s" % connection.qname(Author)
    181183        normal_authors = Author.objects.all()
    182184        raw_authors = Author.objects.raw(query)
    183185
     
    197199
    198200    def testGetItem(self):
    199201        # Indexing on RawQuerySets
    200         query = "SELECT * FROM raw_query_author ORDER BY id ASC"
     202        query = "SELECT * FROM %s ORDER BY id ASC" % connection.qname(Author)
    201203        third_author = Author.objects.raw(query)[2]
    202204        self.assertEqual(third_author.first_name, 'Bob')
    203205
     
    211213        # Wesley was bron
    212214        f = FriendlyAuthor.objects.create(first_name="Wesley", last_name="Chun",
    213215            dob=date(1962, 10, 28))
    214         query = "SELECT * FROM raw_query_friendlyauthor"
     216        query = "SELECT * FROM %s" % connection.qname(FriendlyAuthor)
    215217        self.assertEqual(
    216218            [o.pk for o in FriendlyAuthor.objects.raw(query)], [f.pk]
    217219        )
    218220
    219221    def test_query_count(self):
    220222        self.assertNumQueries(1,
    221             list, Author.objects.raw("SELECT * FROM raw_query_author")
     223            list, Author.objects.raw("SELECT * FROM %s" % connection.qname(Author))
    222224        )
  • tests/modeltests/timezones/tests.py

     
    264264        dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
    265265        event = Event.objects.create(dt=dt)
    266266        self.assertQuerysetEqual(
    267                 Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]),
     267                Event.objects.raw('SELECT * FROM %s WHERE dt = %%s'
     268                                  % connection.qname(Event),
     269                                  [dt]),
    268270                [event],
    269271                transform=lambda d: d)
    270272
     
    476478        dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
    477479        event = Event.objects.create(dt=dt)
    478480        self.assertQuerysetEqual(
    479                 Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]),
     481                Event.objects.raw('SELECT * FROM %s WHERE dt = %%s'
     482                                  % connection.qname(Event), [dt]),
    480483                [event],
    481484                transform=lambda d: d)
    482485
  • tests/modeltests/proxy_models/tests.py

     
    33from django.contrib.contenttypes.models import ContentType
    44from django.core import management
    55from django.core.exceptions import FieldError
    6 from django.db import models, DEFAULT_DB_ALIAS
     6from django.db import models, DEFAULT_DB_ALIAS, transaction, IntegrityError
    77from django.db.models import signals
    8 from django.test import TestCase
     8from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature
    99
    1010
    1111from .models import (MyPerson, Person, StatusPerson, LowerStatusPerson,
     
    326326        management.call_command('loaddata', 'mypeople.json', verbosity=0, commit=False)
    327327        p = MyPerson.objects.get(pk=100)
    328328        self.assertEqual(p.name, 'Elvis Presley')
     329
     330class TransactionalProxyModelTests(TransactionTestCase):
     331    @skipUnlessDBFeature('supports_foreign_keys')
     332    def test_proxy_fk(self):
     333        """
     334        Test that the DB contains proper foreign keys for proxy model references.
     335        """
     336        @transaction.commit_on_success
     337        def create_failing_pk():
     338            t = TrackerUser.objects.create(status='bar')
     339            Improvement.objects.create(summary='foof', version='foof',
     340                                       reporter_id=1, associated_bug_id=1,
     341                                       assignee=t)
     342        self.assertRaises(IntegrityError, create_failing_pk)
  • tests/modeltests/select_for_update/tests.py

     
    3636        # issuing a SELECT ... FOR UPDATE will block.
    3737        new_connections = ConnectionHandler(settings.DATABASES)
    3838        self.new_connection = new_connections[DEFAULT_DB_ALIAS]
    39 
    4039        # We need to set settings.DEBUG to True so we can capture
    4140        # the output SQL to examine.
    4241        self._old_debug = settings.DEBUG
     
    6261        # end_blocking_transaction() should be called.
    6362        self.cursor = self.new_connection.cursor()
    6463        sql = 'SELECT * FROM %(db_table)s %(for_update)s;' % {
    65             'db_table': Person._meta.db_table,
     64            'db_table': self.new_connection.qname(Person),
    6665            'for_update': self.new_connection.ops.for_update_sql(),
    6766            }
    6867        self.cursor.execute(sql, ())
    69         result = self.cursor.fetchone()
     68        self.cursor.fetchone()
    7069
    7170    def end_blocking_transaction(self):
    7271        # Roll back the blocking transaction.
     
    241240                list(
    242241                    Person.objects.raw(
    243242                        'SELECT * FROM %s %s' % (
    244                             Person._meta.db_table,
     243                            connection.qname(Person),
    245244                            connection.ops.for_update_sql(nowait=True)
    246245                        )
    247246                    )
  • tests/modeltests/unmanaged_models/tests.py

     
    4848        """
    4949        The intermediary table between two unmanaged models should not be created.
    5050        """
    51         table = Unmanaged2._meta.get_field('mm').m2m_db_table()
    52         tables = connection.introspection.table_names()
    53         self.assertTrue(table not in tables, "Table '%s' should not exist, but it does." % table)
     51        conv = connection.introspection.qname_converter
     52        table = conv(Unmanaged2.mm.through._meta.qualified_name)
     53        tables = connection.introspection.all_qualified_names()
     54        self.assertTrue(table not in tables, "Table '%s' should not exist, but it does." % table[1])
    5455
    5556    def test_many_to_many_between_unmanaged_and_managed(self):
    5657        """
    5758        An intermediary table between a managed and an unmanaged model should be created.
    5859        """
    59         table = Managed1._meta.get_field('mm').m2m_db_table()
    60         tables = connection.introspection.table_names()
    61         self.assertTrue(table in tables, "Table '%s' does not exist." % table)
     60        conv = connection.introspection.qname_converter
     61        table = conv(Managed1.mm.through._meta.qualified_name)
     62        tables = connection.introspection.all_qualified_names()
     63        self.assertTrue(table in tables, "Table '%s' does not exist." % table[1])
  • tests/modeltests/many_to_one/tests.py

     
    44from datetime import datetime
    55
    66from django.core.exceptions import MultipleObjectsReturned
     7from django.db import connection
    78from django.test import TestCase
    89from django.utils.translation import ugettext_lazy
    910
     
    169170        # The automatically joined table has a predictable name.
    170171        self.assertQuerysetEqual(
    171172            Article.objects.filter(reporter__first_name__exact='John').extra(
    172                 where=["many_to_one_reporter.last_name='Smith'"]),
     173                where=["%s.last_name='Smith'" % connection.qname(Reporter)]),
    173174            [
    174175                "<Article: John's second story>",
    175176                "<Article: This is a test>",
     
    177178        # ... and should work fine with the unicode that comes out of forms.Form.cleaned_data
    178179        self.assertQuerysetEqual(
    179180            Article.objects.filter(reporter__first_name__exact='John'
    180                                   ).extra(where=["many_to_one_reporter.last_name='%s'" % u'Smith']),
     181                                  ).extra(where=["%s.last_name='%s'" %
     182                                                 (connection.qname(Reporter), u'Smith')]),
    181183            [
    182184                "<Article: John's second story>",
    183185                "<Article: This is a test>",
  • tests/modeltests/prefetch_related/tests.py

     
    372372            l = [a.authorwithage for a in Author.objects.prefetch_related('authorwithage')]
    373373
    374374        # Regression for #18090: the prefetching query must include an IN clause.
    375         self.assertIn('authorwithage', connection.queries[-1]['sql'])
    376         self.assertIn(' IN ', connection.queries[-1]['sql'])
     375        executed_sql = connection.queries[-1]['sql'].lower()
     376        self.assertIn('authorwithage', executed_sql)
     377        self.assertIn(' in ', executed_sql)
    377378
    378379        self.assertEqual(l, [a.authorwithage for a in Author.objects.all()])
    379380
  • tests/modeltests/select_related/tests.py

     
    11from __future__ import absolute_import
    22
     3from django.db import connection
    34from django.test import TestCase
    45
    56from .models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species
     
    120121
    121122    def test_select_related_with_extra(self):
    122123        s = Species.objects.all().select_related(depth=1)\
    123             .extra(select={'a': 'select_related_species.id + 10'})[0]
     124            .extra(select={'a': '%s.id + 10' % connection.qname(Species)})[0]
    124125        self.assertEqual(s.id + 10, s.a)
    125126
    126127    def test_certain_fields(self):
  • tests/modeltests/custom_methods/models.py

     
    3131        cursor = connection.cursor()
    3232        cursor.execute("""
    3333            SELECT id, headline, pub_date
    34             FROM custom_methods_article
    35             WHERE pub_date = %s
    36                 AND id != %s""", [connection.ops.value_to_db_date(self.pub_date),
    37                                   self.id])
     34            FROM %s
     35            WHERE pub_date = %%s
     36                AND id != %%s""" % connection.qname(self),
     37                       [connection.ops.value_to_db_date(self.pub_date),
     38                        self.id])
    3839        return [self.__class__(*row) for row in cursor.fetchall()]
  • tests/modeltests/transactions/tests.py

     
    164164class TransactionRollbackTests(TransactionTestCase):
    165165    def execute_bad_sql(self):
    166166        cursor = connection.cursor()
    167         cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
     167        tbl = connection.qname(Reporter)
     168        cursor.execute("INSERT INTO %s (first_name, last_name) VALUES ('Douglas', 'Adams');" % tbl)
    168169        transaction.set_dirty()
    169170
    170171    @skipUnlessDBFeature('requires_rollback_on_dirty_transaction')
     
    305306        with self.assertRaises(IntegrityError):
    306307            with transaction.commit_on_success():
    307308                cursor = connection.cursor()
    308                 cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
     309                tbl = connection.qname(Reporter)
     310                cursor.execute("INSERT INTO %s (first_name, last_name) VALUES ('Douglas', 'Adams');" % tbl)
    309311                transaction.set_dirty()
    310312        transaction.rollback()
  • tests/regressiontests/transactions_regress/tests.py

     
    2525        def raw_sql():
    2626            "Write a record using raw sql under a commit_on_success decorator"
    2727            cursor = connection.cursor()
    28             cursor.execute("INSERT into transactions_regress_mod (id,fld) values (17,18)")
     28            tbl = connection.qname(Mod)
     29            cursor.execute("INSERT into %s (id,fld) values (17,18)" % tbl)
    2930
    3031        raw_sql()
    3132        # Rollback so that if the decorator didn't commit, the record is unwritten
     
    116117            be committed.
    117118            """
    118119            cursor = connection.cursor()
    119             cursor.execute("INSERT into transactions_regress_mod (id,fld) values (1,2)")
     120            tbl = connection.qname(Mod)
     121            cursor.execute("INSERT into %s (id,fld) values (1,2)" % tbl)
    120122            transaction.rollback()
    121             cursor.execute("INSERT into transactions_regress_mod (id,fld) values (1,2)")
     123            cursor.execute("INSERT into %s (id,fld) values (1,2)" % tbl)
    122124
    123125        reuse_cursor_ref()
    124126        # Rollback so that if the decorator didn't commit, the record is unwritten
  • tests/regressiontests/queries/tests.py

     
    66
    77from django.conf import settings
    88from django.core.exceptions import FieldError
     9from django.contrib.sites.models import Site
    910from django.db import DatabaseError, connection, connections, DEFAULT_DB_ALIAS
    1011from django.db.models import Count
    1112from django.db.models.query import Q, ITER_CHUNK_SIZE, EmptyQuerySet
     
    387388
    388389    def test_ticket2496(self):
    389390        self.assertQuerysetEqual(
    390             Item.objects.extra(tables=['queries_author']).select_related().order_by('name')[:1],
     391            Item.objects.extra(tables=[Author._meta.qualified_name]).select_related().order_by('name')[:1],
    391392            ['<Item: four>']
    392393        )
    393394
     
    507508        self.assertEqual(d, {'a': u'one', 'b': u'two'})
    508509
    509510        # Order by the number of tags attached to an item.
    510         l = Item.objects.extra(select={'count': 'select count(*) from queries_item_tags where queries_item_tags.item_id = queries_item.id'}).order_by('-count')
     511        l = Item.objects.extra(select={
     512            'count':'select count(*) from %s where %s.item_id = %s.id' %
     513            (connection.qname(Item.tags.through), connection.qname(Item.tags.through),
     514             connection.qname(Item))
     515        }).order_by('-count')
    511516        self.assertEqual([o.count for o in l], [2, 2, 1, 0])
    512517
    513518    def test_ticket6154(self):
     
    577582
    578583    def test_ticket7098(self):
    579584        # Make sure semi-deprecated ordering by related models syntax still
    580         # works.
    581         self.assertValueQuerysetEqual(
    582             Item.objects.values('note__note').order_by('queries_note.note', 'id'),
    583             [{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
    584         )
     585        # works.       
     586        # Skip this test if schema support is in effect - there is little point to fix the
     587        # deprecated .order_by() notation to support schemas.
     588        if not connection.schema:
     589            self.assertValueQuerysetEqual(
     590                # Need to remove the quotes from the table name for this test...
     591                Item.objects.values('note__note').order_by('%s.note' % connection.qname(Note)[1:-1], 'id'),
     592                [{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
     593            )
    585594
    586595    def test_ticket7096(self):
    587596        # Make sure exclude() with multiple conditions continues to work.
     
    12161225        # Ordering of extra() pieces is possible, too and you can mix extra
    12171226        # fields and model fields in the ordering.
    12181227        self.assertQuerysetEqual(
    1219             Ranking.objects.extra(tables=['django_site'], order_by=['-django_site.id', 'rank']),
     1228            Ranking.objects.extra(tables=[Site._meta.qualified_name],
     1229                                  order_by=['-%s.id' % connection.qname(Site), 'rank']),
    12201230            ['<Ranking: 1: a3>', '<Ranking: 2: a2>', '<Ranking: 3: a1>']
    12211231        )
    12221232
     
    12511261
    12521262    def test_ticket7045(self):
    12531263        # Extra tables used to crash SQL construction on the second use.
    1254         qs = Ranking.objects.extra(tables=['django_site'])
     1264        qs = Ranking.objects.extra(tables=[connection.qname(Site)])
    12551265        qs.query.get_compiler(qs.db).as_sql()
    12561266        # test passes if this doesn't raise an exception.
    12571267        qs.query.get_compiler(qs.db).as_sql()
  • tests/regressiontests/delete_regress/tests.py

     
    1717    def setUp(self):
    1818        # Create a second connection to the default database
    1919        conn_settings = settings.DATABASES[DEFAULT_DB_ALIAS]
     20        # TODO: there must be a better way to do this copying. .deepcopy()
     21        # perhaps?
    2022        self.conn2 = backend.DatabaseWrapper({
    2123            'HOST': conn_settings['HOST'],
    2224            'NAME': conn_settings['NAME'],
     
    2527            'PORT': conn_settings['PORT'],
    2628            'USER': conn_settings['USER'],
    2729            'TIME_ZONE': settings.TIME_ZONE,
     30            'SCHEMA': conn_settings['SCHEMA'],
     31            'TEST_SCHEMA_PREFIX': conn_settings['TEST_SCHEMA_PREFIX'],
     32            'TEST_SCHEMAS': conn_settings['TEST_SCHEMAS'],
    2833        })
    2934
    3035        # Put both DB connections into managed transaction mode
     
    5560
    5661        # Delete something using connection 2.
    5762        cursor2 = self.conn2.cursor()
    58         cursor2.execute('DELETE from delete_regress_book WHERE id=1')
     63        cursor2.execute('DELETE from %s WHERE id=1' % self.conn2.qname(Book))
    5964        self.conn2._commit()
    6065
    6166        # Now perform a queryset delete that covers the object
  • tests/regressiontests/backends/tests.py

     
    99from django.core.management.color import no_style
    1010from django.core.exceptions import ImproperlyConfigured
    1111from django.db import (backend, connection, connections, DEFAULT_DB_ALIAS,
    12     IntegrityError, transaction)
     12    IntegrityError, transaction, QName)
    1313from django.db.backends.signals import connection_created
    1414from django.db.backends.postgresql_psycopg2 import version as pg_version
    1515from django.db.utils import ConnectionHandler, DatabaseError, load_backend
     
    129129        "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)"
    130130        cursor = connection.cursor()
    131131        query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (
    132             connection.introspection.table_name_converter('backends_square'),
     132            connection.introspection.identifier_converter('backends_square'),
    133133            connection.ops.quote_name('root'),
    134134            connection.ops.quote_name('square')
    135135        ))
     
    169169        VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
    170170        VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
    171171        tables = [
    172             VLM._meta.db_table,
    173             VLM_m2m._meta.db_table,
     172            VLM._meta.qualified_name,
     173            VLM_m2m._meta.qualified_name
    174174        ]
    175175        sequences = [
    176176            {
    177177                'column': VLM._meta.pk.column,
    178                 'table': VLM._meta.db_table
     178                'qname': QName(None, VLM._meta.db_table, False),
    179179            },
    180180        ]
    181181        cursor = connection.cursor()
     
    313313    def create_squares_with_executemany(self, args):
    314314        cursor = connection.cursor()
    315315        opts = models.Square._meta
    316         tbl = connection.introspection.table_name_converter(opts.db_table)
     316        tbl = connection.qname(models.Square)
    317317        f1 = connection.ops.quote_name(opts.get_field('root').column)
    318318        f2 = connection.ops.quote_name(opts.get_field('square').column)
    319319        query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2)
     
    358358        opts2 = models.Person._meta
    359359        f3, f4 = opts2.get_field('first_name'), opts2.get_field('last_name')
    360360        query2 = ('SELECT %s, %s FROM %s ORDER BY %s'
    361           % (qn(f3.column), qn(f4.column), connection.introspection.table_name_converter(opts2.db_table),
     361          % (qn(f3.column), qn(f4.column), connection.qname(models.Person),
    362362             qn(f3.column)))
    363363        cursor = connection.cursor()
    364364        cursor.execute(query2)
     
    375375    def test_duplicate_table_error(self):
    376376        """ Test that creating an existing table returns a DatabaseError """
    377377        cursor = connection.cursor()
    378         query = 'CREATE TABLE %s (id INTEGER);' % models.Article._meta.db_table
     378        query = 'CREATE TABLE %s (id INTEGER);' % connection.qname(models.Article)
    379379        with self.assertRaises(DatabaseError):
    380380            cursor.execute(query)
    381381
  • tests/regressiontests/inspectdb/tests.py

     
     1from __future__ import absolute_import
     2
    13from StringIO import StringIO
    24
    35from django.core.management import call_command
     
    24from django.test import TestCase, skipUnlessDBFeature
     5from .models import People
    36
     
    811
    912    @skipUnlessDBFeature('can_introspect_foreign_keys')
    1013    def test_attribute_name_not_python_keyword(self):
     14        from django.db import connection
     15        _, tbl, _ = connection.introspection.qname_converter(People._meta.qualified_name)
     16        mname = ''.join(t.title() for t in tbl.split('_'))
    1117        out = StringIO()
    1218        call_command('inspectdb', stdout=out)
    1319        error_message = "inspectdb generated an attribute name which is a python keyword"
    14         self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", out.getvalue(), msg=error_message)
    15         self.assertIn("from_field = models.ForeignKey(InspectdbPeople)", out.getvalue())
    16         self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)",
     20        self.assertNotIn("from = models.ForeignKey(%s)" % mname, out.getvalue(), msg=error_message)
     21        self.assertIn("from_field = models.ForeignKey(%s)" % mname, out.getvalue())
     22        self.assertIn("people_pk = models.ForeignKey(%s, primary_key=True)" % mname,
    1723            out.getvalue())
    18         self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)",
     24        self.assertIn("people_unique = models.ForeignKey(%s, unique=True)" % mname,
    1925            out.getvalue())
    2026        out.close()
    2127
  • tests/regressiontests/extra_regress/tests.py

     
    33import datetime
    44
    55from django.contrib.auth.models import User
     6from django.db import connection
    67from django.test import TestCase
    78from django.utils.datastructures import SortedDict
    89
     
    4243        # Queryset to match most recent revision:
    4344        qs = RevisionableModel.objects.extra(
    4445                where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {
    45                     'table': RevisionableModel._meta.db_table,
     46                    'table': connection.qname(RevisionableModel),
    4647                }]
    4748        )
    4849
  • tests/regressiontests/aggregation_regress/tests.py

     
    66from operator import attrgetter
    77
    88from django.core.exceptions import FieldError
     9from django.db import connection
    910from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F, Q
    1011from django.test import TestCase, Approximate, skipUnlessDBFeature
    1112
     
    6970        #oracle doesn't support subqueries in group by clause
    7071        shortest_book_sql = """
    7172        SELECT name
    72         FROM aggregation_regress_book b
    73         WHERE b.publisher_id = aggregation_regress_publisher.id
     73        FROM %s b
     74        WHERE b.publisher_id = %s.id
    7475        ORDER BY b.pages
    7576        LIMIT 1
    76         """
     77        """ % (connection.qname(Book), connection.qname(Book))
    7778        # tests that this query does not raise a DatabaseError due to the full
    7879        # subselect being (erroneously) added to the GROUP BY parameters
    7980        qs = Publisher.objects.extra(select={
  • tests/regressiontests/admin_scripts/tests.py

     
    33advertised - especially with regards to the handling of the DJANGO_SETTINGS_MODULE
    44and default settings.py files.
    55"""
    6 
    76import os
    87import re
    98import shutil
     
    1312
    1413from django import conf, bin, get_version
    1514from django.conf import settings
     15from django.db import connection
    1616from django.test.simple import DjangoTestSuiteRunner
    1717from django.utils import unittest
    1818from django.test import LiveServerTestCase
    1919
     20from .models import Article
     21
    2022test_dir = os.path.dirname(os.path.dirname(__file__))
    21 expected_query_re = re.compile(r'CREATE TABLE [`"]admin_scripts_article[`"]', re.IGNORECASE)
    2223
    23 
    2424class AdminScriptTestCase(unittest.TestCase):
    2525    def write_settings(self, filename, apps=None, is_dir=False, sdict=None):
    2626        test_dir = os.path.dirname(os.path.dirname(__file__))
     
    844844    """
    845845    def setUp(self):
    846846        self.write_settings('alternate_settings.py')
     847        tblname = connection.qname(Article)
     848        self.expected_query_re = re.compile(r'CREATE TABLE %s' % tblname, re.IGNORECASE)
    847849
    848850    def tearDown(self):
    849851        self.remove_settings('alternate_settings.py')
     
    859861        "alternate: manage.py builtin commands work with settings provided as argument"
    860862        args = ['sqlall', '--settings=alternate_settings', 'admin_scripts']
    861863        out, err = self.run_manage(args)
    862         self.assertRegexpMatches(out, expected_query_re)
     864        self.assertRegexpMatches(out, self.expected_query_re)
    863865        self.assertNoOutput(err)
    864866
    865867    def test_builtin_with_environment(self):
    866868        "alternate: manage.py builtin commands work if settings are provided in the environment"
    867869        args = ['sqlall', 'admin_scripts']
    868870        out, err = self.run_manage(args, 'alternate_settings')
    869         self.assertRegexpMatches(out, expected_query_re)
     871        self.assertRegexpMatches(out, self.expected_query_re)
    870872        self.assertNoOutput(err)
    871873
    872874    def test_builtin_with_bad_settings(self):
  • tests/regressiontests/introspection/tests.py

     
    22
    33from functools import update_wrapper
    44
    5 from django.db import connection
     5from django.conf import settings
     6from django.db import connection, QName
    67from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature
    78
    89from .models import Reporter, Article
     
    3940    __metaclass__ = IgnoreNotimplementedError
    4041
    4142    def test_table_names(self):
    42         tl = connection.introspection.table_names()
    43         self.assertTrue(Reporter._meta.db_table in tl,
     43        conv = connection.introspection.qname_converter
     44        tl = connection.introspection.all_qualified_names()
     45        self.assertTrue(conv(Reporter._meta.qualified_name) in tl,
    4446                     "'%s' isn't in table_list()." % Reporter._meta.db_table)
    45         self.assertTrue(Article._meta.db_table in tl,
     47        self.assertTrue(conv(Article._meta.qualified_name) in tl,
    4648                     "'%s' isn't in table_list()." % Article._meta.db_table)
    4749
    4850    def test_django_table_names(self):
    4951        cursor = connection.cursor()
    50         cursor.execute('CREATE TABLE django_ixn_test_table (id INTEGER);')
     52        tblname = connection.ops.qualified_name(
     53            QName(None, 'django_ixn_test_table', False))
     54        cursor.execute('CREATE TABLE %s (id INTEGER);' % tblname)
    5155        tl = connection.introspection.django_table_names()
    52         cursor.execute("DROP TABLE django_ixn_test_table;")
    53         self.assertTrue('django_ixn_testcase_table' not in tl,
     56        cursor.execute("DROP TABLE %s;" % tblname)
     57        self.assertTrue(tblname not in tl,
    5458                     "django_table_names() returned a non-Django table")
    5559
    5660    def test_django_table_names_retval_type(self):
    5761        # Ticket #15216
    5862        cursor = connection.cursor()
    59         cursor.execute('CREATE TABLE django_ixn_test_table (id INTEGER);')
     63        tblname = connection.ops.qualified_name(
     64            QName(None, 'django_ixn_test_table', False))
     65        cursor.execute('CREATE TABLE %s (id INTEGER);' % tblname)
    6066
    6167        tl = connection.introspection.django_table_names(only_existing=True)
    6268        self.assertIs(type(tl), list)
     
    6571        self.assertIs(type(tl), list)
    6672
    6773    def test_installed_models(self):
    68         tables = [Article._meta.db_table, Reporter._meta.db_table]
     74        conv = connection.introspection.qname_converter
     75        tables = [conv(Article._meta.qualified_name),
     76                  conv(Reporter._meta.qualified_name)]
    6977        models = connection.introspection.installed_models(tables)
    7078        self.assertEqual(models, set([Article, Reporter]))
    7179
    7280    def test_sequence_list(self):
    7381        sequences = connection.introspection.sequence_list()
    74         expected = {'table': Reporter._meta.db_table, 'column': 'id'}
     82        qname = connection.introspection.qname_converter(Reporter._meta.qualified_name)
     83        expected = {'qname': qname, 'column': 'id'}
    7584        self.assertTrue(expected in sequences,
    7685                     'Reporter sequence not found in sequence_list()')
    7786
    7887    def test_get_table_description_names(self):
    7988        cursor = connection.cursor()
    80         desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
     89        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
     90        desc = connection.introspection.get_table_description(cursor, tbl)
    8191        self.assertEqual([r[0] for r in desc],
    8292                         [f.column for f in Reporter._meta.fields])
    8393
    8494    def test_get_table_description_types(self):
    8595        cursor = connection.cursor()
    86         desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
     96        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
     97        desc = connection.introspection.get_table_description(cursor, tbl)
    8798        self.assertEqual(
    8899            [datatype(r[1], r) for r in desc],
    89100            ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField']
     
    95106    @skipIfDBFeature('interprets_empty_strings_as_nulls')
    96107    def test_get_table_description_nullable(self):
    97108        cursor = connection.cursor()
    98         desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
     109        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
     110        desc = connection.introspection.get_table_description(cursor, tbl)
    99111        self.assertEqual(
    100112            [r[6] for r in desc],
    101113            [False, False, False, False, True]
     
    105117    @skipUnlessDBFeature('has_real_datatype')
    106118    def test_postgresql_real_type(self):
    107119        cursor = connection.cursor()
    108         cursor.execute("CREATE TABLE django_ixn_real_test_table (number REAL);")
    109         desc = connection.introspection.get_table_description(cursor, 'django_ixn_real_test_table')
    110         cursor.execute('DROP TABLE django_ixn_real_test_table;')
     120        tblname = connection.ops.qualified_name(
     121            QName(None, 'django_ixn_real_test_table', False))
     122        cursor.execute("CREATE TABLE %s (number REAL);" % tblname)
     123        desc = connection.introspection.get_table_description(
     124            cursor, QName(None, 'django_ixn_real_test_table', False))
     125        cursor.execute('DROP TABLE %s;' % tblname)
    111126        self.assertEqual(datatype(desc[0][1], desc[0]), 'FloatField')
    112127
    113128    def test_get_relations(self):
    114129        cursor = connection.cursor()
    115         relations = connection.introspection.get_relations(cursor, Article._meta.db_table)
     130        tbl = connection.introspection.qname_converter(Article._meta.qualified_name)
     131        relations = connection.introspection.get_relations(cursor, tbl)
     132        rep_tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
    116133
    117134        # Older versions of MySQL don't have the chops to report on this stuff,
    118135        # so just skip it if no relations come back. If they do, though, we
    119136        # should test that the response is correct.
    120137        if relations:
    121138            # That's {field_index: (field_index_other_table, other_table)}
    122             self.assertEqual(relations, {3: (0, Reporter._meta.db_table)})
     139            # We have a small problem here: the Reporter model is installed
     140            # into the default schema (qualified_name[0] == None). The
     141            # relation introspection is going to see it in that schema, but we
     142            # do not know what that schema is. So, test everything except the
     143            # schema.
     144            # TODO: this testing logic is UGLY!
     145            schema = connection.convert_schema(Reporter._meta.qualified_name[0])
     146            self.assertTrue(3 in relations)
     147            relations[3] = (relations[3][0], (schema, relations[3][1][1], True))
     148            self.assertEqual(relations, {3: (0, rep_tbl)})
    123149
    124150    def test_get_key_columns(self):
    125151        cursor = connection.cursor()
    126         key_columns = connection.introspection.get_key_columns(cursor, Article._meta.db_table)
    127         self.assertEqual(key_columns, [(u'reporter_id', Reporter._meta.db_table, u'id')])
     152        rep_tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name, force_schema=True)
     153        key_columns = connection.introspection.get_key_columns(cursor, Article._meta.qualified_name)
     154        self.assertEqual(key_columns, [(u'reporter_id', rep_tbl, u'id')])
    128155
    129156    def test_get_primary_key_column(self):
    130157        cursor = connection.cursor()
    131         primary_key_column = connection.introspection.get_primary_key_column(cursor, Article._meta.db_table)
     158        primary_key_column = connection.introspection.get_primary_key_column(cursor, Article._meta.qualified_name)
    132159        self.assertEqual(primary_key_column, u'id')
    133160
    134161    def test_get_indexes(self):
    135162        cursor = connection.cursor()
    136         indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table)
     163        tbl = connection.introspection.qname_converter(Article._meta.qualified_name)
     164        indexes = connection.introspection.get_indexes(cursor, tbl)
    137165        self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False})
    138166
    139167
  • docs/topics/db/models.txt

     
    636636            verbose_name_plural = "oxen"
    637637
    638638Model metadata is "anything that's not a field", such as ordering options
    639 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or
     639(:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`),
     640or custom schema for the tables (:attr:`~Options.db_schema`), or
    640641human-readable singular and plural names (:attr:`~Options.verbose_name` and
    641642:attr:`~Options.verbose_name_plural`). None are required, and adding ``class
    642643Meta`` to a model is completely optional.
  • docs/ref/models/options.txt

     
    6767    the table name via ``db_table``, particularly if you are using the MySQL
    6868    backend. See the :ref:`MySQL notes <mysql-notes>` for more details.
    6969
     70.. _db_schema:
    7071
     72``db_schema``
     73-------------
     74
     75.. attribute:: Options.db_schema
     76
     77.. versionadded:: 1.5
     78
     79The name of the database schema to use for the model. If the backend
     80doesn't support multiple schemas, this option is ignored.
     81
     82If this is used then Django will place the table in the given schema.
     83Usually this means turning the pair db_schema, db_table into a fully
     84qualified name. For example: "db_schema"."db_table".
     85
     86
    7187``db_tablespace``
    7288-----------------
    7389
  • docs/ref/settings.txt

     
    512512
    513513The username to use when connecting to the database. Not used with SQLite.
    514514
     515.. setting:: SCHEMA
     516
     517SCHEMA
     518~~~~~~
     519
     520.. versionadded:: 1.5
     521
     522Default: ``''`` (Empty string)
     523
     524The name of the database schema to use for models. If the backend
     525doesn't support multiple schemas, this option is ignored. An empty
     526string means that schema qualified table names are not used by default.
     527
     528If this is used then Django will prefix any table names with the schema name.
     529The schema can be overriden on a per-model basis, for details see
     530:ref:`db_schema`.
     531
    515532.. setting:: TEST_CHARSET
    516533
    517534TEST_CHARSET
Back to Top