Ticket #6148: 6148-r16443-2.diff

File 6148-r16443-2.diff, 78.5 KB (added by Demetrius Cassidy <dcassidy36@…>, 4 years ago)
  • docs/topics/db/models.txt

     
    626626            verbose_name_plural = "oxen"
    627627
    628628Model metadata is "anything that's not a field", such as ordering options
    629 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or
     629(:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`),
     630custom schema for the tables (:attr:`~Options.db_schema`), or
    630631human-readable singular and plural names (:attr:`~Options.verbose_name` and
    631632:attr:`~Options.verbose_name_plural`). None are required, and adding ``class
    632633Meta`` to a model is completely optional.
  • docs/ref/models/options.txt

     
    6161aren't allowed in Python variable names -- notably, the hyphen -- that's OK.
    6262Django quotes column and table names behind the scenes.
    6363
     64.. _db_schema:
     65
     66``db_schema``
     67-------------
     68
     69.. attribute:: Options.db_schema
     70
     71.. versionadded:: 1.3
     72
     73The name of the database schema to use for the model. If the backend
     74doesn't support multiple schemas, this option is ignored.
     75
     76If this is used then Django will prefix the model table names with the schema
     77name. For example with MySQL Django would use ``db_schema + '.' + db_table``.
     78Be aware that PostgreSQL supports different schemas within the database.
     79MySQL solves the same thing by treating it as just another database.
     80
    6481``db_tablespace``
    6582-----------------
    6683
  • docs/ref/settings.txt

     
    510510The port to use when connecting to the database. An empty string means the
    511511default port. Not used with SQLite.
    512512
     513.. setting:: SCHEMA
     514
     515SCHEMA
     516~~~~~~
     517
     518.. versionadded:: 1.3
     519
     520Default: ``''`` (Empty string)
     521
     522The name of the database schema to use for models. If the backend
     523doesn't support multiple schemas, this option is ignored. An empty
     524string means the default schema.
     525
     526If this is used then Django will prefix any table names with the schema name.
     527The schema can be overriden on a per-model basis, for details see
     528:ref:`db_schema`.
     529
    513530.. setting:: USER
    514531
    515532USER
  • django/db/models/sql/compiler.py

     
    2323        might not have all the pieces in place at that time.
    2424        """
    2525        if not self.query.tables:
    26             self.query.join((None, self.query.model._meta.db_table, None, None))
     26            self.query.join((None, self.query.model._meta.qualified_name, None, None))
    2727        if (not self.query.select and self.query.default_cols and not
    2828                self.query.included_inherited_models):
    2929            self.query.setup_inherited_models()
     
    261261                        alias = start_alias
    262262                    else:
    263263                        link_field = opts.get_ancestor_link(model)
    264                         alias = self.query.join((start_alias, model._meta.db_table,
     264                        alias = self.query.join((start_alias, model._meta.qualified_name,
    265265                                link_field.column, model._meta.pk.column))
    266266                    seen[model] = alias
    267267            else:
     
    462462                result.append('%s%s%s' % (connector, qn(name), alias_str))
    463463            first = False
    464464        for t in self.query.extra_tables:
    465             alias, unused = self.query.table_alias(t)
     465            alias, unused = self.query.table_alias(qn(t))
    466466            # Only add the alias if it's not already present (the table_alias()
    467467            # calls increments the refcount, so an alias refcount of one means
    468468            # this is the only reference.
     
    550550            # what "used" specifies).
    551551            avoid = avoid_set.copy()
    552552            dupe_set = orig_dupe_set.copy()
    553             table = f.rel.to._meta.db_table
     553            table = f.rel.to._meta.qualified_name
    554554            promote = nullable or f.null
    555555            if model:
    556556                int_opts = opts
     
    571571                                ()))
    572572                        dupe_set.add((opts, lhs_col))
    573573                    int_opts = int_model._meta
    574                     alias = self.query.join((alias, int_opts.db_table, lhs_col,
     574                    alias = self.query.join((alias, int_opts.qualified_name, lhs_col,
    575575                            int_opts.pk.column), exclusions=used,
    576576                            promote=promote)
    577577                    alias_chain.append(alias)
     
    623623                # what "used" specifies).
    624624                avoid = avoid_set.copy()
    625625                dupe_set = orig_dupe_set.copy()
    626                 table = model._meta.db_table
     626                table = model._meta.qualified_name
    627627
    628628                int_opts = opts
    629629                alias = root_alias
     
    646646                            dupe_set.add((opts, lhs_col))
    647647                        int_opts = int_model._meta
    648648                        alias = self.query.join(
    649                             (alias, int_opts.db_table, lhs_col, int_opts.pk.column),
     649                            (alias, int_opts.qualified_name, lhs_col, int_opts.pk.column),
    650650                            exclusions=used, promote=True, reuse=used
    651651                        )
    652652                        alias_chain.append(alias)
     
    793793        # going to be column names (so we can avoid the extra overhead).
    794794        qn = self.connection.ops.quote_name
    795795        opts = self.query.model._meta
    796         result = ['INSERT INTO %s' % qn(opts.db_table)]
     796        result = ['INSERT INTO %s' % opts.qualified_name]
    797797        result.append('(%s)' % ', '.join([qn(c) for c in self.query.columns]))
    798798        values = [self.placeholder(*v) for v in self.query.values]
    799799        result.append('VALUES (%s)' % ', '.join(values))
    800800        params = self.query.params
    801801        if self.return_id and self.connection.features.can_return_id_from_insert:
    802             col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
     802            col = "%s.%s" % (opts.qualified_name, qn(opts.pk.column))
    803803            r_fmt, r_params = self.connection.ops.return_insert_id()
    804804            result.append(r_fmt % col)
    805805            params = params + r_params
     
    813813        if self.connection.features.can_return_id_from_insert:
    814814            return self.connection.ops.fetch_returned_insert_id(cursor)
    815815        return self.connection.ops.last_insert_id(cursor,
    816                 self.query.model._meta.db_table, self.query.model._meta.pk.column)
     816                self.query.model._meta.db_schema, self.query.model._meta.db_table,
     817                self.query.model._meta.pk.column)
    817818
    818819
    819820class SQLDeleteCompiler(SQLCompiler):
  • django/db/models/sql/query.py

     
    620620        Callback used by deferred_to_columns(). The "target" parameter should
    621621        be a set instance.
    622622        """
    623         table = model._meta.db_table
     623        table = model._meta.qualified_name
    624624        if table not in target:
    625625            target[table] = set()
    626626        for field in fields:
     
    809809            alias = self.tables[0]
    810810            self.ref_alias(alias)
    811811        else:
    812             alias = self.join((None, self.model._meta.db_table, None, None))
     812            alias = self.join((None, self.model._meta.qualified_name,
     813                               None, None))
    813814        return alias
    814815
    815816    def count_active_tables(self):
     
    922923                    seen[model] = root_alias
    923924                else:
    924925                    link_field = opts.get_ancestor_link(model)
    925                     seen[model] = self.join((root_alias, model._meta.db_table,
     926                    seen[model] = self.join((root_alias,
     927                            model._meta.qualified_name,
    926928                            link_field.column, model._meta.pk.column))
    927929        self.included_inherited_models = seen
    928930
     
    12691271                                    (id(opts), lhs_col), ()))
    12701272                            dupe_set.add((opts, lhs_col))
    12711273                        opts = int_model._meta
    1272                         alias = self.join((alias, opts.db_table, lhs_col,
    1273                                 opts.pk.column), exclusions=exclusions)
     1274                        alias = self.join((alias, opts.qualified_name,
     1275                                           lhs_col, opts.pk.column),
     1276                                          exclusions=exclusions)
    12741277                        joins.append(alias)
    12751278                        exclusions.add(alias)
    12761279                        for (dupe_opts, dupe_col) in dupe_set:
     
    12951298                        (table1, from_col1, to_col1, table2, from_col2,
    12961299                                to_col2, opts, target) = cached_data
    12971300                    else:
    1298                         table1 = field.m2m_db_table()
     1301                        table1 = field.m2m_qualified_name()
    12991302                        from_col1 = opts.get_field_by_name(
    13001303                            field.m2m_target_field_name())[0].column
    13011304                        to_col1 = field.m2m_column_name()
    13021305                        opts = field.rel.to._meta
    1303                         table2 = opts.db_table
     1306                        table2 = opts.qualified_name
    13041307                        from_col2 = field.m2m_reverse_name()
    13051308                        to_col2 = opts.get_field_by_name(
    13061309                            field.m2m_reverse_target_field_name())[0].column
     
    13281331                    else:
    13291332                        opts = field.rel.to._meta
    13301333                        target = field.rel.get_related_field()
    1331                         table = opts.db_table
     1334                        table = opts.qualified_name
    13321335                        from_col = field.column
    13331336                        to_col = target.column
    13341337                        orig_opts._join_cache[name] = (table, from_col, to_col,
     
    13501353                        (table1, from_col1, to_col1, table2, from_col2,
    13511354                                to_col2, opts, target) = cached_data
    13521355                    else:
    1353                         table1 = field.m2m_db_table()
     1356                        table1 = field.m2m_qualified_name()
    13541357                        from_col1 = opts.get_field_by_name(
    13551358                            field.m2m_reverse_target_field_name())[0].column
    13561359                        to_col1 = field.m2m_reverse_name()
    13571360                        opts = orig_field.opts
    1358                         table2 = opts.db_table
     1361                        table2 = opts.qualified_name
    13591362                        from_col2 = field.m2m_column_name()
    13601363                        to_col2 = opts.get_field_by_name(
    13611364                            field.m2m_target_field_name())[0].column
     
    13791382                        local_field = opts.get_field_by_name(
    13801383                                field.rel.field_name)[0]
    13811384                        opts = orig_field.opts
    1382                         table = opts.db_table
     1385                        table = opts.qualified_name
    13831386                        from_col = local_field.column
    13841387                        to_col = field.column
    13851388                        # In case of a recursive FK, use the to_field for
     
    16491652        else:
    16501653            opts = self.model._meta
    16511654            if not self.select:
    1652                 count = self.aggregates_module.Count((self.join((None, opts.db_table, None, None)), opts.pk.column),
     1655                count = self.aggregates_module.Count((self.join((None,
     1656                           opts.qualified_name, None, None)), opts.pk.column),
    16531657                                         is_summary=True, distinct=True)
    16541658            else:
    16551659                # 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/options.py

     
    1717DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
    1818                 'unique_together', 'permissions', 'get_latest_by',
    1919                 'order_with_respect_to', 'app_label', 'db_tablespace',
    20                  'abstract', 'managed', 'proxy', 'auto_created')
     20                 'abstract', 'managed', 'proxy', 'auto_created', 'db_schema')
    2121
    2222class Options(object):
    2323    def __init__(self, meta, app_label=None):
     
    2626        self.module_name, self.verbose_name = None, None
    2727        self.verbose_name_plural = None
    2828        self.db_table = ''
     29        #self.db_schema = settings.DATABASE_SCHEMA
     30        self.db_schema = None
     31        self.qualified_name = ''
    2932        self.ordering = []
    3033        self.unique_together =  []
    3134        self.permissions =  []
     
    5558        self.related_fkey_lookups = []
    5659
    5760    def contribute_to_class(self, cls, name):
    58         from django.db import connection
     61        from django.db import connections, router
    5962        from django.db.backends.util import truncate_name
     63        conn = connections[router.db_for_read(cls)]
    6064
    6165        cls._meta = self
    6266        self.installed = re.sub('\.models$', '', cls.__module__) in settings.INSTALLED_APPS
     
    6468        self.object_name = cls.__name__
    6569        self.module_name = self.object_name.lower()
    6670        self.verbose_name = get_verbose_name(self.object_name)
     71        self.db_schema = conn.settings_dict['SCHEMA']
    6772
    6873        # Next, apply any overridden values from 'class Meta'.
    6974        if self.meta:
     
    103108        # If the db_table wasn't provided, use the app_label + module_name.
    104109        if not self.db_table:
    105110            self.db_table = "%s_%s" % (self.app_label, self.module_name)
    106             self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
     111            self.db_table = truncate_name(self.db_table, conn.ops.max_name_length())
    107112
     113        # Construct qualified table name.
     114        self.qualified_name = conn.ops.prep_db_table(self.db_schema,
     115                                                           self.db_table)
     116        if self.qualified_name == conn.ops.quote_name(self.db_table):
     117            # If unchanged, the backend doesn't support schemas.
     118            self.db_schema = ''
    108119    def _prepare(self, model):
    109120        if self.order_with_respect_to:
    110121            self.order_with_respect_to = self.get_field(self.order_with_respect_to)
     
    184195        self.pk = target._meta.pk
    185196        self.proxy_for_model = target
    186197        self.db_table = target._meta.db_table
     198        self.db_schema = target._meta.db_schema
     199        self.qualified_name = target._meta.qualified_name
    187200
    188201    def __repr__(self):
    189202        return '<Options for %s>' % self.object_name
  • django/db/models/fields/related.py

     
    892892        if isinstance(self.rel.to, basestring):
    893893            target = self.rel.to
    894894        else:
    895             target = self.rel.to._meta.db_table
     895            target = self.rel.to._meta.qualified_name
    896896        cls._meta.duplicate_targets[self.column] = (target, "o2m")
    897897
    898898    def contribute_to_related_class(self, cls, related):
     
    983983        to = to.lower()
    984984    meta = type('Meta', (object,), {
    985985        'db_table': field._get_m2m_db_table(klass._meta),
     986        'db_schema': field._get_m2m_db_schema(klass._meta),
    986987        'managed': managed,
    987988        'auto_created': klass,
    988989        'app_label': klass._meta.app_label,
     
    10141015            through=kwargs.pop('through', None))
    10151016
    10161017        self.db_table = kwargs.pop('db_table', None)
     1018        self.db_schema = kwargs.pop('db_schema', '')
    10171019        if kwargs['rel'].through is not None:
    10181020            assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
    10191021
     
    10351037            return util.truncate_name('%s_%s' % (opts.db_table, self.name),
    10361038                                      connection.ops.max_name_length())
    10371039
     1040    def _get_m2m_db_schema(self, opts):
     1041        "Function that can be curried to provide the m2m schema name for this relation"
     1042        if self.rel.through is not None and self.rel.through._meta.db_schema:
     1043            return self.rel.through._meta.db_schema
     1044        return self.db_schema
     1045
     1046    def _get_m2m_qualified_name(self, opts):
     1047        "Function that can be curried to provide the qualified m2m table name for this relation"
     1048        schema = self._get_m2m_db_schema(opts)
     1049        table = self._get_m2m_db_table(opts)
     1050        return connection.ops.prep_db_table(schema, table)
     1051
    10381052    def _get_m2m_attr(self, related, attr):
    10391053        "Function that can be curried to provide the source accessor or DB column name for the m2m table"
    10401054        cache_attr = '_m2m_%s_cache' % attr
     
    11051119
    11061120        # Set up the accessor for the m2m table name for the relation
    11071121        self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
     1122        self.m2m_db_schema = curry(self._get_m2m_db_schema, cls._meta)
     1123        self.m2m_qualified_name = curry(self._get_m2m_qualified_name,
     1124                                        cls._meta)
    11081125
    11091126        # Populate some necessary rel arguments so that cross-app relations
    11101127        # work correctly.
     
    11161133        if isinstance(self.rel.to, basestring):
    11171134            target = self.rel.to
    11181135        else:
    1119             target = self.rel.to._meta.db_table
     1136            target = self.rel.to._meta.qualified_name
    11201137        cls._meta.duplicate_targets[self.column] = (target, "m2m")
    11211138
    11221139    def contribute_to_related_class(self, cls, related):
  • django/db/__init__.py

     
    77__all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
    88    'IntegrityError', 'DEFAULT_DB_ALIAS')
    99
    10 
     10       
    1111if DEFAULT_DB_ALIAS not in settings.DATABASES:
    1212    raise ImproperlyConfigured("You must define a '%s' database" % DEFAULT_DB_ALIAS)
    1313
  • django/db/utils.py

     
    6767            conn['ENGINE'] = 'django.db.backends.dummy'
    6868        conn.setdefault('OPTIONS', {})
    6969        conn.setdefault('TIME_ZONE', settings.TIME_ZONE)
    70         for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']:
     70
     71        for setting in ('NAME', 'USER', 'PASSWORD', 'HOST', 'PORT', 'SCHEMA'):
    7172            conn.setdefault(setting, '')
    7273        for setting in ['TEST_CHARSET', 'TEST_COLLATION', 'TEST_NAME', 'TEST_MIRROR']:
    7374            conn.setdefault(setting, None)
  • django/db/backends/postgresql/base.py

     
    8080
    8181class DatabaseFeatures(BaseDatabaseFeatures):
    8282    uses_savepoints = True
     83    default_schema_name = u'public'
    8384
    8485class DatabaseWrapper(BaseDatabaseWrapper):
    8586    operators = {
  • django/db/backends/postgresql/introspection.py

     
    3131                AND pg_catalog.pg_table_is_visible(c.oid)""")
    3232        return [row[0] for row in cursor.fetchall()]
    3333
     34    def get_schema_list(self, cursor):
     35        cursor.execute("""
     36            SELECT DISTINCT n.nspname
     37            FROM pg_catalog.pg_class c
     38            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
     39            WHERE c.relkind IN ('r', 'v', '')
     40            AND n.nspname NOT IN ('pg_catalog', 'pg_toast', 'information_schema')""")
     41        return [row[0] for row in cursor.fetchall()]
     42
     43    def get_schema_table_list(self, cursor, schema):
     44        cursor.execute("""
     45            SELECT c.relname
     46            FROM pg_catalog.pg_class c
     47            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
     48            WHERE c.relkind IN ('r', 'v', '')
     49                AND n.nspname = '%s'""" % schema)
     50        return [row[0] for row in cursor.fetchall()]
     51
    3452    def get_table_description(self, cursor, table_name):
    3553        "Returns a description of the table, with the DB-API cursor.description interface."
    3654        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
  • django/db/backends/postgresql/operations.py

     
    5353            return 'HOST(%s)'
    5454        return '%s'
    5555
    56     def last_insert_id(self, cursor, table_name, pk_name):
     56    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
    5757        # Use pg_get_serial_sequence to get the underlying sequence name
    5858        # from the table name and column name (available since PostgreSQL 8)
     59        table_name = self.prep_db_table(schema_name, table_name)
    5960        cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (table_name, pk_name))
    6061        return cursor.fetchone()[0]
    6162
     
    6768            return name # Quoting once is enough.
    6869        return '"%s"' % name
    6970
     71    def prep_db_table(self, db_schema, db_table):
     72        qn = self.quote_name
     73        if db_schema:
     74            return "%s.%s" % (qn(db_schema), qn(db_table))
     75        else:
     76            return qn(db_table)
     77
    7078    def sql_flush(self, style, tables, sequences):
    7179        if tables:
     80            qnames = [self.prep_db_table(schema, table)
     81                      for (schema, table) in tables]
    7282            if self.postgres_version[0:2] >= (8,1):
    7383                # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
    7484                # in order to be able to truncate tables referenced by a foreign
     
    7686                # statement.
    7787                sql = ['%s %s;' % \
    7888                    (style.SQL_KEYWORD('TRUNCATE'),
    79                      style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
     89                     style.SQL_FIELD(', '.join(qnames))
    8090                )]
    8191            else:
    8292                # Older versions of Postgres can't do TRUNCATE in a single call, so
     
    8494                sql = ['%s %s %s;' % \
    8595                        (style.SQL_KEYWORD('DELETE'),
    8696                         style.SQL_KEYWORD('FROM'),
    87                          style.SQL_FIELD(self.quote_name(table))
    88                          ) for table in tables]
     97                         style.SQL_FIELD(qname)
     98                         ) for qname in qnames]
    8999
    90100            # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
    91101            # to reset sequence indices
    92102            for sequence_info in sequences:
     103                schema_name = sequence_info['schema']
    93104                table_name = sequence_info['table']
    94105                column_name = sequence_info['column']
    95106                if not (column_name and len(column_name) > 0):
    96107                    # This will be the case if it's an m2m using an autogenerated
    97108                    # intermediate table (see BaseDatabaseIntrospection.sequence_list)
    98109                    column_name = 'id'
     110                table_name = self.prep_db_table(schema_name, table_name)
    99111                sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
    100112                    (style.SQL_KEYWORD('SELECT'),
    101113                    style.SQL_TABLE(table_name),
     
    118130
    119131            for f in model._meta.local_fields:
    120132                if isinstance(f, models.AutoField):
     133                    table_name = self.prep_db_table(model._meta.db_schema, model._meta.db_table) # XXX: generic schemas support.
    121134                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    122135                        (style.SQL_KEYWORD('SELECT'),
    123                         style.SQL_TABLE(model._meta.db_table),
    124                         style.SQL_FIELD(f.column),
     136                        style.SQL_TABLE(table_name),
     137                        style.SQL_FIELD(f.column), # XXX: Do we need qn() here?
    125138                        style.SQL_FIELD(qn(f.column)),
    126139                        style.SQL_FIELD(qn(f.column)),
    127140                        style.SQL_KEYWORD('IS NOT'),
    128141                        style.SQL_KEYWORD('FROM'),
    129                         style.SQL_TABLE(qn(model._meta.db_table))))
     142                        style.SQL_TABLE(model._meta.qualified_name)))
    130143                    break # Only one AutoField is allowed per model, so don't bother continuing.
    131144            for f in model._meta.many_to_many:
    132145                if not f.rel.through:
     146                    table_name = self.prep_db_table(f.m2m_db_schema(), f.m2m_db_table()) # XXX: Do we need qn(f.m2m_db_table()) here? / XXX: generic schemas support.
    133147                    output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    134148                        (style.SQL_KEYWORD('SELECT'),
    135                         style.SQL_TABLE(model._meta.db_table),
    136                         style.SQL_FIELD('id'),
     149                        style.SQL_TABLE(table_name),
     150                        style.SQL_FIELD('id'), # XXX: Do we need qn() here?
    137151                        style.SQL_FIELD(qn('id')),
    138152                        style.SQL_FIELD(qn('id')),
    139153                        style.SQL_KEYWORD('IS NOT'),
    140154                        style.SQL_KEYWORD('FROM'),
    141                         style.SQL_TABLE(qn(f.m2m_db_table()))))
     155                        style.SQL_TABLE(qn(f.m2m_qualified_name()))))
    142156        return output
    143157
    144158    def savepoint_create_sql(self, sid):
  • django/db/backends/postgresql/creation.py

     
    5151                tablespace_sql = ''
    5252
    5353            def get_index_sql(index_name, opclass=''):
     54                index_name = truncate_name(index_name, self.connection.ops.max_name_length())
     55                index_name = self.connection.ops.prep_db_index(model._meta.db_schema, index_name)
    5456                return (style.SQL_KEYWORD('CREATE INDEX') + ' ' +
    55                         style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' +
     57                        style.SQL_TABLE(index_name) + ' ' +
    5658                        style.SQL_KEYWORD('ON') + ' ' +
    57                         style.SQL_TABLE(qn(db_table)) + ' ' +
     59                        style.SQL_TABLE(model._meta.qualified_name) + ' ' +
    5860                        "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) +
    5961                        "%s;" % tablespace_sql)
    6062
  • django/db/backends/sqlite3/base.py

     
    123123                (style.SQL_KEYWORD('DELETE'),
    124124                 style.SQL_KEYWORD('FROM'),
    125125                 style.SQL_FIELD(self.quote_name(table))
    126                  ) for table in tables]
     126                 ) for (_, table) in tables]
    127127        # Note: No requirement for reset of auto-incremented indices (cf. other
    128128        # sql_flush() implementations). Just return SQL at this point
    129129        return sql
  • django/db/backends/sqlite3/creation.py

     
    4545            return test_database_name
    4646        return ':memory:'
    4747
    48     def _create_test_db(self, verbosity, autoclobber):
     48    def _create_test_db(self, verbosity, autoclobber, schemas):
    4949        test_database_name = self._get_test_db_name()
    5050        if test_database_name != ':memory:':
    5151            # Erase the old test database
  • django/db/backends/mysql/base.py

     
    206206            return name # Quoting once is enough.
    207207        return "`%s`" % name
    208208
     209    def prep_db_table(self, db_schema, db_table):
     210        qn = self.quote_name
     211        if db_schema:
     212            return "%s.%s" % (qn(db_schema), qn(db_table))
     213        else:
     214            return qn(db_table)
     215
     216    def prep_db_index(self, db_schema, db_index):
     217        return self.prep_db_table(db_schema, db_index)
     218
    209219    def random_function_sql(self):
    210220        return 'RAND()'
    211221
     
    215225        # to clear all tables of all data
    216226        if tables:
    217227            sql = ['SET FOREIGN_KEY_CHECKS = 0;']
    218             for table in tables:
    219                 sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
     228            for (schema, table) in tables:
     229                sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.prep_db_table(schema, table))))
    220230            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
    221231
    222232            # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
    223233            # to reset sequence indices
    224             sql.extend(["%s %s %s %s %s;" % \
    225                 (style.SQL_KEYWORD('ALTER'),
    226                  style.SQL_KEYWORD('TABLE'),
    227                  style.SQL_TABLE(self.quote_name(sequence['table'])),
    228                  style.SQL_KEYWORD('AUTO_INCREMENT'),
    229                  style.SQL_FIELD('= 1'),
    230                 ) for sequence in sequences])
     234            for sequence_info in sequences:
     235                schema_name = sequence_info['schema']
     236                table_name = self.prep_db_table(schema_name, sequence_info['table'])
     237                sql.append("%s %s %s %s %s;" % \
     238                           (style.SQL_KEYWORD('ALTER'),
     239                            style.SQL_KEYWORD('TABLE'),
     240                            style.SQL_TABLE(table_name),
     241                            style.SQL_KEYWORD('AUTO_INCREMENT'),
     242                            style.SQL_FIELD('= 1'),
     243                            ))
    231244            return sql
    232245        else:
    233246            return []
  • django/db/backends/mysql/introspection.py

     
    3333        cursor.execute("SHOW TABLES")
    3434        return [row[0] for row in cursor.fetchall()]
    3535
     36    def get_schema_list(self, cursor):
     37        cursor.execute("SHOW SCHEMAS")
     38        return [row[0] for row in cursor.fetchall()]
     39
     40    def get_schema_table_list(self, cursor, schema):
     41        cursor.execute("SHOW TABLES FROM %s" % self.connection.ops.quote_name(schema))
     42        return [schema + "." + row[0] for row in cursor.fetchall()]
     43
    3644    def get_table_description(self, cursor, table_name):
    3745        "Returns a description of the table, with the DB-API cursor.description interface."
    3846        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
  • django/db/backends/mysql/creation.py

     
    6464                field.rel.to._meta.db_table, field.rel.to._meta.pk.column)
    6565            ]
    6666        return table_output, deferred
     67
     68    def default_schema(self):
     69        return settings.DATABASE_NAME
     70
     71    def sql_create_schema(self, schema, style):
     72        """
     73        Returns the SQL required to create a single schema.
     74        In MySQL schemas are synonymous to databases
     75        """
     76        qn = self.connection.ops.quote_name
     77        output = "%s %s;" % (style.SQL_KEYWORD('CREATE DATABASE'), qn(schema))
     78        return output
     79
     80    def sql_destroy_schema(self, schema, style):
     81        """"
     82        Returns the SQL required to destroy a single schema.
     83        """
     84        qn = self.connection.ops.quote_name
     85        output = "%s %s;" % (style.SQL_KEYWORD('DROP DATABASE IF EXISTS'), qn(schema))
     86        return output
  • django/db/backends/oracle/base.py

     
    8282class DatabaseOperations(BaseDatabaseOperations):
    8383    compiler_module = "django.db.backends.oracle.compiler"
    8484
    85     def autoinc_sql(self, table, column):
     85    def autoinc_sql(self, schema, table, column):
    8686        # To simulate auto-incrementing primary keys in Oracle, we have to
    8787        # create a sequence and a trigger.
    8888        sq_name = self._get_sequence_name(table)
    8989        tr_name = self._get_trigger_name(table)
    90         tbl_name = self.quote_name(table)
     90        tbl_name = self.prep_db_table(schema, table)
     91        sq_qname = self.prep_db_table(schema, sq_name)
     92        tr_qname = self.prep_db_table(schema, tr_name)
    9193        col_name = self.quote_name(column)
    9294        sequence_sql = """
    9395DECLARE
     
    9698    SELECT COUNT(*) INTO i FROM USER_CATALOG
    9799        WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
    98100    IF i = 0 THEN
    99         EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
     101        EXECUTE IMMEDIATE 'CREATE SEQUENCE %(sq_qname)s';
    100102    END IF;
    101103END;
    102104/""" % locals()
    103105        trigger_sql = """
    104 CREATE OR REPLACE TRIGGER "%(tr_name)s"
     106CREATE OR REPLACE TRIGGER %(tr_qname)s
    105107BEFORE INSERT ON %(tbl_name)s
    106108FOR EACH ROW
    107109WHEN (new.%(col_name)s IS NULL)
    108110    BEGIN
    109         SELECT "%(sq_name)s".nextval
     111        SELECT %(sq_qname)s.nextval
    110112        INTO :new.%(col_name)s FROM dual;
    111113    END;
    112114/""" % locals()
     
    191193    def deferrable_sql(self):
    192194        return " DEFERRABLE INITIALLY DEFERRED"
    193195
    194     def drop_sequence_sql(self, table):
    195         return "DROP SEQUENCE %s;" % self.quote_name(self._get_sequence_name(table))
     196    def drop_sequence_sql(self, schema, table):
     197        sequence_name = self.prep_db_table(schema, get_sequence_name(table))
     198        return "DROP SEQUENCE %s;" % sequence_name
    196199
    197200    def fetch_returned_insert_id(self, cursor):
    198201        return long(cursor._insert_id_var.getvalue())
     
    203206        else:
    204207            return "%s"
    205208
     209    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
     210        sq_name = self.prep_db_table(schema_name, get_sequence_name(table_name))
     211        cursor.execute('SELECT %s.currval FROM dual' % sq_name)
     212
    206213    def last_executed_query(self, cursor, sql, params):
    207214        # http://cx-oracle.sourceforge.net/html/cursor.html#Cursor.statement
    208215        # The DB API definition does not define this attribute.
    209216        return cursor.statement
    210217
    211     def last_insert_id(self, cursor, table_name, pk_name):
    212         sq_name = self._get_sequence_name(table_name)
    213         cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
    214         return cursor.fetchone()[0]
    215 
    216218    def lookup_cast(self, lookup_type):
    217219        if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
    218220            return "UPPER(%s)"
     
    224226    def max_name_length(self):
    225227        return 30
    226228
     229    def prep_db_table(self, db_schema, db_table):
     230        qn = self.quote_name
     231        if db_schema:
     232            return "%s.%s" % (qn(db_schema), qn(db_table))
     233        else:
     234            return qn(db_table)
     235
     236    def prep_db_index(self, db_schema, db_index):
     237        return self.prep_db_table(db_schema, db_index)
     238
    227239    def prep_for_iexact_query(self, x):
    228240        return x
    229241
     
    273285    def sql_flush(self, style, tables, sequences):
    274286        # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
    275287        # 'TRUNCATE z;'... style SQL statements
     288        sql = []
    276289        if tables:
    277290            # Oracle does support TRUNCATE, but it seems to get us into
    278291            # FK referential trouble, whereas DELETE FROM table works.
    279             sql = ['%s %s %s;' % \
    280                     (style.SQL_KEYWORD('DELETE'),
    281                      style.SQL_KEYWORD('FROM'),
    282                      style.SQL_FIELD(self.quote_name(table)))
    283                     for table in tables]
     292            for schema, table in tables:
     293                table = self.prep_db_table(schema, table)
     294                sql.append('%s %s %s;' % \
     295                           (style.SQL_KEYWORD('DELETE'),
     296                            style.SQL_KEYWORD('FROM'),
     297                            style.SQL_FIELD(table)))
    284298            # Since we've just deleted all the rows, running our sequence
    285299            # ALTER code will reset the sequence to 0.
    286300            for sequence_info in sequences:
    287                 sequence_name = self._get_sequence_name(sequence_info['table'])
    288                 table_name = self.quote_name(sequence_info['table'])
     301                schema_name = sequence_info['schema']
     302                sequence_name = self.prep_db_table(schema_name,
     303                                    get_sequence_name(sequence_info['table']))
     304                table_name = self.prep_db_table(schema_name,
     305                                                sequence_info['table'])
     306
    289307                column_name = self.quote_name(sequence_info['column'] or 'id')
    290308                query = _get_sequence_reset_sql() % {'sequence': sequence_name,
    291309                                                     'table': table_name,
    292310                                                     'column': column_name}
    293311                sql.append(query)
    294             return sql
    295         else:
    296             return []
     312        return sql
    297313
    298314    def sequence_reset_sql(self, style, model_list):
    299315        from django.db import models
     
    302318        for model in model_list:
    303319            for f in model._meta.local_fields:
    304320                if isinstance(f, models.AutoField):
    305                     table_name = self.quote_name(model._meta.db_table)
    306                     sequence_name = self._get_sequence_name(model._meta.db_table)
     321                    table_name = model._meta.qualified_name
     322                    sequence_name = self.prep_db_table(model._meta.db_schema,
     323                                       get_sequence_name(model._meta.db_table))
     324
    307325                    column_name = self.quote_name(f.column)
    308326                    output.append(query % {'sequence': sequence_name,
    309327                                           'table': table_name,
     
    313331                    break
    314332            for f in model._meta.many_to_many:
    315333                if not f.rel.through:
    316                     table_name = self.quote_name(f.m2m_db_table())
    317                     sequence_name = self._get_sequence_name(f.m2m_db_table())
     334                    table_name = self.quote_name(f.m2m_qualified_name())
     335                    sequence_name = self.prep_db_table(f.m2m_db_schema(),
     336                                           get_sequence_name(f.m2m_db_table()))
     337
    318338                    column_name = self.quote_name('id')
    319339                    output.append(query % {'sequence': sequence_name,
    320340                                           'table': table_name,
  • django/db/backends/oracle/introspection.py

     
    120120        for row in cursor.fetchall():
    121121            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
    122122        return indexes
     123
     124    def schema_name_converter(self, name):
     125        """Convert to lowercase for case-sensitive schema name comparison."""
     126        return name.lower()
     127
     128    def get_schema_list(self, cursor):
     129        "Returns a list of schemas that exist in the database."
     130        sql = """
     131        select distinct username
     132        from all_users, all_objects
     133        where username = owner
     134        """
     135        cursor.execute(sql)
     136        return [schema.lower() for (schema,) in cursor]
     137
     138    def get_schema_table_list(self, cursor, schema):
     139        "Returns a list of tables in a specific schema."
     140        sql = """
     141        select table_name from all_tables
     142        where owner = upper(%s)
     143        """
     144        cursor.execute(sql, [schema])
     145        return [table.lower() for (table,) in cursor]
  • django/db/backends/oracle/creation.py

     
    3838        'TimeField':                    'TIMESTAMP',
    3939        'URLField':                     'VARCHAR2(%(max_length)s)',
    4040    }
    41 
    42     def __init__(self, connection):
     41       
     42        def __init__(self, connection):
    4343        self.remember = {}
    4444        super(DatabaseCreation, self).__init__(connection)
     45       
     46    def sql_create_schema(self, schema, style, password=None,
     47                          tablespace=None, temp_tablespace=None):
     48        qn = self.connection.ops.quote_name
     49        lock_account = (password is None)
     50        if lock_account:
     51            password = schema
     52        output = []
     53        output.append("%s %s %s %s" % (style.SQL_KEYWORD('CREATE USER'),
     54                                       qn(schema),
     55                                       style.SQL_KEYWORD('IDENTIFIED BY'),
     56                                       qn(password)))
     57        if tablespace:
     58            output.append("%s %s" % (style.SQL_KEYWORD('DEFAULT TABLESPACE'),
     59                                     qn(tablespace)))
     60        if temp_tablespace:
     61            output.append("%s %s" % (style.SQL_KEYWORD('TEMPORARY TABLESPACE'),
     62                                     qn(temp_tablespace)))
     63        if lock_account:
     64            output.append(style.SQL_KEYWORD('ACCOUNT LOCK'))
     65        return '\n'.join(output)
    4566
    46     def _create_test_db(self, verbosity=1, autoclobber=False):
     67    def sql_destroy_schema(self, schema, style):
     68        qn = self.connection.ops.quote_name
     69        return "%s %s %s" % (style.SQL_KEYWORD('DROP USER'), qn(schema),
     70                             style.SQL_KEYWORD('CASCADE'))
     71
     72    def _create_test_db(self, verbosity=1, autoclobber=False, schema=None):
    4773        TEST_NAME = self._test_database_name()
    4874        TEST_USER = self._test_database_user()
    4975        TEST_PASSWD = self._test_database_passwd()
     
    169195               DEFAULT TABLESPACE %(tblspace)s
    170196               TEMPORARY TABLESPACE %(tblspace_temp)s
    171197            """,
    172             """GRANT CONNECT, RESOURCE TO %(user)s""",
     198            """GRANT CONNECT, RESOURCE, CREATE USER, DROP USER, CREATE ANY TABLE, ALTER ANY TABLE, CREATE ANY INDEX, CREATE ANY SEQUENCE, CREATE ANY TRIGGER, SELECT ANY TABLE, INSERT ANY TABLE, UPDATE ANY TABLE, DELETE ANY TABLE TO %(user)s""",
    173199        ]
    174200        self._execute_statements(cursor, statements, parameters, verbosity)
    175201
  • django/db/backends/__init__.py

     
    279279    # integer primary keys.
    280280    related_fields_match_type = False
    281281    allow_sliced_subqueries = True
     282       
     283    default_schema_name = ''
     284       
    282285    has_select_for_update = False
    283286    has_select_for_update_nowait = False
    284287
     
    394397        self.connection = connection
    395398        self._cache = None
    396399
    397     def autoinc_sql(self, table, column):
     400    def autoinc_sql(self, schema, table, column):
    398401        """
    399402        Returns any SQL needed to support auto-incrementing primary keys, or
    400403        None if no SQL is necessary.
     
    446449        """
    447450        return "DROP CONSTRAINT"
    448451
    449     def drop_sequence_sql(self, table):
     452    def drop_sequence_sql(self, schema, table):
    450453        """
    451454        Returns any SQL necessary to drop the sequence for the given table.
    452455        Returns None if no SQL is necessary.
     
    516519
    517520        return smart_unicode(sql) % u_params
    518521
    519     def last_insert_id(self, cursor, table_name, pk_name):
     522    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
    520523        """
    521524        Given a cursor object that has just performed an INSERT statement into
    522525        a table that has an auto-incrementing ID, returns the newly created ID.
     
    595598        """
    596599        raise NotImplementedError()
    597600
     601    def prep_db_table(self, db_schema, db_table):
     602        """
     603        Prepares and formats the table name if necessary.
     604        Just returns quoted db_table if not supported.
     605        """
     606        return self.quote_name(db_table)
     607
     608    def prep_db_index(self, db_schema, db_index):
     609        """
     610        Prepares and formats the table index name if necessary.
     611        Just returns quoted db_index if not supported.
     612        """
     613        return self.quote_name(db_index)
     614
    598615    def random_function_sql(self):
    599616        """
    600617        Returns a SQL expression that returns a random value.
     
    799816        return name
    800817
    801818    def table_names(self):
    802         "Returns a list of names of all tables that exist in the database."
     819        "Returns a list of names of all tables that exist in the default schema."
    803820        cursor = self.connection.cursor()
    804821        return self.get_table_list(cursor)
    805822
     823    def schema_name_converter(self, name):
     824        """Apply a conversion to the name for the purposes of comparison.
     825
     826        The default schema name converter is for case sensitive comparison.
     827        """
     828        return name
     829
     830    def get_schema_list(self, cursor):
     831        "Returns a list of schemas that exist in the database"
     832        return []
     833
     834    def get_schema_table_list(self, cursor, schema):
     835        "Returns a list of tables in a specific schema"
     836        return []
     837
     838    def schema_names(self):
     839        cursor = self.connection.cursor()
     840        return self.get_schema_list(cursor)
     841
     842    def schema_table_names(self, schema):
     843        "Returns a list of names of all tables that exist in the database schema."
     844        cursor = self.connection.cursor()
     845        return self.get_schema_table_list(cursor, schema)
     846
    806847    def django_table_names(self, only_existing=False):
    807848        """
    808         Returns a list of all table names that have associated Django models and
    809         are in INSTALLED_APPS.
     849        Returns a list of tuples containing all schema and table names that
     850        have associated Django models and are in INSTALLED_APPS.
    810851
    811         If only_existing is True, the resulting list will only include the tables
    812         that actually exist in the database.
     852        If only_existing is True, the resulting list will only include the
     853        tables that actually exist in the database.
    813854        """
    814855        from django.db import models, router
    815856        tables = set()
    816         for app in models.get_apps():
    817             for model in models.get_models(app):
    818                 if not model._meta.managed:
    819                     continue
    820                 if not router.allow_syncdb(self.connection.alias, model):
    821                     continue
    822                 tables.add(model._meta.db_table)
    823                 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
    824857        if only_existing:
    825             existing_tables = self.table_names()
    826             tables = [
    827                 t
    828                 for t in tables
    829                 if self.table_name_converter(t) in existing_tables
    830             ]
     858            existing_tables = set([('', tn) for tn in self.table_names()])
     859            seen_schemas = set()
     860        for model in models.get_models():
     861            if not model._meta.managed:
     862                continue
     863            if not router.allow_syncdb(self.connection.alias, model):
     864                continue
     865            db_schema = model._meta.db_schema
     866            if only_existing and db_schema and db_schema not in seen_schemas:
     867                existing_tables.update([(db_schema, tn) for tn in
     868                                        self.schema_table_names(db_schema)])
     869                seen_schemas.add(db_schema)
     870            tables.add((db_schema, model._meta.db_table))
     871            m2m_tables = []
     872            for f in model._meta.local_many_to_many:
     873                m2m_schema = f.m2m_db_schema()
     874                m2m_table = f.m2m_db_table()
     875                if only_existing and m2m_schema and m2m_schema not in seen_schemas:
     876                    existing_tables.update([(m2m_schema, tn) for tn in
     877                                        self.schema_table_names(m2m_schema)])
     878                    seen_schemas.add(m2m_schema)
     879                m2m_tables.append((m2m_schema, m2m_table))
     880            tables.update(m2m_tables)
     881        if only_existing:
     882            tables = [(s, t) for (s, t) in tables
     883                      if (self.schema_name_converter(s),
     884                          self.table_name_converter(t)) in existing_tables]
    831885        return tables
    832886
    833887    def installed_models(self, tables):
     
    859913                    continue
    860914                for f in model._meta.local_fields:
    861915                    if isinstance(f, models.AutoField):
    862                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
     916                        sequence_list.append({'table': model._meta.db_table,
     917                                              'column': f.column,
     918                                              'schema': model._meta.db_schema})
    863919                        break # Only one AutoField is allowed per model, so don't bother continuing.
    864920
    865921                for f in model._meta.local_many_to_many:
     922                    schema = f.m2m_db_schema()
    866923                    # If this is an m2m using an intermediate table,
    867924                    # we don't need to reset the sequence.
    868925                    if f.rel.through is None:
    869                         sequence_list.append({'table': f.m2m_db_table(), 'column': None})
     926                        sequence_list.append({'table': f.m2m_db_table(),
     927                                              'column': None,
     928                                              'schema': schema})
    870929
    871930        return sequence_list
    872931
  • django/db/backends/postgresql_psycopg2/base.py

     
    7272    can_defer_constraint_checks = True
    7373    has_select_for_update = True
    7474    has_select_for_update_nowait = True
     75    default_schema_name = u'public'
    7576
    7677
    7778class DatabaseWrapper(BaseDatabaseWrapper):
  • django/db/backends/creation.py

     
    2626        """
    2727        return '%x' % (abs(hash(args)) % 4294967296L)  # 2**32
    2828
     29    def default_schema(self):
     30        return ""
     31
     32    def sql_create_schema(self, schema, style):
     33        """"
     34        Returns the SQL required to create a single schema
     35        """
     36        qn = self.connection.ops.quote_name
     37        output = "%s %s;" % (style.SQL_KEYWORD('CREATE SCHEMA'), qn(schema))
     38        return output
     39
    2940    def sql_create_model(self, model, style, known_models=set()):
    3041        """
    3142        Returns the SQL required to create a single model, as a tuple of:
     
    6980            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    7081                ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
    7182
    72         full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     83        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(opts.qualified_name) + ' (']
    7384        for i, line in enumerate(table_output): # Combine and add commas.
    7485            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    7586        full_statement.append(')')
     
    8192        if opts.has_auto_field:
    8293            # Add any extra SQL needed to support auto-incrementing primary keys.
    8394            auto_column = opts.auto_field.db_column or opts.auto_field.name
    84             autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column)
     95            autoinc_sql = self.connection.ops.autoinc_sql(opts.db_schema,
     96                                                          opts.db_table,
     97                                                          auto_column)
    8598            if autoinc_sql:
    8699                for stmt in autoinc_sql:
    87100                    final_output.append(stmt)
     
    93106        qn = self.connection.ops.quote_name
    94107        if field.rel.to in known_models:
    95108            output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \
    96                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \
     109                style.SQL_TABLE(field.rel.to._meta.qualified_name) + ' (' + \
    97110                style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' +
    98111                self.connection.ops.deferrable_sql()
    99112            ]
     
    119132            for rel_class, f in pending_references[model]:
    120133                rel_opts = rel_class._meta
    121134                r_table = rel_opts.db_table
     135                r_qname = rel_opts.qualified_name
    122136                r_col = f.column
    123137                table = opts.db_table
     138                qname = opts.qualified_name
    124139                col = opts.get_field(f.rel.field_name).column
    125140                # For MySQL, r_name must be unique in the first 64 characters.
    126141                # So we are careful with character usage here.
    127142                r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table))
    128143                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    129                     (qn(r_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())),
    130                     qn(r_col), qn(table), qn(col),
     144                    (r_qname, qn(truncate_name(r_name, self.connection.ops.max_name_length())),
     145                    qn(r_col), qname, qn(col),
    131146                    self.connection.ops.deferrable_sql()))
    132147            del pending_references[model]
    133148        return final_output
    134149
     150    def sql_for_many_to_many(self, model, style):
     151        "Return the CREATE TABLE statments for all the many-to-many tables defined on a model"
     152        import warnings
     153        warnings.warn(
     154            'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
     155            PendingDeprecationWarning
     156        )
     157
     158        output = []
     159        for f in model._meta.local_many_to_many:
     160            if model._meta.managed or f.rel.to._meta.managed:
     161                output.extend(self.sql_for_many_to_many_field(model, f, style))
     162        return output
     163
     164    def sql_for_many_to_many_field(self, model, f, style):
     165        "Return the CREATE TABLE statements for a single m2m field"
     166        import warnings
     167        warnings.warn(
     168            'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
     169            PendingDeprecationWarning
     170        )
     171
     172        from django.db import models
     173        from django.db.backends.util import truncate_name
     174
     175        output = []
     176        if f.rel.through._meta.auto_created:
     177            opts = model._meta
     178            qn = self.connection.ops.quote_name
     179            tablespace = f.db_tablespace or opts.db_tablespace
     180            if tablespace:
     181                sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
     182                if sql:
     183                    tablespace_sql = ' ' + sql
     184                else:
     185                    tablespace_sql = ''
     186            else:
     187                tablespace_sql = ''
     188            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     189                style.SQL_TABLE(qn(f.m2m_qualified_name())) + ' (']
     190            table_output.append('    %s %s %s%s,' %
     191                (style.SQL_FIELD(qn('id')),
     192                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type(connection=self.connection)),
     193                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
     194                tablespace_sql))
     195
     196            deferred = []
     197            inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style)
     198            table_output.extend(inline_output)
     199
     200            table_output.append('    %s (%s, %s)%s' %
     201                (style.SQL_KEYWORD('UNIQUE'),
     202                style.SQL_FIELD(qn(f.m2m_column_name())),
     203                style.SQL_FIELD(qn(f.m2m_reverse_name())),
     204                tablespace_sql))
     205            table_output.append(')')
     206            if opts.db_tablespace:
     207                # f.db_tablespace is only for indices, so ignore its value here.
     208                table_output.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
     209            table_output.append(';')
     210            output.append('\n'.join(table_output))
     211
     212            for r_table, r_col, table, col in deferred:
     213                r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table))
     214                output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
     215                (qn(r_table),
     216                qn(truncate_name(r_name, self.connection.ops.max_name_length())),
     217                qn(r_col), qn(table), qn(col),
     218                self.connection.ops.deferrable_sql()))
     219
     220            # Add any extra SQL needed to support auto-incrementing PKs
     221            autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_schema(),
     222                                                          f.m2m_db_table(),
     223                                                          'id')
     224            if autoinc_sql:
     225                for stmt in autoinc_sql:
     226                    output.append(stmt)
     227        return output
     228
     229    def sql_for_inline_many_to_many_references(self, model, field, style):
     230        "Create the references to other tables required by a many-to-many table"
     231        import warnings
     232        warnings.warn(
     233            'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
     234            PendingDeprecationWarning
     235        )
     236
     237        from django.db import models
     238        opts = model._meta
     239        qn = self.connection.ops.quote_name
     240
     241        table_output = [
     242            '    %s %s %s %s (%s)%s,' %
     243                (style.SQL_FIELD(qn(field.m2m_column_name())),
     244                style.SQL_COLTYPE(models.ForeignKey(model).db_type(connection=self.connection)),
     245                style.SQL_KEYWORD('NOT NULL REFERENCES'),
     246                style.SQL_TABLE(opts.qualified_name),
     247                style.SQL_FIELD(qn(opts.pk.column)),
     248                self.connection.ops.deferrable_sql()),
     249            '    %s %s %s %s (%s)%s,' %
     250                (style.SQL_FIELD(qn(field.m2m_reverse_name())),
     251                style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type(connection=self.connection)),
     252                style.SQL_KEYWORD('NOT NULL REFERENCES'),
     253                style.SQL_TABLE(field.rel.to._meta.qualified_name),
     254                style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
     255                self.connection.ops.deferrable_sql())
     256        ]
     257        deferred = []
     258
     259        return table_output, deferred
     260
    135261    def sql_indexes_for_model(self, model, style):
    136262        "Returns the CREATE INDEX SQL statements for a single model"
    137263        if not model._meta.managed or model._meta.proxy:
     
    157283            else:
    158284                tablespace_sql = ''
    159285            i_name = '%s_%s' % (model._meta.db_table, self._digest(f.column))
     286            i_name = self.connection.ops.prep_db_index(model._meta.db_schema, i_name)
    160287            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
    161288                style.SQL_TABLE(qn(truncate_name(i_name, self.connection.ops.max_name_length()))) + ' ' +
    162289                style.SQL_KEYWORD('ON') + ' ' +
    163                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
     290                style.SQL_TABLE(model._meta.qualified_name) + ' ' +
    164291                "(%s)" % style.SQL_FIELD(qn(f.column)) +
    165292                "%s;" % tablespace_sql]
    166293        else:
    167294            output = []
    168295        return output
    169296
     297    def sql_destroy_schema(self, schema, style):
     298        """"
     299        Returns the SQL required to destroy a single schema.
     300        """
     301        qn = self.connection.ops.quote_name
     302        output = "%s %s CASCADE;" % (style.SQL_KEYWORD('DROP SCHEMA IF EXISTS'), qn(schema))
     303        return output
     304
    170305    def sql_destroy_model(self, model, references_to_delete, style):
    171306        "Return the DROP TABLE and restraint dropping statements for a single model"
    172307        if not model._meta.managed or model._meta.proxy:
     
    174309        # Drop the table now
    175310        qn = self.connection.ops.quote_name
    176311        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
    177                               style.SQL_TABLE(qn(model._meta.db_table)))]
     312                              style.SQL_TABLE(model._meta.qualified_name))]
    178313        if model in references_to_delete:
    179314            output.extend(self.sql_remove_table_constraints(model, references_to_delete, style))
    180315
    181316        if model._meta.has_auto_field:
    182             ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
     317            ds = self.connection.ops.drop_sequence_sql(model._meta.db_schema,
     318                                                       model._meta.db_table)
    183319            if ds:
    184320                output.append(ds)
    185321        return output
     
    193329        qn = self.connection.ops.quote_name
    194330        for rel_class, f in references_to_delete[model]:
    195331            table = rel_class._meta.db_table
     332            qname = rel_class._meta.qualified_name
    196333            col = f.column
    197334            r_table = model._meta.db_table
    198335            r_col = model._meta.get_field(f.rel.field_name).column
    199336            r_name = '%s_refs_%s_%s' % (col, r_col, self._digest(table, r_table))
    200337            output.append('%s %s %s %s;' % \
    201338                (style.SQL_KEYWORD('ALTER TABLE'),
    202                 style.SQL_TABLE(qn(table)),
     339                style.SQL_TABLE(qname),
    203340                style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
    204341                style.SQL_FIELD(qn(truncate_name(r_name, self.connection.ops.max_name_length())))))
    205342        del references_to_delete[model]
    206343        return output
    207344
     345    def sql_destroy_many_to_many(self, model, f, style):
     346        "Returns the DROP TABLE statements for a single m2m field"
     347        import warnings
     348        warnings.warn(
     349            'Database creation API for m2m tables has been deprecated. M2M models are now automatically generated',
     350            PendingDeprecationWarning
     351        )
     352
     353        qn = self.connection.ops.quote_name
     354        output = []
     355        if f.rel.through._meta.auto_created:
     356            output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
     357                style.SQL_TABLE(f.m2m_qualified_name())))
     358            ds = self.connection.ops.drop_sequence_sql(model._meta.db_schema,
     359                               "%s_%s" % (model._meta.db_table, f.column))
     360            if ds:
     361                output.append(ds)
     362        return output
     363
    208364    def create_test_db(self, verbosity=1, autoclobber=False):
    209365        """
    210366        Creates a test database, prompting the user for confirmation if the
     
    221377                test_db_repr = " ('%s')" % test_database_name
    222378            print "Creating test database for alias '%s'%s..." % (self.connection.alias, test_db_repr)
    223379
    224         self._create_test_db(verbosity, autoclobber)
     380        schema_apps = self._get_app_with_schemas()
     381        schemas = self._get_schemas(schema_apps)
     382        test_database_name = self._create_test_db(verbosity, autoclobber, schemas)
    225383
    226384        self.connection.close()
    227385        self.connection.settings_dict["NAME"] = test_database_name
    228386
     387        # Create the test schemas.
     388        cursor = self.connection.cursor()
     389        self._create_test_schemas(verbosity, schemas, cursor)
     390
     391        call_command('syncdb', verbosity=verbosity, interactive=False, database=self.connection.alias)
    229392        # Confirm the feature set of the test database
    230393        self.connection.features.confirm()
    231394
     
    271434
    272435        return test_database_name
    273436
     437    def _create_test_schemas(self, verbosity, schemas, cursor):
     438        from django.core.management.color import no_style
     439        style = no_style()
     440        for schema in schemas:
     441            if verbosity >= 1:
     442                print "Creating schema %s" % schema
     443            cursor.execute(self.sql_create_schema(schema, style))
     444
     445    def _destroy_test_schemas(self, verbosity, schemas, cursor):
     446        from django.core.management.color import no_style
     447        style = no_style()
     448        for schema in schemas:
     449            if verbosity >= 1:
     450                print "Destroying schema %s" % schema
     451            cursor.execute(self.sql_destroy_schema(schema, style))
     452            if verbosity >= 1:
     453                print "Schema %s destroyed" % schema
     454
     455    def _get_schemas(self, apps):
     456        from django.db import models
     457        schemas = set()
     458        for app in apps:
     459            app_models = models.get_models(app)
     460            for model in app_models:
     461                schema = model._meta.db_schema
     462                if not schema or schema in schemas:
     463                    continue
     464                schemas.add(schema)
     465        return schemas
     466
     467    def _get_app_with_schemas(self):
     468        from django.db import models
     469        apps = models.get_apps()
     470        schema_apps = set()
     471        for app in apps:
     472            app_models = models.get_models(app)
     473            for model in app_models:
     474                schema = model._meta.db_schema
     475                if not schema or app in schema_apps:
     476                    continue
     477                schema_apps.add(app)
     478        return schema_apps
     479
    274480    def _get_test_db_name(self):
    275481        """
    276482        Internal implementation - returns the name of the test DB that will be
     
    282488            return self.connection.settings_dict['TEST_NAME']
    283489        return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
    284490
    285     def _create_test_db(self, verbosity, autoclobber):
     491    def _create_test_db(self, verbosity, autoclobber, schemas):
    286492        "Internal implementation - creates the test db tables."
    287493        suffix = self.sql_table_creation_suffix()
    288494
     
    305511                try:
    306512                    if verbosity >= 1:
    307513                        print "Destroying old test database '%s'..." % self.connection.alias
     514                    self._destroy_test_schemas(verbosity, schemas, cursor)
    308515                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
    309516                    cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
    310517                except Exception, e:
  • django/conf/project_template/settings.py

     
    1717        'PASSWORD': '',                  # Not used with sqlite3.
    1818        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
    1919        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
     20        'SCHEMA': '',                    # Set to empty string for default.
    2021    }
    2122}
    2223
  • django/conf/global_settings.py

     
    150150DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
    151151DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
    152152DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
     153DATABASE_SCHEMA = ''           # Set to empty string for default.
    153154
    154155# New format
    155156DATABASES = {
  • django/core/management/commands/syncdb.py

     
    5656        cursor = connection.cursor()
    5757
    5858        # Get a list of already installed *models* so that references work right.
    59         tables = connection.introspection.table_names()
     59        schemas = connection.introspection.schema_names()
     60        if schemas:
     61            tables = []
     62            default_schema_name = connection.features.default_schema_name
     63            for schema in connection.introspection.schema_names():
     64               if default_schema_name and schema == default_schema_name:
     65                   sn = ''
     66               else:
     67                   sn = schema
     68               for tn in connection.introspection.schema_table_names(schema):
     69                   tables.append((sn, tn))
     70        else:
     71            tables = [('', tn) for tn in connection.introspection.table_names()]
    6072        seen_models = connection.introspection.installed_models(tables)
     73        seen_schemas = set()
    6174        created_models = set()
    6275        pending_references = {}
    6376
     
    6881                if router.allow_syncdb(db, m)])
    6982            for app in models.get_apps()
    7083        ]
     84
     85        def model_schema(model):
     86            db_schema = model._meta.db_schema
     87            if db_schema:
     88                db_schema = connection.introspection.table_name_converter(db_schema)
     89            return db_schema
     90
    7191        def model_installed(model):
    7292            opts = model._meta
    7393            converter = connection.introspection.table_name_converter
    74             return not ((converter(opts.db_table) in tables) or
    75                 (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
     94            db_schema = model_schema(model)
     95            schema_table = (db_schema, converter(opts.db_table))
     96            return not ((schema_table in tables) or
     97                (opts.auto_created and \
     98                 (db_schema, converter(opts.auto_created._meta.db_table)) in tables)
     99                 #(model_schema(opts.auto_created), converter(opts.auto_created._meta.db_table)) in tables)
     100             )
    76101
    77102        manifest = SortedDict(
    78103            (app_name, filter(model_installed, model_list))
     
    84109            print "Creating tables ..."
    85110        for app_name, model_list in manifest.items():
    86111            for model in model_list:
     112                # Add model-defined schema tables if any.
     113                db_schema = model_schema(model)
     114                if db_schema and db_schema not in seen_schemas:
     115                    tables += [(db_schema, tn) for tn in
     116                               connection.introspection.schema_table_names(db_schema)]
     117                    seen_schemas.add(db_schema)
     118
    87119                # Create the model's database table, if it doesn't already exist.
    88120                if verbosity >= 3:
    89121                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
     
    96128                        sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
    97129                sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
    98130                if verbosity >= 1 and sql:
    99                     print "Creating table %s" % model._meta.db_table
     131                    if db_schema:
     132                        print "Creating table %s.%s" % (db_schema, model._meta.db_table)
     133                    else:
     134                        print "Creating table %s" % model._meta.db_table
    100135                for statement in sql:
    101136                    cursor.execute(statement)
    102                 tables.append(connection.introspection.table_name_converter(model._meta.db_table))
     137                if sql:
     138                    tables.append((db_schema, connection.introspection.table_name_converter(model._meta.db_table)))
    103139
    104140
    105141        transaction.commit_unless_managed(using=db)
  • django/core/management/sql.py

     
    6363
    6464    # Figure out which tables already exist
    6565    if cursor:
    66         table_names = connection.introspection.get_table_list(cursor)
     66        table_names = [('', tn) for tn in
     67                       connection.introspection.get_table_list(cursor)]
    6768    else:
    6869        table_names = []
    6970
     
    7475
    7576    references_to_delete = {}
    7677    app_models = models.get_models(app, include_auto_created=True)
     78    seen_schemas = set()
    7779    for model in app_models:
    78         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     80        db_schema = model._meta.db_schema
     81        # Find additional tables in model-defined schemas.
     82        if db_schema:
     83            db_schema = connection.introspection.schema_name_converter(db_schema)
     84            if db_schema not in seen_schemas:
     85                table_names += [(db_schema, tn) for tn in connection.introspection.get_schema_table_list(cursor, db_schema)]
     86                seen_schemas.add(db_schema)
     87        schema_table = (db_schema,
     88                        connection.introspection.table_name_converter(model._meta.db_table))
     89
     90        if cursor and schema_table in table_names:
    7991            # The table exists, so it needs to be dropped
    8092            opts = model._meta
    8193            for f in opts.local_fields:
     
    8597            to_delete.add(model)
    8698
    8799    for model in app_models:
    88         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     100        db_schema = model._meta.db_schema
     101        if db_schema:
     102            db_schema = connection.introspection.schema_name_converter(db_schema)
     103        schema_table = (db_schema,
     104                        connection.introspection.table_name_converter(model._meta.db_table))
     105        if schema_table in table_names:
    89106            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    90107
    91108    # Close database connection explicitly, in case this output is being piped
     
    116133    if only_django:
    117134        tables = connection.introspection.django_table_names(only_existing=True)
    118135    else:
    119         tables = connection.introspection.table_names()
     136        tables = [('', tn) for tn in connection.introspection.table_names()]
    120137    statements = connection.ops.sql_flush(
    121138        style, tables, connection.introspection.sequence_list()
    122139    )
  • django/contrib/contenttypes/generic.py

     
    133133    def m2m_reverse_name(self):
    134134        return self.rel.to._meta.pk.column
    135135
     136    def m2m_db_schema(self):
     137        return self.rel.to._meta.db_schema
     138
     139    def m2m_qualified_name(self):
     140        schema = self.m2m_db_schema()
     141        table = self.m2m_db_table()
     142        return connection.ops.prep_db_table(schema, table)
     143
    136144    def m2m_target_field_name(self):
    137145        return self.model._meta.pk.name
    138146
  • tests/regressiontests/introspection/tests.py

     
    5858
    5959    def test_sequence_list(self):
    6060        sequences = connection.introspection.sequence_list()
    61         expected = {'table': Reporter._meta.db_table, 'column': 'id'}
     61        expected = {'table': Reporter._meta.db_table, 'column': 'id', 'schema': ''}
    6262        self.assertTrue(expected in sequences,
    6363                     'Reporter sequence not found in sequence_list()')
    6464
  • tests/regressiontests/backends/tests.py

     
    157157        # A full flush is expensive to the full test, so we dig into the
    158158        # internals to generate the likely offending SQL and run it manually
    159159
    160         # Some convenience aliases
    161         VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
    162         VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
    163         tables = [
    164             VLM._meta.db_table,
    165             VLM_m2m._meta.db_table,
    166         ]
    167         sequences = [
    168             {
    169                 'column': VLM._meta.pk.column,
    170                 'table': VLM._meta.db_table
    171             },
    172         ]
    173         cursor = connection.cursor()
    174         for statement in connection.ops.sql_flush(no_style(), tables, sequences):
    175             cursor.execute(statement)
     160                # Some convenience aliases
     161                VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
     162                VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
     163                tables = [
     164                        (VLM._meta.db_schema, VLM._meta.db_table),
     165                        (VLM_m2m._meta.db_schema, VLM_m2m._meta.db_table),
     166                ]
     167                sequences = [
     168                        {
     169                                'column': VLM._meta.pk.column,
     170                                'table': VLM._meta.db_table,
     171                                'schema': VLM._meta.db_schema,
     172                        },
     173                ]
     174                cursor = connection.cursor()
     175                for statement in connection.ops.sql_flush(no_style(), tables, sequences):
     176                        cursor.execute(statement)
    176177
    177178class SequenceResetTest(TestCase):
    178179    def test_generic_relation(self):
Back to Top