Code

Ticket #6148: 6148_django1.5.diff

File 6148_django1.5.diff, 194.6 KB (added by akaariai, 2 years ago)
Line 
1Index: django/conf/global_settings.py
2===================================================================
3--- django/conf/global_settings.py      (revision 17914)
4+++ django/conf/global_settings.py      (working copy)
5@@ -407,6 +407,9 @@
6 DEFAULT_TABLESPACE = ''
7 DEFAULT_INDEX_TABLESPACE = ''
8 
9+# The default database schema to use for tables
10+DEFAULT_SCHEMA = None
11+
12 # Default X-Frame-Options header value
13 X_FRAME_OPTIONS = 'SAMEORIGIN'
14 
15Index: django/db/models/fields/related.py
16===================================================================
17--- django/db/models/fields/related.py  (revision 17914)
18+++ django/db/models/fields/related.py  (working copy)
19@@ -1,6 +1,6 @@
20 from operator import attrgetter
21 
22-from django.db import connection, router
23+from django.db import connection, router, QName
24 from django.db.backends import util
25 from django.db.models import signals, get_model
26 from django.db.models.fields import (AutoField, Field, IntegerField,
27@@ -568,11 +568,11 @@
28             # dealing with PK values.
29             fk = self.through._meta.get_field(self.source_field_name)
30             source_col = fk.column
31-            join_table = self.through._meta.db_table
32             connection = connections[db]
33             qn = connection.ops.quote_name
34+            join_alias = connection.qname(self.through)
35             qs = qs.extra(select={'_prefetch_related_val':
36-                                      '%s.%s' % (qn(join_table), qn(source_col))})
37+                                      '%s.%s' % (join_alias, qn(source_col))})
38             select_attname = fk.rel.get_related_field().get_attname()
39             return (qs,
40                     attrgetter('_prefetch_related_val'),
41@@ -1085,6 +1085,7 @@
42         to = to.lower()
43     meta = type('Meta', (object,), {
44         'db_table': field._get_m2m_db_table(klass._meta),
45+        'db_schema': field._get_m2m_db_schema(klass._meta),
46         'managed': managed,
47         'auto_created': klass,
48         'app_label': klass._meta.app_label,
49@@ -1121,8 +1122,12 @@
50             through=kwargs.pop('through', None))
51 
52         self.db_table = kwargs.pop('db_table', None)
53+        self.db_schema = kwargs.pop('db_schema', None)
54         if kwargs['rel'].through is not None:
55             assert self.db_table is None, "Cannot specify a db_table if an intermediary model is used."
56+            assert self.db_schema is None, "Cannot specify a db_schema if an intermediary model is used."
57+        # TODO: by default use the containing model's db_schema
58+           
59 
60         Field.__init__(self, **kwargs)
61 
62@@ -1142,6 +1147,22 @@
63             return util.truncate_name('%s_%s' % (opts.db_table, self.name),
64                                       connection.ops.max_name_length())
65 
66+    def _get_m2m_db_schema(self, opts):
67+        "Function that can be curried to provide the m2m schema name for this relation"
68+        if self.rel.through is not None and self.rel.through._meta.db_schema:
69+            return self.rel.through._meta.db_schema
70+        elif self.db_schema:
71+            return self.db_schema
72+        else:
73+            return opts.db_schema
74+
75+
76+    def _get_m2m_qualified_name(self, opts):
77+        "Function that can be curried to provide the qualified m2m table name for this relation"
78+        schema = self._get_m2m_db_schema(opts)
79+        table = self._get_m2m_db_table(opts)
80+        return QName(schema, table, False)
81+
82     def _get_m2m_attr(self, related, attr):
83         "Function that can be curried to provide the source accessor or DB column name for the m2m table"
84         cache_attr = '_m2m_%s_cache' % attr
85@@ -1212,6 +1233,9 @@
86 
87         # Set up the accessor for the m2m table name for the relation
88         self.m2m_db_table = curry(self._get_m2m_db_table, cls._meta)
89+        self.m2m_db_schema = curry(self._get_m2m_db_schema, cls._meta)
90+        self.m2m_qualified_name = curry(self._get_m2m_qualified_name,
91+                                        cls._meta)
92 
93         # Populate some necessary rel arguments so that cross-app relations
94         # work correctly.
95@@ -1223,7 +1247,7 @@
96         if isinstance(self.rel.to, basestring):
97             target = self.rel.to
98         else:
99-            target = self.rel.to._meta.db_table
100+            target = self.rel.to._meta.qualified_name
101         cls._meta.duplicate_targets[self.column] = (target, "m2m")
102 
103     def contribute_to_related_class(self, cls, related):
104Index: django/db/models/query.py
105===================================================================
106--- django/db/models/query.py   (revision 17914)
107+++ django/db/models/query.py   (working copy)
108@@ -1558,7 +1558,7 @@
109         A dict mapping column names to model field names.
110         """
111         if not hasattr(self, '_model_fields'):
112-            converter = connections[self.db].introspection.table_name_converter
113+            converter = connections[self.db].introspection.identifier_converter
114             self._model_fields = {}
115             for field in self.model._meta.fields:
116                 name, column = field.get_attname_column()
117Index: django/db/models/sql/query.py
118===================================================================
119--- django/db/models/sql/query.py       (revision 17914)
120+++ django/db/models/sql/query.py       (working copy)
121@@ -16,7 +16,6 @@
122 from django.db.models import signals
123 from django.db.models.expressions import ExpressionNode
124 from django.db.models.fields import FieldDoesNotExist
125-from django.db.models.query_utils import InvalidQuery
126 from django.db.models.sql import aggregates as base_aggregates_module
127 from django.db.models.sql.constants import *
128 from django.db.models.sql.datastructures import EmptyResultSet, Empty, MultiJoin
129@@ -59,7 +58,7 @@
130     def get_columns(self):
131         if self.cursor is None:
132             self._execute_query()
133-        converter = connections[self.using].introspection.table_name_converter
134+        converter = connections[self.using].introspection.identifier_converter
135         return [converter(column_meta[0])
136                 for column_meta in self.cursor.description]
137 
138@@ -638,7 +637,7 @@
139         Callback used by deferred_to_columns(). The "target" parameter should
140         be a set instance.
141         """
142-        table = model._meta.db_table
143+        table = model._meta.qualified_name
144         if table not in target:
145             target[table] = set()
146         for field in fields:
147@@ -659,10 +658,13 @@
148             self.alias_refcount[alias] += 1
149             return alias, False
150 
151-        # Create a new alias for this table.
152-        if current:
153+        # Create a new alias for this table if this table is already used
154+        # in the query, or if there is the same table name used in the query
155+        # under different schema qualified name.
156+        if current or table_name[1] in [t[1] for t in self.tables]:
157             alias = '%s%d' % (self.alias_prefix, len(self.alias_map) + 1)
158-            current.append(alias)
159+            if current:
160+                current.append(alias)
161         else:
162             # The first occurence of a table uses the table name directly.
163             alias = table_name
164@@ -832,7 +834,7 @@
165             alias = self.tables[0]
166             self.ref_alias(alias)
167         else:
168-            alias = self.join((None, self.model._meta.db_table, None, None))
169+            alias = self.join((None, self.model._meta.qualified_name, None, None))
170         return alias
171 
172     def count_active_tables(self):
173@@ -945,7 +947,7 @@
174                     seen[model] = root_alias
175                 else:
176                     link_field = opts.get_ancestor_link(model)
177-                    seen[model] = self.join((root_alias, model._meta.db_table,
178+                    seen[model] = self.join((root_alias, model._meta.qualified_name,
179                             link_field.column, model._meta.pk.column))
180         self.included_inherited_models = seen
181 
182@@ -1329,7 +1331,7 @@
183                                     (id(opts), lhs_col), ()))
184                             dupe_set.add((opts, lhs_col))
185                         opts = int_model._meta
186-                        alias = self.join((alias, opts.db_table, lhs_col,
187+                        alias = self.join((alias, opts.qualified_name, lhs_col,
188                                 opts.pk.column), exclusions=exclusions)
189                         joins.append(alias)
190                         exclusions.add(alias)
191@@ -1355,12 +1357,12 @@
192                         (table1, from_col1, to_col1, table2, from_col2,
193                                 to_col2, opts, target) = cached_data
194                     else:
195-                        table1 = field.m2m_db_table()
196+                        table1 = field.m2m_qualified_name()
197                         from_col1 = opts.get_field_by_name(
198                             field.m2m_target_field_name())[0].column
199                         to_col1 = field.m2m_column_name()
200                         opts = field.rel.to._meta
201-                        table2 = opts.db_table
202+                        table2 = opts.qualified_name
203                         from_col2 = field.m2m_reverse_name()
204                         to_col2 = opts.get_field_by_name(
205                             field.m2m_reverse_target_field_name())[0].column
206@@ -1388,7 +1390,7 @@
207                     else:
208                         opts = field.rel.to._meta
209                         target = field.rel.get_related_field()
210-                        table = opts.db_table
211+                        table = opts.qualified_name
212                         from_col = field.column
213                         to_col = target.column
214                         orig_opts._join_cache[name] = (table, from_col, to_col,
215@@ -1410,12 +1412,12 @@
216                         (table1, from_col1, to_col1, table2, from_col2,
217                                 to_col2, opts, target) = cached_data
218                     else:
219-                        table1 = field.m2m_db_table()
220+                        table1 = field.m2m_qualified_name()
221                         from_col1 = opts.get_field_by_name(
222                             field.m2m_reverse_target_field_name())[0].column
223                         to_col1 = field.m2m_reverse_name()
224                         opts = orig_field.opts
225-                        table2 = opts.db_table
226+                        table2 = opts.qualified_name
227                         from_col2 = field.m2m_column_name()
228                         to_col2 = opts.get_field_by_name(
229                             field.m2m_target_field_name())[0].column
230@@ -1439,7 +1441,7 @@
231                         local_field = opts.get_field_by_name(
232                                 field.rel.field_name)[0]
233                         opts = orig_field.opts
234-                        table = opts.db_table
235+                        table = opts.qualified_name
236                         from_col = local_field.column
237                         to_col = field.column
238                         # In case of a recursive FK, use the to_field for
239@@ -1722,7 +1724,7 @@
240         else:
241             opts = self.model._meta
242             if not self.select:
243-                count = self.aggregates_module.Count((self.join((None, opts.db_table, None, None)), opts.pk.column),
244+                count = self.aggregates_module.Count((self.join((None, opts.qualified_name, None, None)), opts.pk.column),
245                                          is_summary=True, distinct=True)
246             else:
247                 # Because of SQL portability issues, multi-column, distinct
248Index: django/db/models/sql/subqueries.py
249===================================================================
250--- django/db/models/sql/subqueries.py  (revision 17914)
251+++ django/db/models/sql/subqueries.py  (working copy)
252@@ -41,7 +41,7 @@
253             where = self.where_class()
254             where.add((Constraint(None, field.column, field), 'in',
255                     pk_list[offset:offset + GET_ITERATOR_CHUNK_SIZE]), AND)
256-            self.do_query(self.model._meta.db_table, where, using=using)
257+            self.do_query(self.model._meta.qualified_name, where, using=using)
258 
259 class UpdateQuery(Query):
260     """
261Index: django/db/models/sql/compiler.py
262===================================================================
263--- django/db/models/sql/compiler.py    (revision 17914)
264+++ django/db/models/sql/compiler.py    (working copy)
265@@ -1,7 +1,8 @@
266 from itertools import izip
267 
268+from django.conf import settings
269 from django.core.exceptions import FieldError
270-from django.db import transaction
271+from django.db import transaction, QName
272 from django.db.backends.util import truncate_name
273 from django.db.models.query_utils import select_related_descend
274 from django.db.models.sql.constants import *
275@@ -27,7 +28,7 @@
276         # cleaned. We are not using a clone() of the query here.
277         """
278         if not self.query.tables:
279-            self.query.join((None, self.query.model._meta.db_table, None, None))
280+            self.query.join((None, self.query.model._meta.qualified_name, None, None))
281         if (not self.query.select and self.query.default_cols and not
282                 self.query.included_inherited_models):
283             self.query.setup_inherited_models()
284@@ -42,11 +43,13 @@
285         """
286         if name in self.quote_cache:
287             return self.quote_cache[name]
288-        if ((name in self.query.alias_map and name not in self.query.table_map) or
289-                name in self.query.extra_select):
290+        if name in self.query.alias_map and not isinstance(name, tuple):
291             self.quote_cache[name] = name
292             return name
293-        r = self.connection.ops.quote_name(name)
294+        if isinstance(name, tuple):
295+            r = self.connection.ops.qualified_name(name)
296+        else:
297+            r = self.connection.ops.quote_name(name)
298         self.quote_cache[name] = r
299         return r
300 
301@@ -281,8 +284,10 @@
302                         alias = start_alias
303                     else:
304                         link_field = opts.get_ancestor_link(model)
305-                        alias = self.query.join((start_alias, model._meta.db_table,
306-                                link_field.column, model._meta.pk.column))
307+                        alias = self.query.join((start_alias,
308+                                                 model._meta.qualified_name,
309+                                                 link_field.column,
310+                                                 model._meta.pk.column))
311                     seen[model] = alias
312             else:
313                 # If we're starting from the base model of the queryset, the
314@@ -508,15 +513,12 @@
315         qn2 = self.connection.ops.quote_name
316         first = True
317         for alias in self.query.tables:
318-            if not self.query.alias_refcount[alias]:
319+            # Extra tables can end up in self.tables, but not in the
320+            # alias_map if they aren't in a join. That's OK. We skip them.
321+            if not self.query.alias_refcount[alias] or alias not in self.query.alias_map:
322                 continue
323-            try:
324-                name, alias, join_type, lhs, lhs_col, col, nullable = self.query.alias_map[alias]
325-            except KeyError:
326-                # Extra tables can end up in self.tables, but not in the
327-                # alias_map if they aren't in a join. That's OK. We skip them.
328-                continue
329-            alias_str = (alias != name and ' %s' % alias or '')
330+            name, alias, join_type, lhs, lhs_col, col, nullable = self.query.alias_map[alias]
331+            alias_str = alias != name and ' %s' % qn(alias) or ''
332             if join_type and not first:
333                 result.append('%s %s%s ON (%s.%s = %s.%s)'
334                         % (join_type, qn(name), alias_str, qn(lhs),
335@@ -526,13 +528,17 @@
336                 result.append('%s%s%s' % (connector, qn(name), alias_str))
337             first = False
338         for t in self.query.extra_tables:
339-            alias, unused = self.query.table_alias(t)
340-            # Only add the alias if it's not already present (the table_alias()
341-            # calls increments the refcount, so an alias refcount of one means
342-            # this is the only reference.
343-            if alias not in self.query.alias_map or self.query.alias_refcount[alias] == 1:
344+            # Plain string table names are assumed to be in default schema
345+            if not isinstance(t, QName):
346+                t = QName(self.connection.schema, t, False)
347+            # Only add the table if it is not already in the query.
348+            if t not in self.query.table_map or self.query.alias_refcount[t] == 0:
349+                # This will add the table into the query properly, however we
350+                # are not interested in the alias it gets, we add it as a
351+                # plain table.
352+                self.query.table_alias(t)
353                 connector = not first and ', ' or ''
354-                result.append('%s%s' % (connector, qn(alias)))
355+                result.append('%s%s' % (connector, qn(t)))
356                 first = False
357         return result, []
358 
359@@ -546,7 +552,7 @@
360             if (len(self.query.model._meta.fields) == len(self.query.select) and
361                 self.connection.features.allows_group_by_pk):
362                 self.query.group_by = [
363-                    (self.query.model._meta.db_table, self.query.model._meta.pk.column)
364+                    (self.query.model._meta.qualified_name, self.query.model._meta.pk.column)
365                 ]
366 
367             group_by = self.query.group_by or []
368@@ -614,7 +620,7 @@
369             # what "used" specifies).
370             avoid = avoid_set.copy()
371             dupe_set = orig_dupe_set.copy()
372-            table = f.rel.to._meta.db_table
373+            table = f.rel.to._meta.qualified_name
374             promote = nullable or f.null
375             if model:
376                 int_opts = opts
377@@ -635,7 +641,7 @@
378                                 ()))
379                         dupe_set.add((opts, lhs_col))
380                     int_opts = int_model._meta
381-                    alias = self.query.join((alias, int_opts.db_table, lhs_col,
382+                    alias = self.query.join((alias, int_opts.qualified_name, lhs_col,
383                             int_opts.pk.column), exclusions=used,
384                             promote=promote)
385                     alias_chain.append(alias)
386@@ -687,7 +693,7 @@
387                 # what "used" specifies).
388                 avoid = avoid_set.copy()
389                 dupe_set = orig_dupe_set.copy()
390-                table = model._meta.db_table
391+                table = model._meta.qualified_name
392 
393                 int_opts = opts
394                 alias = root_alias
395@@ -710,7 +716,7 @@
396                             dupe_set.add((opts, lhs_col))
397                         int_opts = int_model._meta
398                         alias = self.query.join(
399-                            (alias, int_opts.db_table, lhs_col, int_opts.pk.column),
400+                            (alias, int_opts.qualified_name, lhs_col, int_opts.pk.column),
401                             exclusions=used, promote=True, reuse=used
402                         )
403                         alias_chain.append(alias)
404@@ -775,7 +781,7 @@
405                         # into `resolve_columns` because it wasn't selected.
406                         only_load = self.deferred_to_columns()
407                         if only_load:
408-                            db_table = self.query.model._meta.db_table
409+                            db_table = self.query.model._meta.qualified_name
410                             fields = [f for f in fields if db_table in only_load and
411                                       f.column in only_load[db_table]]
412                     row = self.resolve_columns(row, fields)
413@@ -856,8 +862,9 @@
414         # We don't need quote_name_unless_alias() here, since these are all
415         # going to be column names (so we can avoid the extra overhead).
416         qn = self.connection.ops.quote_name
417+        qn3 = self.connection.ops.qualified_name
418         opts = self.query.model._meta
419-        result = ['INSERT INTO %s' % qn(opts.db_table)]
420+        result = ['INSERT INTO %s' % qn3(opts.qualified_name)]
421 
422         has_fields = bool(self.query.fields)
423         fields = self.query.fields if has_fields else [opts.pk]
424@@ -887,7 +894,7 @@
425             ]
426         if self.return_id and self.connection.features.can_return_id_from_insert:
427             params = params[0]
428-            col = "%s.%s" % (qn(opts.db_table), qn(opts.pk.column))
429+            col = "%s.%s" % (qn3(opts.qualified_name), qn(opts.pk.column))
430             result.append("VALUES (%s)" % ", ".join(placeholders[0]))
431             r_fmt, r_params = self.connection.ops.return_insert_id()
432             result.append(r_fmt % col)
433@@ -913,7 +920,7 @@
434         if self.connection.features.can_return_id_from_insert:
435             return self.connection.ops.fetch_returned_insert_id(cursor)
436         return self.connection.ops.last_insert_id(cursor,
437-                self.query.model._meta.db_table, self.query.model._meta.pk.column)
438+                self.query.model._meta.qualified_name, self.query.model._meta.pk.column)
439 
440 
441 class SQLDeleteCompiler(SQLCompiler):
442@@ -939,9 +946,15 @@
443         self.pre_sql_setup()
444         if not self.query.values:
445             return '', ()
446-        table = self.query.tables[0]
447+        opts = self.query.model._meta
448+        table = self.connection.ops.qualified_name(opts.qualified_name)
449         qn = self.quote_name_unless_alias
450-        result = ['UPDATE %s' % qn(table)]
451+        alias = qn(self.query.tables[0])
452+        if table == alias:
453+            alias = ''
454+        else:
455+            alias = ' %s' % alias
456+        result = ['UPDATE %s%s' % (table, alias)]
457         result.append('SET')
458         values, update_params = [], []
459         for field, model, val in self.query.values:
460Index: django/db/models/options.py
461===================================================================
462--- django/db/models/options.py (revision 17914)
463+++ django/db/models/options.py (working copy)
464@@ -2,6 +2,7 @@
465 from bisect import bisect
466 
467 from django.conf import settings
468+from django.db import QName
469 from django.db.models.related import RelatedObject
470 from django.db.models.fields.related import ManyToManyRel
471 from django.db.models.fields import AutoField, FieldDoesNotExist
472@@ -17,7 +18,7 @@
473 DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering',
474                  'unique_together', 'permissions', 'get_latest_by',
475                  'order_with_respect_to', 'app_label', 'db_tablespace',
476-                 'abstract', 'managed', 'proxy', 'auto_created')
477+                 'abstract', 'managed', 'proxy', 'auto_created', 'db_schema')
478 
479 class Options(object):
480     def __init__(self, meta, app_label=None):
481@@ -26,6 +27,8 @@
482         self.module_name, self.verbose_name = None, None
483         self.verbose_name_plural = None
484         self.db_table = ''
485+        self.db_schema = ''
486+        self.qualified_name = QName(schema=None, table='', db_format=False)
487         self.ordering = []
488         self.unique_together =  []
489         self.permissions =  []
490@@ -112,7 +115,12 @@
491         # If the db_table wasn't provided, use the app_label + module_name.
492         if not self.db_table:
493             self.db_table = "%s_%s" % (self.app_label, self.module_name)
494+            # TODO: Using connection.ops is wrong: in multidb setup this doesn't work
495+            # correctly except if different connections happen to have the same
496+            # max_name_length.
497             self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
498+        self.qualified_name = QName(schema=self.db_schema, table=self.db_table,
499+                                    db_format=False)
500 
501     def _prepare(self, model):
502         if self.order_with_respect_to:
503@@ -193,6 +201,8 @@
504         self.pk = target._meta.pk
505         self.proxy_for_model = target
506         self.db_table = target._meta.db_table
507+        self.db_schema = target._meta.db_schema
508+        self.qualified_name = target._meta.qualified_name
509 
510     def __repr__(self):
511         return '<Options for %s>' % self.object_name
512Index: django/db/__init__.py
513===================================================================
514--- django/db/__init__.py       (revision 17914)
515+++ django/db/__init__.py       (working copy)
516@@ -3,11 +3,17 @@
517 from django.core.exceptions import ImproperlyConfigured
518 from django.db.utils import (ConnectionHandler, ConnectionRouter,
519     load_backend, DEFAULT_DB_ALIAS, DatabaseError, IntegrityError)
520+from collections import namedtuple
521 
522+# All table names must be QNames. The db_format argument
523+# tells if the qualified name is in a format that is ready
524+# to be used in the database or if the qname needs to be
525+# converted first.
526+QName = namedtuple("QName", "schema table db_format")
527+
528 __all__ = ('backend', 'connection', 'connections', 'router', 'DatabaseError',
529     'IntegrityError', 'DEFAULT_DB_ALIAS')
530 
531-
532 if DEFAULT_DB_ALIAS not in settings.DATABASES:
533     raise ImproperlyConfigured("You must define a '%s' database" % DEFAULT_DB_ALIAS)
534 
535Index: django/db/utils.py
536===================================================================
537--- django/db/utils.py  (revision 17914)
538+++ django/db/utils.py  (working copy)
539@@ -78,10 +78,17 @@
540             conn['ENGINE'] = 'django.db.backends.dummy'
541         conn.setdefault('OPTIONS', {})
542         conn.setdefault('TIME_ZONE', 'UTC' if settings.USE_TZ else settings.TIME_ZONE)
543-        for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT']:
544+        conn.setdefault('SCHEMA', settings.DEFAULT_SCHEMA)
545+        for setting in ['NAME', 'USER', 'PASSWORD', 'HOST', 'PORT', 'SCHEMA']:
546             conn.setdefault(setting, '')
547         for setting in ['TEST_CHARSET', 'TEST_COLLATION', 'TEST_NAME', 'TEST_MIRROR']:
548             conn.setdefault(setting, None)
549+        # Some databases need a unique prefix for schemas to avoid name
550+        # collisions between production database or another testing database
551+        # in the same instance. This needs to be a string unlike the other
552+        # TEST_ settings above.
553+        conn.setdefault('TEST_SCHEMA_PREFIX', '')
554+        conn.setdefault('TEST_SCHEMAS', [])
555 
556     def __getitem__(self, alias):
557         if hasattr(self._connections, alias):
558Index: django/db/backends/postgresql_psycopg2/introspection.py
559===================================================================
560--- django/db/backends/postgresql_psycopg2/introspection.py     (revision 17914)
561+++ django/db/backends/postgresql_psycopg2/introspection.py     (working copy)
562@@ -1,3 +1,4 @@
563+from django.db import QName
564 from django.db.backends import BaseDatabaseIntrospection
565 
566 
567@@ -21,50 +22,111 @@
568         1266: 'TimeField',
569         1700: 'DecimalField',
570     }
571+
572+    def get_schema_list(self, cursor):
573+        cursor.execute("""
574+            SELECT n.nspname
575+            FROM pg_catalog.pg_namespace n
576+            WHERE n.nspname != 'information_schema' AND n.nspname not like 'pg_%%'""")
577+        return [row[0] for row in cursor.fetchall()]
578         
579-    def get_table_list(self, cursor):
580-        "Returns a list of table names in the current database."
581+    def get_visible_tables_list(self, cursor):
582+        """
583+        Returns a list of all by-default visible table names in the current
584+        database.
585+        """
586+        sql = """
587+            SELECT n.nspname, c.relname
588+            FROM pg_catalog.pg_class c
589+            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
590+            WHERE c.relkind IN ('r', 'v', '')
591+                AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
592+                AND (pg_catalog.pg_table_is_visible(c.oid)"""
593+        # We must add the default schema to always visible schemas to make
594+        # things work nicely.
595+        if self.connection.schema:
596+            sql += " OR n.nspname = %s)"
597+            cursor.execute(sql, (self.connection.schema,))
598+        else:
599+            cursor.execute(sql + ')')
600+        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
601+   
602+    def get_qualified_tables_list(self, cursor, schemas):
603+        """
604+        Returns schema qualified names of all tables in the current database.
605+        """
606+        if not schemas:
607+            return []
608         cursor.execute("""
609-            SELECT c.relname
610+            SELECT n.nspname, c.relname
611             FROM pg_catalog.pg_class c
612             LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
613             WHERE c.relkind IN ('r', 'v', '')
614-                AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
615-                AND pg_catalog.pg_table_is_visible(c.oid)""")
616-        return [row[0] for row in cursor.fetchall()]
617+                AND n.nspname IN %s""", (tuple(schemas),))
618+        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
619 
620-    def get_table_description(self, cursor, table_name):
621+    def get_table_description(self, cursor, qname):
622         "Returns a description of the table, with the DB-API cursor.description interface."
623         # As cursor.description does not return reliably the nullable property,
624         # we have to query the information_schema (#7783)
625-        cursor.execute("""
626-            SELECT column_name, is_nullable
627-            FROM information_schema.columns
628-            WHERE table_name = %s""", [table_name])
629+        qname = self.qname_converter(qname)
630+        if not qname.schema:
631+            cursor.execute("""
632+                SELECT column_name, is_nullable
633+                FROM information_schema.columns
634+                WHERE table_name = %s""",
635+                [qname.table])
636+        else:
637+            cursor.execute("""
638+                SELECT column_name, is_nullable
639+                FROM information_schema.columns
640+                WHERE table_schema = %s and table_name = %s""",
641+                [qname.schema, qname.table])
642         null_map = dict(cursor.fetchall())
643-        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
644+        cursor.execute(
645+            "SELECT * FROM %s LIMIT 1" % self.connection.ops.qualified_name(qname))
646         return [tuple([item for item in line[:6]] + [null_map[line[0]]==u'YES'])
647-            for line in cursor.description]
648+                for line in cursor.description]
649 
650-    def get_relations(self, cursor, table_name):
651+    def get_relations(self, cursor, qname):
652         """
653         Returns a dictionary of {field_index: (field_index_other_table, other_table)}
654-        representing all relationships to the given table. Indexes are 0-based.
655+        representing all relationships to the given table. Indexes are 0-based. The
656+        other_table will be in qualified format.
657         """
658-        cursor.execute("""
659-            SELECT con.conkey, con.confkey, c2.relname
660-            FROM pg_constraint con, pg_class c1, pg_class c2
661-            WHERE c1.oid = con.conrelid
662-                AND c2.oid = con.confrelid
663-                AND c1.relname = %s
664-                AND con.contype = 'f'""", [table_name])
665+        qname = self.qname_converter(qname)
666+        if not qname.schema:
667+            cursor.execute("""
668+                SELECT con.conkey, con.confkey, nsp2.nspname, c2.relname
669+                FROM pg_constraint con, pg_class c1, pg_class c2,
670+                     pg_namespace nsp2
671+                WHERE c1.oid = con.conrelid
672+                    AND c2.oid = con.confrelid
673+                    AND nsp2.oid = c2.relnamespace
674+                    AND c1.relname = %s
675+                    AND con.contype = 'f'""",
676+                [qname.table])
677+        else:
678+            cursor.execute("""
679+                SELECT con.conkey, con.confkey, nsp2.nspname, c2.relname
680+                FROM pg_constraint con, pg_class c1, pg_class c2,
681+                     pg_namespace nsp1, pg_namespace nsp2
682+                WHERE c1.oid = con.conrelid
683+                    AND nsp1.oid = c1.relnamespace
684+                    AND c2.oid = con.confrelid
685+                    AND nsp2.oid = c2.relnamespace
686+                    AND nsp1.nspname = %s
687+                    AND c1.relname = %s
688+                    AND con.contype = 'f'""",
689+                [qname.schema, qname.table])
690         relations = {}
691         for row in cursor.fetchall():
692             # row[0] and row[1] are single-item lists, so grab the single item.
693-            relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
694+            relations[row[0][0] - 1] = (row[1][0] - 1,
695+                                        QName(row[2], row[3], True))
696         return relations
697 
698-    def get_indexes(self, cursor, table_name):
699+    def get_indexes(self, cursor, qname):
700         """
701         Returns a dictionary of fieldname -> infodict for the given table,
702         where each infodict is in the format:
703@@ -73,15 +135,31 @@
704         """
705         # This query retrieves each index on the given table, including the
706         # first associated field name
707-        cursor.execute("""
708-            SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
709-            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
710-                pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
711-            WHERE c.oid = idx.indrelid
712-                AND idx.indexrelid = c2.oid
713-                AND attr.attrelid = c.oid
714-                AND attr.attnum = idx.indkey[0]
715-                AND c.relname = %s""", [table_name])
716+        qname = self.qname_converter(qname)
717+        if not qname.schema:
718+            cursor.execute("""
719+                SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
720+                FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
721+                    pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
722+                WHERE c.oid = idx.indrelid
723+                    AND idx.indexrelid = c2.oid
724+                    AND attr.attrelid = c.oid
725+                    AND attr.attnum = idx.indkey[0]
726+                    AND c.relname = %s""",
727+                [qname.table])
728+        else:
729+            cursor.execute("""
730+                SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
731+                FROM pg_catalog.pg_class c, pg_catalog.pg_namespace nsp,
732+                    pg_catalog.pg_class c2, pg_catalog.pg_index idx,
733+                    pg_catalog.pg_attribute attr
734+                WHERE c.oid = idx.indrelid
735+                    AND nsp.oid = c.relnamespace
736+                    AND idx.indexrelid = c2.oid
737+                    AND attr.attrelid = c.oid
738+                    AND attr.attnum = idx.indkey[0]
739+                    AND nsp.nspname = %s AND c.relname = %s""",
740+                [qname.schema, qname.table])
741         indexes = {}
742         for row in cursor.fetchall():
743             # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
744@@ -92,3 +170,15 @@
745                 continue
746             indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
747         return indexes
748+
749+    def qname_converter(self, qname, force_schema=False):
750+        """
751+        On postgresql it is impossible to force usage of schema -
752+        we do not know what the default schema is. In fact, there can be
753+        multiple schemas.
754+        """
755+        assert isinstance(qname, QName)
756+        if qname.db_format:
757+            return qname
758+        return QName((qname.schema or self.connection.schema), qname.table,
759+                     True)
760Index: django/db/backends/postgresql_psycopg2/operations.py
761===================================================================
762--- django/db/backends/postgresql_psycopg2/operations.py        (revision 17914)
763+++ django/db/backends/postgresql_psycopg2/operations.py        (working copy)
764@@ -1,3 +1,4 @@
765+from django.db import QName
766 from django.db.backends import BaseDatabaseOperations
767 
768 
769@@ -56,11 +57,11 @@
770             return 'HOST(%s)'
771         return '%s'
772 
773-    def last_insert_id(self, cursor, table_name, pk_name):
774+    def last_insert_id(self, cursor, qualified_name, pk_name):
775         # Use pg_get_serial_sequence to get the underlying sequence name
776         # from the table name and column name (available since PostgreSQL 8)
777         cursor.execute("SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (
778-            self.quote_name(table_name), pk_name))
779+            self.qualified_name(qualified_name), pk_name))
780         return cursor.fetchone()[0]
781 
782     def no_limit_value(self):
783@@ -71,6 +72,25 @@
784             return name # Quoting once is enough.
785         return '"%s"' % name
786 
787+    def qualified_name(self, qname):
788+        """
789+        Return the table's name in fully qualified format
790+        (schema_name.tbl_name) if there is a schema_name, else returns just
791+        the tbl_name in quoted format.
792+
793+        convert_name has no effect on PostgreSQL, as there is no need
794+        to do anything else than quoting for the name even in testing.
795+        """
796+        assert isinstance(qname, QName)
797+        schema = qname.schema
798+        if not qname.db_format and not schema:
799+            schema = self.connection.schema
800+        if schema:
801+            return "%s.%s" % (self.quote_name(schema),
802+                              self.quote_name(qname.table))
803+        else:
804+            return self.quote_name(qname.table)
805+
806     def set_time_zone_sql(self):
807         return "SET TIME ZONE %s"
808 
809@@ -81,13 +101,15 @@
810             # table.
811             sql = ['%s %s;' % \
812                 (style.SQL_KEYWORD('TRUNCATE'),
813-                    style.SQL_FIELD(', '.join([self.quote_name(table) for table in tables]))
814+                    style.SQL_FIELD(', '.join([self.qualified_name(table)
815+                                               for table in tables]))
816             )]
817 
818             # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements
819             # to reset sequence indices
820             for sequence_info in sequences:
821-                table_name = sequence_info['table']
822+                qname = sequence_info['qname']
823+                qualified_name = self.qualified_name(qname)
824                 column_name = sequence_info['column']
825                 if not (column_name and len(column_name) > 0):
826                     # This will be the case if it's an m2m using an autogenerated
827@@ -95,7 +117,7 @@
828                     column_name = 'id'
829                 sql.append("%s setval(pg_get_serial_sequence('%s','%s'), 1, false);" % \
830                     (style.SQL_KEYWORD('SELECT'),
831-                    style.SQL_TABLE(self.quote_name(table_name)),
832+                    style.SQL_TABLE(qualified_name),
833                     style.SQL_FIELD(column_name))
834                 )
835             return sql
836@@ -121,27 +143,29 @@
837 
838             for f in model._meta.local_fields:
839                 if isinstance(f, models.AutoField):
840+                    qualified_name = self.qualified_name(model._meta.qualified_name)
841                     output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
842                         (style.SQL_KEYWORD('SELECT'),
843-                        style.SQL_TABLE(qn(model._meta.db_table)),
844+                        style.SQL_TABLE(qualified_name),
845                         style.SQL_FIELD(f.column),
846                         style.SQL_FIELD(qn(f.column)),
847                         style.SQL_FIELD(qn(f.column)),
848                         style.SQL_KEYWORD('IS NOT'),
849                         style.SQL_KEYWORD('FROM'),
850-                        style.SQL_TABLE(qn(model._meta.db_table))))
851+                        style.SQL_TABLE(qualified_name)))
852                     break # Only one AutoField is allowed per model, so don't bother continuing.
853             for f in model._meta.many_to_many:
854                 if not f.rel.through:
855+                    qualified_name = self.qualified_name(f.m2m_qualified_name())
856                     output.append("%s setval(pg_get_serial_sequence('%s','%s'), coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
857                         (style.SQL_KEYWORD('SELECT'),
858-                        style.SQL_TABLE(qn(f.m2m_db_table())),
859+                        style.SQL_TABLE(qualified_name),
860                         style.SQL_FIELD('id'),
861                         style.SQL_FIELD(qn('id')),
862                         style.SQL_FIELD(qn('id')),
863                         style.SQL_KEYWORD('IS NOT'),
864                         style.SQL_KEYWORD('FROM'),
865-                        style.SQL_TABLE(qn(f.m2m_db_table()))))
866+                        style.SQL_TABLE(qualified_name)))
867         return output
868 
869     def savepoint_create_sql(self, sid):
870Index: django/db/backends/postgresql_psycopg2/creation.py
871===================================================================
872--- django/db/backends/postgresql_psycopg2/creation.py  (revision 17914)
873+++ django/db/backends/postgresql_psycopg2/creation.py  (working copy)
874@@ -43,7 +43,9 @@
875     def sql_indexes_for_field(self, model, f, style):
876         if f.db_index and not f.unique:
877             qn = self.connection.ops.quote_name
878-            db_table = model._meta.db_table
879+            qn3 = self.connection.ops.qualified_name
880+            qualified_name = model._meta.qualified_name
881+            db_table = qualified_name[1]
882             tablespace = f.db_tablespace or model._meta.db_tablespace
883             if tablespace:
884                 tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
885@@ -56,7 +58,7 @@
886                 return (style.SQL_KEYWORD('CREATE INDEX') + ' ' +
887                         style.SQL_TABLE(qn(truncate_name(index_name,self.connection.ops.max_name_length()))) + ' ' +
888                         style.SQL_KEYWORD('ON') + ' ' +
889-                        style.SQL_TABLE(qn(db_table)) + ' ' +
890+                        style.SQL_TABLE(qn3(qualified_name)) + ' ' +
891                         "(%s%s)" % (style.SQL_FIELD(qn(f.column)), opclass) +
892                         "%s;" % tablespace_sql)
893 
894@@ -85,3 +87,7 @@
895         self.connection.connection.rollback()
896         self.connection.connection.set_isolation_level(
897                 psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
898+
899+    def sql_destroy_schema(self, schema, style):
900+        qn = self.connection.ops.quote_name
901+        return "%s %s CASCADE;" % (style.SQL_KEYWORD('DROP SCHEMA'), qn(schema))
902Index: django/db/backends/postgresql_psycopg2/base.py
903===================================================================
904--- django/db/backends/postgresql_psycopg2/base.py      (revision 17914)
905+++ django/db/backends/postgresql_psycopg2/base.py      (working copy)
906@@ -83,6 +83,7 @@
907     has_bulk_insert = True
908     supports_tablespaces = True
909     can_distinct_on_fields = True
910+    namespaced_schemas = True
911 
912 class DatabaseWrapper(BaseDatabaseWrapper):
913     vendor = 'postgresql'
914@@ -121,6 +122,10 @@
915         self.validation = BaseDatabaseValidation(self)
916         self._pg_version = None
917 
918+    def _get_test_schema_prefix(self):
919+        return ''
920+    test_schema_prefix = property(_get_test_schema_prefix)
921+
922     def check_constraints(self, table_names=None):
923         """
924         To check constraints, we set constraints to immediate. Then, when, we're done we must ensure they
925Index: django/db/backends/creation.py
926===================================================================
927--- django/db/backends/creation.py      (revision 17914)
928+++ django/db/backends/creation.py      (working copy)
929@@ -2,7 +2,9 @@
930 import time
931 
932 from django.conf import settings
933+from django.db import QName
934 from django.db.utils import load_backend
935+from django.core.management.color import no_style
936 
937 # The prefix to put on the default database name when creating
938 # the test database.
939@@ -28,6 +30,14 @@
940         """
941         return '%x' % (abs(hash(args)) % 4294967296L)  # 2**32
942 
943+    def sql_create_schema(self, schema, style):
944+        """
945+        Returns the SQL required to create a single schema
946+        """
947+        qn = self.connection.ops.quote_name
948+        output = "%s %s;" % (style.SQL_KEYWORD('CREATE SCHEMA'), qn(schema))
949+        return output
950+
951     def sql_create_model(self, model, style, known_models=set()):
952         """
953         Returns the SQL required to create a single model, as a tuple of:
954@@ -40,6 +50,7 @@
955         table_output = []
956         pending_references = {}
957         qn = self.connection.ops.quote_name
958+        qn3 = self.connection.ops.qualified_name
959         for f in opts.local_fields:
960             col_type = f.db_type(connection=self.connection)
961             tablespace = f.db_tablespace or opts.db_tablespace
962@@ -79,7 +90,7 @@
963                      for f in field_constraints]))
964 
965         full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' +
966-                          style.SQL_TABLE(qn(opts.db_table)) + ' (']
967+                          style.SQL_TABLE(qn3(opts.qualified_name)) + ' (']
968         for i, line in enumerate(table_output): # Combine and add commas.
969             full_statement.append(
970                 '    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
971@@ -96,7 +107,7 @@
972             # Add any extra SQL needed to support auto-incrementing primary
973             # keys.
974             auto_column = opts.auto_field.db_column or opts.auto_field.name
975-            autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table,
976+            autoinc_sql = self.connection.ops.autoinc_sql(opts.qualified_name,
977                                                           auto_column)
978             if autoinc_sql:
979                 for stmt in autoinc_sql:
980@@ -109,9 +120,12 @@
981         Return the SQL snippet defining the foreign key reference for a field.
982         """
983         qn = self.connection.ops.quote_name
984+        from_qname = field.model._meta.qualified_name
985+        to_qname = field.rel.to._meta.qualified_name
986+        qname = self.qualified_name_for_ref(from_qname, to_qname)
987         if field.rel.to in known_models:
988             output = [style.SQL_KEYWORD('REFERENCES') + ' ' +
989-                style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' +
990+                style.SQL_TABLE(qname) + ' (' +
991                 style.SQL_FIELD(qn(field.rel.to._meta.get_field(
992                     field.rel.field_name).column)) + ')' +
993                 self.connection.ops.deferrable_sql()
994@@ -132,16 +146,24 @@
995         from django.db.backends.util import truncate_name
996 
997         if not model._meta.managed or model._meta.proxy:
998+            # So, we have a reference to either unmanaged model or to
999+            # a proxy model. Lets just clear the pending_references
1000+            # for now.
1001+            if model in pending_references:
1002+                del pending_references[model]
1003             return []
1004         qn = self.connection.ops.quote_name
1005+        qn3 = self.connection.ops.qualified_name
1006         final_output = []
1007         opts = model._meta
1008         if model in pending_references:
1009             for rel_class, f in pending_references[model]:
1010                 rel_opts = rel_class._meta
1011                 r_table = rel_opts.db_table
1012+                r_qname = rel_opts.qualified_name
1013                 r_col = f.column
1014                 table = opts.db_table
1015+                qname = self.qualified_name_for_ref(r_qname, opts.qualified_name)
1016                 col = opts.get_field(f.rel.field_name).column
1017                 # For MySQL, r_name must be unique in the first 64 characters.
1018                 # So we are careful with character usage here.
1019@@ -149,13 +171,23 @@
1020                     r_col, col, self._digest(r_table, table))
1021                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') +
1022                     ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
1023-                    (qn(r_table), qn(truncate_name(
1024+                    (qn3(r_qname), qn(truncate_name(
1025                         r_name, self.connection.ops.max_name_length())),
1026-                    qn(r_col), qn(table), qn(col),
1027+                    qn(r_col), qname, qn(col),
1028                     self.connection.ops.deferrable_sql()))
1029             del pending_references[model]
1030         return final_output
1031 
1032+    def qualified_name_for_ref(self, from_table, ref_table):
1033+        """
1034+        In certain databases if the from_table is in qualified format and
1035+        ref_table is not, it is assumed the ref_table references a table
1036+        in the same schema as from_table is from. However, we want the
1037+        reference to be to default schema, not the same schema the from_table
1038+        is. This method will fix this issue where that is a problem.
1039+        """
1040+        return self.connection.ops.qualified_name(ref_table)
1041+
1042     def sql_indexes_for_model(self, model, style):
1043         """
1044         Returns the CREATE INDEX SQL statements for a single model.
1045@@ -171,10 +203,9 @@
1046         """
1047         Return the CREATE INDEX SQL statements for a single model field.
1048         """
1049-        from django.db.backends.util import truncate_name
1050-
1051         if f.db_index and not f.unique:
1052             qn = self.connection.ops.quote_name
1053+            qn3 = self.connection.ops.qualified_name
1054             tablespace = f.db_tablespace or model._meta.db_tablespace
1055             if tablespace:
1056                 tablespace_sql = self.connection.ops.tablespace_sql(tablespace)
1057@@ -182,18 +213,36 @@
1058                     tablespace_sql = ' ' + tablespace_sql
1059             else:
1060                 tablespace_sql = ''
1061-            i_name = '%s_%s' % (model._meta.db_table, self._digest(f.column))
1062+            qualified_name = self.qualified_index_name(model, f.column)
1063             output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
1064-                style.SQL_TABLE(qn(truncate_name(
1065-                    i_name, self.connection.ops.max_name_length()))) + ' ' +
1066+                style.SQL_TABLE(qualified_name) + ' ' +
1067                 style.SQL_KEYWORD('ON') + ' ' +
1068-                style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
1069+                style.SQL_TABLE(qn3(model._meta.qualified_name)) + ' ' +
1070                 "(%s)" % style.SQL_FIELD(qn(f.column)) +
1071                 "%s;" % tablespace_sql]
1072         else:
1073             output = []
1074         return output
1075 
1076+    def qualified_index_name(self, model, col):
1077+        """
1078+        Some databases do support schemas, but indexes can not be placed in a
1079+        different schema. So, to support those databases, we need to be able
1080+        to return the index name in different qualified format than the rest
1081+        of the database identifiers.
1082+        """
1083+        from django.db.backends.util import truncate_name
1084+        i_name = '%s_%s' % (model._meta.db_table, self._digest(col))
1085+        i_name = truncate_name(i_name, self.connection.ops.max_name_length())
1086+        return self.connection.ops.qualified_name(
1087+            QName(model._meta.db_schema, i_name, False))
1088+
1089+    def sql_destroy_schema(self, schema, style):
1090+        """
1091+        Returns the SQL required to destroy a single schema.
1092+        """
1093+        return ""
1094+
1095     def sql_destroy_model(self, model, references_to_delete, style):
1096         """
1097         Return the DROP TABLE and restraint dropping statements for a single
1098@@ -202,14 +251,14 @@
1099         if not model._meta.managed or model._meta.proxy:
1100             return []
1101         # Drop the table now
1102-        qn = self.connection.ops.quote_name
1103+        qn3 = self.connection.ops.qualified_name
1104         output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
1105-                              style.SQL_TABLE(qn(model._meta.db_table)))]
1106+                              style.SQL_TABLE(qn3(model._meta.qualified_name)))]
1107         if model in references_to_delete:
1108             output.extend(self.sql_remove_table_constraints(
1109                 model, references_to_delete, style))
1110         if model._meta.has_auto_field:
1111-            ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
1112+            ds = self.connection.ops.drop_sequence_sql(model._meta.qualified_name)
1113             if ds:
1114                 output.append(ds)
1115         return output
1116@@ -220,8 +269,10 @@
1117             return []
1118         output = []
1119         qn = self.connection.ops.quote_name
1120+        qn3 = self.connection.ops.qualified_name
1121         for rel_class, f in references_to_delete[model]:
1122             table = rel_class._meta.db_table
1123+            qname = rel_class._meta.qualified_name
1124             col = f.column
1125             r_table = model._meta.db_table
1126             r_col = model._meta.get_field(f.rel.field_name).column
1127@@ -229,7 +280,7 @@
1128                 col, r_col, self._digest(table, r_table))
1129             output.append('%s %s %s %s;' % \
1130                 (style.SQL_KEYWORD('ALTER TABLE'),
1131-                style.SQL_TABLE(qn(table)),
1132+                style.SQL_TABLE(qn3(qname)),
1133                 style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
1134                 style.SQL_FIELD(qn(truncate_name(
1135                     r_name, self.connection.ops.max_name_length())))))
1136@@ -240,6 +291,10 @@
1137         """
1138         Creates a test database, prompting the user for confirmation if the
1139         database already exists. Returns the name of the test database created.
1140+
1141+        Also creates needed schemas, which on some backends live in the same
1142+        namespace than databases. If there are schema name clashes, prompts
1143+        the user for confirmation.
1144         """
1145         # Don't import django.core.management if it isn't needed.
1146         from django.core.management import call_command
1147@@ -253,10 +308,14 @@
1148             print "Creating test database for alias '%s'%s..." % (
1149                 self.connection.alias, test_db_repr)
1150 
1151-        self._create_test_db(verbosity, autoclobber)
1152+        schemas = self.get_schemas()
1153+        self._create_test_db(verbosity, autoclobber, schemas)
1154 
1155+        # Create the test schemas.
1156+        self.connection.settings_dict["NAME"] = test_database_name
1157         self.connection.close()
1158-        self.connection.settings_dict["NAME"] = test_database_name
1159+        schemas = ['%s%s' % (self.connection.test_schema_prefix, s) for s in schemas]
1160+        created_schemas = self._create_test_schemas(verbosity, schemas, autoclobber)
1161 
1162         # Confirm the feature set of the test database
1163         self.connection.features.confirm()
1164@@ -292,8 +351,61 @@
1165         # the side effect of initializing the test database.
1166         self.connection.cursor()
1167 
1168-        return test_database_name
1169+        return test_database_name, created_schemas
1170 
1171+    def _create_test_schemas(self, verbosity, schemas, autoclobber):
1172+        style = no_style()
1173+        cursor = self.connection.cursor()
1174+        existing_schemas = self.connection.introspection.get_schema_list(cursor)
1175+        if not self.connection.features.namespaced_schemas:
1176+            conflicts = [s for s in existing_schemas if s in schemas]
1177+        else:
1178+            conflicts = []
1179+        if conflicts:
1180+            print 'The following schemas already exists: %s' % ', '.join(conflicts)
1181+            if not autoclobber:
1182+                confirm = raw_input(
1183+                    "Type 'yes' if you would like to try deleting these schemas "
1184+                    "or 'no' to cancel: ")
1185+            if autoclobber or confirm == 'yes':
1186+                try:
1187+                    # Some databases (well, MySQL) complain about foreign keys when
1188+                    # dropping a database. So, disable the constraints temporarily.
1189+                    self.connection.disable_constraint_checking()
1190+                    for schema in conflicts:
1191+                        if verbosity >= 1:
1192+                            print "Destroying schema %s" % schema
1193+                        cursor.execute(self.sql_destroy_schema(schema, style))
1194+                        existing_schemas.remove(schema)
1195+                finally:
1196+                    self.connection.enable_constraint_checking()
1197+            else:
1198+                print "Tests cancelled."
1199+                sys.exit(1)
1200+           
1201+        to_create = [s for s in schemas if s not in existing_schemas]
1202+        for schema in to_create:
1203+            if verbosity >= 1:
1204+                print "Creating schema %s" % schema
1205+            cursor.execute(self.sql_create_schema(schema, style))
1206+            self.connection.settings_dict['TEST_SCHEMAS'].append(schema)
1207+        return to_create
1208+
1209+    def get_schemas(self):
1210+        from django.db import models
1211+        apps = models.get_apps()
1212+        schemas = set()
1213+        for app in apps:
1214+            app_models = models.get_models(app, include_auto_created=True)
1215+            for model in app_models:
1216+                schema = model._meta.db_schema
1217+                if schema:
1218+                    schemas.add(schema)
1219+        conn_default_schema = self.connection.settings_dict['SCHEMA']
1220+        if conn_default_schema:
1221+            schemas.add(conn_default_schema)
1222+        return schemas
1223+
1224     def _get_test_db_name(self):
1225         """
1226         Internal implementation - returns the name of the test DB that will be
1227@@ -305,7 +417,7 @@
1228             return self.connection.settings_dict['TEST_NAME']
1229         return TEST_DATABASE_PREFIX + self.connection.settings_dict['NAME']
1230 
1231-    def _create_test_db(self, verbosity, autoclobber):
1232+    def _create_test_db(self, verbosity, autoclobber, schemas):
1233         """
1234         Internal implementation - creates the test db tables.
1235         """
1236@@ -335,9 +447,19 @@
1237                     if verbosity >= 1:
1238                         print ("Destroying old test database '%s'..."
1239                                % self.connection.alias)
1240+                    # MySQL nicely doesn't have a drop-cascade option, nor
1241+                    # does it allow dropping a database having foreign key
1242+                    # references pointing to it. So, we just disable foreign
1243+                    # key checks and then immediately enable them. MySQL is
1244+                    # happy after this hack, and other databases simply do
1245+                    # not care.
1246+                    try:
1247+                        self.connection.disable_constraint_checking()
1248+                        cursor.execute(
1249+                            "DROP DATABASE %s" % qn(test_database_name))
1250+                    finally:
1251+                        self.connection.enable_constraint_checking()
1252                     cursor.execute(
1253-                        "DROP DATABASE %s" % qn(test_database_name))
1254-                    cursor.execute(
1255                         "CREATE DATABASE %s %s" % (qn(test_database_name),
1256                                                    suffix))
1257                 except Exception, e:
1258@@ -348,15 +470,32 @@
1259                 print "Tests cancelled."
1260                 sys.exit(1)
1261 
1262+        self.connection.settings_dict['TEST_SCHEMAS'].append(test_database_name)
1263         return test_database_name
1264 
1265-    def destroy_test_db(self, old_database_name, verbosity=1):
1266+    def destroy_test_db(self, old_database_name, created_schemas, verbosity=1):
1267         """
1268         Destroy a test database, prompting the user for confirmation if the
1269         database already exists.
1270         """
1271+        # On databases where there is no support for multiple databases
1272+        # with multiple schemas we need to destroy the created schemas
1273+        # manually.
1274+        cursor = self.connection.cursor()
1275+        style = no_style()
1276+        if not self.connection.features.namespaced_schemas:
1277+            try:
1278+                self.connection.disable_constraint_checking()
1279+                for schema in created_schemas:
1280+                    if verbosity >= 1:
1281+                        print "Destroying schema '%s'..." % schema
1282+                    cursor.execute(self.sql_destroy_schema(schema, style))
1283+            finally:
1284+                self.connection.enable_constraint_checking()
1285         self.connection.close()
1286         test_database_name = self.connection.settings_dict['NAME']
1287+                   
1288+
1289         if verbosity >= 1:
1290             test_db_repr = ''
1291             if verbosity >= 2:
1292@@ -429,3 +568,11 @@
1293             settings_dict['ENGINE'],
1294             settings_dict['NAME']
1295         )
1296+
1297+    def post_create_pending_references(self, pending_references, as_sql=False):
1298+        """
1299+        Create any pending references which need special handling (for example
1300+        different connections). The as_sql flag tells us if we should return
1301+        the raw SQL used. This is needed for the "sql" management commands.
1302+        """
1303+        raise NotImplementedError
1304Index: django/db/backends/sqlite3/base.py
1305===================================================================
1306--- django/db/backends/sqlite3/base.py  (revision 17914)
1307+++ django/db/backends/sqlite3/base.py  (working copy)
1308@@ -11,7 +11,7 @@
1309 import re
1310 import sys
1311 
1312-from django.db import utils
1313+from django.db import utils, QName
1314 from django.db.backends import *
1315 from django.db.backends.signals import connection_created
1316 from django.db.backends.sqlite3.client import DatabaseClient
1317@@ -84,6 +84,11 @@
1318     supports_mixed_date_datetime_comparisons = False
1319     has_bulk_insert = True
1320     can_combine_inserts_with_and_without_auto_increment_pk = True
1321+    supports_foreign_keys = False
1322+    # SQLite doesn't support schemas at all, but our hack of appending
1323+    # the schema name to table name creates namespaced schemas from
1324+    # Django's perspective
1325+    namespaced_schemas = True
1326 
1327     def _supports_stddev(self):
1328         """Confirm support for STDDEV and related stats functions
1329@@ -139,6 +144,22 @@
1330             return name # Quoting once is enough.
1331         return '"%s"' % name
1332 
1333+    def qualified_name(self, qname):
1334+        # Fake schema support by using the schema as a prefix to the
1335+        # table name. Keep record of what names are already qualified
1336+        # to avoid double-qualifying.
1337+        assert isinstance(qname, QName)
1338+        if qname.db_format:
1339+            # A name from DB must not have a schema (no schema support)
1340+            assert not qname.schema
1341+            schema = None
1342+        else:
1343+            schema = qname.schema or self.connection.schema
1344+        if schema:
1345+            return self.quote_name('%s_%s' % (schema, qname.table))
1346+        else:
1347+            return self.quote_name(qname.table)
1348+
1349     def no_limit_value(self):
1350         return -1
1351 
1352@@ -146,11 +167,13 @@
1353         # NB: The generated SQL below is specific to SQLite
1354         # Note: The DELETE FROM... SQL generated below works for SQLite databases
1355         # because constraints don't exist
1356-        sql = ['%s %s %s;' % \
1357+        sql = []
1358+        for table in tables:
1359+            sql.append('%s %s %s;' % \
1360                 (style.SQL_KEYWORD('DELETE'),
1361                  style.SQL_KEYWORD('FROM'),
1362-                 style.SQL_FIELD(self.quote_name(table))
1363-                 ) for table in tables]
1364+                 style.SQL_FIELD(self.qualified_name(table))
1365+                 ))
1366         # Note: No requirement for reset of auto-incremented indices (cf. other
1367         # sql_flush() implementations). Just return SQL at this point
1368         return sql
1369@@ -243,6 +266,10 @@
1370         self.introspection = DatabaseIntrospection(self)
1371         self.validation = BaseDatabaseValidation(self)
1372 
1373+    def convert_schema(self, schema):
1374+        # No real schema support.
1375+        return None
1376+
1377     def _sqlite_create_connection(self):
1378         settings_dict = self.settings_dict
1379         if not settings_dict['NAME']:
1380@@ -295,7 +322,9 @@
1381         """
1382         cursor = self.cursor()
1383         if table_names is None:
1384-            table_names = self.introspection.get_table_list(cursor)
1385+            table_names = self.introspection.get_visible_tables_list(cursor)
1386+        else:
1387+            table_names = [self.introspection.qname_converter(t) for t in table_names]
1388         for table_name in table_names:
1389             primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
1390             if not primary_key_column_name:
1391@@ -307,12 +336,12 @@
1392                     LEFT JOIN `%s` as REFERRED
1393                     ON (REFERRING.`%s` = REFERRED.`%s`)
1394                     WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL"""
1395-                    % (primary_key_column_name, column_name, table_name, referenced_table_name,
1396+                    % (primary_key_column_name, column_name, table_name[1], referenced_table_name[1],
1397                     column_name, referenced_column_name, column_name, referenced_column_name))
1398                 for bad_row in cursor.fetchall():
1399                     raise utils.IntegrityError("The row in table '%s' with primary key '%s' has an invalid "
1400                         "foreign key: %s.%s contains a value '%s' that does not have a corresponding value in %s.%s."
1401-                        % (table_name, bad_row[0], table_name, column_name, bad_row[1],
1402+                        % (table_name, bad_row[0], table_name[1], column_name, bad_row[1],
1403                         referenced_table_name, referenced_column_name))
1404 
1405     def close(self):
1406Index: django/db/backends/sqlite3/introspection.py
1407===================================================================
1408--- django/db/backends/sqlite3/introspection.py (revision 17914)
1409+++ django/db/backends/sqlite3/introspection.py (working copy)
1410@@ -1,4 +1,5 @@
1411 import re
1412+from django.db import QName
1413 from django.db.backends import BaseDatabaseIntrospection
1414 
1415 # This light wrapper "fakes" a dictionary interface, because some SQLite data
1416@@ -41,26 +42,27 @@
1417 class DatabaseIntrospection(BaseDatabaseIntrospection):
1418     data_types_reverse = FlexibleFieldLookupDict()
1419 
1420-    def get_table_list(self, cursor):
1421-        "Returns a list of table names in the current database."
1422+    def get_visible_tables_list(self, cursor):
1423+        "Returns a list of table names in the current database"
1424         # Skip the sqlite_sequence system table used for autoincrement key
1425         # generation.
1426         cursor.execute("""
1427             SELECT name FROM sqlite_master
1428             WHERE type='table' AND NOT name='sqlite_sequence'
1429             ORDER BY name""")
1430-        return [row[0] for row in cursor.fetchall()]
1431+        return [QName(None, row[0], True) for row in cursor.fetchall()]
1432 
1433-    def get_table_description(self, cursor, table_name):
1434+    def get_table_description(self, cursor, qualified_name):
1435         "Returns a description of the table, with the DB-API cursor.description interface."
1436         return [(info['name'], info['type'], None, None, None, None,
1437-                 info['null_ok']) for info in self._table_info(cursor, table_name)]
1438+                 info['null_ok']) for info in self._table_info(cursor, qualified_name[1])]
1439 
1440-    def get_relations(self, cursor, table_name):
1441+    def get_relations(self, cursor, qualified_name):
1442         """
1443         Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1444         representing all relationships to the given table. Indexes are 0-based.
1445         """
1446+        table_name = qualified_name[1]
1447 
1448         # Dictionary of relations to return
1449         relations = {}
1450@@ -98,16 +100,18 @@
1451 
1452                 name = other_desc.split(' ', 1)[0].strip('"')
1453                 if name == column:
1454-                    relations[field_index] = (other_index, table)
1455+                    relations[field_index] = (other_index,
1456+                                              QName(None, table, True))
1457                     break
1458 
1459         return relations
1460 
1461-    def get_key_columns(self, cursor, table_name):
1462+    def get_key_columns(self, cursor, qname):
1463         """
1464         Returns a list of (column_name, referenced_table_name, referenced_column_name) for all
1465         key columns in given table.
1466         """
1467+        table_name = self.qname_converter(qname)[1]
1468         key_columns = []
1469 
1470         # Schema for this table
1471@@ -128,17 +132,20 @@
1472                 continue
1473 
1474             # This will append (column_name, referenced_table_name, referenced_column_name) to key_columns
1475-            key_columns.append(tuple([s.strip('"') for s in m.groups()]))
1476+            add = tuple([s.strip('"') for s in m.groups()])
1477+            add = add[0], (None, add[1], True), add[2]
1478+            key_columns.append(add)
1479 
1480         return key_columns
1481 
1482-    def get_indexes(self, cursor, table_name):
1483+    def get_indexes(self, cursor, qualified_name):
1484         """
1485         Returns a dictionary of fieldname -> infodict for the given table,
1486         where each infodict is in the format:
1487             {'primary_key': boolean representing whether it's the primary key,
1488              'unique': boolean representing whether it's a unique index}
1489         """
1490+        table_name = qualified_name[1]
1491         indexes = {}
1492         for info in self._table_info(cursor, table_name):
1493             indexes[info['name']] = {'primary_key': info['pk'] != 0,
1494@@ -157,10 +164,12 @@
1495             indexes[name]['unique'] = True
1496         return indexes
1497 
1498-    def get_primary_key_column(self, cursor, table_name):
1499+    def get_primary_key_column(self, cursor, qname):
1500         """
1501         Get the column name of the primary key for the given table.
1502         """
1503+        qname = self.qname_converter(qname)
1504+        table_name = qname[1]
1505         # Don't use PRAGMA because that causes issues with some transactions
1506         cursor.execute("SELECT sql FROM sqlite_master WHERE tbl_name = %s AND type = %s", [table_name, "table"])
1507         results = cursor.fetchone()[0].strip()
1508@@ -180,3 +189,14 @@
1509                  'null_ok': not field[3],
1510                  'pk': field[5]     # undocumented
1511                  } for field in cursor.fetchall()]
1512+
1513+    def identifier_converter(self, identifier):
1514+        return identifier
1515+
1516+    def qname_converter(self, qname, force_schema=False):
1517+        # For SQLite force_schema does nothing, as the default schema is
1518+        # None.
1519+        assert isinstance(qname, QName)
1520+        assert not (qname.schema and qname.db_format)
1521+        return QName(None, self.connection.ops.qualified_name(qname)[1:-1],
1522+                     True)
1523Index: django/db/backends/sqlite3/creation.py
1524===================================================================
1525--- django/db/backends/sqlite3/creation.py      (revision 17914)
1526+++ django/db/backends/sqlite3/creation.py      (working copy)
1527@@ -33,19 +33,25 @@
1528 
1529     def sql_for_pending_references(self, model, style, pending_references):
1530         "SQLite3 doesn't support constraints"
1531+        if model in pending_references:
1532+            del pending_references[model]
1533         return []
1534 
1535     def sql_remove_table_constraints(self, model, references_to_delete, style):
1536         "SQLite3 doesn't support constraints"
1537         return []
1538 
1539+    def sql_create_schema(self, schema, verbosity):
1540+        "SQLite3 doesn't support schemas"
1541+        return
1542+
1543     def _get_test_db_name(self):
1544         test_database_name = self.connection.settings_dict['TEST_NAME']
1545         if test_database_name and test_database_name != ':memory:':
1546             return test_database_name
1547         return ':memory:'
1548 
1549-    def _create_test_db(self, verbosity, autoclobber):
1550+    def _create_test_db(self, verbosity, autoclobber, schemas):
1551         test_database_name = self._get_test_db_name()
1552         if test_database_name != ':memory:':
1553             # Erase the old test database
1554@@ -65,6 +71,9 @@
1555                     sys.exit(1)
1556         return test_database_name
1557 
1558+    def _create_test_schemas(self, verbosity, schemas, cursor):
1559+        return []
1560+
1561     def _destroy_test_db(self, test_database_name, verbosity):
1562         if test_database_name and test_database_name != ":memory:":
1563             # Remove the SQLite database file
1564@@ -81,7 +90,6 @@
1565         SQLite since the databases will be distinct despite having the same
1566         TEST_NAME. See http://www.sqlite.org/inmemorydb.html
1567         """
1568-        settings_dict = self.connection.settings_dict
1569         test_dbname = self._get_test_db_name()
1570         sig = [self.connection.settings_dict['NAME']]
1571         if test_dbname == ':memory:':
1572Index: django/db/backends/mysql/introspection.py
1573===================================================================
1574--- django/db/backends/mysql/introspection.py   (revision 17914)
1575+++ django/db/backends/mysql/introspection.py   (working copy)
1576@@ -1,3 +1,4 @@
1577+from django.db import QName
1578 from django.db.backends import BaseDatabaseIntrospection
1579 from MySQLdb import ProgrammingError, OperationalError
1580 from MySQLdb.constants import FIELD_TYPE
1581@@ -28,30 +29,44 @@
1582         FIELD_TYPE.VAR_STRING: 'CharField',
1583     }
1584 
1585-    def get_table_list(self, cursor):
1586-        "Returns a list of table names in the current database."
1587-        cursor.execute("SHOW TABLES")
1588-        return [row[0] for row in cursor.fetchall()]
1589+    def get_visible_tables_list(self, cursor):
1590+        "Returns a list of visible tables"
1591+        return self.get_qualified_tables_list(cursor, [self.connection.settings_dict['NAME']])
1592 
1593-    def get_table_description(self, cursor, table_name):
1594+    def get_qualified_tables_list(self, cursor, schemas):
1595+        default_schema = self.connection.convert_schema(None)
1596+        if default_schema:
1597+            schemas.append(default_schema)
1598+        if not schemas:
1599+            return []
1600+        param_list = ', '.join(['%s']*len(schemas))
1601+        cursor.execute("""
1602+            SELECT table_schema, table_name
1603+              FROM information_schema.tables
1604+             WHERE table_schema in (%s)""" % param_list, schemas)
1605+        return [QName(row[0], row[1], True) for row in cursor.fetchall()]
1606+
1607+    def get_table_description(self, cursor, qname):
1608         "Returns a description of the table, with the DB-API cursor.description interface."
1609-        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
1610+        qname = self.qname_converter(qname)
1611+        cursor.execute("SELECT * FROM %s LIMIT 1"
1612+                       % self.connection.ops.qualified_name(qname))
1613         return cursor.description
1614 
1615-    def _name_to_index(self, cursor, table_name):
1616+    def _name_to_index(self, cursor, qname):
1617         """
1618         Returns a dictionary of {field_name: field_index} for the given table.
1619         Indexes are 0-based.
1620         """
1621-        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
1622+        return dict((d[0], i) for i, d in enumerate(self.get_table_description(cursor, qname)))
1623 
1624-    def get_relations(self, cursor, table_name):
1625+    def get_relations(self, cursor, qname):
1626         """
1627         Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1628         representing all relationships to the given table. Indexes are 0-based.
1629         """
1630-        my_field_dict = self._name_to_index(cursor, table_name)
1631-        constraints = self.get_key_columns(cursor, table_name)
1632+        my_field_dict = self._name_to_index(cursor, qname)
1633+        constraints = self.get_key_columns(cursor, qname)
1634         relations = {}
1635         for my_fieldname, other_table, other_field in constraints:
1636             other_field_index = self._name_to_index(cursor, other_table)[other_field]
1637@@ -59,25 +74,32 @@
1638             relations[my_field_index] = (other_field_index, other_table)
1639         return relations
1640 
1641-    def get_key_columns(self, cursor, table_name):
1642+    def get_key_columns(self, cursor, qname):
1643         """
1644-        Returns a list of (column_name, referenced_table_name, referenced_column_name) for all
1645-        key columns in given table.
1646+        Returns a list of
1647+            (column_name,
1648+            (reference_table_schema, referenced_table_name),
1649+            referenced_column_name)
1650+        for all key columns in given table.
1651         """
1652         key_columns = []
1653+        qname = self.qname_converter(qname, force_schema=True)
1654         try:
1655             cursor.execute("""
1656-                SELECT column_name, referenced_table_name, referenced_column_name
1657+                SELECT column_name, referenced_table_schema, referenced_table_name, referenced_column_name
1658                 FROM information_schema.key_column_usage
1659-                WHERE table_name = %s
1660-                    AND table_schema = DATABASE()
1661+                WHERE table_schema = %s
1662+                    AND table_name = %s
1663                     AND referenced_table_name IS NOT NULL
1664-                    AND referenced_column_name IS NOT NULL""", [table_name])
1665-            key_columns.extend(cursor.fetchall())
1666+                    AND referenced_column_name IS NOT NULL""",
1667+                           [qname.schema, qname.table])
1668+            for row in cursor.fetchall():
1669+                key_columns.append((row[0], QName(row[1], row[2], True),
1670+                                    row[3]))
1671         except (ProgrammingError, OperationalError):
1672             # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
1673             # Go through all constraints and save the equal matches.
1674-            cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.quote_name(table_name))
1675+            cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.qualified_name(qname))
1676             for row in cursor.fetchall():
1677                 pos = 0
1678                 while True:
1679@@ -85,28 +107,47 @@
1680                     if match == None:
1681                         break
1682                     pos = match.end()
1683-                    key_columns.append(match.groups())
1684+                    groups = match.groups()
1685+                    tblname = groups[1]
1686+                    if '.' in tblname:
1687+                        tblname = tblname.split('.')
1688+                    else:
1689+                        tblname = None, tblname
1690+                    key_columns.append((groups[0], tblname, groups[2]))
1691         return key_columns
1692 
1693-    def get_primary_key_column(self, cursor, table_name):
1694+    def get_primary_key_column(self, cursor, qname):
1695         """
1696         Returns the name of the primary key column for the given table
1697         """
1698-        for column in self.get_indexes(cursor, table_name).iteritems():
1699+        for column in self.get_indexes(cursor, qname).iteritems():
1700             if column[1]['primary_key']:
1701                 return column[0]
1702         return None
1703 
1704-    def get_indexes(self, cursor, table_name):
1705+    def get_indexes(self, cursor, qname):
1706         """
1707         Returns a dictionary of fieldname -> infodict for the given table,
1708         where each infodict is in the format:
1709             {'primary_key': boolean representing whether it's the primary key,
1710              'unique': boolean representing whether it's a unique index}
1711         """
1712-        cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
1713+        qname = self.qname_converter(qname)
1714+        cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.qualified_name(qname))
1715         indexes = {}
1716         for row in cursor.fetchall():
1717             indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
1718         return indexes
1719 
1720+    def get_schema_list(self, cursor):
1721+        cursor.execute("SHOW DATABASES")
1722+        return [r[0] for r in cursor.fetchall()]
1723+
1724+    def qname_converter(self, qname, force_schema=False):
1725+        assert isinstance(qname, QName)
1726+        if qname.db_format and (qname.schema or not force_schema):
1727+            return qname
1728+        schema = self.connection.convert_schema(qname.schema)
1729+        if not schema and force_schema:
1730+            schema = self.connection.settings_dict['NAME']
1731+        return QName(schema, qname.table, True)
1732Index: django/db/backends/mysql/creation.py
1733===================================================================
1734--- django/db/backends/mysql/creation.py        (revision 17914)
1735+++ django/db/backends/mysql/creation.py        (working copy)
1736@@ -1,3 +1,4 @@
1737+from django.db import QName
1738 from django.db.backends.creation import BaseDatabaseCreation
1739 
1740 class DatabaseCreation(BaseDatabaseCreation):
1741@@ -58,9 +59,41 @@
1742             style.SQL_KEYWORD('NOT NULL'))
1743         ]
1744         deferred = [
1745-            (field.m2m_db_table(), field.m2m_column_name(), opts.db_table,
1746+            (field.m2m_qualified_table(), field.m2m_column_name(), opts.qualified_name,
1747                 opts.pk.column),
1748-            (field.m2m_db_table(), field.m2m_reverse_name(),
1749-                field.rel.to._meta.db_table, field.rel.to._meta.pk.column)
1750+            (field.m2m_qualified_table(), field.m2m_reverse_name(),
1751+                field.rel.to._meta.qualified_name, field.rel.to._meta.pk.column)
1752             ]
1753         return table_output, deferred
1754+
1755+    def sql_destroy_schema(self, schema, style):
1756+        qn = self.connection.ops.quote_name
1757+        return "%s %s;" % (style.SQL_KEYWORD('DROP DATABASE'), qn(schema))
1758+   
1759+    def qualified_index_name(self, model, col):
1760+        """
1761+        On MySQL we must use the db_schema prefixed to the index name as
1762+        indexes can not be placed into different schemas.
1763+        """
1764+        from django.db.backends.util import truncate_name
1765+        schema = model._meta.db_schema or self.connection.schema
1766+        max_len = self.connection.ops.max_name_length()
1767+        schema_prefix = ''
1768+        if schema:
1769+             schema = self.connection.convert_schema(schema)
1770+             schema_prefix = truncate_name(schema, max_len / 2) + '_'
1771+        i_name = '%s%s_%s' % (schema_prefix, model._meta.db_table, self._digest(col))
1772+        i_name = self.connection.ops.quote_name(truncate_name(i_name, max_len))
1773+        return i_name
1774+
1775+    def qualified_name_for_ref(self, from_table, ref_table):
1776+        """
1777+        MySQL does not have qualified name format for indexes, so make sure to
1778+        use qualified names if needed.
1779+        """
1780+        from_qn = self.connection.introspection.qname_converter(from_table)
1781+        to_qn = self.connection.introspection.qname_converter(ref_table)
1782+        if to_qn.schema is None:
1783+            to_qn = QName(self.connection.settings_dict['NAME'],
1784+                          to_qn.table, to_qn.db_format)
1785+        return super(DatabaseCreation, self).qualified_name_for_ref(from_qn, to_qn)
1786Index: django/db/backends/mysql/base.py
1787===================================================================
1788--- django/db/backends/mysql/base.py    (revision 17914)
1789+++ django/db/backends/mysql/base.py    (working copy)
1790@@ -15,6 +15,8 @@
1791     from django.core.exceptions import ImproperlyConfigured
1792     raise ImproperlyConfigured("Error loading MySQLdb module: %s" % e)
1793 
1794+from django.db.backends.util import truncate_name
1795+
1796 # We want version (1, 2, 1, 'final', 2) or later. We can't just use
1797 # lexicographic ordering in this check because then (1, 2, 1, 'gamma')
1798 # inadvertently passes the version test.
1799@@ -187,6 +189,10 @@
1800         "Confirm support for introspected foreign keys"
1801         return self._mysql_storage_engine() != 'MyISAM'
1802 
1803+    def confirm(self):
1804+        super(DatabaseFeatures, self).confirm()
1805+        self.supports_foreign_keys != self.can_introspect_foreign_keys
1806+
1807 class DatabaseOperations(BaseDatabaseOperations):
1808     compiler_module = "django.db.backends.mysql.compiler"
1809 
1810@@ -245,6 +251,16 @@
1811             return name # Quoting once is enough.
1812         return "`%s`" % name
1813 
1814+    def qualified_name(self, qname):
1815+        schema = qname.schema
1816+        if not qname.db_format:
1817+            schema = self.connection.convert_schema(schema)
1818+        if schema:
1819+            return "%s.%s" % (self.quote_name(schema),
1820+                              self.quote_name(qname.table))
1821+        else:
1822+            return self.quote_name(qname.table)
1823+
1824     def random_function_sql(self):
1825         return 'RAND()'
1826 
1827@@ -255,7 +271,9 @@
1828         if tables:
1829             sql = ['SET FOREIGN_KEY_CHECKS = 0;']
1830             for table in tables:
1831-                sql.append('%s %s;' % (style.SQL_KEYWORD('TRUNCATE'), style.SQL_FIELD(self.quote_name(table))))
1832+                sql.append('%s %s;'
1833+                           % (style.SQL_KEYWORD('TRUNCATE'),
1834+                              style.SQL_FIELD(self.qualified_name(table))))
1835             sql.append('SET FOREIGN_KEY_CHECKS = 1;')
1836 
1837             # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements
1838@@ -263,7 +281,7 @@
1839             sql.extend(["%s %s %s %s %s;" % \
1840                 (style.SQL_KEYWORD('ALTER'),
1841                  style.SQL_KEYWORD('TABLE'),
1842-                 style.SQL_TABLE(self.quote_name(sequence['table'])),
1843+                 style.SQL_TABLE(self.qualified_name((sequence['qname']))),
1844                  style.SQL_KEYWORD('AUTO_INCREMENT'),
1845                  style.SQL_FIELD('= 1'),
1846                 ) for sequence in sequences])
1847@@ -347,6 +365,13 @@
1848         self.creation = DatabaseCreation(self)
1849         self.introspection = DatabaseIntrospection(self)
1850         self.validation = DatabaseValidation(self)
1851+   
1852+    def convert_schema(self, schema):
1853+        schema = schema or self.schema
1854+        if schema and self.test_schema_prefix:
1855+            return truncate_name('%s%s' % (self.test_schema_prefix, schema),
1856+                                 self.ops.max_name_length())
1857+        return schema
1858 
1859     def _valid_connection(self):
1860         if self.connection is not None:
1861@@ -442,8 +467,9 @@
1862         ALL IMMEDIATE")
1863         """
1864         cursor = self.cursor()
1865+        qn3 = self.ops.qualified_name
1866         if table_names is None:
1867-            table_names = self.introspection.get_table_list(cursor)
1868+            table_names = self.introspection.get_visible_tables_list(cursor)
1869         for table_name in table_names:
1870             primary_key_column_name = self.introspection.get_primary_key_column(cursor, table_name)
1871             if not primary_key_column_name:
1872@@ -451,15 +477,17 @@
1873             key_columns = self.introspection.get_key_columns(cursor, table_name)
1874             for column_name, referenced_table_name, referenced_column_name in key_columns:
1875                 cursor.execute("""
1876-                    SELECT REFERRING.`%s`, REFERRING.`%s` FROM `%s` as REFERRING
1877-                    LEFT JOIN `%s` as REFERRED
1878+                    SELECT REFERRING.`%s`, REFERRING.`%s` FROM %s as REFERRING
1879+                    LEFT JOIN %s as REFERRED
1880                     ON (REFERRING.`%s` = REFERRED.`%s`)
1881                     WHERE REFERRING.`%s` IS NOT NULL AND REFERRED.`%s` IS NULL"""
1882-                    % (primary_key_column_name, column_name, table_name, referenced_table_name,
1883-                    column_name, referenced_column_name, column_name, referenced_column_name))
1884+                    % (primary_key_column_name, column_name, qn3(table_name),
1885+                       qn3(referenced_table_name), column_name,
1886+                       referenced_column_name, column_name,
1887+                       referenced_column_name))
1888                 for bad_row in cursor.fetchall():
1889                     raise utils.IntegrityError("The row in table '%s' with primary key '%s' has an invalid "
1890                         "foreign key: %s.%s contains a value '%s' that does not have a corresponding value in %s.%s."
1891-                        % (table_name, bad_row[0],
1892-                        table_name, column_name, bad_row[1],
1893-                        referenced_table_name, referenced_column_name))
1894+                        % (table_name[1], bad_row[0],
1895+                        table_name[1], column_name, bad_row[1],
1896+                        referenced_table_name[1], referenced_column_name))
1897Index: django/db/backends/oracle/base.py
1898===================================================================
1899--- django/db/backends/oracle/base.py   (revision 17914)
1900+++ django/db/backends/oracle/base.py   (working copy)
1901@@ -4,12 +4,12 @@
1902 Requires cx_Oracle: http://cx-oracle.sourceforge.net/
1903 """
1904 
1905-
1906 import datetime
1907 import decimal
1908 import sys
1909 import warnings
1910 
1911+from django.db import QName
1912 
1913 def _setup_environment(environ):
1914     import platform
1915@@ -48,6 +48,7 @@
1916 from django.conf import settings
1917 from django.db import utils
1918 from django.db.backends import *
1919+from django.db.backends.util import truncate_name
1920 from django.db.backends.signals import connection_created
1921 from django.db.backends.oracle.client import DatabaseClient
1922 from django.db.backends.oracle.creation import DatabaseCreation
1923@@ -87,34 +88,42 @@
1924 class DatabaseOperations(BaseDatabaseOperations):
1925     compiler_module = "django.db.backends.oracle.compiler"
1926 
1927-    def autoinc_sql(self, table, column):
1928+    def autoinc_sql(self, qname, column):
1929         # To simulate auto-incrementing primary keys in Oracle, we have to
1930         # create a sequence and a trigger.
1931-        sq_name = self._get_sequence_name(table)
1932-        tr_name = self._get_trigger_name(table)
1933-        tbl_name = self.quote_name(table)
1934-        col_name = self.quote_name(column)
1935+        seq_name = self._get_sequence_name(qname)
1936+        schema = seq_name.schema.upper()
1937+        sname = self.connection.ops.qualified_name(seq_name).upper()
1938+        params = {
1939+            'sq_name': seq_name.table,
1940+            'schema': schema,
1941+            'qualified_sq_name': sname,
1942+            'tr_name': self._get_trigger_name(qname),
1943+            'tbl_name': self.qualified_name(qname),
1944+            'col_name' : self.quote_name(column),
1945+        }
1946         sequence_sql = """
1947 DECLARE
1948     i INTEGER;
1949 BEGIN
1950-    SELECT COUNT(*) INTO i FROM USER_CATALOG
1951-        WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
1952+    SELECT COUNT(*) INTO i FROM ALL_CATALOG
1953+        WHERE TABLE_NAME = '%(sq_name)s' AND OWNER = '%(schema)s'
1954+              AND TABLE_TYPE = 'SEQUENCE';
1955     IF i = 0 THEN
1956-        EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
1957+        EXECUTE IMMEDIATE 'CREATE SEQUENCE %(qualified_sq_name)s';
1958     END IF;
1959 END;
1960-/""" % locals()
1961+/""" % params
1962         trigger_sql = """
1963-CREATE OR REPLACE TRIGGER "%(tr_name)s"
1964+CREATE OR REPLACE TRIGGER %(tr_name)s
1965 BEFORE INSERT ON %(tbl_name)s
1966 FOR EACH ROW
1967 WHEN (new.%(col_name)s IS NULL)
1968     BEGIN
1969-        SELECT "%(sq_name)s".nextval
1970+        SELECT %(qualified_sq_name)s.nextval
1971         INTO :new.%(col_name)s FROM dual;
1972     END;
1973-/""" % locals()
1974+/""" % params
1975         return sequence_sql, trigger_sql
1976 
1977     def date_extract_sql(self, lookup_type, field_name):
1978@@ -196,8 +205,10 @@
1979     def deferrable_sql(self):
1980         return " DEFERRABLE INITIALLY DEFERRED"
1981 
1982-    def drop_sequence_sql(self, table):
1983-        return "DROP SEQUENCE %s;" % self.quote_name(self._get_sequence_name(table))
1984+    def drop_sequence_sql(self, qname):
1985+        seq_name = self._get_sequence_name(qname)
1986+        qname = self.connection.ops.qualified_name(seq_name)
1987+        return "DROP SEQUENCE %s;" % qname
1988 
1989     def fetch_returned_insert_id(self, cursor):
1990         return long(cursor._insert_id_var.getvalue())
1991@@ -213,9 +224,9 @@
1992         # The DB API definition does not define this attribute.
1993         return cursor.statement
1994 
1995-    def last_insert_id(self, cursor, table_name, pk_name):
1996-        sq_name = self._get_sequence_name(table_name)
1997-        cursor.execute('SELECT "%s".currval FROM dual' % sq_name)
1998+    def last_insert_id(self, cursor, qualified_name, pk_name):
1999+        sq_name = self._get_sequence_name(qualified_name)
2000+        cursor.execute('SELECT %s.currval FROM dual' % sq_name)
2001         return cursor.fetchone()[0]
2002 
2003     def lookup_cast(self, lookup_type):
2004@@ -247,9 +258,23 @@
2005                                                self.max_name_length())
2006         return name.upper()
2007 
2008+    def qualified_name(self, qname):
2009+        assert isinstance(qname, QName)
2010+        schema = qname.schema
2011+        if not qname.db_format:
2012+            if not schema:
2013+                schema = self.connection.schema
2014+            schema = self.connection.convert_schema(schema)
2015+        if schema:
2016+            return "%s.%s" % (self.quote_name(schema),
2017+                              self.quote_name(qname.table))
2018+        else:
2019+            return self.quote_name(qname.table)
2020+
2021     def random_function_sql(self):
2022         return "DBMS_RANDOM.RANDOM"
2023 
2024+
2025     def regex_lookup_9(self, lookup_type):
2026         raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
2027 
2028@@ -284,46 +309,46 @@
2029             sql = ['%s %s %s;' % \
2030                     (style.SQL_KEYWORD('DELETE'),
2031                      style.SQL_KEYWORD('FROM'),
2032-                     style.SQL_FIELD(self.quote_name(table)))
2033+                     style.SQL_FIELD(self.qualified_name(table)))
2034                     for table in tables]
2035             # Since we've just deleted all the rows, running our sequence
2036             # ALTER code will reset the sequence to 0.
2037             for sequence_info in sequences:
2038-                sequence_name = self._get_sequence_name(sequence_info['table'])
2039-                table_name = self.quote_name(sequence_info['table'])
2040-                column_name = self.quote_name(sequence_info['column'] or 'id')
2041-                query = _get_sequence_reset_sql() % {'sequence': sequence_name,
2042-                                                     'table': table_name,
2043-                                                     'column': column_name}
2044-                sql.append(query)
2045+                q = self._sequence_reset_sql_for_col(
2046+                    sequence_info['qname'], sequence_info['column'] or 'id')
2047+                sql.append(q)
2048             return sql
2049         else:
2050             return []
2051 
2052+    def _sequence_reset_sql_for_col(self, qname, column):
2053+        qname = self.connection.introspection.qname_converter(qname)
2054+        qn = self.connection.ops.qualified_name
2055+        table = qn(qname)
2056+        sequence_name = self._get_sequence_name(qname)
2057+        column_name = self.quote_name(column)
2058+        params = {'sequence': sequence_name.table, 'table': table,
2059+                  'schema': sequence_name.schema.upper(), 'column': column_name}
2060+        query = _get_sequence_reset_sql(bool(params['schema']))
2061+        return query % params
2062+
2063     def sequence_reset_sql(self, style, model_list):
2064         from django.db import models
2065         output = []
2066-        query = _get_sequence_reset_sql()
2067         for model in model_list:
2068             for f in model._meta.local_fields:
2069                 if isinstance(f, models.AutoField):
2070-                    table_name = self.quote_name(model._meta.db_table)
2071-                    sequence_name = self._get_sequence_name(model._meta.db_table)
2072-                    column_name = self.quote_name(f.column)
2073-                    output.append(query % {'sequence': sequence_name,
2074-                                           'table': table_name,
2075-                                           'column': column_name})
2076+                    q = self._sequence_reset_sql_for_col(
2077+                        model._meta.qualified_name, f.column)
2078+                    output.append(q)
2079                     # Only one AutoField is allowed per model, so don't
2080                     # continue to loop
2081                     break
2082             for f in model._meta.many_to_many:
2083                 if not f.rel.through:
2084-                    table_name = self.quote_name(f.m2m_db_table())
2085-                    sequence_name = self._get_sequence_name(f.m2m_db_table())
2086-                    column_name = self.quote_name('id')
2087-                    output.append(query % {'sequence': sequence_name,
2088-                                           'table': table_name,
2089-                                           'column': column_name})
2090+                    q = self._sequence_reset_sql_for_col(
2091+                        f.m2m_qualified_name(), 'id')
2092+                    output.append(q)
2093         return output
2094 
2095     def start_transaction_sql(self):
2096@@ -377,13 +402,23 @@
2097             raise NotImplementedError("Bit-wise or is not supported in Oracle.")
2098         return super(DatabaseOperations, self).combine_expression(connector, sub_expressions)
2099 
2100-    def _get_sequence_name(self, table):
2101+    def _get_sequence_name(self, qname):
2102+        assert isinstance(qname, QName)
2103+        qname = self.connection.introspection.qname_converter(qname,
2104+                                                              force_schema=True)
2105         name_length = self.max_name_length() - 3
2106-        return '%s_SQ' % util.truncate_name(table, name_length).upper()
2107+        seq_name = '%s_SQ' % util.truncate_name(qname.table,
2108+                                                name_length).upper()
2109+        return QName(qname.schema, seq_name, True)
2110+        #return '%s_SQ' % util.truncate_name(name, name_length).upper()
2111 
2112-    def _get_trigger_name(self, table):
2113+    def _get_trigger_name(self, qname):
2114+        assert isinstance(qname, QName)
2115+        qname = self.connection.introspection.qname_converter(qname)
2116         name_length = self.max_name_length() - 3
2117-        return '%s_TR' % util.truncate_name(table, name_length).upper()
2118+        trig_name = '%s_TR' % util.truncate_name(qname.table,
2119+                                                 name_length).upper()
2120+        return self.qualified_name(QName(qname.schema, trig_name, True))
2121 
2122     def bulk_insert_sql(self, fields, num_values):
2123         items_sql = "SELECT %s FROM DUAL" % ", ".join(["%s"] * len(fields))
2124@@ -444,6 +479,13 @@
2125         self.creation = DatabaseCreation(self)
2126         self.introspection = DatabaseIntrospection(self)
2127         self.validation = BaseDatabaseValidation(self)
2128+   
2129+    def convert_schema(self, schema):
2130+        schema = schema or self.schema
2131+        if schema and self.test_schema_prefix:
2132+            return truncate_name('%s%s' % (self.test_schema_prefix, schema),
2133+                                 self.ops.max_name_length())
2134+        return schema
2135 
2136     def check_constraints(self, table_names=None):
2137         """
2138@@ -476,7 +518,11 @@
2139             conn_params = self.settings_dict['OPTIONS'].copy()
2140             if 'use_returning_into' in conn_params:
2141                 del conn_params['use_returning_into']
2142-            self.connection = Database.connect(conn_string, **conn_params)
2143+            try:
2144+                self.connection = Database.connect(conn_string, **conn_params)
2145+            except:
2146+                print conn_string
2147+                raise
2148             cursor = FormatStylePlaceholderCursor(self.connection)
2149             # Set oracle date to ansi date format.  This only needs to execute
2150             # once when we create a new connection. We also set the Territory
2151@@ -814,14 +860,29 @@
2152     return s
2153 
2154 
2155-def _get_sequence_reset_sql():
2156+def _get_sequence_reset_sql(with_schema=False):
2157     # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
2158-    return """
2159+    if with_schema:
2160+        return """
2161 DECLARE
2162     table_value integer;
2163     seq_value integer;
2164 BEGIN
2165     SELECT NVL(MAX(%(column)s), 0) INTO table_value FROM %(table)s;
2166+    SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM all_sequences
2167+           WHERE sequence_name = '%(sequence)s' AND sequence_owner = '%(schema)s';
2168+    WHILE table_value > seq_value LOOP
2169+        SELECT "%(schema)s"."%(sequence)s".nextval INTO seq_value FROM dual;
2170+    END LOOP;
2171+END;
2172+/"""
2173+    else:
2174+        return """
2175+DECLARE
2176+    table_value integer;
2177+    seq_value integer;
2178+BEGIN
2179+    SELECT NVL(MAX(%(column)s), 0) INTO table_value FROM %(table)s;
2180     SELECT NVL(last_number - cache_size, 0) INTO seq_value FROM user_sequences
2181            WHERE sequence_name = '%(sequence)s';
2182     WHILE table_value > seq_value LOOP
2183Index: django/db/backends/oracle/introspection.py
2184===================================================================
2185--- django/db/backends/oracle/introspection.py  (revision 17914)
2186+++ django/db/backends/oracle/introspection.py  (working copy)
2187@@ -1,3 +1,4 @@
2188+from django.db import QName
2189 from django.db.backends import BaseDatabaseIntrospection
2190 import cx_Oracle
2191 import re
2192@@ -37,56 +38,85 @@
2193             return super(DatabaseIntrospection, self).get_field_type(
2194                 data_type, description)
2195 
2196-    def get_table_list(self, cursor):
2197-        "Returns a list of table names in the current database."
2198-        cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
2199-        return [row[0].lower() for row in cursor.fetchall()]
2200+    def get_visible_tables_list(self, cursor):
2201+        "Returns a list of visible tables"
2202+        return self.get_qualified_tables_list(cursor, [self.connection.settings_dict['USER']])
2203 
2204-    def get_table_description(self, cursor, table_name):
2205+    def get_qualified_tables_list(self, cursor, schemas):
2206+        "Returns a list of table names in the given schemas list."
2207+        default_schema = self.connection.convert_schema(None)
2208+        if default_schema:
2209+            schemas.append(default_schema)
2210+        if not schemas:
2211+            return []
2212+        param_list = ', '.join(['%s']*len(schemas))
2213+        schemas = [s.upper() for s in schemas]
2214+        cursor.execute("""
2215+            SELECT OWNER, TABLE_NAME
2216+              FROM ALL_TABLES WHERE OWNER in (%s)""" % param_list, schemas)
2217+        return [QName(row[0].lower(), row[1].lower(), True)
2218+                for row in cursor.fetchall()]
2219+
2220+    def get_table_description(self, cursor, qname):
2221         "Returns a description of the table, with the DB-API cursor.description interface."
2222-        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
2223+        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2"
2224+                       % self.connection.ops.qualified_name(qname))
2225         description = []
2226         for desc in cursor.description:
2227             description.append((desc[0].lower(),) + desc[1:])
2228         return description
2229 
2230-    def table_name_converter(self, name):
2231-        "Table name comparison is case insensitive under Oracle"
2232+    def identifier_converter(self, name):
2233         return name.lower()
2234 
2235-    def _name_to_index(self, cursor, table_name):
2236+    def qname_converter(self, qname, force_schema=False):
2237+        assert isinstance(qname, QName)
2238+        if qname.db_format and (qname.schema or not force_schema):
2239+            return qname
2240+        schema = self.connection.convert_schema(qname.schema)
2241+        if not schema and force_schema:
2242+            schema = self.connection.settings_dict['USER']
2243+        return QName(schema, qname.table, True)
2244+
2245+    def _name_to_index(self, cursor, qname):
2246         """
2247         Returns a dictionary of {field_name: field_index} for the given table.
2248         Indexes are 0-based.
2249         """
2250-        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
2251+        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, qname))])
2252 
2253-    def get_relations(self, cursor, table_name):
2254+    def get_relations(self, cursor, qname):
2255         """
2256         Returns a dictionary of {field_index: (field_index_other_table, other_table)}
2257         representing all relationships to the given table. Indexes are 0-based.
2258         """
2259-        table_name = table_name.upper()
2260+        qname = self.qname_converter(qname, force_schema=True)
2261+        schema, table = qname.schema.upper(), qname.table.upper()
2262         cursor.execute("""
2263-    SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
2264-    FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
2265-           user_tab_cols ta, user_tab_cols tb
2266-    WHERE  user_constraints.table_name = %s AND
2267+    SELECT ta.column_id - 1, tb.table_name, tb.owner, tb.column_id - 1
2268+    FROM   all_constraints, ALL_CONS_COLUMNS ca, ALL_CONS_COLUMNS cb,
2269+           all_tab_cols ta, all_tab_cols tb
2270+    WHERE  all_constraints.table_name = %s AND
2271+           all_constraints.owner = %s AND
2272            ta.table_name = %s AND
2273+           ta.owner = %s AND
2274            ta.column_name = ca.column_name AND
2275            ca.table_name = %s AND
2276-           user_constraints.constraint_name = ca.constraint_name AND
2277-           user_constraints.r_constraint_name = cb.constraint_name AND
2278+           ca.owner = %s AND
2279+           all_constraints.constraint_name = ca.constraint_name AND
2280+           all_constraints.r_constraint_name = cb.constraint_name AND
2281            cb.table_name = tb.table_name AND
2282            cb.column_name = tb.column_name AND
2283-           ca.position = cb.position""", [table_name, table_name, table_name])
2284+           ca.position = cb.position""", [table, schema, table, schema,
2285+                                          table, schema])
2286 
2287         relations = {}
2288         for row in cursor.fetchall():
2289-            relations[row[0]] = (row[2], row[1].lower())
2290+            relations[row[0]] = (
2291+                row[3], QName(row[2].lower(), row[1].lower(), True))
2292         return relations
2293 
2294-    def get_indexes(self, cursor, table_name):
2295+    def get_indexes(self, cursor, qname):
2296         """
2297         Returns a dictionary of fieldname -> infodict for the given table,
2298         where each infodict is in the format:
2299@@ -96,27 +126,45 @@
2300         # This query retrieves each index on the given table, including the
2301         # first associated field name
2302         # "We were in the nick of time; you were in great peril!"
2303+        qname = self.qname_converter(qname, force_schema=True)
2304+        schema, table = qname.schema.upper(), qname.table.upper()
2305+        # There can be multiple constraints for a given column, and we
2306+        # are interested if _any_ of them is unique or primary key, hence
2307+        # the group by + max.
2308         sql = """\
2309-SELECT LOWER(all_tab_cols.column_name) AS column_name,
2310-       CASE user_constraints.constraint_type
2311-           WHEN 'P' THEN 1 ELSE 0
2312-       END AS is_primary_key,
2313-       CASE user_indexes.uniqueness
2314-           WHEN 'UNIQUE' THEN 1 ELSE 0
2315-       END AS is_unique
2316-FROM   all_tab_cols, user_cons_columns, user_constraints, user_ind_columns, user_indexes
2317-WHERE  all_tab_cols.column_name = user_cons_columns.column_name (+)
2318-  AND  all_tab_cols.table_name = user_cons_columns.table_name (+)
2319-  AND  user_cons_columns.constraint_name = user_constraints.constraint_name (+)
2320-  AND  user_constraints.constraint_type (+) = 'P'
2321-  AND  user_ind_columns.column_name (+) = all_tab_cols.column_name
2322-  AND  user_ind_columns.table_name (+) = all_tab_cols.table_name
2323-  AND  user_indexes.uniqueness (+) = 'UNIQUE'
2324-  AND  user_indexes.index_name (+) = user_ind_columns.index_name
2325-  AND  all_tab_cols.table_name = UPPER(%s)
2326+SELECT column_name, max(is_primary_key), max(is_unique)
2327+  FROM (
2328+    SELECT LOWER(all_tab_cols.column_name) AS column_name,
2329+           CASE all_constraints.constraint_type
2330+               WHEN 'P' THEN 1 ELSE 0
2331+           END AS is_primary_key,
2332+           CASE all_indexes.uniqueness
2333+               WHEN 'UNIQUE' THEN 1 ELSE 0
2334+           END AS is_unique
2335+    FROM   all_tab_cols, all_cons_columns, all_constraints, all_ind_columns, all_indexes
2336+    WHERE  all_tab_cols.column_name = all_cons_columns.column_name (+)
2337+      AND  all_tab_cols.table_name = all_cons_columns.table_name (+)
2338+      AND  all_tab_cols.owner = all_cons_columns.owner (+)
2339+      AND  all_cons_columns.constraint_name = all_constraints.constraint_name (+)
2340+      AND  all_cons_columns.owner = all_constraints.owner (+)
2341+      AND  all_constraints.constraint_type (+) = 'P'
2342+      AND  all_ind_columns.column_name (+) = all_tab_cols.column_name
2343+      AND  all_ind_columns.table_name (+) = all_tab_cols.table_name
2344+      AND  all_ind_columns.index_owner (+) = all_tab_cols.owner
2345+      AND  all_indexes.uniqueness (+) = 'UNIQUE'
2346+      AND  all_indexes.index_name (+) = all_ind_columns.index_name
2347+      AND  all_indexes.table_owner (+) = all_ind_columns.index_owner
2348+      AND  all_tab_cols.table_name = %s
2349+      AND  all_tab_cols.owner = %s
2350+    )
2351+GROUP BY column_name
2352 """
2353-        cursor.execute(sql, [table_name])
2354+        cursor.execute(sql, [table, schema])
2355         indexes = {}
2356         for row in cursor.fetchall():
2357             indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
2358         return indexes
2359+
2360+    def get_schema_list(self, cursor):
2361+        cursor.execute("SELECT USERNAME FROM ALL_USERS")
2362+        return [r[0] for r in cursor.fetchall()]
2363Index: django/db/backends/oracle/creation.py
2364===================================================================
2365--- django/db/backends/oracle/creation.py       (revision 17914)
2366+++ django/db/backends/oracle/creation.py       (working copy)
2367@@ -1,6 +1,8 @@
2368 import sys
2369 import time
2370+from django.db import QName
2371 from django.db.backends.creation import BaseDatabaseCreation
2372+from django.core.management.color import no_style
2373 
2374 TEST_DATABASE_PREFIX = 'test_'
2375 PASSWORD = 'Im_a_lumberjack'
2376@@ -43,14 +45,13 @@
2377     def __init__(self, connection):
2378         super(DatabaseCreation, self).__init__(connection)
2379 
2380-    def _create_test_db(self, verbosity=1, autoclobber=False):
2381+    def _get_ddl_parameters(self):
2382         TEST_NAME = self._test_database_name()
2383         TEST_USER = self._test_database_user()
2384         TEST_PASSWD = self._test_database_passwd()
2385         TEST_TBLSPACE = self._test_database_tblspace()
2386         TEST_TBLSPACE_TMP = self._test_database_tblspace_tmp()
2387-
2388-        parameters = {
2389+        return {
2390             'dbname': TEST_NAME,
2391             'user': TEST_USER,
2392             'password': TEST_PASSWD,
2393@@ -58,6 +59,8 @@
2394             'tblspace_temp': TEST_TBLSPACE_TMP,
2395         }
2396 
2397+    def _create_test_db(self, verbosity=1, autoclobber=False, schemas=[]):
2398+        parameters = self._get_ddl_parameters()
2399         cursor = self.connection.cursor()
2400         if self._test_database_create():
2401             try:
2402@@ -65,7 +68,9 @@
2403             except Exception, e:
2404                 sys.stderr.write("Got an error creating the test database: %s\n" % e)
2405                 if not autoclobber:
2406-                    confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_NAME)
2407+                    confirm = raw_input("It appears the test database, %(dbname)s, already "
2408+                                        "exists. Type 'yes' to delete it, or 'no' to cancel: "
2409+                                        % parameters)
2410                 if autoclobber or confirm == 'yes':
2411                     try:
2412                         if verbosity >= 1:
2413@@ -83,11 +88,13 @@
2414             if verbosity >= 1:
2415                 print "Creating test user..."
2416             try:
2417-                self._create_test_user(cursor, parameters, verbosity)
2418+                self._create_test_user(cursor, parameters, verbosity, dba=bool(schemas))
2419             except Exception, e:
2420                 sys.stderr.write("Got an error creating the test user: %s\n" % e)
2421                 if not autoclobber:
2422-                    confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_USER)
2423+                    confirm = raw_input("It appears the test user, %(user)s, already exists. "
2424+                                        "Type 'yes' to delete it, or 'no' to cancel: "
2425+                                        % parameters)
2426                 if autoclobber or confirm == 'yes':
2427                     try:
2428                         if verbosity >= 1:
2429@@ -95,7 +102,7 @@
2430                         self._destroy_test_user(cursor, parameters, verbosity)
2431                         if verbosity >= 1:
2432                             print "Creating test user..."
2433-                        self._create_test_user(cursor, parameters, verbosity)
2434+                        self._create_test_user(cursor, parameters, verbosity, dba=bool(schemas))
2435                     except Exception, e:
2436                         sys.stderr.write("Got an error recreating the test user: %s\n" % e)
2437                         sys.exit(2)
2438@@ -105,8 +112,8 @@
2439 
2440         self.connection.settings_dict['SAVED_USER'] = self.connection.settings_dict['USER']
2441         self.connection.settings_dict['SAVED_PASSWORD'] = self.connection.settings_dict['PASSWORD']
2442-        self.connection.settings_dict['TEST_USER'] = self.connection.settings_dict['USER'] = TEST_USER
2443-        self.connection.settings_dict['PASSWORD'] = TEST_PASSWD
2444+        self.connection.settings_dict['TEST_USER'] = self.connection.settings_dict['USER'] = parameters['user']
2445+        self.connection.settings_dict['PASSWORD'] = parameters['password']
2446 
2447         return self.connection.settings_dict['NAME']
2448 
2449@@ -115,33 +122,21 @@
2450         Destroy a test database, prompting the user for confirmation if the
2451         database already exists. Returns the name of the test database created.
2452         """
2453-        TEST_NAME = self._test_database_name()
2454-        TEST_USER = self._test_database_user()
2455-        TEST_PASSWD = self._test_database_passwd()
2456-        TEST_TBLSPACE = self._test_database_tblspace()
2457-        TEST_TBLSPACE_TMP = self._test_database_tblspace_tmp()
2458+        parameters = self._get_ddl_parameters()
2459 
2460         self.connection.settings_dict['USER'] = self.connection.settings_dict['SAVED_USER']
2461         self.connection.settings_dict['PASSWORD'] = self.connection.settings_dict['SAVED_PASSWORD']
2462 
2463-        parameters = {
2464-            'dbname': TEST_NAME,
2465-            'user': TEST_USER,
2466-            'password': TEST_PASSWD,
2467-            'tblspace': TEST_TBLSPACE,
2468-            'tblspace_temp': TEST_TBLSPACE_TMP,
2469-        }
2470-
2471         cursor = self.connection.cursor()
2472         time.sleep(1) # To avoid "database is being accessed by other users" errors.
2473+        if self._test_database_create():
2474+            if verbosity >= 1:
2475+                print 'Destroying test database tables...'
2476+            self._execute_test_db_destruction(cursor, parameters, verbosity)
2477         if self._test_user_create():
2478             if verbosity >= 1:
2479                 print 'Destroying test user...'
2480             self._destroy_test_user(cursor, parameters, verbosity)
2481-        if self._test_database_create():
2482-            if verbosity >= 1:
2483-                print 'Destroying test database tables...'
2484-            self._execute_test_db_destruction(cursor, parameters, verbosity)
2485         self.connection.close()
2486 
2487     def _execute_test_db_creation(self, cursor, parameters, verbosity):
2488@@ -159,16 +154,19 @@
2489         ]
2490         self._execute_statements(cursor, statements, parameters, verbosity)
2491 
2492-    def _create_test_user(self, cursor, parameters, verbosity):
2493+    def _create_test_user(self, cursor, parameters, verbosity, dba=False):
2494         if verbosity >= 2:
2495             print "_create_test_user(): username = %s" % parameters['user']
2496+        parameters = parameters.copy()
2497+        parameters['dba'] = ', DBA' if dba else ''
2498+
2499         statements = [
2500             """CREATE USER %(user)s
2501                IDENTIFIED BY %(password)s
2502                DEFAULT TABLESPACE %(tblspace)s
2503                TEMPORARY TABLESPACE %(tblspace_temp)s
2504             """,
2505-            """GRANT CONNECT, RESOURCE TO %(user)s""",
2506+            """GRANT CONNECT, RESOURCE %(dba)s TO %(user)s""",
2507         ]
2508         self._execute_statements(cursor, statements, parameters, verbosity)
2509 
2510@@ -186,10 +184,14 @@
2511             print "_destroy_test_user(): user=%s" % parameters['user']
2512             print "Be patient.  This can take some time..."
2513         statements = [
2514-            'DROP USER %(user)s CASCADE',
2515+            self.sql_destroy_schema(parameters['user'], style=None)
2516         ]
2517         self._execute_statements(cursor, statements, parameters, verbosity)
2518 
2519+    def sql_destroy_schema(self, schema, style):
2520+        return "DROP USER %s CASCADE" % schema
2521+
2522+
2523     def _execute_statements(self, cursor, statements, parameters, verbosity):
2524         for template in statements:
2525             stmt = template % parameters
2526@@ -272,3 +274,175 @@
2527 
2528     def set_autocommit(self):
2529         self.connection.connection.autocommit = True
2530+
2531+    def _create_test_schemas(self, verbosity, schemas, autoclobber):
2532+        if not self._test_user_create():
2533+            return []
2534+        cursor = self.connection.cursor()
2535+        parameters = self._get_ddl_parameters()
2536+        parameters['authorization'] = parameters['user']
2537+        conv = self.connection.introspection.identifier_converter
2538+        existing_schemas = [conv(s) for s in self.connection.introspection.get_schema_list(cursor)]
2539+        conflicts = [conv(s) for s in existing_schemas if conv(s) in schemas]
2540+        if conflicts:
2541+            print 'The following users already exists: %s' % ', '.join(conflicts)
2542+            if not autoclobber:
2543+                confirm = raw_input(
2544+                    "Type 'yes' if you would like to try deleting these users "
2545+                    "or 'no' to cancel: ")
2546+            if autoclobber or confirm == 'yes':
2547+                for schema in conflicts:
2548+                    parameters['user'] = schema
2549+                    if verbosity >= 1:
2550+                        print "Destroying user %s" % schema
2551+                    self._destroy_test_user(cursor, parameters, verbosity)
2552+                    existing_schemas.remove(schema)
2553+            else:
2554+                print "Tests cancelled."
2555+                sys.exit(1)
2556+           
2557+        to_create = [s for s in schemas if s not in existing_schemas]
2558+        for schema in to_create:
2559+            parameters['user'] = schema
2560+            if verbosity >= 1:
2561+                print "Creating user %s" % schema
2562+            self._create_test_user(cursor, parameters, verbosity)
2563+        return to_create
2564+
2565+    def needs_separate_conn(self, from_qname, to_qname):
2566+        conv = self.connection.introspection.qname_converter
2567+        def_schema = conv(QName(None, None, False), force_schema=True).schema
2568+        from_qname = conv(from_qname, force_schema=True)
2569+        to_qname = conv(to_qname, force_schema=True)
2570+        return (def_schema != from_qname.schema
2571+                or to_qname.schema != from_qname.schema)
2572+
2573+    def sql_for_inline_foreign_key_references(self, field, known_models, style):
2574+        """
2575+        Return the SQL snippet defining the foreign key reference for a field.
2576+
2577+        Oracle doesn't let you do cross-schema foreign keys, except if you
2578+        are connected to the "from" schema. Don't ask why.
2579+        """
2580+        if self.needs_separate_conn(field.model._meta.qualified_name,
2581+                                    field.rel.to._meta.qualified_name):
2582+            return [], True
2583+        return super(DatabaseCreation, self).sql_for_inline_foreign_key_references(field, known_models, style)
2584+
2585+    def sql_for_pending_references(self, model, style, pending_references,
2586+                                   second_pass=False):
2587+        """
2588+        Sad fact of life: On Oracle it is impossible to do cross-schema
2589+        references unless you explisitly grant REFERENCES on the referenced
2590+        table, and in addition the reference is made from the schema
2591+        containing the altered table (the one getting the new constraint).
2592+        To make this even nicer, it is impossible to do the GRANT using the
2593+        same user we are giving the REFERENCES right, as you can't GRANT
2594+        yourself.
2595+
2596+        The solution we are using is to do the pending cross-schema references
2597+        in two stages after all tables have been created:
2598+            1) Connect as the foreign key's target table owner, and grant
2599+               REFERENCES to all users needing to do foreign keys.
2600+            2) Connect as the source table's owner, and create the foreign
2601+               keys.
2602+        To support this arrangement, we will create only non-cross-schema
2603+        references unless we are explicitly told by the second_pass flag
2604+        that it is safe to do the cross schema references.
2605+
2606+        It is possible to grant REFERENCES to public (but it seems other roles
2607+        will not work), but as we need to anyways do this multi-connection
2608+        dance it seems better to do the grants explicitly only when needed.
2609+        """
2610+        if second_pass:
2611+            return super(DatabaseCreation, self).sql_for_pending_references(
2612+                model, style, pending_references)
2613+        # Split the "safe" and "unsafe" references apart, and call
2614+        # the super() method for the safe set.
2615+        cross_schema_refs = []
2616+        single_schema_refs = []
2617+        if model in pending_references:
2618+            for rel_class, f in pending_references[model]:
2619+                if self.needs_separate_conn(rel_class._meta.qualified_name,
2620+                                            model._meta.qualified_name):
2621+                    cross_schema_refs.append((rel_class, f))
2622+                else:
2623+                    single_schema_refs.append((rel_class, f))
2624+        sql = []
2625+        if single_schema_refs:
2626+            pending_references[model] = single_schema_refs
2627+            sql = super(DatabaseCreation, self).sql_for_pending_references(
2628+                model, style, pending_references)
2629+        if cross_schema_refs:
2630+            pending_references[model] = cross_schema_refs
2631+        return sql
2632+
2633+    def post_create_pending_references(self, pending_references, as_sql=False):
2634+        # Build a dictionary: from_schema -> [(model, refs)...]
2635+        references_to_schema = {}
2636+        sql = []
2637+        conv = self.connection.introspection.qname_converter
2638+        for model, refs in pending_references.items():
2639+            schema = conv(model._meta.qualified_name, force_schema=True).schema
2640+            if schema not in references_to_schema:
2641+                references_to_schema[schema] = []
2642+            references_to_schema[schema].append((model, refs))
2643+        # Pass 1: give grants.
2644+        for schema, all_refs in references_to_schema.items():
2645+            grant_to = set()
2646+            for model, refs in all_refs:
2647+                for ref in refs:
2648+                    to_user = conv(ref[0]._meta.qualified_name,
2649+                                   force_schema=True).schema
2650+                    if to_user != schema:
2651+                        grant_to.add((model, to_user))
2652+            sql.extend(self._grant_references(schema, grant_to, as_sql))
2653+        # Prepare for pass 2. This time we must connect as the user
2654+        # of the altered table's schema. So, first build a dictionary of
2655+        # from_schema -> [{model: [refs]}], that is, build a
2656+        # pending_references for each schema separately.
2657+        references_from_schema = {}
2658+        for model, refs in pending_references.items():
2659+            for ref in refs:
2660+                schema = conv(ref[0]._meta.qualified_name,
2661+                              force_schema=True).schema
2662+                if schema not in references_from_schema:
2663+                    references_from_schema[schema] = {}
2664+                if model not in references_from_schema[schema]:
2665+                    references_from_schema[schema][model] = []
2666+                references_from_schema[schema][model].append(ref)
2667+        # Pass 2: create the actual references
2668+        for schema, ref_dict in references_from_schema.items():
2669+            per_schema_sql = ['-- Connect as user "%s"' % schema] if as_sql else []
2670+            for model, refs in ref_dict.items():
2671+                ref_sql = self.sql_for_pending_references(model, no_style(),
2672+                                                          ref_dict, second_pass=True)
2673+                per_schema_sql.extend(ref_sql)
2674+            if not as_sql:
2675+                self._run_sql_as_user(schema, per_schema_sql)
2676+            sql.extend(per_schema_sql)
2677+        return sql
2678+
2679+    def _grant_references(self, schema, grant_to, as_sql):
2680+        sql = ['-- Connect as user "%s"' % schema] if as_sql else []
2681+        qn = self.connection.ops.quote_name
2682+        for model, user in grant_to:
2683+            sql.append('GRANT REFERENCES ON %s TO %s'
2684+                       % (qn(model._meta.db_table), qn(user)))
2685+        if not as_sql:
2686+            self._run_sql_as_user(schema, sql)
2687+        return sql
2688+
2689+    def _run_sql_as_user(self, user, sql):
2690+        if not sql:
2691+            return
2692+        self.connection.close()
2693+        try:
2694+            old_settings = self.connection.settings_dict.copy()
2695+            self.connection.settings_dict['USER'] = user
2696+            cursor = self.connection.cursor()
2697+            for q in sql:
2698+                cursor.execute(q)
2699+        finally:
2700+            self.connection.close()
2701+            self.connection.settings_dict = old_settings
2702Index: django/db/backends/__init__.py
2703===================================================================
2704--- django/db/backends/__init__.py      (revision 17914)
2705+++ django/db/backends/__init__.py      (working copy)
2706@@ -7,7 +7,7 @@
2707 from contextlib import contextmanager
2708 
2709 from django.conf import settings
2710-from django.db import DEFAULT_DB_ALIAS
2711+from django.db import DEFAULT_DB_ALIAS, QName
2712 from django.db.backends import util
2713 from django.db.transaction import TransactionManagementError
2714 from django.utils.importlib import import_module
2715@@ -44,7 +44,18 @@
2716 
2717     def __ne__(self, other):
2718         return not self == other
2719+   
2720+    def _get_schema(self):
2721+        return self.settings_dict['SCHEMA']
2722+    schema = property(_get_schema)
2723 
2724+    def _get_test_schema_prefix(self):
2725+        return self.settings_dict['TEST_SCHEMA_PREFIX']
2726+    test_schema_prefix = property(_get_test_schema_prefix)
2727+
2728+    def convert_schema(self, schema):
2729+        return schema or self.schema
2730+
2731     def _commit(self):
2732         if self.connection is not None:
2733             return self.connection.commit()
2734@@ -311,6 +322,13 @@
2735     def make_debug_cursor(self, cursor):
2736         return util.CursorDebugWrapper(cursor, self)
2737 
2738+    def qname(self, model):
2739+        """
2740+        Given a model class or instance, returns its current database table
2741+        name in schema qualified format.
2742+        """
2743+        return self.ops.qualified_name(model._meta.qualified_name)
2744+
2745 class BaseDatabaseFeatures(object):
2746     allows_group_by_pk = False
2747     # True if django.db.backend.utils.typecast_timestamp is used on values
2748@@ -404,11 +422,17 @@
2749     _confirmed = False
2750     supports_transactions = None
2751     supports_stddev = None
2752+    supports_foreign_keys = True
2753     can_introspect_foreign_keys = None
2754 
2755     # Support for the DISTINCT ON clause
2756     can_distinct_on_fields = False
2757 
2758+    # If the database has databases and schemas as different concepts
2759+    # or plain fakes schemas, it is safe to skip conflicts checking in
2760+    # testing on that database.
2761+    namespaced_schemas = False
2762+
2763     def __init__(self, connection):
2764         self.connection = connection
2765 
2766@@ -461,7 +485,7 @@
2767         self.connection = connection
2768         self._cache = None
2769 
2770-    def autoinc_sql(self, table, column):
2771+    def autoinc_sql(self, qualified_name, column):
2772         """
2773         Returns any SQL needed to support auto-incrementing primary keys, or
2774         None if no SQL is necessary.
2775@@ -513,7 +537,7 @@
2776         """
2777         return "DROP CONSTRAINT"
2778 
2779-    def drop_sequence_sql(self, table):
2780+    def drop_sequence_sql(self, qualified_name):
2781         """
2782         Returns any SQL necessary to drop the sequence for the given table.
2783         Returns None if no SQL is necessary.
2784@@ -594,7 +618,7 @@
2785 
2786         return smart_unicode(sql) % u_params
2787 
2788-    def last_insert_id(self, cursor, table_name, pk_name):
2789+    def last_insert_id(self, cursor, qualified_name, pk_name):
2790         """
2791         Given a cursor object that has just performed an INSERT statement into
2792         a table that has an auto-incrementing ID, returns the newly created ID.
2793@@ -673,6 +697,20 @@
2794         """
2795         raise NotImplementedError()
2796 
2797+    def qualified_name(self, qualified_name, from_model=False):
2798+        """
2799+        Formats the given schema, table_name tuple into database's
2800+        qualified and quoted name format. The schema can be None.
2801+
2802+        We need to know if the name if from an existing database
2803+        table, or from a model. The reason is that some backends
2804+        do modifications to the name (schema-prefix the table name)
2805+        at runtime, and we must not do that repeatedly. Hence, if
2806+        the name comes from the DB and is already schema-prefixed,
2807+        then we must not schema-prefix it again.
2808+        """
2809+        raise NotImplementedError
2810+
2811     def random_function_sql(self):
2812         """
2813         Returns a SQL expression that returns a random value.
2814@@ -718,7 +756,7 @@
2815         """
2816         return ''
2817 
2818-    def sql_flush(self, style, tables, sequences):
2819+    def sql_flush(self, style, tables, sequences, from_db):
2820         """
2821         Returns a list of SQL statements required to remove all data from
2822         the given database tables (without actually removing the tables
2823@@ -726,6 +764,9 @@
2824 
2825         The `style` argument is a Style object as returned by either
2826         color_style() or no_style() in django.core.management.color.
2827+
2828+        The from_db argument tells if the names are coming from database
2829+        or from model._meta, needed for schema support.
2830         """
2831         raise NotImplementedError()
2832 
2833@@ -883,25 +924,88 @@
2834         distinguish between a FloatField and IntegerField, for example."""
2835         return self.data_types_reverse[data_type]
2836 
2837-    def table_name_converter(self, name):
2838-        """Apply a conversion to the name for the purposes of comparison.
2839+    def qname_converter(self, qname, force_schema=False):
2840+        """
2841+        Apply a conversion to the name for the purposes of comparison.
2842 
2843         The default table name converter is for case sensitive comparison.
2844+
2845+        The given name must be a QName. If force_schema is set, then backends
2846+        should try to append a default schema name to the given name if
2847+        applicable for the backend.
2848         """
2849-        return name
2850+        return qname
2851 
2852+    def identifier_converter(self, identifier):
2853+        """
2854+        On some backends we need to do a little acrobaty to convert the names
2855+        from the DB and names from Models into consistent format. For example,
2856+        the return types from DB might be upper-case, but we need them
2857+        lower-case for comparisons. This method can be used to convert
2858+        identifiers into consistent format.
2859+        """
2860+        return identifier
2861+
2862+
2863     def table_names(self):
2864-        "Returns a list of names of all tables that exist in the database."
2865+        "Returns a list of names of all tables that are visible in the database."
2866         cursor = self.connection.cursor()
2867-        return self.get_table_list(cursor)
2868+        return self.get_visible_tables_list(cursor)
2869 
2870+    def get_visible_tables_list(self, cursoe):
2871+        """
2872+        Returns all visible (in "search path") tables from the database.
2873+        """
2874+        return []
2875+
2876+    def qualified_names(self, schemas=None):
2877+        """
2878+        Returns qualified table names for all schemas Django is using.
2879+        """
2880+        cursor = self.connection.cursor()
2881+        if schemas is None:
2882+            return self.get_visible_tables_list(cursor)
2883+        else:
2884+            return
2885+       
2886+    def all_qualified_names(self):
2887+        """
2888+        Gets all table names from the database. Note that it is intentional
2889+        that visible tables appear both as unqualified and qualified tables.
2890+        """
2891+        cursor = self.connection.cursor()
2892+        nonqualified_tables = self.get_visible_tables_list(cursor)
2893+        schemas = self.connection.creation.get_schemas()
2894+        schemas = [self.connection.convert_schema(s) for s in schemas]
2895+        qualified_tables = self.get_qualified_tables_list(cursor, schemas)
2896+        return set([QName(None, t, from_db) for _, t, from_db
2897+                    in nonqualified_tables]
2898+                   + qualified_tables)
2899+   
2900+    def get_qualified_tables_list(self, cursor, schemas):
2901+        """
2902+        Returns schema qualified names (as pair schema, tbl_name) of all
2903+        tables in the given schemas.
2904+        """
2905+        return []
2906+
2907+    def get_schema_list(self, cursor):
2908+        "Returns a list of schemas that exist in the database"
2909+        return []
2910+   
2911+
2912+    def schema_names(self):
2913+        "Returns a list of schemas that exist in the database"
2914+        cursor = self.connection.cursor()
2915+        return self.get_schema_list(cursor)
2916+
2917     def django_table_names(self, only_existing=False):
2918         """
2919-        Returns a list of all table names that have associated Django models and
2920-        are in INSTALLED_APPS.
2921+        Returns a list of all table's qualified names that have associated
2922+        Django models and are in INSTALLED_APPS.
2923 
2924-        If only_existing is True, the resulting list will only include the tables
2925-        that actually exist in the database.
2926+        If only_existing is True, the resulting list will only include the
2927+        tables that actually exist in the database.
2928         """
2929         from django.db import models, router
2930         tables = set()
2931@@ -911,34 +1015,43 @@
2932                     continue
2933                 if not router.allow_syncdb(self.connection.alias, model):
2934                     continue
2935-                tables.add(model._meta.db_table)
2936-                tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
2937+                tables.add(model._meta.qualified_name)
2938+                tables.update([f.m2m_qualified_name()
2939+                               for f in model._meta.local_many_to_many])
2940         tables = list(tables)
2941         if only_existing:
2942-            existing_tables = self.table_names()
2943-            tables = [
2944-                t
2945-                for t in tables
2946-                if self.table_name_converter(t) in existing_tables
2947-            ]
2948-        return tables
2949+            found_tables = []
2950+            existing_tables = self.all_qualified_names()
2951+            found_tables.extend([
2952+                t for t in tables
2953+                if self.qname_converter(t) in existing_tables
2954+            ])
2955+            return found_tables
2956+        else:
2957+            return tables
2958 
2959     def installed_models(self, tables):
2960-        "Returns a set of all models represented by the provided list of table names."
2961+        """
2962+        Returns a set of all models represented by the provided list of table names.
2963+
2964+        The given tables are assumed to be pre-converted.
2965+        """
2966         from django.db import models, router
2967         all_models = []
2968         for app in models.get_apps():
2969             for model in models.get_models(app):
2970                 if router.allow_syncdb(self.connection.alias, model):
2971                     all_models.append(model)
2972-        tables = map(self.table_name_converter, tables)
2973         return set([
2974             m for m in all_models
2975-            if self.table_name_converter(m._meta.db_table) in tables
2976+            if self.qname_converter(m._meta.qualified_name) in tables
2977         ])
2978 
2979     def sequence_list(self):
2980-        "Returns a list of information about all DB sequences for all models in all apps."
2981+        """
2982+        Returns a list of information about all DB sequences for all models
2983+        in all apps.
2984+        """
2985         from django.db import models, router
2986 
2987         apps = models.get_apps()
2988@@ -952,14 +1065,17 @@
2989                     continue
2990                 for f in model._meta.local_fields:
2991                     if isinstance(f, models.AutoField):
2992-                        sequence_list.append({'table': model._meta.db_table, 'column': f.column})
2993+                        qname = self.qname_converter(model._meta.qualified_name)
2994+                        sequence_list.append({'qname': qname, 'column': f.column})
2995                         break # Only one AutoField is allowed per model, so don't bother continuing.
2996 
2997                 for f in model._meta.local_many_to_many:
2998                     # If this is an m2m using an intermediate table,
2999                     # we don't need to reset the sequence.
3000                     if f.rel.through is None:
3001-                        sequence_list.append({'table': f.m2m_db_table(), 'column': None})
3002+                        qname = self.qname_converter(f.m2m_qualified_name())
3003+                        sequence_list.append({'qname': qname,
3004+                                              'column': None})
3005 
3006         return sequence_list
3007 
3008Index: django/core/management/commands/syncdb.py
3009===================================================================
3010--- django/core/management/commands/syncdb.py   (revision 17914)
3011+++ django/core/management/commands/syncdb.py   (working copy)
3012@@ -55,9 +55,14 @@
3013         db = options.get('database')
3014         connection = connections[db]
3015         cursor = connection.cursor()
3016-
3017+        converter = connection.introspection.qname_converter
3018+        # We might fetch the same table multiple times - once as qualified and
3019+        # once as visible table (None, t). That is wanted, so that we can easily
3020+        # see if a model with schema = None is installed, as well as if model with
3021+        # locked schema is installed.
3022+        tables = connection.introspection.all_qualified_names()
3023+       
3024         # Get a list of already installed *models* so that references work right.
3025-        tables = connection.introspection.table_names()
3026         seen_models = connection.introspection.installed_models(tables)
3027         created_models = set()
3028         pending_references = {}
3029@@ -71,9 +76,8 @@
3030         ]
3031         def model_installed(model):
3032             opts = model._meta
3033-            converter = connection.introspection.table_name_converter
3034-            return not ((converter(opts.db_table) in tables) or
3035-                (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables))
3036+            return not ((converter(opts.qualified_name) in tables) or
3037+                (opts.auto_created and converter(opts.auto_created._meta.qualified_name) in tables))
3038 
3039         manifest = SortedDict(
3040             (app_name, filter(model_installed, model_list))
3041@@ -83,30 +87,57 @@
3042         # Create the tables for each model
3043         if verbosity >= 1:
3044             print "Creating tables ..."
3045+        seen_schemas = connection.introspection.schema_names()
3046+        seen_schemas = set([connection.introspection.identifier_converter(s)
3047+                            for s in seen_schemas])
3048+
3049         for app_name, model_list in manifest.items():
3050             for model in model_list:
3051                 # Create the model's database table, if it doesn't already exist.
3052                 if verbosity >= 3:
3053                     print "Processing %s.%s model" % (app_name, model._meta.object_name)
3054-                sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
3055+                sql = []
3056+                schema = connection.convert_schema(model._meta.qualified_name[0])
3057+                if schema and schema not in seen_schemas:
3058+                    q = connection.creation.sql_create_schema(schema, self.style)
3059+                    if q:
3060+                        sql.append(q)
3061+                    seen_schemas.add(schema)
3062+                table_sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
3063+                sql.extend(table_sql)
3064                 seen_models.add(model)
3065                 created_models.add(model)
3066                 for refto, refs in references.items():
3067                     pending_references.setdefault(refto, []).extend(refs)
3068                     if refto in seen_models:
3069-                        sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
3070-                sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
3071+                        ref_sql = connection.creation.sql_for_pending_references(
3072+                            refto, self.style, pending_references)
3073+                        if ref_sql:
3074+                            sql.extend(ref_sql)
3075+                ref_sql = sql.extend(connection.creation.sql_for_pending_references(
3076+                    model, self.style, pending_references))
3077+                if ref_sql:
3078+                    sql.extend(ref_sql)
3079                 if verbosity >= 1 and sql:
3080-                    print "Creating table %s" % model._meta.db_table
3081+                    if model._meta.db_schema:
3082+                        print "Creating table %s.%s" % model._meta.qualified_name
3083+                    else:
3084+                        print "Creating table %s" % model._meta.db_table
3085                 for statement in sql:
3086                     cursor.execute(statement)
3087-                tables.append(connection.introspection.table_name_converter(model._meta.db_table))
3088+                tables.add(connection.introspection.qname_converter(model._meta.qualified_name))
3089 
3090-
3091         transaction.commit_unless_managed(using=db)
3092+        # We need to see if there are still some pending references left: this
3093+        # is possible on backends where we must do cross-schema references
3094+        # using different connections (hence also outside the above
3095+        # transaction)
3096+        if pending_references:
3097+            # Pass the references to connection-specific handler.
3098+            connection.creation.post_create_pending_references(pending_references)
3099 
3100-        # Send the post_syncdb signal, so individual apps can do whatever they need
3101-        # to do at this point.
3102+        # Send the post_syncdb signal, so individual apps can do whatever they
3103+        # need to do at this point.
3104         emit_post_sync_signal(created_models, verbosity, interactive, db)
3105 
3106         # The connection may have been closed by a syncdb handler.
3107Index: django/core/management/commands/loaddata.py
3108===================================================================
3109--- django/core/management/commands/loaddata.py (revision 17914)
3110+++ django/core/management/commands/loaddata.py (working copy)
3111@@ -218,8 +218,8 @@
3112 
3113             # Since we disabled constraint checks, we must manually check for
3114             # any invalid keys that might have been added
3115-            table_names = [model._meta.db_table for model in models]
3116-            connection.check_constraints(table_names=table_names)
3117+            qualified_names = [model._meta.qualified_name for model in models]
3118+            connection.check_constraints(table_names=qualified_names)
3119 
3120         except (SystemExit, KeyboardInterrupt):
3121             raise
3122Index: django/core/management/commands/inspectdb.py
3123===================================================================
3124--- django/core/management/commands/inspectdb.py        (revision 17914)
3125+++ django/core/management/commands/inspectdb.py        (working copy)
3126@@ -27,7 +27,7 @@
3127     def handle_inspection(self, options):
3128         connection = connections[options.get('database')]
3129 
3130-        table2model = lambda table_name: table_name.title().replace('_', '').replace(' ', '').replace('-', '')
3131+        table2model = lambda qname: qname[1].title().replace('_', '').replace(' ', '').replace('-', '')
3132 
3133         cursor = connection.cursor()
3134         yield "# This is an auto-generated Django model module."
3135@@ -41,17 +41,18 @@
3136         yield ''
3137         yield 'from %s import models' % self.db_module
3138         yield ''
3139-        for table_name in connection.introspection.get_table_list(cursor):
3140-            yield 'class %s(models.Model):' % table2model(table_name)
3141+        inspect = connection.introspection
3142+        for qname in inspect.get_visible_tables_list(cursor):
3143+            yield 'class %s(models.Model):' % table2model(qname)
3144             try:
3145-                relations = connection.introspection.get_relations(cursor, table_name)
3146+                relations = inspect.get_relations(cursor, qname)
3147             except NotImplementedError:
3148                 relations = {}
3149             try:
3150-                indexes = connection.introspection.get_indexes(cursor, table_name)
3151+                indexes = inspect.get_indexes(cursor, qname)
3152             except NotImplementedError:
3153                 indexes = {}
3154-            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
3155+            for i, row in enumerate(inspect.get_table_description(cursor, qname)):
3156                 column_name = row[0]
3157                 att_name = column_name.lower()
3158                 comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
3159@@ -82,7 +83,7 @@
3160                     comment_notes.append('Field name made lowercase.')
3161 
3162                 if i in relations:
3163-                    rel_to = relations[i][1] == table_name and "'self'" or table2model(relations[i][1])
3164+                    rel_to = relations[i][1] == qname and "'self'" or table2model(relations[i][1])
3165                     field_type = 'ForeignKey(%s' % rel_to
3166                     if att_name.endswith('_id'):
3167                         att_name = att_name[:-3]
3168@@ -91,7 +92,7 @@
3169                 else:
3170                     # Calling `get_field_type` to get the field type string and any
3171                     # additional paramters and notes.
3172-                    field_type, field_params, field_notes = self.get_field_type(connection, table_name, row)
3173+                    field_type, field_params, field_notes = self.get_field_type(connection, qname, row)
3174                     extra_params.update(field_params)
3175                     comment_notes.extend(field_notes)
3176 
3177@@ -118,7 +119,6 @@
3178                     extra_params['blank'] = True
3179                     if not field_type in ('TextField(', 'CharField('):
3180                         extra_params['null'] = True
3181-
3182                 field_desc = '%s = models.%s' % (att_name, field_type)
3183                 if extra_params:
3184                     if not field_desc.endswith('('):
3185@@ -128,10 +128,10 @@
3186                 if comment_notes:
3187                     field_desc += ' # ' + ' '.join(comment_notes)
3188                 yield '    %s' % field_desc
3189-            for meta_line in self.get_meta(table_name):
3190+            for meta_line in self.get_meta(qname):
3191                 yield meta_line
3192 
3193-    def get_field_type(self, connection, table_name, row):
3194+    def get_field_type(self, connection, qname, row):
3195         """
3196         Given the database connection, the table name, and the cursor row
3197         description, this routine will return the given field type name, as
3198@@ -162,12 +162,13 @@
3199 
3200         return field_type, field_params, field_notes
3201 
3202-    def get_meta(self, table_name):
3203+    def get_meta(self, qname):
3204         """
3205         Return a sequence comprising the lines of code necessary
3206         to construct the inner Meta class for the model corresponding
3207         to the given database table name.
3208         """
3209         return ['    class Meta:',
3210-                '        db_table = %r' % table_name,
3211+                '        db_table = %r' % qname[1],
3212+                '        db_schema = %r' % qname[0] or 'None',
3213                 '']
3214Index: django/core/management/validation.py
3215===================================================================
3216--- django/core/management/validation.py        (revision 17914)
3217+++ django/core/management/validation.py        (working copy)
3218@@ -19,7 +19,6 @@
3219     validates all models of all installed apps. Writes errors, if any, to outfile.
3220     Returns number of errors.
3221     """
3222-    from django.conf import settings
3223     from django.db import models, connection
3224     from django.db.models.loading import get_app_errors
3225     from django.db.models.fields.related import RelatedObject
3226@@ -32,7 +31,7 @@
3227 
3228     for cls in models.get_models(app):
3229         opts = cls._meta
3230-
3231+       
3232         # Do field-specific validation.
3233         for f in opts.local_fields:
3234             if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
3235Index: django/core/management/sql.py
3236===================================================================
3237--- django/core/management/sql.py       (revision 17914)
3238+++ django/core/management/sql.py       (working copy)
3239@@ -3,12 +3,11 @@
3240 
3241 from django.conf import settings
3242 from django.core.management.base import CommandError
3243-from django.db import models
3244+from django.db import models, QName
3245 from django.db.models import get_models
3246 
3247 def sql_create(app, style, connection):
3248     "Returns a list of the CREATE TABLE SQL statements for the given app."
3249-
3250     if connection.settings_dict['ENGINE'] == 'django.db.backends.dummy':
3251         # This must be the "dummy" database backend, which means the user
3252         # hasn't set ENGINE for the database.
3253@@ -23,11 +22,16 @@
3254     # we can be conservative).
3255     app_models = models.get_models(app, include_auto_created=True)
3256     final_output = []
3257-    tables = connection.introspection.table_names()
3258+    tables = connection.introspection.all_qualified_names()
3259     known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
3260     pending_references = {}
3261 
3262     for model in app_models:
3263+        schema = connection.convert_schema(model._meta.db_schema)
3264+        if schema:
3265+            output = connection.creation.sql_create_schema(schema, style)
3266+            if output:
3267+                final_output.append(output)
3268         output, references = connection.creation.sql_create_model(model, style, known_models)
3269         final_output.extend(output)
3270         for refto, refs in references.items():
3271@@ -63,7 +67,7 @@
3272 
3273     # Figure out which tables already exist
3274     if cursor:
3275-        table_names = connection.introspection.get_table_list(cursor)
3276+        table_names = connection.introspection.all_qualified_names(converted=True)
3277     else:
3278         table_names = []
3279 
3280@@ -75,7 +79,7 @@
3281     references_to_delete = {}
3282     app_models = models.get_models(app, include_auto_created=True)
3283     for model in app_models:
3284-        if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
3285+        if cursor and connection.introspection.qname_converter(model._meta.qualified_name) in table_names:
3286             # The table exists, so it needs to be dropped
3287             opts = model._meta
3288             for f in opts.local_fields:
3289@@ -85,7 +89,7 @@
3290             to_delete.add(model)
3291 
3292     for model in app_models:
3293-        if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
3294+        if connection.introspection.qname_converter(model._meta.qualified_name) in table_names:
3295             output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
3296 
3297     # Close database connection explicitly, in case this output is being piped
3298@@ -106,7 +110,9 @@
3299     if only_django:
3300         tables = connection.introspection.django_table_names(only_existing=True)
3301     else:
3302-        tables = connection.introspection.table_names()
3303+        tables = connection.introspection.all_qualified_names()
3304+    if [t for t in tables if not isinstance(t, QName)]:
3305+        import ipdb; ipdb.set_trace()
3306     statements = connection.ops.sql_flush(
3307         style, tables, connection.introspection.sequence_list()
3308     )
3309@@ -145,7 +151,7 @@
3310     if opts.managed:
3311         post_sql_fields = [f for f in opts.local_fields if hasattr(f, 'post_create_sql')]
3312         for f in post_sql_fields:
3313-            output.extend(f.post_create_sql(style, model._meta.db_table))
3314+            output.extend(f.post_create_sql(style, model._meta.qualified_name))
3315 
3316     # Some backends can't execute more than one SQL statement at a time,
3317     # so split into separate statements.
3318Index: django/contrib/contenttypes/generic.py
3319===================================================================
3320--- django/contrib/contenttypes/generic.py      (revision 17914)
3321+++ django/contrib/contenttypes/generic.py      (working copy)
3322@@ -173,6 +173,12 @@
3323     def m2m_db_table(self):
3324         return self.rel.to._meta.db_table
3325 
3326+    def m2m_qualified_name(self):
3327+        return self.rel.to._meta.qualified_name
3328+
3329+    def m2m_db_schema(self):
3330+        return self.rel.to._meta.db_schema
3331+
3332     def m2m_column_name(self):
3333         return self.object_id_field_name
3334 
3335Index: django/test/simple.py
3336===================================================================
3337--- django/test/simple.py       (revision 17914)
3338+++ django/test/simple.py       (working copy)
3339@@ -271,6 +271,8 @@
3340         dependencies = {}
3341         for alias in connections:
3342             connection = connections[alias]
3343+            if not connection.settings_dict['TEST_SCHEMA_PREFIX'] is None:
3344+                connection.settings_dict['TEST_SCHEMA_PREFIX'] = '%s_' % alias
3345             if connection.settings_dict['TEST_MIRROR']:
3346                 # If the database is marked as a test mirror, save
3347                 # the alias.
3348@@ -301,20 +303,20 @@
3349             test_databases.items(), dependencies):
3350             # Actually create the database for the first connection
3351             connection = connections[aliases[0]]
3352-            old_names.append((connection, db_name, True))
3353-            test_db_name = connection.creation.create_test_db(
3354+            test_db_name, created_schemas = connection.creation.create_test_db(
3355                 self.verbosity, autoclobber=not self.interactive)
3356+            old_names.append((connection, db_name, True, created_schemas))
3357             for alias in aliases[1:]:
3358                 connection = connections[alias]
3359                 if db_name:
3360-                    old_names.append((connection, db_name, False))
3361+                    old_names.append((connection, db_name, False, []))
3362                     connection.settings_dict['NAME'] = test_db_name
3363                 else:
3364                     # If settings_dict['NAME'] isn't defined, we have a backend
3365                     # where the name isn't important -- e.g., SQLite, which
3366                     # uses :memory:. Force create the database instead of
3367                     # assuming it's a duplicate.
3368-                    old_names.append((connection, db_name, True))
3369+                    old_names.append((connection, db_name, True, []))
3370                     connection.creation.create_test_db(
3371                         self.verbosity, autoclobber=not self.interactive)
3372 
3373@@ -335,9 +337,9 @@
3374         Destroys all the non-mirror databases.
3375         """
3376         old_names, mirrors = old_config
3377-        for connection, old_name, destroy in old_names:
3378+        for connection, old_name, destroy, created_schemas in old_names:
3379             if destroy:
3380-                connection.creation.destroy_test_db(old_name, self.verbosity)
3381+                connection.creation.destroy_test_db(old_name, created_schemas, self.verbosity)
3382 
3383     def teardown_test_environment(self, **kwargs):
3384         unittest.removeHandler()
3385Index: tests/modeltests/raw_query/tests.py
3386===================================================================
3387--- tests/modeltests/raw_query/tests.py (revision 17914)
3388+++ tests/modeltests/raw_query/tests.py (working copy)
3389@@ -2,7 +2,8 @@
3390 
3391 from datetime import date
3392 
3393-from django.db.models.sql.query import InvalidQuery
3394+from django.db.models.query_utils import InvalidQuery
3395+from django.db import connection
3396 from django.test import TestCase
3397 
3398 from .models import Author, Book, Coffee, Reviewer, FriendlyAuthor
3399@@ -59,7 +60,7 @@
3400         """
3401         Basic test of raw query with a simple database query
3402         """
3403-        query = "SELECT * FROM raw_query_author"
3404+        query = "SELECT * FROM %s" % connection.qname(Author)
3405         authors = Author.objects.all()
3406         self.assertSuccessfulRawQuery(Author, query, authors)
3407 
3408@@ -68,7 +69,7 @@
3409         Raw queries are lazy: they aren't actually executed until they're
3410         iterated over.
3411         """
3412-        q = Author.objects.raw('SELECT * FROM raw_query_author')
3413+        q = Author.objects.raw('SELECT * FROM %s' % connection.qname(Author))
3414         self.assertTrue(q.query.cursor is None)
3415         list(q)
3416         self.assertTrue(q.query.cursor is not None)
3417@@ -77,7 +78,7 @@
3418         """
3419         Test of a simple raw query against a model containing a foreign key
3420         """
3421-        query = "SELECT * FROM raw_query_book"
3422+        query = "SELECT * FROM %s" % connection.qname(Book)
3423         books = Book.objects.all()
3424         self.assertSuccessfulRawQuery(Book, query, books)
3425 
3426@@ -86,7 +87,7 @@
3427         Test of a simple raw query against a model containing a field with
3428         db_column defined.
3429         """
3430-        query = "SELECT * FROM raw_query_coffee"
3431+        query = "SELECT * FROM %s" % connection.qname(Coffee)
3432         coffees = Coffee.objects.all()
3433         self.assertSuccessfulRawQuery(Coffee, query, coffees)
3434 
3435@@ -102,7 +103,7 @@
3436         )
3437 
3438         for select in selects:
3439-            query = "SELECT %s FROM raw_query_author" % select
3440+            query = "SELECT %s FROM %s" % (select, connection.qname(Author))
3441             authors = Author.objects.all()
3442             self.assertSuccessfulRawQuery(Author, query, authors)
3443 
3444@@ -111,7 +112,8 @@
3445         Test of raw query's optional ability to translate unexpected result
3446         column names to specific model fields
3447         """
3448-        query = "SELECT first_name AS first, last_name AS last, dob, id FROM raw_query_author"
3449+        query = ("SELECT first_name AS first, last_name AS last, dob, id FROM %s"
3450+                 % connection.qname(Author))
3451         translations = {'first': 'first_name', 'last': 'last_name'}
3452         authors = Author.objects.all()
3453         self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
3454@@ -120,7 +122,7 @@
3455         """
3456         Test passing optional query parameters
3457         """
3458-        query = "SELECT * FROM raw_query_author WHERE first_name = %s"
3459+        query = "SELECT * FROM %s WHERE first_name = %%s" % connection.qname(Author)
3460         author = Author.objects.all()[2]
3461         params = [author.first_name]
3462         results = list(Author.objects.raw(query, params=params))
3463@@ -132,7 +134,7 @@
3464         """
3465         Test of a simple raw query against a model containing a m2m field
3466         """
3467-        query = "SELECT * FROM raw_query_reviewer"
3468+        query = "SELECT * FROM %s" % connection.qname(Reviewer)
3469         reviewers = Reviewer.objects.all()
3470         self.assertSuccessfulRawQuery(Reviewer, query, reviewers)
3471 
3472@@ -140,20 +142,20 @@
3473         """
3474         Test to insure that extra translations are ignored.
3475         """
3476-        query = "SELECT * FROM raw_query_author"
3477+        query = "SELECT * FROM %s" % connection.qname(Author)
3478         translations = {'something': 'else'}
3479         authors = Author.objects.all()
3480         self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
3481 
3482     def testMissingFields(self):
3483-        query = "SELECT id, first_name, dob FROM raw_query_author"
3484+        query = "SELECT id, first_name, dob FROM %s" % connection.qname(Author)
3485         for author in Author.objects.raw(query):
3486             self.assertNotEqual(author.first_name, None)
3487             # last_name isn't given, but it will be retrieved on demand
3488             self.assertNotEqual(author.last_name, None)
3489 
3490     def testMissingFieldsWithoutPK(self):
3491-        query = "SELECT first_name, dob FROM raw_query_author"
3492+        query = "SELECT first_name, dob FROM %s" % connection.qname(Author)
3493         try:
3494             list(Author.objects.raw(query))
3495             self.fail('Query without primary key should fail')
3496@@ -161,7 +163,7 @@
3497             pass
3498 
3499     def testAnnotations(self):
3500-        query = "SELECT a.*, count(b.id) as book_count FROM raw_query_author a LEFT JOIN raw_query_book b ON a.id = b.author_id GROUP BY a.id, a.first_name, a.last_name, a.dob ORDER BY a.id"
3501+        query = "SELECT a.*, count(b.id) as book_count FROM %s a LEFT JOIN %s b ON a.id = b.author_id GROUP BY a.id, a.first_name, a.last_name, a.dob ORDER BY a.id" % (connection.qname(Author), connection.qname(Book))
3502         expected_annotations = (
3503             ('book_count', 3),
3504             ('book_count', 0),
3505@@ -172,12 +174,12 @@
3506         self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
3507 
3508     def testWhiteSpaceQuery(self):
3509-        query = "    SELECT * FROM raw_query_author"
3510+        query = "    SELECT * FROM %s" % connection.qname(Author)
3511         authors = Author.objects.all()
3512         self.assertSuccessfulRawQuery(Author, query, authors)
3513 
3514     def testMultipleIterations(self):
3515-        query = "SELECT * FROM raw_query_author"
3516+        query = "SELECT * FROM %s" % connection.qname(Author)
3517         normal_authors = Author.objects.all()
3518         raw_authors = Author.objects.raw(query)
3519 
3520@@ -197,7 +199,7 @@
3521 
3522     def testGetItem(self):
3523         # Indexing on RawQuerySets
3524-        query = "SELECT * FROM raw_query_author ORDER BY id ASC"
3525+        query = "SELECT * FROM %s ORDER BY id ASC" % connection.qname(Author)
3526         third_author = Author.objects.raw(query)[2]
3527         self.assertEqual(third_author.first_name, 'Bob')
3528 
3529@@ -211,12 +213,12 @@
3530         # Wesley was bron
3531         f = FriendlyAuthor.objects.create(first_name="Wesley", last_name="Chun",
3532             dob=date(1962, 10, 28))
3533-        query = "SELECT * FROM raw_query_friendlyauthor"
3534+        query = "SELECT * FROM %s" % connection.qname(FriendlyAuthor)
3535         self.assertEqual(
3536             [o.pk for o in FriendlyAuthor.objects.raw(query)], [f.pk]
3537         )
3538 
3539     def test_query_count(self):
3540         self.assertNumQueries(1,
3541-            list, Author.objects.raw("SELECT * FROM raw_query_author")
3542+            list, Author.objects.raw("SELECT * FROM %s" % connection.qname(Author))
3543         )
3544Index: tests/modeltests/timezones/tests.py
3545===================================================================
3546--- tests/modeltests/timezones/tests.py (revision 17914)
3547+++ tests/modeltests/timezones/tests.py (working copy)
3548@@ -264,7 +264,9 @@
3549         dt = datetime.datetime(2011, 9, 1, 13, 20, 30)
3550         event = Event.objects.create(dt=dt)
3551         self.assertQuerysetEqual(
3552-                Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]),
3553+                Event.objects.raw('SELECT * FROM %s WHERE dt = %%s'
3554+                                  % connection.qname(Event),
3555+                                  [dt]),
3556                 [event],
3557                 transform=lambda d: d)
3558 
3559@@ -476,7 +478,8 @@
3560         dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)
3561         event = Event.objects.create(dt=dt)
3562         self.assertQuerysetEqual(
3563-                Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]),
3564+                Event.objects.raw('SELECT * FROM %s WHERE dt = %%s'
3565+                                  % connection.qname(Event), [dt]),
3566                 [event],
3567                 transform=lambda d: d)
3568 
3569Index: tests/modeltests/proxy_models/tests.py
3570===================================================================
3571--- tests/modeltests/proxy_models/tests.py      (revision 17914)
3572+++ tests/modeltests/proxy_models/tests.py      (working copy)
3573@@ -3,9 +3,9 @@
3574 from django.contrib.contenttypes.models import ContentType
3575 from django.core import management
3576 from django.core.exceptions import FieldError
3577-from django.db import models, DEFAULT_DB_ALIAS
3578+from django.db import models, DEFAULT_DB_ALIAS, transaction, IntegrityError
3579 from django.db.models import signals
3580-from django.test import TestCase
3581+from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature
3582 
3583 
3584 from .models import (MyPerson, Person, StatusPerson, LowerStatusPerson,
3585@@ -326,3 +326,17 @@
3586         management.call_command('loaddata', 'mypeople.json', verbosity=0, commit=False)
3587         p = MyPerson.objects.get(pk=100)
3588         self.assertEqual(p.name, 'Elvis Presley')
3589+
3590+class TransactionalProxyModelTests(TransactionTestCase):
3591+    @skipUnlessDBFeature('supports_foreign_keys')
3592+    def test_proxy_fk(self):
3593+        """
3594+        Test that the DB contains proper foreign keys for proxy model references.
3595+        """
3596+        @transaction.commit_on_success
3597+        def create_failing_pk():
3598+            t = TrackerUser.objects.create(status='bar')
3599+            Improvement.objects.create(summary='foof', version='foof',
3600+                                       reporter_id=1, associated_bug_id=1,
3601+                                       assignee=t)
3602+        self.assertRaises(IntegrityError, create_failing_pk)
3603Index: tests/modeltests/select_for_update/tests.py
3604===================================================================
3605--- tests/modeltests/select_for_update/tests.py (revision 17914)
3606+++ tests/modeltests/select_for_update/tests.py (working copy)
3607@@ -36,7 +36,6 @@
3608         # issuing a SELECT ... FOR UPDATE will block.
3609         new_connections = ConnectionHandler(settings.DATABASES)
3610         self.new_connection = new_connections[DEFAULT_DB_ALIAS]
3611-
3612         # We need to set settings.DEBUG to True so we can capture
3613         # the output SQL to examine.
3614         self._old_debug = settings.DEBUG
3615@@ -62,11 +61,11 @@
3616         # end_blocking_transaction() should be called.
3617         self.cursor = self.new_connection.cursor()
3618         sql = 'SELECT * FROM %(db_table)s %(for_update)s;' % {
3619-            'db_table': Person._meta.db_table,
3620+            'db_table': self.new_connection.qname(Person),
3621             'for_update': self.new_connection.ops.for_update_sql(),
3622             }
3623         self.cursor.execute(sql, ())
3624-        result = self.cursor.fetchone()
3625+        self.cursor.fetchone()
3626 
3627     def end_blocking_transaction(self):
3628         # Roll back the blocking transaction.
3629@@ -241,7 +240,7 @@
3630                 list(
3631                     Person.objects.raw(
3632                         'SELECT * FROM %s %s' % (
3633-                            Person._meta.db_table,
3634+                            connection.qname(Person),
3635                             connection.ops.for_update_sql(nowait=True)
3636                         )
3637                     )
3638Index: tests/modeltests/unmanaged_models/tests.py
3639===================================================================
3640--- tests/modeltests/unmanaged_models/tests.py  (revision 17914)
3641+++ tests/modeltests/unmanaged_models/tests.py  (working copy)
3642@@ -48,14 +48,16 @@
3643         """
3644         The intermediary table between two unmanaged models should not be created.
3645         """
3646-        table = Unmanaged2._meta.get_field('mm').m2m_db_table()
3647-        tables = connection.introspection.table_names()
3648-        self.assertTrue(table not in tables, "Table '%s' should not exist, but it does." % table)
3649+        conv = connection.introspection.qname_converter
3650+        table = conv(Unmanaged2.mm.through._meta.qualified_name)
3651+        tables = connection.introspection.all_qualified_names()
3652+        self.assertTrue(table not in tables, "Table '%s' should not exist, but it does." % table[1])
3653 
3654     def test_many_to_many_between_unmanaged_and_managed(self):
3655         """
3656         An intermediary table between a managed and an unmanaged model should be created.
3657         """
3658-        table = Managed1._meta.get_field('mm').m2m_db_table()
3659-        tables = connection.introspection.table_names()
3660-        self.assertTrue(table in tables, "Table '%s' does not exist." % table)
3661+        conv = connection.introspection.qname_converter
3662+        table = conv(Managed1.mm.through._meta.qualified_name)
3663+        tables = connection.introspection.all_qualified_names()
3664+        self.assertTrue(table in tables, "Table '%s' does not exist." % table[1])
3665Index: tests/modeltests/many_to_one/tests.py
3666===================================================================
3667--- tests/modeltests/many_to_one/tests.py       (revision 17914)
3668+++ tests/modeltests/many_to_one/tests.py       (working copy)
3669@@ -4,6 +4,7 @@
3670 from datetime import datetime
3671 
3672 from django.core.exceptions import MultipleObjectsReturned
3673+from django.db import connection
3674 from django.test import TestCase
3675 from django.utils.translation import ugettext_lazy
3676 
3677@@ -169,7 +170,7 @@
3678         # The automatically joined table has a predictable name.
3679         self.assertQuerysetEqual(
3680             Article.objects.filter(reporter__first_name__exact='John').extra(
3681-                where=["many_to_one_reporter.last_name='Smith'"]),
3682+                where=["%s.last_name='Smith'" % connection.qname(Reporter)]),
3683             [
3684                 "<Article: John's second story>",
3685                 "<Article: This is a test>",
3686@@ -177,7 +178,8 @@
3687         # ... and should work fine with the unicode that comes out of forms.Form.cleaned_data
3688         self.assertQuerysetEqual(
3689             Article.objects.filter(reporter__first_name__exact='John'
3690-                                  ).extra(where=["many_to_one_reporter.last_name='%s'" % u'Smith']),
3691+                                  ).extra(where=["%s.last_name='%s'" %
3692+                                                 (connection.qname(Reporter), u'Smith')]),
3693             [
3694                 "<Article: John's second story>",
3695                 "<Article: This is a test>",
3696Index: tests/modeltests/prefetch_related/tests.py
3697===================================================================
3698--- tests/modeltests/prefetch_related/tests.py  (revision 17914)
3699+++ tests/modeltests/prefetch_related/tests.py  (working copy)
3700@@ -372,8 +372,9 @@
3701             l = [a.authorwithage for a in Author.objects.prefetch_related('authorwithage')]
3702 
3703         # Regression for #18090: the prefetching query must include an IN clause.
3704-        self.assertIn('authorwithage', connection.queries[-1]['sql'])
3705-        self.assertIn(' IN ', connection.queries[-1]['sql'])
3706+        executed_sql = connection.queries[-1]['sql'].lower()
3707+        self.assertIn('authorwithage', executed_sql)
3708+        self.assertIn(' in ', executed_sql)
3709 
3710         self.assertEqual(l, [a.authorwithage for a in Author.objects.all()])
3711 
3712Index: tests/modeltests/select_related/tests.py
3713===================================================================
3714--- tests/modeltests/select_related/tests.py    (revision 17914)
3715+++ tests/modeltests/select_related/tests.py    (working copy)
3716@@ -1,5 +1,6 @@
3717 from __future__ import absolute_import
3718 
3719+from django.db import connection
3720 from django.test import TestCase
3721 
3722 from .models import Domain, Kingdom, Phylum, Klass, Order, Family, Genus, Species
3723@@ -120,7 +121,7 @@
3724 
3725     def test_select_related_with_extra(self):
3726         s = Species.objects.all().select_related(depth=1)\
3727-            .extra(select={'a': 'select_related_species.id + 10'})[0]
3728+            .extra(select={'a': '%s.id + 10' % connection.qname(Species)})[0]
3729         self.assertEqual(s.id + 10, s.a)
3730 
3731     def test_certain_fields(self):
3732Index: tests/modeltests/custom_methods/models.py
3733===================================================================
3734--- tests/modeltests/custom_methods/models.py   (revision 17914)
3735+++ tests/modeltests/custom_methods/models.py   (working copy)
3736@@ -31,8 +31,9 @@
3737         cursor = connection.cursor()
3738         cursor.execute("""
3739             SELECT id, headline, pub_date
3740-            FROM custom_methods_article
3741-            WHERE pub_date = %s
3742-                AND id != %s""", [connection.ops.value_to_db_date(self.pub_date),
3743-                                  self.id])
3744+            FROM %s
3745+            WHERE pub_date = %%s
3746+                AND id != %%s""" % connection.qname(self),
3747+                       [connection.ops.value_to_db_date(self.pub_date),
3748+                        self.id])
3749         return [self.__class__(*row) for row in cursor.fetchall()]
3750Index: tests/modeltests/transactions/tests.py
3751===================================================================
3752--- tests/modeltests/transactions/tests.py      (revision 17914)
3753+++ tests/modeltests/transactions/tests.py      (working copy)
3754@@ -164,7 +164,8 @@
3755 class TransactionRollbackTests(TransactionTestCase):
3756     def execute_bad_sql(self):
3757         cursor = connection.cursor()
3758-        cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
3759+        tbl = connection.qname(Reporter)
3760+        cursor.execute("INSERT INTO %s (first_name, last_name) VALUES ('Douglas', 'Adams');" % tbl)
3761         transaction.set_dirty()
3762 
3763     @skipUnlessDBFeature('requires_rollback_on_dirty_transaction')
3764@@ -305,6 +306,7 @@
3765         with self.assertRaises(IntegrityError):
3766             with transaction.commit_on_success():
3767                 cursor = connection.cursor()
3768-                cursor.execute("INSERT INTO transactions_reporter (first_name, last_name) VALUES ('Douglas', 'Adams');")
3769+                tbl = connection.qname(Reporter)
3770+                cursor.execute("INSERT INTO %s (first_name, last_name) VALUES ('Douglas', 'Adams');" % tbl)
3771                 transaction.set_dirty()
3772         transaction.rollback()
3773Index: tests/regressiontests/transactions_regress/tests.py
3774===================================================================
3775--- tests/regressiontests/transactions_regress/tests.py (revision 17914)
3776+++ tests/regressiontests/transactions_regress/tests.py (working copy)
3777@@ -25,7 +25,8 @@
3778         def raw_sql():
3779             "Write a record using raw sql under a commit_on_success decorator"
3780             cursor = connection.cursor()
3781-            cursor.execute("INSERT into transactions_regress_mod (id,fld) values (17,18)")
3782+            tbl = connection.qname(Mod)
3783+            cursor.execute("INSERT into %s (id,fld) values (17,18)" % tbl)
3784 
3785         raw_sql()
3786         # Rollback so that if the decorator didn't commit, the record is unwritten
3787@@ -116,9 +117,10 @@
3788             be committed.
3789             """
3790             cursor = connection.cursor()
3791-            cursor.execute("INSERT into transactions_regress_mod (id,fld) values (1,2)")
3792+            tbl = connection.qname(Mod)
3793+            cursor.execute("INSERT into %s (id,fld) values (1,2)" % tbl)
3794             transaction.rollback()
3795-            cursor.execute("INSERT into transactions_regress_mod (id,fld) values (1,2)")
3796+            cursor.execute("INSERT into %s (id,fld) values (1,2)" % tbl)
3797 
3798         reuse_cursor_ref()
3799         # Rollback so that if the decorator didn't commit, the record is unwritten
3800Index: tests/regressiontests/queries/tests.py
3801===================================================================
3802--- tests/regressiontests/queries/tests.py      (revision 17914)
3803+++ tests/regressiontests/queries/tests.py      (working copy)
3804@@ -6,6 +6,7 @@
3805 
3806 from django.conf import settings
3807 from django.core.exceptions import FieldError
3808+from django.contrib.sites.models import Site
3809 from django.db import DatabaseError, connection, connections, DEFAULT_DB_ALIAS
3810 from django.db.models import Count
3811 from django.db.models.query import Q, ITER_CHUNK_SIZE, EmptyQuerySet
3812@@ -387,7 +388,7 @@
3813 
3814     def test_ticket2496(self):
3815         self.assertQuerysetEqual(
3816-            Item.objects.extra(tables=['queries_author']).select_related().order_by('name')[:1],
3817+            Item.objects.extra(tables=[Author._meta.qualified_name]).select_related().order_by('name')[:1],
3818             ['<Item: four>']
3819         )
3820 
3821@@ -507,7 +508,11 @@
3822         self.assertEqual(d, {'a': u'one', 'b': u'two'})
3823 
3824         # Order by the number of tags attached to an item.
3825-        l = Item.objects.extra(select={'count': 'select count(*) from queries_item_tags where queries_item_tags.item_id = queries_item.id'}).order_by('-count')
3826+        l = Item.objects.extra(select={
3827+            'count':'select count(*) from %s where %s.item_id = %s.id' %
3828+            (connection.qname(Item.tags.through), connection.qname(Item.tags.through),
3829+             connection.qname(Item))
3830+        }).order_by('-count')
3831         self.assertEqual([o.count for o in l], [2, 2, 1, 0])
3832 
3833     def test_ticket6154(self):
3834@@ -577,11 +582,15 @@
3835 
3836     def test_ticket7098(self):
3837         # Make sure semi-deprecated ordering by related models syntax still
3838-        # works.
3839-        self.assertValueQuerysetEqual(
3840-            Item.objects.values('note__note').order_by('queries_note.note', 'id'),
3841-            [{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
3842-        )
3843+        # works.       
3844+        # Skip this test if schema support is in effect - there is little point to fix the
3845+        # deprecated .order_by() notation to support schemas.
3846+        if not connection.schema:
3847+            self.assertValueQuerysetEqual(
3848+                # Need to remove the quotes from the table name for this test...
3849+                Item.objects.values('note__note').order_by('%s.note' % connection.qname(Note)[1:-1], 'id'),
3850+                [{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
3851+            )
3852 
3853     def test_ticket7096(self):
3854         # Make sure exclude() with multiple conditions continues to work.
3855@@ -1216,7 +1225,8 @@
3856         # Ordering of extra() pieces is possible, too and you can mix extra
3857         # fields and model fields in the ordering.
3858         self.assertQuerysetEqual(
3859-            Ranking.objects.extra(tables=['django_site'], order_by=['-django_site.id', 'rank']),
3860+            Ranking.objects.extra(tables=[Site._meta.qualified_name],
3861+                                  order_by=['-%s.id' % connection.qname(Site), 'rank']),
3862             ['<Ranking: 1: a3>', '<Ranking: 2: a2>', '<Ranking: 3: a1>']
3863         )
3864 
3865@@ -1251,7 +1261,7 @@
3866 
3867     def test_ticket7045(self):
3868         # Extra tables used to crash SQL construction on the second use.
3869-        qs = Ranking.objects.extra(tables=['django_site'])
3870+        qs = Ranking.objects.extra(tables=[connection.qname(Site)])
3871         qs.query.get_compiler(qs.db).as_sql()
3872         # test passes if this doesn't raise an exception.
3873         qs.query.get_compiler(qs.db).as_sql()
3874Index: tests/regressiontests/delete_regress/tests.py
3875===================================================================
3876--- tests/regressiontests/delete_regress/tests.py       (revision 17914)
3877+++ tests/regressiontests/delete_regress/tests.py       (working copy)
3878@@ -17,6 +17,8 @@
3879     def setUp(self):
3880         # Create a second connection to the default database
3881         conn_settings = settings.DATABASES[DEFAULT_DB_ALIAS]
3882+        # TODO: there must be a better way to do this copying. .deepcopy()
3883+        # perhaps?
3884         self.conn2 = backend.DatabaseWrapper({
3885             'HOST': conn_settings['HOST'],
3886             'NAME': conn_settings['NAME'],
3887@@ -25,6 +27,9 @@
3888             'PORT': conn_settings['PORT'],
3889             'USER': conn_settings['USER'],
3890             'TIME_ZONE': settings.TIME_ZONE,
3891+            'SCHEMA': conn_settings['SCHEMA'],
3892+            'TEST_SCHEMA_PREFIX': conn_settings['TEST_SCHEMA_PREFIX'],
3893+            'TEST_SCHEMAS': conn_settings['TEST_SCHEMAS'],
3894         })
3895 
3896         # Put both DB connections into managed transaction mode
3897@@ -55,7 +60,7 @@
3898 
3899         # Delete something using connection 2.
3900         cursor2 = self.conn2.cursor()
3901-        cursor2.execute('DELETE from delete_regress_book WHERE id=1')
3902+        cursor2.execute('DELETE from %s WHERE id=1' % self.conn2.qname(Book))
3903         self.conn2._commit()
3904 
3905         # Now perform a queryset delete that covers the object
3906Index: tests/regressiontests/backends/tests.py
3907===================================================================
3908--- tests/regressiontests/backends/tests.py     (revision 17914)
3909+++ tests/regressiontests/backends/tests.py     (working copy)
3910@@ -9,7 +9,7 @@
3911 from django.core.management.color import no_style
3912 from django.core.exceptions import ImproperlyConfigured
3913 from django.db import (backend, connection, connections, DEFAULT_DB_ALIAS,
3914-    IntegrityError, transaction)
3915+    IntegrityError, transaction, QName)
3916 from django.db.backends.signals import connection_created
3917 from django.db.backends.postgresql_psycopg2 import version as pg_version
3918 from django.db.utils import ConnectionHandler, DatabaseError, load_backend
3919@@ -129,7 +129,7 @@
3920         "An executemany call with too many/not enough parameters will raise an exception (Refs #12612)"
3921         cursor = connection.cursor()
3922         query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (
3923-            connection.introspection.table_name_converter('backends_square'),
3924+            connection.introspection.identifier_converter('backends_square'),
3925             connection.ops.quote_name('root'),
3926             connection.ops.quote_name('square')
3927         ))
3928@@ -169,13 +169,13 @@
3929         VLM = models.VeryLongModelNameZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ
3930         VLM_m2m = VLM.m2m_also_quite_long_zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz.through
3931         tables = [
3932-            VLM._meta.db_table,
3933-            VLM_m2m._meta.db_table,
3934+            VLM._meta.qualified_name,
3935+            VLM_m2m._meta.qualified_name
3936         ]
3937         sequences = [
3938             {
3939                 'column': VLM._meta.pk.column,
3940-                'table': VLM._meta.db_table
3941+                'qname': QName(None, VLM._meta.db_table, False),
3942             },
3943         ]
3944         cursor = connection.cursor()
3945@@ -313,7 +313,7 @@
3946     def create_squares_with_executemany(self, args):
3947         cursor = connection.cursor()
3948         opts = models.Square._meta
3949-        tbl = connection.introspection.table_name_converter(opts.db_table)
3950+        tbl = connection.qname(models.Square)
3951         f1 = connection.ops.quote_name(opts.get_field('root').column)
3952         f2 = connection.ops.quote_name(opts.get_field('square').column)
3953         query = 'INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % (tbl, f1, f2)
3954@@ -358,7 +358,7 @@
3955         opts2 = models.Person._meta
3956         f3, f4 = opts2.get_field('first_name'), opts2.get_field('last_name')
3957         query2 = ('SELECT %s, %s FROM %s ORDER BY %s'
3958-          % (qn(f3.column), qn(f4.column), connection.introspection.table_name_converter(opts2.db_table),
3959+          % (qn(f3.column), qn(f4.column), connection.qname(models.Person),
3960              qn(f3.column)))
3961         cursor = connection.cursor()
3962         cursor.execute(query2)
3963@@ -375,7 +375,7 @@
3964     def test_duplicate_table_error(self):
3965         """ Test that creating an existing table returns a DatabaseError """
3966         cursor = connection.cursor()
3967-        query = 'CREATE TABLE %s (id INTEGER);' % models.Article._meta.db_table
3968+        query = 'CREATE TABLE %s (id INTEGER);' % connection.qname(models.Article)
3969         with self.assertRaises(DatabaseError):
3970             cursor.execute(query)
3971 
3972Index: tests/regressiontests/inspectdb/tests.py
3973===================================================================
3974--- tests/regressiontests/inspectdb/tests.py    (revision 17914)
3975+++ tests/regressiontests/inspectdb/tests.py    (working copy)
3976@@ -1,3 +1,5 @@
3977+from __future__ import absolute_import
3978+
3979 from StringIO import StringIO
3980 
3981 from django.core.management import call_command
3982@@ -2,2 +4,3 @@
3983 from django.test import TestCase, skipUnlessDBFeature
3984+from .models import People
3985 
3986@@ -8,14 +11,17 @@
3987 
3988     @skipUnlessDBFeature('can_introspect_foreign_keys')
3989     def test_attribute_name_not_python_keyword(self):
3990+        from django.db import connection
3991+        _, tbl, _ = connection.introspection.qname_converter(People._meta.qualified_name)
3992+        mname = ''.join(t.title() for t in tbl.split('_'))
3993         out = StringIO()
3994         call_command('inspectdb', stdout=out)
3995         error_message = "inspectdb generated an attribute name which is a python keyword"
3996-        self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", out.getvalue(), msg=error_message)
3997-        self.assertIn("from_field = models.ForeignKey(InspectdbPeople)", out.getvalue())
3998-        self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)",
3999+        self.assertNotIn("from = models.ForeignKey(%s)" % mname, out.getvalue(), msg=error_message)
4000+        self.assertIn("from_field = models.ForeignKey(%s)" % mname, out.getvalue())
4001+        self.assertIn("people_pk = models.ForeignKey(%s, primary_key=True)" % mname,
4002             out.getvalue())
4003-        self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)",
4004+        self.assertIn("people_unique = models.ForeignKey(%s, unique=True)" % mname,
4005             out.getvalue())
4006         out.close()
4007 
4008Index: tests/regressiontests/extra_regress/tests.py
4009===================================================================
4010--- tests/regressiontests/extra_regress/tests.py        (revision 17914)
4011+++ tests/regressiontests/extra_regress/tests.py        (working copy)
4012@@ -3,6 +3,7 @@
4013 import datetime
4014 
4015 from django.contrib.auth.models import User
4016+from django.db import connection
4017 from django.test import TestCase
4018 from django.utils.datastructures import SortedDict
4019 
4020@@ -42,7 +43,7 @@
4021         # Queryset to match most recent revision:
4022         qs = RevisionableModel.objects.extra(
4023                 where=["%(table)s.id IN (SELECT MAX(rev.id) FROM %(table)s rev GROUP BY rev.base_id)" % {
4024-                    'table': RevisionableModel._meta.db_table,
4025+                    'table': connection.qname(RevisionableModel),
4026                 }]
4027         )
4028 
4029Index: tests/regressiontests/aggregation_regress/tests.py
4030===================================================================
4031--- tests/regressiontests/aggregation_regress/tests.py  (revision 17914)
4032+++ tests/regressiontests/aggregation_regress/tests.py  (working copy)
4033@@ -6,6 +6,7 @@
4034 from operator import attrgetter
4035 
4036 from django.core.exceptions import FieldError
4037+from django.db import connection
4038 from django.db.models import Count, Max, Avg, Sum, StdDev, Variance, F, Q
4039 from django.test import TestCase, Approximate, skipUnlessDBFeature
4040 
4041@@ -69,11 +70,11 @@
4042         #oracle doesn't support subqueries in group by clause
4043         shortest_book_sql = """
4044         SELECT name
4045-        FROM aggregation_regress_book b
4046-        WHERE b.publisher_id = aggregation_regress_publisher.id
4047+        FROM %s b
4048+        WHERE b.publisher_id = %s.id
4049         ORDER BY b.pages
4050         LIMIT 1
4051-        """
4052+        """ % (connection.qname(Book), connection.qname(Book))
4053         # tests that this query does not raise a DatabaseError due to the full
4054         # subselect being (erroneously) added to the GROUP BY parameters
4055         qs = Publisher.objects.extra(select={
4056Index: tests/regressiontests/admin_scripts/tests.py
4057===================================================================
4058--- tests/regressiontests/admin_scripts/tests.py        (revision 17914)
4059+++ tests/regressiontests/admin_scripts/tests.py        (working copy)
4060@@ -3,7 +3,6 @@
4061 advertised - especially with regards to the handling of the DJANGO_SETTINGS_MODULE
4062 and default settings.py files.
4063 """
4064-
4065 import os
4066 import re
4067 import shutil
4068@@ -13,14 +12,15 @@
4069 
4070 from django import conf, bin, get_version
4071 from django.conf import settings
4072+from django.db import connection
4073 from django.test.simple import DjangoTestSuiteRunner
4074 from django.utils import unittest
4075 from django.test import LiveServerTestCase
4076 
4077+from .models import Article
4078+
4079 test_dir = os.path.dirname(os.path.dirname(__file__))
4080-expected_query_re = re.compile(r'CREATE TABLE [`"]admin_scripts_article[`"]', re.IGNORECASE)
4081 
4082-
4083 class AdminScriptTestCase(unittest.TestCase):
4084     def write_settings(self, filename, apps=None, is_dir=False, sdict=None):
4085         test_dir = os.path.dirname(os.path.dirname(__file__))
4086@@ -844,6 +844,8 @@
4087     """
4088     def setUp(self):
4089         self.write_settings('alternate_settings.py')
4090+        tblname = connection.qname(Article)
4091+        self.expected_query_re = re.compile(r'CREATE TABLE %s' % tblname, re.IGNORECASE)
4092 
4093     def tearDown(self):
4094         self.remove_settings('alternate_settings.py')
4095@@ -859,14 +861,14 @@
4096         "alternate: manage.py builtin commands work with settings provided as argument"
4097         args = ['sqlall', '--settings=alternate_settings', 'admin_scripts']
4098         out, err = self.run_manage(args)
4099-        self.assertRegexpMatches(out, expected_query_re)
4100+        self.assertRegexpMatches(out, self.expected_query_re)
4101         self.assertNoOutput(err)
4102 
4103     def test_builtin_with_environment(self):
4104         "alternate: manage.py builtin commands work if settings are provided in the environment"
4105         args = ['sqlall', 'admin_scripts']
4106         out, err = self.run_manage(args, 'alternate_settings')
4107-        self.assertRegexpMatches(out, expected_query_re)
4108+        self.assertRegexpMatches(out, self.expected_query_re)
4109         self.assertNoOutput(err)
4110 
4111     def test_builtin_with_bad_settings(self):
4112Index: tests/regressiontests/introspection/tests.py
4113===================================================================
4114--- tests/regressiontests/introspection/tests.py        (revision 17914)
4115+++ tests/regressiontests/introspection/tests.py        (working copy)
4116@@ -2,7 +2,8 @@
4117 
4118 from functools import update_wrapper
4119 
4120-from django.db import connection
4121+from django.conf import settings
4122+from django.db import connection, QName
4123 from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature
4124 
4125 from .models import Reporter, Article
4126@@ -39,24 +40,29 @@
4127     __metaclass__ = IgnoreNotimplementedError
4128 
4129     def test_table_names(self):
4130-        tl = connection.introspection.table_names()
4131-        self.assertTrue(Reporter._meta.db_table in tl,
4132+        conv = connection.introspection.qname_converter
4133+        tl = connection.introspection.all_qualified_names()
4134+        self.assertTrue(conv(Reporter._meta.qualified_name) in tl,
4135                      "'%s' isn't in table_list()." % Reporter._meta.db_table)
4136-        self.assertTrue(Article._meta.db_table in tl,
4137+        self.assertTrue(conv(Article._meta.qualified_name) in tl,
4138                      "'%s' isn't in table_list()." % Article._meta.db_table)
4139 
4140     def test_django_table_names(self):
4141         cursor = connection.cursor()
4142-        cursor.execute('CREATE TABLE django_ixn_test_table (id INTEGER);')
4143+        tblname = connection.ops.qualified_name(
4144+            QName(None, 'django_ixn_test_table', False))
4145+        cursor.execute('CREATE TABLE %s (id INTEGER);' % tblname)
4146         tl = connection.introspection.django_table_names()
4147-        cursor.execute("DROP TABLE django_ixn_test_table;")
4148-        self.assertTrue('django_ixn_testcase_table' not in tl,
4149+        cursor.execute("DROP TABLE %s;" % tblname)
4150+        self.assertTrue(tblname not in tl,
4151                      "django_table_names() returned a non-Django table")
4152 
4153     def test_django_table_names_retval_type(self):
4154         # Ticket #15216
4155         cursor = connection.cursor()
4156-        cursor.execute('CREATE TABLE django_ixn_test_table (id INTEGER);')
4157+        tblname = connection.ops.qualified_name(
4158+            QName(None, 'django_ixn_test_table', False))
4159+        cursor.execute('CREATE TABLE %s (id INTEGER);' % tblname)
4160 
4161         tl = connection.introspection.django_table_names(only_existing=True)
4162         self.assertIs(type(tl), list)
4163@@ -65,25 +71,30 @@
4164         self.assertIs(type(tl), list)
4165 
4166     def test_installed_models(self):
4167-        tables = [Article._meta.db_table, Reporter._meta.db_table]
4168+        conv = connection.introspection.qname_converter
4169+        tables = [conv(Article._meta.qualified_name),
4170+                  conv(Reporter._meta.qualified_name)]
4171         models = connection.introspection.installed_models(tables)
4172         self.assertEqual(models, set([Article, Reporter]))
4173 
4174     def test_sequence_list(self):
4175         sequences = connection.introspection.sequence_list()
4176-        expected = {'table': Reporter._meta.db_table, 'column': 'id'}
4177+        qname = connection.introspection.qname_converter(Reporter._meta.qualified_name)
4178+        expected = {'qname': qname, 'column': 'id'}
4179         self.assertTrue(expected in sequences,
4180                      'Reporter sequence not found in sequence_list()')
4181 
4182     def test_get_table_description_names(self):
4183         cursor = connection.cursor()
4184-        desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
4185+        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
4186+        desc = connection.introspection.get_table_description(cursor, tbl)
4187         self.assertEqual([r[0] for r in desc],
4188                          [f.column for f in Reporter._meta.fields])
4189 
4190     def test_get_table_description_types(self):
4191         cursor = connection.cursor()
4192-        desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
4193+        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
4194+        desc = connection.introspection.get_table_description(cursor, tbl)
4195         self.assertEqual(
4196             [datatype(r[1], r) for r in desc],
4197             ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField']
4198@@ -95,7 +106,8 @@
4199     @skipIfDBFeature('interprets_empty_strings_as_nulls')
4200     def test_get_table_description_nullable(self):
4201         cursor = connection.cursor()
4202-        desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
4203+        tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
4204+        desc = connection.introspection.get_table_description(cursor, tbl)
4205         self.assertEqual(
4206             [r[6] for r in desc],
4207             [False, False, False, False, True]
4208@@ -105,35 +117,51 @@
4209     @skipUnlessDBFeature('has_real_datatype')
4210     def test_postgresql_real_type(self):
4211         cursor = connection.cursor()
4212-        cursor.execute("CREATE TABLE django_ixn_real_test_table (number REAL);")
4213-        desc = connection.introspection.get_table_description(cursor, 'django_ixn_real_test_table')
4214-        cursor.execute('DROP TABLE django_ixn_real_test_table;')
4215+        tblname = connection.ops.qualified_name(
4216+            QName(None, 'django_ixn_real_test_table', False))
4217+        cursor.execute("CREATE TABLE %s (number REAL);" % tblname)
4218+        desc = connection.introspection.get_table_description(
4219+            cursor, QName(None, 'django_ixn_real_test_table', False))
4220+        cursor.execute('DROP TABLE %s;' % tblname)
4221         self.assertEqual(datatype(desc[0][1], desc[0]), 'FloatField')
4222 
4223     def test_get_relations(self):
4224         cursor = connection.cursor()
4225-        relations = connection.introspection.get_relations(cursor, Article._meta.db_table)
4226+        tbl = connection.introspection.qname_converter(Article._meta.qualified_name)
4227+        relations = connection.introspection.get_relations(cursor, tbl)
4228+        rep_tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name)
4229 
4230         # Older versions of MySQL don't have the chops to report on this stuff,
4231         # so just skip it if no relations come back. If they do, though, we
4232         # should test that the response is correct.
4233         if relations:
4234             # That's {field_index: (field_index_other_table, other_table)}
4235-            self.assertEqual(relations, {3: (0, Reporter._meta.db_table)})
4236+            # We have a small problem here: the Reporter model is installed
4237+            # into the default schema (qualified_name[0] == None). The
4238+            # relation introspection is going to see it in that schema, but we
4239+            # do not know what that schema is. So, test everything except the
4240+            # schema.
4241+            # TODO: this testing logic is UGLY!
4242+            schema = connection.convert_schema(Reporter._meta.qualified_name[0])
4243+            self.assertTrue(3 in relations)
4244+            relations[3] = (relations[3][0], (schema, relations[3][1][1], True))
4245+            self.assertEqual(relations, {3: (0, rep_tbl)})
4246 
4247     def test_get_key_columns(self):
4248         cursor = connection.cursor()
4249-        key_columns = connection.introspection.get_key_columns(cursor, Article._meta.db_table)
4250-        self.assertEqual(key_columns, [(u'reporter_id', Reporter._meta.db_table, u'id')])
4251+        rep_tbl = connection.introspection.qname_converter(Reporter._meta.qualified_name, force_schema=True)
4252+        key_columns = connection.introspection.get_key_columns(cursor, Article._meta.qualified_name)
4253+        self.assertEqual(key_columns, [(u'reporter_id', rep_tbl, u'id')])
4254 
4255     def test_get_primary_key_column(self):
4256         cursor = connection.cursor()
4257-        primary_key_column = connection.introspection.get_primary_key_column(cursor, Article._meta.db_table)
4258+        primary_key_column = connection.introspection.get_primary_key_column(cursor, Article._meta.qualified_name)
4259         self.assertEqual(primary_key_column, u'id')
4260 
4261     def test_get_indexes(self):
4262         cursor = connection.cursor()
4263-        indexes = connection.introspection.get_indexes(cursor, Article._meta.db_table)
4264+        tbl = connection.introspection.qname_converter(Article._meta.qualified_name)
4265+        indexes = connection.introspection.get_indexes(cursor, tbl)
4266         self.assertEqual(indexes['reporter_id'], {'unique': False, 'primary_key': False})
4267 
4268 
4269Index: docs/topics/db/models.txt
4270===================================================================
4271--- docs/topics/db/models.txt   (revision 17914)
4272+++ docs/topics/db/models.txt   (working copy)
4273@@ -636,7 +636,8 @@
4274             verbose_name_plural = "oxen"
4275 
4276 Model metadata is "anything that's not a field", such as ordering options
4277-(:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`), or
4278+(:attr:`~Options.ordering`), database table name (:attr:`~Options.db_table`),
4279+or custom schema for the tables (:attr:`~Options.db_schema`), or
4280 human-readable singular and plural names (:attr:`~Options.verbose_name` and
4281 :attr:`~Options.verbose_name_plural`). None are required, and adding ``class
4282 Meta`` to a model is completely optional.
4283Index: docs/ref/models/options.txt
4284===================================================================
4285--- docs/ref/models/options.txt (revision 17914)
4286+++ docs/ref/models/options.txt (working copy)
4287@@ -67,7 +67,23 @@
4288     the table name via ``db_table``, particularly if you are using the MySQL
4289     backend. See the :ref:`MySQL notes <mysql-notes>` for more details.
4290 
4291+.. _db_schema:
4292 
4293+``db_schema``
4294+-------------
4295+
4296+.. attribute:: Options.db_schema
4297+
4298+.. versionadded:: 1.5
4299+
4300+The name of the database schema to use for the model. If the backend
4301+doesn't support multiple schemas, this option is ignored.
4302+
4303+If this is used then Django will place the table in the given schema.
4304+Usually this means turning the pair db_schema, db_table into a fully
4305+qualified name. For example: "db_schema"."db_table".
4306+
4307+
4308 ``db_tablespace``
4309 -----------------
4310 
4311Index: docs/ref/settings.txt
4312===================================================================
4313--- docs/ref/settings.txt       (revision 17914)
4314+++ docs/ref/settings.txt       (working copy)
4315@@ -512,6 +512,23 @@
4316 
4317 The username to use when connecting to the database. Not used with SQLite.
4318 
4319+.. setting:: SCHEMA
4320+
4321+SCHEMA
4322+~~~~~~
4323+
4324+.. versionadded:: 1.5
4325+
4326+Default: ``''`` (Empty string)
4327+
4328+The name of the database schema to use for models. If the backend
4329+doesn't support multiple schemas, this option is ignored. An empty
4330+string means that schema qualified table names are not used by default.
4331+
4332+If this is used then Django will prefix any table names with the schema name.
4333+The schema can be overriden on a per-model basis, for details see
4334+:ref:`db_schema`.
4335+
4336 .. setting:: TEST_CHARSET
4337 
4338 TEST_CHARSET