Ticket #6148: generic-db_schema-r11231.diff

File generic-db_schema-r11231.diff, 65.9 KB (added by ikelly, 6 years ago)
  • django/db/models/sql/query.py

     
    581581        might not have all the pieces in place at that time.
    582582        """
    583583        if not self.tables:
    584             self.join((None, self.model._meta.db_table, None, None))
     584            self.join((None, self.model._meta.qualified_name, None, None))
    585585        if (not self.select and self.default_cols and not
    586586                self.included_inherited_models):
    587587            self.setup_inherited_models()
     
    681681        Callback used by deferred_to_columns(). The "target" parameter should
    682682        be a set instance.
    683683        """
    684         table = model._meta.db_table
     684        table = model._meta.qualified_name
    685685        if table not in target:
    686686            target[table] = set()
    687687        for field in fields:
     
    798798                        alias = start_alias
    799799                    else:
    800800                        link_field = opts.get_ancestor_link(model)
    801                         alias = self.join((start_alias, model._meta.db_table,
     801                        alias = self.join((start_alias,
     802                                model._meta.qualified_name,
    802803                                link_field.column, model._meta.pk.column))
    803804                    seen[model] = alias
    804805            else:
     
    12061207            alias = self.tables[0]
    12071208            self.ref_alias(alias)
    12081209        else:
    1209             alias = self.join((None, self.model._meta.db_table, None, None))
     1210            alias = self.join((None, self.model._meta.qualified_name,
     1211                               None, None))
    12101212        return alias
    12111213
    12121214    def count_active_tables(self):
     
    13191321                    seen[model] = root_alias
    13201322                else:
    13211323                    link_field = opts.get_ancestor_link(model)
    1322                     seen[model] = self.join((root_alias, model._meta.db_table,
     1324                    seen[model] = self.join((root_alias,
     1325                            model._meta.qualified_name,
    13231326                            link_field.column, model._meta.pk.column))
    13241327        self.included_inherited_models = seen
    13251328
     
    13771380            # what "used" specifies).
    13781381            avoid = avoid_set.copy()
    13791382            dupe_set = orig_dupe_set.copy()
    1380             table = f.rel.to._meta.db_table
     1383            table = f.rel.to._meta.qualified_name
    13811384            if nullable or f.null:
    13821385                promote = True
    13831386            else:
     
    14011404                                ())
    14021405                        dupe_set.add((opts, lhs_col))
    14031406                    int_opts = int_model._meta
    1404                     alias = self.join((alias, int_opts.db_table, lhs_col,
    1405                             int_opts.pk.column), exclusions=used,
     1407                    alias = self.join((alias, int_opts.qualified_name,
     1408                            lhs_col, int_opts.pk.column), exclusions=used,
    14061409                            promote=promote)
    14071410                    alias_chain.append(alias)
    14081411                    for (dupe_opts, dupe_col) in dupe_set:
     
    17561759                                    (id(opts), lhs_col), ()))
    17571760                            dupe_set.add((opts, lhs_col))
    17581761                        opts = int_model._meta
    1759                         alias = self.join((alias, opts.db_table, lhs_col,
    1760                                 opts.pk.column), exclusions=exclusions)
     1762                        alias = self.join((alias, opts.qualified_name,
     1763                                           lhs_col, opts.pk.column),
     1764                                          exclusions=exclusions)
    17611765                        joins.append(alias)
    17621766                        exclusions.add(alias)
    17631767                        for (dupe_opts, dupe_col) in dupe_set:
     
    17821786                        (table1, from_col1, to_col1, table2, from_col2,
    17831787                                to_col2, opts, target) = cached_data
    17841788                    else:
    1785                         table1 = field.m2m_db_table()
     1789                        table1 = field.m2m_qualified_name()
    17861790                        from_col1 = opts.pk.column
    17871791                        to_col1 = field.m2m_column_name()
    17881792                        opts = field.rel.to._meta
    1789                         table2 = opts.db_table
     1793                        table2 = opts.qualified_name
    17901794                        from_col2 = field.m2m_reverse_name()
    17911795                        to_col2 = opts.pk.column
    17921796                        target = opts.pk
     
    18131817                    else:
    18141818                        opts = field.rel.to._meta
    18151819                        target = field.rel.get_related_field()
    1816                         table = opts.db_table
     1820                        table = opts.qualified_name
    18171821                        from_col = field.column
    18181822                        to_col = target.column
    18191823                        orig_opts._join_cache[name] = (table, from_col, to_col,
     
    18351839                        (table1, from_col1, to_col1, table2, from_col2,
    18361840                                to_col2, opts, target) = cached_data
    18371841                    else:
    1838                         table1 = field.m2m_db_table()
     1842                        table1 = field.m2m_qualified_name()
    18391843                        from_col1 = opts.pk.column
    18401844                        to_col1 = field.m2m_reverse_name()
    18411845                        opts = orig_field.opts
    1842                         table2 = opts.db_table
     1846                        table2 = opts.qualified_name
    18431847                        from_col2 = field.m2m_column_name()
    18441848                        to_col2 = opts.pk.column
    18451849                        target = opts.pk
     
    18621866                        local_field = opts.get_field_by_name(
    18631867                                field.rel.field_name)[0]
    18641868                        opts = orig_field.opts
    1865                         table = opts.db_table
     1869                        table = opts.qualified_name
    18661870                        from_col = local_field.column
    18671871                        to_col = field.column
    18681872                        target = opts.pk
     
    21012105        self.group_by = []
    21022106        if self.connection.features.allows_group_by_pk:
    21032107            if len(self.select) == len(self.model._meta.fields):
    2104                 self.group_by.append((self.model._meta.db_table,
     2108                self.group_by.append((self.model._meta.qualified_name,
    21052109                                      self.model._meta.pk.column))
    21062110                return
    21072111
     
    21232127        else:
    21242128            opts = self.model._meta
    21252129            if not self.select:
    2126                 count = self.aggregates_module.Count((self.join((None, opts.db_table, None, None)), opts.pk.column),
     2130                count = self.aggregates_module.Count((self.join((None,
     2131                           opts.qualified_name, None, None)), opts.pk.column),
    21272132                                         is_summary=True, distinct=True)
    21282133            else:
    21292134                # Because of SQL portability issues, multi-column, distinct
  • django/db/models/sql/subqueries.py

     
    5454                            'in',
    5555                            pk_list[offset : offset+GET_ITERATOR_CHUNK_SIZE]),
    5656                            AND)
    57                     self.do_query(related.field.m2m_db_table(), where)
     57                    self.do_query(related.field.m2m_qualified_name(), where)
    5858
    5959        for f in cls._meta.many_to_many:
    6060            w1 = self.where_class()
     
    7070                        AND)
    7171                if w1:
    7272                    where.add(w1, AND)
    73                 self.do_query(f.m2m_db_table(), where)
     73                self.do_query(f.m2m_qualified_name(), where)
    7474
    7575    def delete_batch(self, pk_list):
    7676        """
     
    8585            field = self.model._meta.pk
    8686            where.add((Constraint(None, field.column, field), 'in',
    8787                    pk_list[offset : offset + GET_ITERATOR_CHUNK_SIZE]), AND)
    88             self.do_query(self.model._meta.db_table, where)
     88            self.do_query(self.model._meta.qualified_name, where)
    8989
    9090class UpdateQuery(Query):
    9191    """
     
    304304        # going to be column names (so we can avoid the extra overhead).
    305305        qn = self.connection.ops.quote_name
    306306        opts = self.model._meta
    307         result = ['INSERT INTO %s' % qn(opts.db_table)]
     307        result = ['INSERT INTO %s' % opts.qualified_name]
    308308        result.append('(%s)' % ', '.join([qn(c) for c in self.columns]))
    309309        result.append('VALUES (%s)' % ', '.join(self.values))
    310310        params = self.params
    311311        if self.return_id and self.connection.features.can_return_id_from_insert:
    312             col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
     312            col = "%s.%s" % (opts.qualified_name, qn(opts.pk.column))
    313313            r_fmt, r_params = self.connection.ops.return_insert_id()
    314314            result.append(r_fmt % col)
    315315            params = params + r_params
     
    323323        if self.connection.features.can_return_id_from_insert:
    324324            return self.connection.ops.fetch_returned_insert_id(cursor)
    325325        return self.connection.ops.last_insert_id(cursor,
    326                 self.model._meta.db_table, self.model._meta.pk.column)
     326                self.model._meta.db_schema, self.model._meta.db_table,
     327                self.model._meta.pk.column)
    327328
    328329    def insert_values(self, insert_values, raw_values=False):
    329330        """
  • django/db/models/base.py

     
    597597            # into a pure queryset operation.
    598598            where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
    599599                (qn('_order'), op, qn('_order'),
    600                 qn(self._meta.db_table), qn(self._meta.pk.column))]
     600                self._meta.qualified_name, qn(self._meta.pk.column))]
    601601            params = [self.pk]
    602602            obj = self._default_manager.filter(**{order_field.name: getattr(self, order_field.attname)}).extra(where=where, params=params).order_by(order)[:1].get()
    603603            setattr(self, cachename, obj)
  • django/db/models/options.py

     
    2121DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering',
    2222                 'unique_together', 'permissions', 'get_latest_by',
    2323                 'order_with_respect_to', 'app_label', 'db_tablespace',
    24                  'abstract', 'managed', 'proxy')
     24                 'abstract', 'managed', 'proxy', 'db_schema')
    2525
    2626class Options(object):
    2727    def __init__(self, meta, app_label=None):
     
    3030        self.module_name, self.verbose_name = None, None
    3131        self.verbose_name_plural = None
    3232        self.db_table = ''
     33        self.db_schema = ''
     34        self.qualified_name = ''
    3335        self.ordering = []
    3436        self.unique_together =  []
    3537        self.permissions =  []
     
    103105            self.db_table = "%s_%s" % (self.app_label, self.module_name)
    104106            self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
    105107
     108        # Construct qualified table name.
     109        self.qualified_name = connection.ops.prep_db_table(self.db_schema,
     110                                                           self.db_table)
     111        if self.qualified_name == connection.ops.quote_name(self.db_table):
     112            # If unchanged, the backend doesn't support schemas.
     113            self.db_schema = ''
    106114
    107115    def _prepare(self, model):
    108116        if self.order_with_respect_to:
     
    176184        self.pk = target._meta.pk
    177185        self.proxy_for_model = target
    178186        self.db_table = target._meta.db_table
     187        self.db_schema = target._meta.db_schema
    179188
    180189    def __repr__(self):
    181190        return '<Options for %s>' % self.object_name
  • django/db/models/fields/related.py

     
    562562            core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
    563563            instance=instance,
    564564            symmetrical=False,
    565             join_table=qn(self.related.field.m2m_db_table()),
     565            join_table=self.related.field.m2m_qualified_name(),
    566566            source_col_name=qn(self.related.field.m2m_reverse_name()),
    567567            target_col_name=qn(self.related.field.m2m_column_name())
    568568        )
     
    607607            core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
    608608            instance=instance,
    609609            symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)),
    610             join_table=qn(self.field.m2m_db_table()),
     610            join_table=self.field.m2m_qualified_name(),
    611611            source_col_name=qn(self.field.m2m_column_name()),
    612612            target_col_name=qn(self.field.m2m_reverse_name())
    613613        )
     
    739739        if isinstance(self.rel.to, basestring):
    740740            target = self.rel.to
    741741        else:
    742             target = self.rel.to._meta.db_table
     742            target = self.rel.to._meta.qualified_name
    743743        cls._meta.duplicate_targets[self.column] = (target, "o2m")
    744744
    745745    def contribute_to_related_class(self, cls, related):
     
    805805            through=kwargs.pop('through', None))
    806806
    807807        self.db_table = kwargs.pop('db_table', None)
     808        self.db_schema = kwargs.pop('db_schema', '')
    808809        if kwargs['rel'].through is not None:
    809810            self.creates_table = False
    810811            assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
     
    829830            return util.truncate_name('%s_%s' % (opts.db_table, self.name),
    830831                                      connection.ops.max_name_length())
    831832
     833    def _get_m2m_db_schema(self, opts):
     834        "Function that can be curried to provide the m2m schema name for this relation"
     835        if self.rel.through is not None and self.rel.through_model._meta.db_schema:
     836            return self.rel.through_model._meta.db_schema
     837        elif self.db_schema:
     838            return self.db_schema
     839        else:
     840            return opts.db_schema
     841
     842    def _get_m2m_qualified_name(self, opts):
     843        "Function that can be curried to provide the qualified m2m table name for this relation"
     844        schema = self._get_m2m_db_schema(opts)
     845        table = self._get_m2m_db_table(opts)
     846        return connection.ops.prep_db_table(schema, table)
     847
    832848    def _get_m2m_column_name(self, related):
    833849        "Function that can be curried to provide the source column name for the m2m table"
    834850        try:
     
    928944
    929945        # Set up the accessor for the m2m table name for the relation
    930946        self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
     947        self.m2m_db_schema = curry(self._get_m2m_db_schema, cls._meta)
     948        self.m2m_qualified_name = curry(self._get_m2m_qualified_name,
     949                                        cls._meta)
    931950
    932951        # Populate some necessary rel arguments so that cross-app relations
    933952        # work correctly.
     
    942961        if isinstance(self.rel.to, basestring):
    943962            target = self.rel.to
    944963        else:
    945             target = self.rel.to._meta.db_table
     964            target = self.rel.to._meta.qualified_name
    946965        cls._meta.duplicate_targets[self.column] = (target, "m2m")
    947966
    948967    def contribute_to_related_class(self, cls, related):
  • django/db/backends/postgresql/introspection.py

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

     
    5252            return 'HOST(%s)'
    5353        return '%s'
    5454
    55     def last_insert_id(self, cursor, table_name, pk_name):
    56         cursor.execute("SELECT CURRVAL('\"%s_%s_seq\"')" % (table_name, pk_name))
     55    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
     56        sequence_name = '%s_%s_seq' % (table_name, pk_name)
     57        sequence_name = self.prep_db_table(schema_name, sequence_name)
     58        cursor.execute("SELECT CURRVAL('%s')" % sequence_name)
    5759        return cursor.fetchone()[0]
    5860
    5961    def no_limit_value(self):
     
    6466            return name # Quoting once is enough.
    6567        return '"%s"' % name
    6668
     69    def prep_db_table(self, db_schema, db_table):
     70        qn = self.quote_name
     71        if db_schema:
     72            return "%s.%s" % (qn(db_schema), qn(db_table))
     73        else:
     74            return qn(db_table)
     75
    6776    def sql_flush(self, style, tables, sequences):
    6877        if tables:
     78            qnames = [self.prep_db_name(schema, table)
     79                      for (schema, table) in tables]
    6980            if self.postgres_version[0:2] >= (8,1):
    7081                # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to*
    7182                # in order to be able to truncate tables referenced by a foreign
     
    7384                # statement.
    7485                sql = ['%s %s;' % \
    7586                    (style.SQL_KEYWORD('TRUNCATE'),
    76                      style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
     87                     style.SQL_FIELD(', '.join(qnames))
    7788                )]
    7889            else:
    7990                # Older versions of Postgres can't do TRUNCATE in a single call, so
     
    8192                sql = ['%s %s %s;' % \
    8293                        (style.SQL_KEYWORD('DELETE'),
    8394                         style.SQL_KEYWORD('FROM'),
    84                          style.SQL_FIELD(self.quote_name(table))
    85                          ) for table in tables]
     95                         style.SQL_FIELD(qname)
     96                         ) for qname in qnames]
    8697
    8798            # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
    8899            # to reset sequence indices
    89100            for sequence_info in sequences:
     101                schema_name = sequence_info['schema']
    90102                table_name = sequence_info['table']
    91103                column_name = sequence_info['column']
    92104                if column_name and len(column_name) > 0:
    93105                    sequence_name = '%s_%s_seq' % (table_name, column_name)
    94106                else:
    95107                    sequence_name = '%s_id_seq' % table_name
     108                sequence_name = self.prep_db_table(schema_name, sequence_name)
    96109                sql.append("%s setval('%s', 1, false);" % \
    97110                    (style.SQL_KEYWORD('SELECT'),
    98                     style.SQL_FIELD(self.quote_name(sequence_name)))
     111                    style.SQL_FIELD(sequence_name))
    99112                )
    100113            return sql
    101114        else:
     
    111124            # if there are records (as the max pk value is already in use), otherwise set it to false.
    112125            for f in model._meta.local_fields:
    113126                if isinstance(f, models.AutoField):
     127                    sequence_name = qn('%s_%s_seq' % (model._meta.db_table,
     128                                                      f.column))
     129                    sequence_name = self.prep_db_table(model._meta.db_schema,
     130                                                       sequence_name)
    114131                    output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    115132                        (style.SQL_KEYWORD('SELECT'),
    116                         style.SQL_FIELD(qn('%s_%s_seq' % (model._meta.db_table, f.column))),
     133                        style.SQL_FIELD(sequence_name),
    117134                        style.SQL_FIELD(qn(f.column)),
    118135                        style.SQL_FIELD(qn(f.column)),
    119136                        style.SQL_KEYWORD('IS NOT'),
    120137                        style.SQL_KEYWORD('FROM'),
    121                         style.SQL_TABLE(qn(model._meta.db_table))))
     138                        style.SQL_TABLE(model._meta.qualified_name)))
    122139                    break # Only one AutoField is allowed per model, so don't bother continuing.
    123140            for f in model._meta.many_to_many:
    124141                if not f.rel.through:
     142                    sequence_name = qn('%s_id_seq' % f.m2m_db_table())
     143                    sequence_name = self.prep_db_table(f.m2m_db_schema(),
     144                                                       sequence_name)
    125145                    output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
    126146                        (style.SQL_KEYWORD('SELECT'),
    127                         style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
     147                        style.SQL_FIELD(sequence_name),
    128148                        style.SQL_FIELD(qn('id')),
    129149                        style.SQL_FIELD(qn('id')),
    130150                        style.SQL_KEYWORD('IS NOT'),
    131151                        style.SQL_KEYWORD('FROM'),
    132                         style.SQL_TABLE(qn(f.m2m_db_table()))))
     152                        style.SQL_TABLE(qn(f.m2m_qualified_name()))))
    133153        return output
    134154
    135155    def savepoint_create_sql(self, sid):
  • django/db/backends/sqlite3/base.py

     
    9494                (style.SQL_KEYWORD('DELETE'),
    9595                 style.SQL_KEYWORD('FROM'),
    9696                 style.SQL_FIELD(self.quote_name(table))
    97                  ) for table in tables]
     97                 ) for (_, table) in tables]
    9898        # Note: No requirement for reset of auto-incremented indices (cf. other
    9999        # sql_flush() implementations). Just return SQL at this point
    100100        return sql
  • django/db/backends/mysql/base.py

     
    160160            return name # Quoting once is enough.
    161161        return "`%s`" % name
    162162
     163    def prep_db_table(self, db_schema, db_table):
     164        qn = self.quote_name
     165        if db_schema:
     166            return "%s.%s" % (qn(db_schema), qn(db_table))
     167        else:
     168            return qn(db_table)
     169
    163170    def random_function_sql(self):
    164171        return 'RAND()'
    165172
     
    169176        # to clear all tables of all data
    170177        if tables:
    171178            sql = ['SET FOREIGN_KEY_CHECKS = 0;']
    172             for table in tables:
    173                 sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
     179            sql.extend(['%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.prep_db_table(schema, table))) for (schema, table) in tables])
    174180            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
    175181
    176182            # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
    177183            # to reset sequence indices
    178             sql.extend(["%s %s %s %s %s;" % \
    179                 (style.SQL_KEYWORD('ALTER'),
    180                  style.SQL_KEYWORD('TABLE'),
    181                  style.SQL_TABLE(self.quote_name(sequence['table'])),
    182                  style.SQL_KEYWORD('AUTO_INCREMENT'),
    183                  style.SQL_FIELD('= 1'),
    184                 ) for sequence in sequences])
     184            for sequence_info in sequences:
     185                schema_name = sequence_info['schema']
     186                table_name = self.prep_db_table(schema_name, sequence_info['table'])
     187                sql.append("%s %s %s %s %s;" % \
     188                           (style.SQL_KEYWORD('ALTER'),
     189                            style.SQL_KEYWORD('TABLE'),
     190                            style.SQL_TABLE(table_name),
     191                            style.SQL_KEYWORD('AUTO_INCREMENT'),
     192                            style.SQL_FIELD('= 1'),
     193                            ))
    185194            return sql
    186195        else:
    187196            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

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

     
    4747
    4848class DatabaseOperations(BaseDatabaseOperations):
    4949
    50     def autoinc_sql(self, table, column):
     50    def autoinc_sql(self, schema, table, column):
    5151        # To simulate auto-incrementing primary keys in Oracle, we have to
    5252        # create a sequence and a trigger.
    5353        sq_name = get_sequence_name(table)
    5454        tr_name = get_trigger_name(table)
    55         tbl_name = self.quote_name(table)
     55        tbl_name = self.prep_db_table(schema, table)
     56        sq_qname = self.prep_db_table(schema, sq_name)
     57        tr_qname = self.prep_db_table(schema, tr_name)
    5658        col_name = self.quote_name(column)
    5759        sequence_sql = """
    5860DECLARE
     
    6163    SELECT COUNT(*) INTO i FROM USER_CATALOG
    6264        WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
    6365    IF i = 0 THEN
    64         EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
     66        EXECUTE IMMEDIATE 'CREATE SEQUENCE %(sq_qname)s';
    6567    END IF;
    6668END;
    6769/""" % locals()
    6870        trigger_sql = """
    69 CREATE OR REPLACE TRIGGER "%(tr_name)s"
     71CREATE OR REPLACE TRIGGER %(tr_qname)s
    7072BEFORE INSERT ON %(tbl_name)s
    7173FOR EACH ROW
    7274WHEN (new.%(col_name)s IS NULL)
    7375    BEGIN
    74         SELECT "%(sq_name)s".nextval
     76        SELECT %(sq_qname)s.nextval
    7577        INTO :new.%(col_name)s FROM dual;
    7678    END;
    7779/""" % locals()
     
    100102    def deferrable_sql(self):
    101103        return " DEFERRABLE INITIALLY DEFERRED"
    102104
    103     def drop_sequence_sql(self, table):
    104         return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
     105    def drop_sequence_sql(self, schema, table):
     106        sequence_name = self.prep_db_table(schema, get_sequence_name(table))
     107        return "DROP SEQUENCE %s;" % sequence_name
    105108
    106109    def fetch_returned_insert_id(self, cursor):
    107110        return long(cursor._insert_id_var.getvalue())
     
    112115        else:
    113116            return "%s"
    114117
    115     def last_insert_id(self, cursor, table_name, pk_name):
    116         sq_name = get_sequence_name(table_name)
    117         cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
     118    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
     119        sq_name = self.prep_db_table(schema_name, get_sequence_name(table_name))
     120        cursor.execute('SELECT %s.currval FROM dual' % sq_name)
    118121        return cursor.fetchone()[0]
    119122
    120123    def lookup_cast(self, lookup_type):
     
    125128    def max_name_length(self):
    126129        return 30
    127130
     131    def prep_db_table(self, db_schema, db_table):
     132        qn = self.quote_name
     133        if db_schema:
     134            return "%s.%s" % (qn(db_schema), qn(db_table))
     135        else:
     136            return qn(db_table)
     137
    128138    def prep_for_iexact_query(self, x):
    129139        return x
    130140
     
    178188    def sql_flush(self, style, tables, sequences):
    179189        # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
    180190        # 'TRUNCATE z;'... style SQL statements
     191        sql = []
    181192        if tables:
    182193            # Oracle does support TRUNCATE, but it seems to get us into
    183194            # FK referential trouble, whereas DELETE FROM table works.
    184             sql = ['%s %s %s;' % \
    185                     (style.SQL_KEYWORD('DELETE'),
    186                      style.SQL_KEYWORD('FROM'),
    187                      style.SQL_FIELD(self.quote_name(table)))
    188                     for table in tables]
     195            for schema, table in tables:
     196                table = self.prep_db_table(schema, table)
     197                sql.append('%s %s %s;' % \
     198                           (style.SQL_KEYWORD('DELETE'),
     199                            style.SQL_KEYWORD('FROM'),
     200                            style.SQL_FIELD(table)))
    189201            # Since we've just deleted all the rows, running our sequence
    190202            # ALTER code will reset the sequence to 0.
    191203            for sequence_info in sequences:
    192                 sequence_name = get_sequence_name(sequence_info['table'])
    193                 table_name = self.quote_name(sequence_info['table'])
     204                schema_name = sequence_info['schema']
     205                sequence_name = self.prep_db_table(schema_name,
     206                                    get_sequence_name(sequence_info['table']))
     207                table_name = self.prep_db_table(schema_name,
     208                                                sequence_info['table'])
    194209                column_name = self.quote_name(sequence_info['column'] or 'id')
    195210                query = _get_sequence_reset_sql() % {'sequence': sequence_name,
    196211                                                     'table': table_name,
    197212                                                     'column': column_name}
    198213                sql.append(query)
    199             return sql
    200         else:
    201             return []
     214        return sql
    202215
    203216    def sequence_reset_sql(self, style, model_list):
    204217        from django.db import models
     
    207220        for model in model_list:
    208221            for f in model._meta.local_fields:
    209222                if isinstance(f, models.AutoField):
    210                     table_name = self.quote_name(model._meta.db_table)
    211                     sequence_name = get_sequence_name(model._meta.db_table)
     223                    table_name = model._meta.qualified_name
     224                    sequence_name = self.prep_db_table(model._meta.db_schema,
     225                                       get_sequence_name(model._meta.db_table))
    212226                    column_name = self.quote_name(f.column)
    213227                    output.append(query % {'sequence': sequence_name,
    214228                                           'table': table_name,
     
    218232                    break
    219233            for f in model._meta.many_to_many:
    220234                if not f.rel.through:
    221                     table_name = self.quote_name(f.m2m_db_table())
    222                     sequence_name = get_sequence_name(f.m2m_db_table())
     235                    table_name = self.quote_name(f.m2m_qualified_name())
     236                    sequence_name = self.prep_db_table(f.m2m_db_schema(),
     237                                           get_sequence_name(f.m2m_db_table()))
    223238                    column_name = self.quote_name('id')
    224239                    output.append(query % {'sequence': sequence_name,
    225240                                           'table': table_name,
     
    425440            query = query[:-1]
    426441        query = smart_str(query, self.charset) % tuple(args)
    427442        self._guess_input_sizes([params])
     443        print >>file('/home/ikelly/django-log.sql', 'w'), query, self._param_generator(params)
    428444        try:
    429445            return self.cursor.execute(query, self._param_generator(params))
    430446        except DatabaseError, e:
     
    542558BEGIN
    543559    LOCK TABLE %(table)s IN SHARE MODE;
    544560    SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
    545     SELECT "%(sequence)s".nextval INTO cval FROM dual;
     561    SELECT %(sequence)s.nextval INTO cval FROM dual;
    546562    cval := startvalue - cval;
    547563    IF cval != 0 THEN
    548         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" MINVALUE 0 INCREMENT BY '||cval;
    549         SELECT "%(sequence)s".nextval INTO cval FROM dual;
    550         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" INCREMENT BY 1';
     564        EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval;
     565        SELECT %(sequence)s.nextval INTO cval FROM dual;
     566        EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1';
    551567    END IF;
    552568    COMMIT;
    553569END;
  • django/db/backends/oracle/introspection.py

     
    108108        for row in cursor.fetchall():
    109109            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
    110110        return indexes
     111
     112    def schema_name_converter(self, name):
     113        """Convert to lowercase for case-sensitive schema name comparison."""
     114        return name.lower()
     115
     116    def get_schema_list(self, cursor):
     117        "Returns a list of schemas that exist in the database."
     118        sql = """
     119        select distinct username
     120        from all_users, all_objects
     121        where username = owner
     122        """
     123        cursor.execute(sql)
     124        return [schema.lower() for (schema,) in cursor]
     125
     126    def get_schema_table_list(self, cursor, schema):
     127        "Returns a list of tables in a specific schema."
     128        sql = """
     129        select table_name from all_tables
     130        where owner = upper(%s)
     131        """
     132        cursor.execute(sql, [schema])
     133        return [table.lower() for (table,) in cursor]
  • django/db/backends/oracle/creation.py

     
    3939        'URLField':                     'VARCHAR2(%(max_length)s)',
    4040    }
    4141
     42    def sql_create_schema(self, schema, style, password=None,
     43                          tablespace=None, temp_tablespace=None):
     44        qn = self.connection.ops.quote_name
     45        lock_account = (password is None)
     46        if lock_account:
     47            password = schema
     48        output = []
     49        output.append("%s %s %s %s" % (style.SQL_KEYWORD('CREATE USER'),
     50                                       qn(schema),
     51                                       style.SQL_KEYWORD('IDENTIFIED BY'),
     52                                       qn(password)))
     53        if tablespace:
     54            output.append("%s %s" % (style.SQL_KEYWORD('DEFAULT TABLESPACE'),
     55                                     qn(tablespace)))
     56        if temp_tablespace:
     57            output.append("%s %s" % (style.SQL_KEYWORD('TEMPORARY TABLESPACE'),
     58                                     qn(temp_tablespace)))
     59        if lock_account:
     60            output.append(style.SQL_KEYWORD('ACCOUNT LOCK'))
     61        return '\n'.join(output)
     62
     63    def sql_destroy_schema(self, schema, style):
     64        qn = self.connection.ops.quote_name
     65        return "%s %s %s" % (style.SQL_KEYWORD('DROP USER'), qn(schema),
     66                             style.SQL_KEYWORD('CASCADE'))
     67
    4268    remember = {}
    4369
    4470    def _create_test_db(self, verbosity=1, autoclobber=False):
     
    174200               DEFAULT TABLESPACE %(tblspace)s
    175201               TEMPORARY TABLESPACE %(tblspace_temp)s
    176202            """,
    177             """GRANT CONNECT, RESOURCE TO %(user)s""",
     203            """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""",
    178204        ]
    179205        self._execute_statements(cursor, statements, parameters, verbosity)
    180206
  • django/db/backends/__init__.py

     
    109109    a backend performs ordering or calculates the ID of a recently-inserted
    110110    row.
    111111    """
    112     def autoinc_sql(self, table, column):
     112    def autoinc_sql(self, schema, table, column):
    113113        """
    114114        Returns any SQL needed to support auto-incrementing primary keys, or
    115115        None if no SQL is necessary.
     
    155155        """
    156156        return "DROP CONSTRAINT"
    157157
    158     def drop_sequence_sql(self, table):
     158    def drop_sequence_sql(self, schema, table):
    159159        """
    160160        Returns any SQL necessary to drop the sequence for the given table.
    161161        Returns None if no SQL is necessary.
     
    216216
    217217        return smart_unicode(sql) % u_params
    218218
    219     def last_insert_id(self, cursor, table_name, pk_name):
     219    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
    220220        """
    221221        Given a cursor object that has just performed an INSERT statement into
    222222        a table that has an auto-incrementing ID, returns the newly created ID.
     
    287287        """
    288288        raise NotImplementedError()
    289289
     290    def prep_db_table(self, db_schema, db_table):
     291        """
     292        Prepares and formats the table name if necessary.
     293        Just returns the db_table if not supported.
     294        """
     295        return db_table
     296
    290297    def random_function_sql(self):
    291298        """
    292299        Returns a SQL expression that returns a random value.
     
    478485        return name
    479486
    480487    def table_names(self):
    481         "Returns a list of names of all tables that exist in the database."
     488        "Returns a list of names of all tables that exist in the default schema."
    482489        cursor = self.connection.cursor()
    483490        return self.get_table_list(cursor)
    484491
     492    def schema_name_converter(self, name):
     493        """Apply a conversion to the name for the purposes of comparison.
     494
     495        The default schema name converter is for case sensitive comparison.
     496        """
     497        return name
     498
     499    def get_schema_list(self, cursor):
     500        "Returns a list of schemas that exist in the database"
     501        return []
     502   
     503    def get_schema_table_list(self, cursor, schema):
     504        "Returns a list of tables in a specific schema"
     505        return []
     506       
     507    def schema_names(self):
     508        cursor = self.connection.cursor()
     509        return self.get_schema_list(cursor)
     510   
     511    def schema_table_names(self, schema):
     512        "Returns a list of names of all tables that exist in the database schema."
     513        cursor = self.connection.cursor()
     514        return self.get_schema_table_list(cursor, schema)
     515
    485516    def django_table_names(self, only_existing=False):
    486517        """
    487         Returns a list of all table names that have associated Django models and
    488         are in INSTALLED_APPS.
     518        Returns a list of tuples containing all schema and table names that
     519        have associated Django models and are in INSTALLED_APPS.
    489520
    490         If only_existing is True, the resulting list will only include the tables
    491         that actually exist in the database.
     521        If only_existing is True, the resulting list will only include the
     522        tables that actually exist in the database.
    492523        """
    493524        from django.db import models
    494525        tables = set()
     526        if only_existing:
     527            existing_tables = set([('', tn) for tn in self.table_names()])
     528            seen_schemas = set()
    495529        for app in models.get_apps():
    496530            for model in models.get_models(app):
    497531                if not model._meta.managed:
    498532                    continue
    499                 tables.add(model._meta.db_table)
    500                 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
     533                db_schema = model._meta.db_schema
     534                db_table = model._meta.db_table
     535                if only_existing and db_schema and db_schema not in seen_schemas:
     536                    existing_tables.update([(db_schema, tn) for tn in
     537                                            self.schema_table_names(db_schema)])
     538                    seen_schemas.add(db_schema)
     539                tables.add((model._meta.db_schema, model._meta.db_table))
     540                for f in model._meta.local_many_to_many:
     541                    m2m_schema = f.m2m_db_schema()
     542                    m2m_table = f.m2m_db_table()
     543                    if only_existing and m2m_schema and m2m_schema not in seen_schemas:
     544                        existing_tables.update([(m2m_schema, tn) for tn in
     545                                            self.schema_table_names(m2m_schema)])
     546                        seen_schemas.add(m2m_schema)
     547                    tables.add((m2m_schema, m2m_table))
    501548        if only_existing:
    502             tables = [t for t in tables if self.table_name_converter(t) in self.table_names()]
     549            tables = [(s, t) for (s, t) in tables
     550                      if (self.schema_name_converter(s),
     551                          self.table_name_converter(t)) in existing_tables]
    503552        return tables
    504553
    505554    def installed_models(self, tables):
     
    526575                    continue
    527576                for f in model._meta.local_fields:
    528577                    if isinstance(f, models.AutoField):
    529                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
     578                        sequence_list.append({'table': model._meta.db_table,
     579                                              'column': f.column,
     580                                              'schema': model._meta.db_schema})
    530581                        break # Only one AutoField is allowed per model, so don't bother continuing.
    531582
    532583                for f in model._meta.local_many_to_many:
    533584                    # If this is an m2m using an intermediate table,
    534585                    # we don't need to reset the sequence.
    535586                    if f.rel.through is None:
    536                         sequence_list.append({'table': f.m2m_db_table(), 'column': None})
     587                        sequence_list.append({'table': f.m2m_db_table(),
     588                                              'column': None,
     589                                              'schema': f.m2m_db_schema()})
    537590
    538591        return sequence_list
    539592
  • django/db/backends/creation.py

     
    3232        """
    3333        return '%x' % (abs(hash(args)) % 4294967296L)  # 2**32
    3434
     35    def default_schema(self):
     36        return ""
     37
     38    def sql_create_schema(self, schema, style):
     39        """"
     40        Returns the SQL required to create a single schema
     41        """
     42        qn = self.connection.ops.quote_name
     43        output = "%s %s;" % (style.SQL_KEYWORD('CREATE SCHEMA'), qn(schema))
     44        return output
     45
    3546    def sql_create_model(self, model, style, known_models=set()):
    3647        """
    3748        Returns the SQL required to create a single model, as a tuple of:
     
    8091            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    8192                ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
    8293
    83         full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     94        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(opts.qualified_name) + ' (']
    8495        for i, line in enumerate(table_output): # Combine and add commas.
    8596            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    8697        full_statement.append(')')
     
    92103        if opts.has_auto_field:
    93104            # Add any extra SQL needed to support auto-incrementing primary keys.
    94105            auto_column = opts.auto_field.db_column or opts.auto_field.name
    95             autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column)
     106            autoinc_sql = self.connection.ops.autoinc_sql(opts.db_schema,
     107                                                          opts.db_table,
     108                                                          auto_column)
    96109            if autoinc_sql:
    97110                for stmt in autoinc_sql:
    98111                    final_output.append(stmt)
     
    104117        qn = self.connection.ops.quote_name
    105118        if field.rel.to in known_models:
    106119            output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \
    107                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \
     120                style.SQL_TABLE(field.rel.to._meta.qualified_name) + ' (' + \
    108121                style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' +
    109122                self.connection.ops.deferrable_sql()
    110123            ]
     
    130143            for rel_class, f in pending_references[model]:
    131144                rel_opts = rel_class._meta
    132145                r_table = rel_opts.db_table
     146                r_qname = rel_opts.qualified_name
    133147                r_col = f.column
    134148                table = opts.db_table
     149                qname = opts.qualified_name
    135150                col = opts.get_field(f.rel.field_name).column
    136151                # For MySQL, r_name must be unique in the first 64 characters.
    137152                # So we are careful with character usage here.
    138153                r_name = '%s_refs_%s_%s' % (r_col, col, self._digest(r_table, table))
    139154                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    140                     (qn(r_table), qn(truncate_name(r_name, self.connection.ops.max_name_length())),
    141                     qn(r_col), qn(table), qn(col),
     155                    (r_qname, qn(truncate_name(r_name, self.connection.ops.max_name_length())),
     156                    qn(r_col), qname, qn(col),
    142157                    self.connection.ops.deferrable_sql()))
    143158            del pending_references[model]
    144159        return final_output
     
    170185            else:
    171186                tablespace_sql = ''
    172187            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
    173                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
     188                style.SQL_TABLE(qn(f.m2m_qualified_name())) + ' (']
    174189            table_output.append('    %s %s %s%s,' %
    175190                (style.SQL_FIELD(qn('id')),
    176191                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
     
    202217                self.connection.ops.deferrable_sql()))
    203218
    204219            # Add any extra SQL needed to support auto-incrementing PKs
    205             autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_table(), 'id')
     220            autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_schema(),
     221                                                          f.m2m_db_table(),
     222                                                          'id')
    206223            if autoinc_sql:
    207224                for stmt in autoinc_sql:
    208225                    output.append(stmt)
     
    219236                (style.SQL_FIELD(qn(field.m2m_column_name())),
    220237                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
    221238                style.SQL_KEYWORD('NOT NULL REFERENCES'),
    222                 style.SQL_TABLE(qn(opts.db_table)),
     239                style.SQL_TABLE(opts.qualified_name),
    223240                style.SQL_FIELD(qn(opts.pk.column)),
    224241                self.connection.ops.deferrable_sql()),
    225242            '    %s %s %s %s (%s)%s,' %
    226243                (style.SQL_FIELD(qn(field.m2m_reverse_name())),
    227244                style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()),
    228245                style.SQL_KEYWORD('NOT NULL REFERENCES'),
    229                 style.SQL_TABLE(qn(field.rel.to._meta.db_table)),
     246                style.SQL_TABLE(opts.qualified_name),
    230247                style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
    231248                self.connection.ops.deferrable_sql())
    232249        ]
     
    256273                    tablespace_sql = ''
    257274            else:
    258275                tablespace_sql = ''
     276            index_name = '%s_%s' % (model._meta.db_table, f.column)
     277            index_name = self.connection.ops.prep_db_table(model._meta.db_schema, index_name)
    259278            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
    260                 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' +
     279                style.SQL_TABLE(index_name) + ' ' +
    261280                style.SQL_KEYWORD('ON') + ' ' +
    262                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
     281                style.SQL_TABLE(model._meta.qualified_name) + ' ' +
    263282                "(%s)" % style.SQL_FIELD(qn(f.column)) +
    264283                "%s;" % tablespace_sql]
    265284        else:
    266285            output = []
    267286        return output
    268287
     288    def sql_destroy_schema(self, schema, style):
     289        """"
     290        Returns the SQL required to create a single schema
     291        """
     292        qn = self.connection.ops.quote_name
     293        output = "%s %s CASCADE;" % (style.SQL_KEYWORD('DROP SCHEMA IF EXISTS'), qn(schema))
     294        return output
     295
    269296    def sql_destroy_model(self, model, references_to_delete, style):
    270297        "Return the DROP TABLE and restraint dropping statements for a single model"
    271298        if not model._meta.managed:
     
    273300        # Drop the table now
    274301        qn = self.connection.ops.quote_name
    275302        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
    276                               style.SQL_TABLE(qn(model._meta.db_table)))]
     303                              style.SQL_TABLE(model._meta.qualified_name))]
    277304        if model in references_to_delete:
    278305            output.extend(self.sql_remove_table_constraints(model, references_to_delete, style))
    279306
    280307        if model._meta.has_auto_field:
    281             ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
     308            ds = self.connection.ops.drop_sequence_sql(model._meta.db_schema,
     309                                                       model._meta.db_table)
    282310            if ds:
    283311                output.append(ds)
    284312        return output
     
    292320        qn = self.connection.ops.quote_name
    293321        for rel_class, f in references_to_delete[model]:
    294322            table = rel_class._meta.db_table
     323            qname = rel_class._meta.qualified_name
    295324            col = f.column
    296325            r_table = model._meta.db_table
    297326            r_col = model._meta.get_field(f.rel.field_name).column
    298327            r_name = '%s_refs_%s_%s' % (col, r_col, self._digest(table, r_table))
    299328            output.append('%s %s %s %s;' % \
    300329                (style.SQL_KEYWORD('ALTER TABLE'),
    301                 style.SQL_TABLE(qn(table)),
     330                style.SQL_TABLE(qname),
    302331                style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
    303332                style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length()))))
    304333        del references_to_delete[model]
     
    310339        output = []
    311340        if f.creates_table:
    312341            output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
    313                 style.SQL_TABLE(qn(f.m2m_db_table()))))
    314             ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
     342                style.SQL_TABLE(f.m2m_qualified_name())))
     343            ds = self.connection.ops.drop_sequence_sql(model._meta.db_schema,
     344                               "%s_%s" % (model._meta.db_table, f.column))
    315345            if ds:
    316346                output.append(ds)
    317347        return output
     
    333363        settings.DATABASE_SUPPORTS_TRANSACTIONS = can_rollback
    334364        self.connection.settings_dict["DATABASE_SUPPORTS_TRANSACTIONS"] = can_rollback
    335365
     366        # Create the test schemas.
     367        schema_apps = self._get_app_with_schemas()
     368        schemas = self._get_schemas(schema_apps)
     369        cursor = self.connection.cursor()
     370        self._create_test_schemas(verbosity, schemas, cursor)
     371
    336372        call_command('syncdb', verbosity=verbosity, interactive=False)
    337373
    338374        if settings.CACHE_BACKEND.startswith('db://'):
     
    346382
    347383        return test_database_name
    348384
     385    def _create_test_schemas(self, verbosity, schemas, cursor):
     386        from django.core.management.color import no_style
     387        style = no_style()
     388        for schema in schemas:
     389            if verbosity >= 1:
     390                print "Creating schema %s" % schema
     391            cursor.execute(self.sql_create_schema(schema, style))
     392
     393    def _destroy_test_schemas(self, verbosity, schemas, cursor):
     394        from django.core.management.color import no_style
     395        style = no_style()
     396        for schema in schemas:
     397            if verbosity >= 1:
     398                print "Destroying schema %s" % schema
     399                cursor.execute(self.sql_destroy_schema(schema, style))
     400            if verbosity >= 1:
     401                print "Schema %s destroyed" % schema
     402
     403    def _get_schemas(self, apps):
     404        from django.db import models
     405        schemas = set()
     406        for app in apps:
     407            app_models = models.get_models(app)
     408            for model in app_models:
     409                schema = model._meta.db_schema
     410                if not schema or schema in schemas:
     411                    continue
     412                schemas.add(schema)
     413        return schemas
     414
     415    def _get_app_with_schemas(self):
     416        from django.db import models
     417        apps = models.get_apps()
     418        schema_apps = set()
     419        for app in apps:
     420            app_models = models.get_models(app)
     421            for model in app_models:
     422                schema = model._meta.db_schema
     423                if not schema or app in schema_apps:
     424                    continue
     425                schema_apps.add(app)
     426                continue
     427               
     428        return schema_apps
     429
    349430    def _create_test_db(self, verbosity, autoclobber):
    350431        "Internal implementation - creates the test db tables."
    351432        suffix = self.sql_table_creation_suffix()
     
    372453                try:
    373454                    if verbosity >= 1:
    374455                        print "Destroying old test database..."
     456                    self._destroy_test_schemas(verbosity, schemas, cursor)
    375457                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
    376458                    if verbosity >= 1:
    377459                        print "Creating test database..."
  • django/core/management/commands/syncdb.py

     
    4949        cursor = connection.cursor()
    5050
    5151        # Get a list of already installed *models* so that references work right.
    52         tables = connection.introspection.table_names()
     52        tables = [('', tn) for tn in connection.introspection.table_names()]
    5353        seen_models = connection.introspection.installed_models(tables)
     54        seen_schemas = set()
    5455        created_models = set()
    5556        pending_references = {}
    5657
     
    5960            app_name = app.__name__.split('.')[-2]
    6061            model_list = models.get_models(app)
    6162            for model in model_list:
    62                 # Create the model's database table, if it doesn't already exist.
     63                # Add model-defined schema tables if any.
     64                db_schema = model._meta.db_schema
     65                if db_schema:
     66                    db_schema = connection.introspection.schema_name_converter(db_schema)
     67                    if db_schema not in seen_schemas:
     68                        tables += [(db_schema, tn) for tn in
     69                                   connection.introspection.schema_table_names(db_schema)]
     70                        seen_schemas.add(db_schema)
     71                # Create the model's database table,
     72                # if it doesn't already exist.
    6373                if verbosity >= 2:
    6474                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
    65                 if connection.introspection.table_name_converter(model._meta.db_table) in tables:
     75                schema_table = (db_schema,
     76                                connection.introspection.table_name_converter(model._meta.db_table))
     77                if schema_table in tables:
    6678                    continue
    6779                sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
    6880                seen_models.add(model)
     
    7688                    print "Creating table %s" % model._meta.db_table
    7789                for statement in sql:
    7890                    cursor.execute(statement)
    79                 tables.append(connection.introspection.table_name_converter(model._meta.db_table))
     91                tables.append(schema_table)
    8092
    8193        # Create the m2m tables. This must be done after all tables have been created
    8294        # to ensure that all referred tables will exist.
  • django/core/management/sql.py

     
    7272
    7373    # Figure out which tables already exist
    7474    if cursor:
    75         table_names = connection.introspection.get_table_list(cursor)
     75        table_names = [('', tn) for tn in
     76                       connection.introspection.get_table_list(cursor)]
    7677    else:
    7778        table_names = []
    7879
     
    8384
    8485    references_to_delete = {}
    8586    app_models = models.get_models(app)
     87    seen_schemas = set()
    8688    for model in app_models:
    87         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     89        db_schema = model._meta.db_schema
     90        # Find additional tables in model-defined schemas.
     91        if db_schema:
     92            db_schema = connection.introspection.schema_name_converter(db_schema)
     93            if db_schema not in seen_schemas:
     94                table_names += connection.introspection.get_schema_table_list(cursor, db_schema)
     95                seen_schemas.add(db_schema)
     96        schema_table = (db_schema,
     97                        connection.introspection.table_name_converter(model._meta.db_table))
     98        if cursor and schema_table in table_names:
    8899            # The table exists, so it needs to be dropped
    89100            opts = model._meta
    90101            for f in opts.local_fields:
     
    94105            to_delete.add(model)
    95106
    96107    for model in app_models:
    97         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     108        db_schema = model._meta.db_schema
     109        if db_schema:
     110            db_schema = connection.introspection.schema_name_converter(db_schema)
     111        schema_table = (db_schema,
     112                        connection.introspection.table_name_converter(model._meta.db_table))
     113        if schema_table in table_names:
    98114            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    99115
    100116    # Output DROP TABLE statements for many-to-many tables.
    101117    for model in app_models:
    102118        opts = model._meta
     119        db_schema = opts.db_schema
     120        if db_schema:
     121            db_schema = connection.introspection.schema_name_converter(db_schema)
    103122        for f in opts.local_many_to_many:
    104             if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names:
     123            schema_table = (db_schema,
     124                            connection.introspection.table_name_converter(f.m2m_db_table()))
     125            if cursor and schema_table in table_names:
    105126                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
    106127
    107128    # Close database connection explicitly, in case this output is being piped
     
    127148    if only_django:
    128149        tables = connection.introspection.django_table_names(only_existing=True)
    129150    else:
    130         tables = connection.introspection.table_names()
     151        tables = [('', tn) for tn in connection.introspection.table_names()]
    131152    statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
    132153    return statements
    133154
  • tests/modeltests/schemas/__init__.py

     
     1#
  • tests/modeltests/schemas/models.py

     
     1# coding: utf-8
     2
     3from django.db import models
     4
     5
     6class Blog(models.Model):
     7    "Model in default schema"
     8    name = models.CharField(max_length=50)
     9
     10   
     11class Entry(models.Model):
     12    "Model in custom schema that references the default"
     13    blog = models.ForeignKey(Blog)   
     14    title = models.CharField(max_length=50)
     15   
     16    class Meta:
     17        "using custom db_table as well"
     18        db_table='schema_blog_entries'
     19        db_schema = 'test_schema'
     20       
     21
     22class Comment(models.Model):
     23    "Model in the custom schema that references Entry in the same schema"
     24    entry = models.ForeignKey(Entry)
     25    text = models.CharField(max_length=50)
     26   
     27    class Meta:
     28        db_schema = 'test_schema'
     29
     30__test__ = {'API_TESTS': """
     31
     32#Test with actual data
     33# Nothing in there yet
     34>>> Blog.objects.all()
     35[]
     36
     37# Create a blog
     38>>> b = Blog(name='Test')
     39>>> b.save()
     40
     41# Verify that we got an ID
     42>>> b.id
     431
     44
     45# Create entry
     46>>> e = Entry(blog=b, title='Test entry')
     47>>> e.save()
     48>>> e.id
     491
     50
     51# Create Comments
     52>>> c1 = Comment(entry=e, text='nice entry')
     53>>> c1.save()
     54>>> c2 = Comment(entry=e, text='really like it')
     55>>> c2.save()
     56
     57#Retrieve the stuff again.
     58>>> b2 = Blog.objects.get(id=b.id)
     59>>> b==b2
     60True
     61
     62>>> b2.entry_set.all()
     63[<Entry: Entry object>]
     64
     65>>> from django.conf import settings
     66>>> from django.db import connection, models
     67
     68# Test if we support schemas and can find the table if so
     69>>> if e._meta.db_schema:
     70...     tables = connection.introspection.schema_table_names(e._meta.db_schema)
     71... else:
     72...     tables = connection.introspection.table_names()
     73>>> if connection.introspection.table_name_converter(e._meta.db_table) in tables:
     74...     print "ok"
     75... else:
     76...     print "schema=" + e._meta.db_schema
     77...     print tables
     78ok
     79
     80# Test that all but sqlite3 backend supports schema.
     81>>> if settings.DATABASE_ENGINE != 'sqlite3':
     82...     e._meta.db_schema
     83'test_schema'
     84
     85"""
     86}
  • docs/topics/db/models.txt

     
    629629            verbose_name_plural = "oxen"
    630630
    631631Model metadata is "anything that's not a field", such as ordering options
    632 (:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or
     632(:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`),
     633(:attr:`~Options.db_schema`) custom schema for the tables, or
    633634human-readable singular and plural names (:attr:`~Options.verbose_name` and
    634635:attr:`~Options.verbose_name_plural`). None are required, and adding ``class
    635636Meta`` to a model is completely optional.
  • docs/ref/models/options.txt

     
    5050aren't allowed in Python variable names -- notably, the hyphen -- that's OK.
    5151Django quotes column and table names behind the scenes.
    5252
     53``db_schema``
     54-----------------
     55
     56**New in Django development version**
     57
     58The name of the database schema to use for the model. If the backend
     59doesn't support multiple schemas, this options is ignored.
     60
     61If this is used Django will prefix any table names with the schema name.
     62For example MySQL Django would use ``db_schema + '.' + db_table``.
     63Be aware that postgres supports different schemas within the database.
     64MySQL solves the same thing by treating it as just another database.
     65
     66
     67
    5368``db_tablespace``
    5469-----------------
    5570
Back to Top