Ticket #6148: generic-db_schema-r11827.diff

File generic-db_schema-r11827.diff, 69.0 KB (added by kmpm, 14 years ago)

Patch for trunk revision 11827

  • django/conf/project_template/settings.py

     
    1515DATABASE_PASSWORD = ''         # Not used with sqlite3.
    1616DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
    1717DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
     18DATABASE_SCHEMA = ''           # Set to empty string for default.
    1819
    1920# Local time zone for this installation. Choices can be found here:
    2021# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
  • django/conf/global_settings.py

     
    130130DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3.
    131131DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3.
    132132DATABASE_OPTIONS = {}          # Set to empty dictionary for default.
     133DATABASE_SCHEMA = ''           # Set to empty string for default.
    133134
    134135# The email backend to use. For possible shortcuts see django.core.mail.
    135136# The default is to use the SMTP backend.
  • django/db/models/sql/query.py

     
    585585        might not have all the pieces in place at that time.
    586586        """
    587587        if not self.tables:
    588             self.join((None, self.model._meta.db_table, None, None))
     588            self.join((None, self.model._meta.qualified_name, None, None))
    589589        if (not self.select and self.default_cols and not
    590590                self.included_inherited_models):
    591591            self.setup_inherited_models()
     
    685685        Callback used by deferred_to_columns(). The "target" parameter should
    686686        be a set instance.
    687687        """
    688         table = model._meta.db_table
     688        table = model._meta.qualified_name
    689689        if table not in target:
    690690            target[table] = set()
    691691        for field in fields:
     
    802802                        alias = start_alias
    803803                    else:
    804804                        link_field = opts.get_ancestor_link(model)
    805                         alias = self.join((start_alias, model._meta.db_table,
     805                        alias = self.join((start_alias,
     806                                model._meta.qualified_name,
    806807                                link_field.column, model._meta.pk.column))
    807808                    seen[model] = alias
    808809            else:
     
    12101211            alias = self.tables[0]
    12111212            self.ref_alias(alias)
    12121213        else:
    1213             alias = self.join((None, self.model._meta.db_table, None, None))
     1214            alias = self.join((None, self.model._meta.qualified_name,
     1215                               None, None))
    12141216        return alias
    12151217
    12161218    def count_active_tables(self):
     
    13231325                    seen[model] = root_alias
    13241326                else:
    13251327                    link_field = opts.get_ancestor_link(model)
    1326                     seen[model] = self.join((root_alias, model._meta.db_table,
     1328                    seen[model] = self.join((root_alias,
     1329                            model._meta.qualified_name,
    13271330                            link_field.column, model._meta.pk.column))
    13281331        self.included_inherited_models = seen
    13291332
     
    13811384            # what "used" specifies).
    13821385            avoid = avoid_set.copy()
    13831386            dupe_set = orig_dupe_set.copy()
    1384             table = f.rel.to._meta.db_table
     1387            table = f.rel.to._meta.qualified_name
    13851388            if nullable or f.null:
    13861389                promote = True
    13871390            else:
     
    14051408                                ())
    14061409                        dupe_set.add((opts, lhs_col))
    14071410                    int_opts = int_model._meta
    1408                     alias = self.join((alias, int_opts.db_table, lhs_col,
    1409                             int_opts.pk.column), exclusions=used,
     1411                    alias = self.join((alias, int_opts.qualified_name,
     1412                            lhs_col, int_opts.pk.column), exclusions=used,
    14101413                            promote=promote)
    14111414                    alias_chain.append(alias)
    14121415                    for (dupe_opts, dupe_col) in dupe_set:
     
    17601763                                    (id(opts), lhs_col), ()))
    17611764                            dupe_set.add((opts, lhs_col))
    17621765                        opts = int_model._meta
    1763                         alias = self.join((alias, opts.db_table, lhs_col,
    1764                                 opts.pk.column), exclusions=exclusions)
     1766                        alias = self.join((alias, opts.qualified_name,
     1767                                           lhs_col, opts.pk.column),
     1768                                          exclusions=exclusions)
    17651769                        joins.append(alias)
    17661770                        exclusions.add(alias)
    17671771                        for (dupe_opts, dupe_col) in dupe_set:
     
    17861790                        (table1, from_col1, to_col1, table2, from_col2,
    17871791                                to_col2, opts, target) = cached_data
    17881792                    else:
    1789                         table1 = field.m2m_db_table()
     1793                        table1 = field.m2m_qualified_name()
    17901794                        from_col1 = opts.pk.column
    17911795                        to_col1 = field.m2m_column_name()
    17921796                        opts = field.rel.to._meta
    1793                         table2 = opts.db_table
     1797                        table2 = opts.qualified_name
    17941798                        from_col2 = field.m2m_reverse_name()
    17951799                        to_col2 = opts.pk.column
    17961800                        target = opts.pk
     
    18171821                    else:
    18181822                        opts = field.rel.to._meta
    18191823                        target = field.rel.get_related_field()
    1820                         table = opts.db_table
     1824                        table = opts.qualified_name
    18211825                        from_col = field.column
    18221826                        to_col = target.column
    18231827                        orig_opts._join_cache[name] = (table, from_col, to_col,
     
    18391843                        (table1, from_col1, to_col1, table2, from_col2,
    18401844                                to_col2, opts, target) = cached_data
    18411845                    else:
    1842                         table1 = field.m2m_db_table()
     1846                        table1 = field.m2m_qualified_name()
    18431847                        from_col1 = opts.pk.column
    18441848                        to_col1 = field.m2m_reverse_name()
    18451849                        opts = orig_field.opts
    1846                         table2 = opts.db_table
     1850                        table2 = opts.qualified_name
    18471851                        from_col2 = field.m2m_column_name()
    18481852                        to_col2 = opts.pk.column
    18491853                        target = opts.pk
     
    18661870                        local_field = opts.get_field_by_name(
    18671871                                field.rel.field_name)[0]
    18681872                        opts = orig_field.opts
    1869                         table = opts.db_table
     1873                        table = opts.qualified_name
    18701874                        from_col = local_field.column
    18711875                        to_col = field.column
    18721876                        target = opts.pk
     
    21052109        self.group_by = []
    21062110        if self.connection.features.allows_group_by_pk:
    21072111            if len(self.select) == len(self.model._meta.fields):
    2108                 self.group_by.append((self.model._meta.db_table,
     2112                self.group_by.append((self.model._meta.qualified_name,
    21092113                                      self.model._meta.pk.column))
    21102114                return
    21112115
     
    21272131        else:
    21282132            opts = self.model._meta
    21292133            if not self.select:
    2130                 count = self.aggregates_module.Count((self.join((None, opts.db_table, None, None)), opts.pk.column),
     2134                count = self.aggregates_module.Count((self.join((None,
     2135                           opts.qualified_name, None, None)), opts.pk.column),
    21312136                                         is_summary=True, distinct=True)
    21322137            else:
    21332138                # 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

     
    611611            # into a pure queryset operation.
    612612            where = ['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
    613613                (qn('_order'), op, qn('_order'),
    614                 qn(self._meta.db_table), qn(self._meta.pk.column))]
     614                self._meta.qualified_name, qn(self._meta.pk.column))]
    615615            params = [self.pk]
    616616            obj = self._default_manager.filter(**{order_field.name: getattr(self, order_field.attname)}).extra(where=where, params=params).order_by(order)[:1].get()
    617617            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', 'auto_created')
     24                 'abstract', 'managed', 'proxy', 'auto_created', '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 = settings.DATABASE_SCHEMA
     34        self.qualified_name = ''
    3335        self.ordering = []
    3436        self.unique_together =  []
    3537        self.permissions =  []
     
    104106            self.db_table = "%s_%s" % (self.app_label, self.module_name)
    105107            self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
    106108
     109        # Construct qualified table name.
     110        self.qualified_name = connection.ops.prep_db_table(self.db_schema,
     111                                                           self.db_table)
     112        if self.qualified_name == connection.ops.quote_name(self.db_table):
     113            # If unchanged, the backend doesn't support schemas.
     114            self.db_schema = ''
    107115
    108116    def _prepare(self, model):
    109117        if self.order_with_respect_to:
     
    177185        self.pk = target._meta.pk
    178186        self.proxy_for_model = target
    179187        self.db_table = target._meta.db_table
     188        self.db_schema = target._meta.db_schema
    180189
    181190    def __repr__(self):
    182191        return '<Options for %s>' % self.object_name
  • django/db/models/fields/related.py

     
    559559            core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
    560560            instance=instance,
    561561            symmetrical=False,
    562             source_field_name=self.related.field.m2m_reverse_field_name(),
    563             target_field_name=self.related.field.m2m_field_name()
     562            join_table=self.related.field.m2m_qualified_name(),
     563            source_field_name=qn(self.related.field.m2m_reverse_field_name()),
     564            target_field_name=qn(self.related.field.m2m_field_name())
    564565        )
    565566
    566567        return manager
     
    609610            core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
    610611            instance=instance,
    611612            symmetrical=(self.field.rel.symmetrical and isinstance(instance, rel_model)),
     613            join_table=self.field.m2m_qualified_name(),
    612614            source_field_name=self.field.m2m_field_name(),
    613615            target_field_name=self.field.m2m_reverse_field_name()
    614616        )
     
    751753        if isinstance(self.rel.to, basestring):
    752754            target = self.rel.to
    753755        else:
    754             target = self.rel.to._meta.db_table
     756            target = self.rel.to._meta.qualified_name
    755757        cls._meta.duplicate_targets[self.column] = (target, "o2m")
    756758
    757759    def contribute_to_related_class(self, cls, related):
     
    861863            through=kwargs.pop('through', None))
    862864
    863865        self.db_table = kwargs.pop('db_table', None)
     866        self.db_schema = kwargs.pop('db_schema', '')
    864867        if kwargs['rel'].through is not None:
    865868            assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
    866869
     
    882885            return util.truncate_name('%s_%s' % (opts.db_table, self.name),
    883886                                      connection.ops.max_name_length())
    884887
     888    def _get_m2m_db_schema(self, opts):
     889        "Function that can be curried to provide the m2m schema name for this relation"
     890        if self.rel.through is not None and self.rel.through._meta.db_schema:
     891            return self.rel.through._meta.db_schema
     892        elif self.db_schema:
     893            return self.db_schema
     894        else:
     895            return opts.db_schema
     896
     897    def _get_m2m_qualified_name(self, opts):
     898        "Function that can be curried to provide the qualified m2m table name for this relation"
     899        schema = self._get_m2m_db_schema(opts)
     900        table = self._get_m2m_db_table(opts)
     901        return connection.ops.prep_db_table(schema, table)
     902
    885903    def _get_m2m_attr(self, related, attr):
    886904        "Function that can be curried to provide the source column name for the m2m table"
    887905        cache_attr = '_m2m_%s_cache' % attr
     
    971989
    972990        # Set up the accessor for the m2m table name for the relation
    973991        self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
     992        self.m2m_db_schema = curry(self._get_m2m_db_schema, cls._meta)
     993        self.m2m_qualified_name = curry(self._get_m2m_qualified_name,
     994                                        cls._meta)
    974995
    975996        # Populate some necessary rel arguments so that cross-app relations
    976997        # work correctly.
     
    9821003        if isinstance(self.rel.to, basestring):
    9831004            target = self.rel.to
    9841005        else:
    985             target = self.rel.to._meta.db_table
     1006            target = self.rel.to._meta.qualified_name
    9861007        cls._meta.duplicate_targets[self.column] = (target, "m2m")
    9871008
    9881009    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 [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
     170    def prep_db_index(self, db_schema, db_index):
     171        return self.prep_db_table(db_schema, db_index)
     172
    163173    def random_function_sql(self):
    164174        return 'RAND()'
    165175
     
    169179        # to clear all tables of all data
    170180        if tables:
    171181            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))))
     182            sql.extend(['%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.prep_db_table(schema, table))) for (schema, table) in tables])
    174183            sql.append('SET FOREIGN_KEY_CHECKS = 1;')
    175184
    176185            # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
    177186            # 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])
     187            for sequence_info in sequences:
     188                schema_name = sequence_info['schema']
     189                table_name = self.prep_db_table(schema_name, sequence_info['table'])
     190                sql.append("%s %s %s %s %s;" % \
     191                           (style.SQL_KEYWORD('ALTER'),
     192                            style.SQL_KEYWORD('TABLE'),
     193                            style.SQL_TABLE(table_name),
     194                            style.SQL_KEYWORD('AUTO_INCREMENT'),
     195                            style.SQL_FIELD('= 1'),
     196                            ))
    185197            return sql
    186198        else:
    187199            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

     
    5555
    5656class DatabaseOperations(BaseDatabaseOperations):
    5757
    58     def autoinc_sql(self, table, column):
     58    def autoinc_sql(self, schema, table, column):
    5959        # To simulate auto-incrementing primary keys in Oracle, we have to
    6060        # create a sequence and a trigger.
    6161        sq_name = get_sequence_name(table)
    6262        tr_name = get_trigger_name(table)
    63         tbl_name = self.quote_name(table)
     63        tbl_name = self.prep_db_table(schema, table)
     64        sq_qname = self.prep_db_table(schema, sq_name)
     65        tr_qname = self.prep_db_table(schema, tr_name)
    6466        col_name = self.quote_name(column)
    6567        sequence_sql = """
    6668DECLARE
     
    6971    SELECT COUNT(*) INTO i FROM USER_CATALOG
    7072        WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
    7173    IF i = 0 THEN
    72         EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
     74        EXECUTE IMMEDIATE 'CREATE SEQUENCE %(sq_qname)s';
    7375    END IF;
    7476END;
    7577/""" % locals()
    7678        trigger_sql = """
    77 CREATE OR REPLACE TRIGGER "%(tr_name)s"
     79CREATE OR REPLACE TRIGGER %(tr_qname)s
    7880BEFORE INSERT ON %(tbl_name)s
    7981FOR EACH ROW
    8082WHEN (new.%(col_name)s IS NULL)
    8183    BEGIN
    82         SELECT "%(sq_name)s".nextval
     84        SELECT %(sq_qname)s.nextval
    8385        INTO :new.%(col_name)s FROM dual;
    8486    END;
    8587/""" % locals()
     
    108110    def deferrable_sql(self):
    109111        return " DEFERRABLE INITIALLY DEFERRED"
    110112
    111     def drop_sequence_sql(self, table):
    112         return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
     113    def drop_sequence_sql(self, schema, table):
     114        sequence_name = self.prep_db_table(schema, get_sequence_name(table))
     115        return "DROP SEQUENCE %s;" % sequence_name
    113116
    114117    def fetch_returned_insert_id(self, cursor):
    115118        return long(cursor._insert_id_var.getvalue())
     
    120123        else:
    121124            return "%s"
    122125
    123     def last_insert_id(self, cursor, table_name, pk_name):
    124         sq_name = get_sequence_name(table_name)
    125         cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
     126    def last_insert_id(self, cursor, schema_name, table_name, pk_name):
     127        sq_name = self.prep_db_table(schema_name, get_sequence_name(table_name))
     128        cursor.execute('SELECT %s.currval FROM dual' % sq_name)
    126129        return cursor.fetchone()[0]
    127130
    128131    def lookup_cast(self, lookup_type):
     
    133136    def max_name_length(self):
    134137        return 30
    135138
     139    def prep_db_table(self, db_schema, db_table):
     140        qn = self.quote_name
     141        if db_schema:
     142            return "%s.%s" % (qn(db_schema), qn(db_table))
     143        else:
     144            return qn(db_table)
     145
     146    def prep_db_index(self, db_schema, db_index):
     147        return self.prep_db_table(db_schema, db_index)
     148
    136149    def prep_for_iexact_query(self, x):
    137150        return x
    138151
     
    186199    def sql_flush(self, style, tables, sequences):
    187200        # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
    188201        # 'TRUNCATE z;'... style SQL statements
     202        sql = []
    189203        if tables:
    190204            # Oracle does support TRUNCATE, but it seems to get us into
    191205            # FK referential trouble, whereas DELETE FROM table works.
    192             sql = ['%s %s %s;' % \
    193                     (style.SQL_KEYWORD('DELETE'),
    194                      style.SQL_KEYWORD('FROM'),
    195                      style.SQL_FIELD(self.quote_name(table)))
    196                     for table in tables]
     206            for schema, table in tables:
     207                table = self.prep_db_table(schema, table)
     208                sql.append('%s %s %s;' % \
     209                           (style.SQL_KEYWORD('DELETE'),
     210                            style.SQL_KEYWORD('FROM'),
     211                            style.SQL_FIELD(table)))
    197212            # Since we've just deleted all the rows, running our sequence
    198213            # ALTER code will reset the sequence to 0.
    199214            for sequence_info in sequences:
    200                 sequence_name = get_sequence_name(sequence_info['table'])
    201                 table_name = self.quote_name(sequence_info['table'])
     215                schema_name = sequence_info['schema']
     216                sequence_name = self.prep_db_table(schema_name,
     217                                    get_sequence_name(sequence_info['table']))
     218                table_name = self.prep_db_table(schema_name,
     219                                                sequence_info['table'])
    202220                column_name = self.quote_name(sequence_info['column'] or 'id')
    203221                query = _get_sequence_reset_sql() % {'sequence': sequence_name,
    204222                                                     'table': table_name,
    205223                                                     'column': column_name}
    206224                sql.append(query)
    207             return sql
    208         else:
    209             return []
     225        return sql
    210226
    211227    def sequence_reset_sql(self, style, model_list):
    212228        from django.db import models
     
    215231        for model in model_list:
    216232            for f in model._meta.local_fields:
    217233                if isinstance(f, models.AutoField):
    218                     table_name = self.quote_name(model._meta.db_table)
    219                     sequence_name = get_sequence_name(model._meta.db_table)
     234                    table_name = model._meta.qualified_name
     235                    sequence_name = self.prep_db_table(model._meta.db_schema,
     236                                       get_sequence_name(model._meta.db_table))
    220237                    column_name = self.quote_name(f.column)
    221238                    output.append(query % {'sequence': sequence_name,
    222239                                           'table': table_name,
     
    226243                    break
    227244            for f in model._meta.many_to_many:
    228245                if not f.rel.through:
    229                     table_name = self.quote_name(f.m2m_db_table())
    230                     sequence_name = get_sequence_name(f.m2m_db_table())
     246                    table_name = self.quote_name(f.m2m_qualified_name())
     247                    sequence_name = self.prep_db_table(f.m2m_db_schema(),
     248                                           get_sequence_name(f.m2m_db_table()))
    231249                    column_name = self.quote_name('id')
    232250                    output.append(query % {'sequence': sequence_name,
    233251                                           'table': table_name,
     
    434452            query = query[:-1]
    435453        query = convert_unicode(query % tuple(args), self.charset)
    436454        self._guess_input_sizes([params])
     455        print >>file('/home/ikelly/django-log.sql', 'w'), query, self._param_generator(params)
    437456        try:
    438457            return self.cursor.execute(query, self._param_generator(params))
    439458        except DatabaseError, e:
     
    551570BEGIN
    552571    LOCK TABLE %(table)s IN SHARE MODE;
    553572    SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
    554     SELECT "%(sequence)s".nextval INTO cval FROM dual;
     573    SELECT %(sequence)s.nextval INTO cval FROM dual;
    555574    cval := startvalue - cval;
    556575    IF cval != 0 THEN
    557         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" MINVALUE 0 INCREMENT BY '||cval;
    558         SELECT "%(sequence)s".nextval INTO cval FROM dual;
    559         EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" INCREMENT BY 1';
     576        EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval;
     577        SELECT %(sequence)s.nextval INTO cval FROM dual;
     578        EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1';
    560579    END IF;
    561580    COMMIT;
    562581END;
  • django/db/backends/oracle/introspection.py

     
    116116        for row in cursor.fetchall():
    117117            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
    118118        return indexes
     119
     120    def schema_name_converter(self, name):
     121        """Convert to lowercase for case-sensitive schema name comparison."""
     122        return name.lower()
     123
     124    def get_schema_list(self, cursor):
     125        "Returns a list of schemas that exist in the database."
     126        sql = """
     127        select distinct username
     128        from all_users, all_objects
     129        where username = owner
     130        """
     131        cursor.execute(sql)
     132        return [schema.lower() for (schema,) in cursor]
     133
     134    def get_schema_table_list(self, cursor, schema):
     135        "Returns a list of tables in a specific schema."
     136        sql = """
     137        select table_name from all_tables
     138        where owner = upper(%s)
     139        """
     140        cursor.execute(sql, [schema])
     141        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 quoted db_table if not supported.
     294        """
     295        return self.quote_name(db_table)
     296
     297    def prep_db_index(self, db_schema, db_index):
     298        """
     299        Prepares and formats the table index name if necessary.
     300        Just returns quoted db_index if not supported.
     301        """
     302        return self.quote_name(db_index)
     303
    290304    def random_function_sql(self):
    291305        """
    292306        Returns a SQL expression that returns a random value.
     
    486500        return name
    487501
    488502    def table_names(self):
    489         "Returns a list of names of all tables that exist in the database."
     503        "Returns a list of names of all tables that exist in the default schema."
    490504        cursor = self.connection.cursor()
    491505        return self.get_table_list(cursor)
    492506
     507    def schema_name_converter(self, name):
     508        """Apply a conversion to the name for the purposes of comparison.
     509
     510        The default schema name converter is for case sensitive comparison.
     511        """
     512        return name
     513
     514    def get_schema_list(self, cursor):
     515        "Returns a list of schemas that exist in the database"
     516        return []
     517
     518    def get_schema_table_list(self, cursor, schema):
     519        "Returns a list of tables in a specific schema"
     520        return []
     521
     522    def schema_names(self):
     523        cursor = self.connection.cursor()
     524        return self.get_schema_list(cursor)
     525
     526    def schema_table_names(self, schema):
     527        "Returns a list of names of all tables that exist in the database schema."
     528        cursor = self.connection.cursor()
     529        return self.get_schema_table_list(cursor, schema)
     530
    493531    def django_table_names(self, only_existing=False):
    494532        """
    495         Returns a list of all table names that have associated Django models and
    496         are in INSTALLED_APPS.
     533        Returns a list of tuples containing all schema and table names that
     534        have associated Django models and are in INSTALLED_APPS.
    497535
    498         If only_existing is True, the resulting list will only include the tables
    499         that actually exist in the database.
     536        If only_existing is True, the resulting list will only include the
     537        tables that actually exist in the database.
    500538        """
    501539        from django.db import models
    502540        tables = set()
     541        if only_existing:
     542            existing_tables = set([('', tn) for tn in self.table_names()])
     543            seen_schemas = set()
    503544        for app in models.get_apps():
    504545            for model in models.get_models(app):
    505546                if not model._meta.managed:
    506547                    continue
    507                 tables.add(model._meta.db_table)
    508                 tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
     548                db_schema = model._meta.db_schema
     549                db_table = model._meta.db_table
     550                if only_existing and db_schema and db_schema not in seen_schemas:
     551                    existing_tables.update([(db_schema, tn) for tn in
     552                                            self.schema_table_names(db_schema)])
     553                    seen_schemas.add(db_schema)
     554                tables.add((model._meta.db_schema, model._meta.db_table))
     555                for f in model._meta.local_many_to_many:
     556                    m2m_schema = f.m2m_db_schema()
     557                    m2m_table = f.m2m_db_table()
     558                    if only_existing and m2m_schema and m2m_schema not in seen_schemas:
     559                        existing_tables.update([(m2m_schema, tn) for tn in
     560                                            self.schema_table_names(m2m_schema)])
     561                        seen_schemas.add(m2m_schema)
     562                    tables.add((m2m_schema, m2m_table))
    509563        if only_existing:
    510             tables = [t for t in tables if self.table_name_converter(t) in self.table_names()]
     564            tables = [(s, t) for (s, t) in tables
     565                      if (self.schema_name_converter(s),
     566                          self.table_name_converter(t)) in existing_tables]
    511567        return tables
    512568
    513569    def installed_models(self, tables):
     
    534590                    continue
    535591                for f in model._meta.local_fields:
    536592                    if isinstance(f, models.AutoField):
    537                         sequence_list.append({'table': model._meta.db_table, 'column': f.column})
     593                        sequence_list.append({'table': model._meta.db_table,
     594                                              'column': f.column,
     595                                              'schema': model._meta.db_schema})
    538596                        break # Only one AutoField is allowed per model, so don't bother continuing.
    539597
    540598                for f in model._meta.local_many_to_many:
    541599                    # If this is an m2m using an intermediate table,
    542600                    # we don't need to reset the sequence.
    543601                    if f.rel.through is None:
    544                         sequence_list.append({'table': f.m2m_db_table(), 'column': None})
     602                        sequence_list.append({'table': f.m2m_db_table(),
     603                                              'column': None,
     604                                              'schema': f.m2m_db_schema()})
    545605
    546606        return sequence_list
    547607
  • 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_index(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 or model._meta.proxy:
     
    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, include_auto_created=True)
    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               
     72                # Create the model's database table,
     73                # if it doesn't already exist.
    6374                if verbosity >= 2:
    6475                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
    6576                opts = model._meta
    66                 if (connection.introspection.table_name_converter(opts.db_table) in tables or
     77                schema_table = (db_schema, connection.introspection.table_name_converter(opts.db_table))
     78                if (schema_table in tables or
    6779                    (opts.auto_created and
    6880                    connection.introspection.table_name_converter(opts.auto_created._meta.db_table) in tables)):
    6981                    continue
     
    7991                    print "Creating table %s" % model._meta.db_table
    8092                for statement in sql:
    8193                    cursor.execute(statement)
    82                 tables.append(connection.introspection.table_name_converter(model._meta.db_table))
     94                tables.append(schema_table)
    8395
    8496
    8597        transaction.commit_unless_managed()
  • django/core/management/sql.py

     
    6868
    6969    # Figure out which tables already exist
    7070    if cursor:
    71         table_names = connection.introspection.get_table_list(cursor)
     71        table_names = [('', tn) for tn in
     72                       connection.introspection.get_table_list(cursor)]
    7273    else:
    7374        table_names = []
    7475
     
    7980
    8081    references_to_delete = {}
    8182    app_models = models.get_models(app, include_auto_created=True)
     83    seen_schemas = set()
    8284    for model in app_models:
    83         if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     85        db_schema = model._meta.db_schema
     86        # Find additional tables in model-defined schemas.
     87        if db_schema:
     88            db_schema = connection.introspection.schema_name_converter(db_schema)
     89            if db_schema not in seen_schemas:
     90                table_names += ((db_schema, tn) for tn in connection.introspection.get_schema_table_list(cursor, db_schema))
     91                seen_schemas.add(db_schema)
     92        schema_table = (db_schema,
     93                        connection.introspection.table_name_converter(model._meta.db_table))
     94
     95        if cursor and schema_table in table_names:
    8496            # The table exists, so it needs to be dropped
    8597            opts = model._meta
    8698            for f in opts.local_fields:
     
    90102            to_delete.add(model)
    91103
    92104    for model in app_models:
    93         if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
     105        db_schema = model._meta.db_schema
     106        if db_schema:
     107            db_schema = connection.introspection.schema_name_converter(db_schema)
     108        schema_table = (db_schema,
     109                        connection.introspection.table_name_converter(model._meta.db_table))
     110        if schema_table in table_names:
    94111            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    95112
     113    # Output DROP TABLE statements for many-to-many tables.
     114    for model in app_models:
     115        opts = model._meta
     116        db_schema = opts.db_schema
     117        if db_schema:
     118            db_schema = connection.introspection.schema_name_converter(db_schema)
     119        for f in opts.local_many_to_many:
     120            schema_table = (db_schema,
     121                            connection.introspection.table_name_converter(f.m2m_db_table()))
     122            if cursor and schema_table in table_names:
     123                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
     124
    96125    # Close database connection explicitly, in case this output is being piped
    97126    # directly into a database client, to avoid locking issues.
    98127    if cursor:
     
    116145    if only_django:
    117146        tables = connection.introspection.django_table_names(only_existing=True)
    118147    else:
    119         tables = connection.introspection.table_names()
     148        tables = [('', tn) for tn in connection.introspection.table_names()]
    120149    statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
    121150    return statements
    122151
  • 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
     30class Tag(models.Model):
     31    "Many to many in default schema which is used byt model in custom schema"
     32    entries=models.ManyToManyField(Entry)
     33    name=models.CharField(max_length=20)
     34   
     35   
     36__test__ = {'API_TESTS': """
     37
     38#Test with actual data
     39# Nothing in there yet
     40>>> Blog.objects.all()
     41[]
     42
     43# Create a blog
     44>>> b = Blog(name='Test')
     45>>> b.save()
     46
     47# Verify that we got an ID
     48>>> b.id
     491
     50
     51# Create entry
     52>>> e = Entry(blog=b, title='Test entry')
     53>>> e.save()
     54>>> e.id
     551
     56
     57# Create Comments
     58>>> c1 = Comment(entry=e, text='nice entry')
     59>>> c1.save()
     60>>> c2 = Comment(entry=e, text='really like it')
     61>>> c2.save()
     62
     63#Retrieve the stuff again.
     64>>> b2 = Blog.objects.get(id=b.id)
     65>>> b==b2
     66True
     67
     68>>> b2.entry_set.all()
     69[<Entry: Entry object>]
     70
     71#make sure we don't break many to many relations
     72>>> t = Tag(name="test")
     73>>> t.save()
     74>>> t.entries = [e]
     75>>> t.entries.all()
     76[<Entry: Entry object>]
     77
     78
     79>>> from django.conf import settings
     80>>> from django.db import connection, models
     81
     82# Test if we support schemas and can find the table if so
     83>>> if e._meta.db_schema:
     84...     tables = connection.introspection.schema_table_names(e._meta.db_schema)
     85... else:
     86...     tables = connection.introspection.table_names()
     87>>> if connection.introspection.table_name_converter(e._meta.db_table) in tables:
     88...     print "ok"
     89... else:
     90...     print "schema=" + e._meta.db_schema
     91...     print "tables=%s" % tables
     92ok
     93
     94# Test that all but sqlite3 backend supports schema.
     95>>> if settings.DATABASE_ENGINE != 'sqlite3':
     96...     e._meta.db_schema
     97'test_schema'
     98
     99"""
     100}
  • 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

     
    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**New in Django development version**
     72
     73The name of the database schema to use for the model. If the backend
     74doesn't support multiple schemas, this options is ignored.
     75
     76If this is used the Django will prefix the model table names with the schema
     77name. For example 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
     81
     82
    6483``db_tablespace``
    6584-----------------
    6685
  • docs/ref/settings.txt

     
    272272The port to use when connecting to the database. An empty string means the
    273273default port. Not used with SQLite.
    274274
     275.. setting:: DATABASE_SCHEMA
     276
     277DATABASE_SCHEMA
     278---------------
     279
     280**New in Django development version**
     281
     282Default: ``''`` (Empty string)
     283
     284The name of the database schema to use for models. If the backend
     285doesn't support multiple schemas, this options is ignored. An empty
     286string means the default schema.
     287
     288If this is used the Django will prefix any table names with the schema name.
     289The schema can be overriden in model, for details see :ref:`db_schema`.
     290
    275291.. setting:: DATABASE_USER
    276292
    277293DATABASE_USER
Back to Top