Code

Ticket #5461: 5461-r8280.diff

File 5461-r8280.diff, 167.4 KB (added by russellm, 6 years ago)

rc3 for Creation backend patch - minor style changes, and some GIS fixes.

Line 
1Index: django/test/simple.py
2===================================================================
3--- django/test/simple.py       (revision 8280)
4+++ django/test/simple.py       (working copy)
5@@ -3,7 +3,6 @@
6 from django.db.models import get_app, get_apps
7 from django.test import _doctest as doctest
8 from django.test.utils import setup_test_environment, teardown_test_environment
9-from django.test.utils import create_test_db, destroy_test_db
10 from django.test.testcases import OutputChecker, DocTestRunner
11 
12 # The module name for tests outside models.py
13@@ -139,9 +138,10 @@
14         suite.addTest(test)
15 
16     old_name = settings.DATABASE_NAME
17-    create_test_db(verbosity, autoclobber=not interactive)
18+    from django.db import connection
19+    connection.creation.create_test_db(verbosity, autoclobber=not interactive)
20     result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
21-    destroy_test_db(old_name, verbosity)
22+    connection.creation.destroy_test_db(old_name, verbosity)
23     
24     teardown_test_environment()
25     
26Index: django/test/utils.py
27===================================================================
28--- django/test/utils.py        (revision 8280)
29+++ django/test/utils.py        (working copy)
30@@ -1,16 +1,11 @@
31 import sys, time, os
32 from django.conf import settings
33-from django.db import connection, get_creation_module
34+from django.db import connection
35 from django.core import mail
36-from django.core.management import call_command
37 from django.test import signals
38 from django.template import Template
39 from django.utils.translation import deactivate
40 
41-# The prefix to put on the default database name when creating
42-# the test database.
43-TEST_DATABASE_PREFIX = 'test_'
44-
45 def instrumented_test_render(self, context):
46     """
47     An instrumented Template render method, providing a signal
48@@ -70,147 +65,3 @@
49 
50     del mail.outbox
51 
52-def _set_autocommit(connection):
53-    "Make sure a connection is in autocommit mode."
54-    if hasattr(connection.connection, "autocommit"):
55-        if callable(connection.connection.autocommit):
56-            connection.connection.autocommit(True)
57-        else:
58-            connection.connection.autocommit = True
59-    elif hasattr(connection.connection, "set_isolation_level"):
60-        connection.connection.set_isolation_level(0)
61-
62-def get_mysql_create_suffix():
63-    suffix = []
64-    if settings.TEST_DATABASE_CHARSET:
65-        suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
66-    if settings.TEST_DATABASE_COLLATION:
67-        suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
68-    return ' '.join(suffix)
69-
70-def get_postgresql_create_suffix():
71-    assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
72-    if settings.TEST_DATABASE_CHARSET:
73-        return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
74-    return ''
75-
76-def create_test_db(verbosity=1, autoclobber=False):
77-    """
78-    Creates a test database, prompting the user for confirmation if the
79-    database already exists. Returns the name of the test database created.
80-    """
81-    # If the database backend wants to create the test DB itself, let it
82-    creation_module = get_creation_module()
83-    if hasattr(creation_module, "create_test_db"):
84-        creation_module.create_test_db(settings, connection, verbosity, autoclobber)
85-        return
86-
87-    if verbosity >= 1:
88-        print "Creating test database..."
89-    # If we're using SQLite, it's more convenient to test against an
90-    # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose
91-    # to run on a physical database.
92-    if settings.DATABASE_ENGINE == "sqlite3":
93-        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
94-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
95-            # Erase the old test database
96-            if verbosity >= 1:
97-                print "Destroying old test database..."
98-            if os.access(TEST_DATABASE_NAME, os.F_OK):
99-                if not autoclobber:
100-                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
101-                if autoclobber or confirm == 'yes':
102-                  try:
103-                      if verbosity >= 1:
104-                          print "Destroying old test database..."
105-                      os.remove(TEST_DATABASE_NAME)
106-                  except Exception, e:
107-                      sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
108-                      sys.exit(2)
109-                else:
110-                    print "Tests cancelled."
111-                    sys.exit(1)
112-            if verbosity >= 1:
113-                print "Creating test database..."
114-        else:
115-            TEST_DATABASE_NAME = ":memory:"
116-    else:
117-        suffix = {
118-            'postgresql': get_postgresql_create_suffix,
119-            'postgresql_psycopg2': get_postgresql_create_suffix,
120-            'mysql': get_mysql_create_suffix,
121-        }.get(settings.DATABASE_ENGINE, lambda: '')()
122-        if settings.TEST_DATABASE_NAME:
123-            TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
124-        else:
125-            TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
126-
127-        qn = connection.ops.quote_name
128-
129-        # Create the test database and connect to it. We need to autocommit
130-        # if the database supports it because PostgreSQL doesn't allow
131-        # CREATE/DROP DATABASE statements within transactions.
132-        cursor = connection.cursor()
133-        _set_autocommit(connection)
134-        try:
135-            cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
136-        except Exception, e:
137-            sys.stderr.write("Got an error creating the test database: %s\n" % e)
138-            if not autoclobber:
139-                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
140-            if autoclobber or confirm == 'yes':
141-                try:
142-                    if verbosity >= 1:
143-                        print "Destroying old test database..."
144-                    cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME))
145-                    if verbosity >= 1:
146-                        print "Creating test database..."
147-                    cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
148-                except Exception, e:
149-                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
150-                    sys.exit(2)
151-            else:
152-                print "Tests cancelled."
153-                sys.exit(1)
154-
155-    connection.close()
156-    settings.DATABASE_NAME = TEST_DATABASE_NAME
157-
158-    call_command('syncdb', verbosity=verbosity, interactive=False)
159-
160-    if settings.CACHE_BACKEND.startswith('db://'):
161-        cache_name = settings.CACHE_BACKEND[len('db://'):]
162-        call_command('createcachetable', cache_name)
163-
164-    # Get a cursor (even though we don't need one yet). This has
165-    # the side effect of initializing the test database.
166-    cursor = connection.cursor()
167-
168-    return TEST_DATABASE_NAME
169-
170-def destroy_test_db(old_database_name, verbosity=1):
171-    # If the database wants to drop the test DB itself, let it
172-    creation_module = get_creation_module()
173-    if hasattr(creation_module, "destroy_test_db"):
174-        creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
175-        return
176-
177-    if verbosity >= 1:
178-        print "Destroying test database..."
179-    connection.close()
180-    TEST_DATABASE_NAME = settings.DATABASE_NAME
181-    settings.DATABASE_NAME = old_database_name
182-    if settings.DATABASE_ENGINE == "sqlite3":
183-        if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":
184-            # Remove the SQLite database file
185-            os.remove(TEST_DATABASE_NAME)
186-    else:
187-        # Remove the test database to clean up after
188-        # ourselves. Connect to the previous database (not the test database)
189-        # to do so, because it's not allowed to delete a database while being
190-        # connected to it.
191-        cursor = connection.cursor()
192-        _set_autocommit(connection)
193-        time.sleep(1) # To avoid "database is being accessed by other users" errors.
194-        cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME))
195-        connection.close()
196Index: django/db/models/fields/__init__.py
197===================================================================
198--- django/db/models/fields/__init__.py (revision 8280)
199+++ django/db/models/fields/__init__.py (working copy)
200@@ -7,7 +7,7 @@
201 except ImportError:
202     from django.utils import _decimal as decimal    # for Python 2.3
203 
204-from django.db import connection, get_creation_module
205+from django.db import connection
206 from django.db.models import signals
207 from django.db.models.query_utils import QueryWrapper
208 from django.dispatch import dispatcher
209@@ -145,14 +145,14 @@
210         # as the TextField Django field type, which means XMLField's
211         # get_internal_type() returns 'TextField'.
212         #
213-        # But the limitation of the get_internal_type() / DATA_TYPES approach
214+        # But the limitation of the get_internal_type() / data_types approach
215         # is that it cannot handle database column types that aren't already
216         # mapped to one of the built-in Django field types. In this case, you
217         # can implement db_type() instead of get_internal_type() to specify
218         # exactly which wacky database column type you want to use.
219         data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
220         try:
221-            return get_creation_module().DATA_TYPES[self.get_internal_type()] % data
222+            return connection.creation.data_types[self.get_internal_type()] % data
223         except KeyError:
224             return None
225 
226Index: django/db/__init__.py
227===================================================================
228--- django/db/__init__.py       (revision 8280)
229+++ django/db/__init__.py       (working copy)
230@@ -14,14 +14,12 @@
231     # backends that ships with Django, so look there first.
232     _import_path = 'django.db.backends.'
233     backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
234-    creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
235 except ImportError, e:
236     # If the import failed, we might be looking for a database backend
237     # distributed external to Django. So we'll try that next.
238     try:
239         _import_path = ''
240         backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
241-        creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
242     except ImportError, e_user:
243         # The database backend wasn't found. Display a helpful error message
244         # listing all possible (built-in) database backends.
245@@ -29,27 +27,11 @@
246         available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
247         available_backends.sort()
248         if settings.DATABASE_ENGINE not in available_backends:
249-            raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
250-                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
251+            raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s\nError was: %s" % \
252+                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends, e_user)))
253         else:
254             raise # If there's some other error, this must be an error in Django itself.
255 
256-def _import_database_module(import_path='', module_name=''):
257-    """Lazily import a database module when requested."""
258-    return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
259-
260-# We don't want to import the introspect module unless someone asks for it, so
261-# lazily load it on demmand.
262-get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
263-
264-def get_creation_module():
265-    return creation
266-
267-# We want runshell() to work the same way, but we have to treat it a
268-# little differently (since it just runs instead of returning a module like
269-# the above) and wrap the lazily-loaded runshell() method.
270-runshell = lambda: _import_database_module(_import_path, "client").runshell()
271-
272 # Convenient aliases for backend bits.
273 connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
274 DatabaseError = backend.DatabaseError
275Index: django/db/backends/postgresql/base.py
276===================================================================
277--- django/db/backends/postgresql/base.py       (revision 8280)
278+++ django/db/backends/postgresql/base.py       (working copy)
279@@ -4,9 +4,13 @@
280 Requires psycopg 1: http://initd.org/projects/psycopg1
281 """
282 
283+from django.db.backends import *
284+from django.db.backends.postgresql.client import DatabaseClient
285+from django.db.backends.postgresql.creation import DatabaseCreation
286+from django.db.backends.postgresql.introspection import DatabaseIntrospection
287+from django.db.backends.postgresql.operations import DatabaseOperations
288 from django.utils.encoding import smart_str, smart_unicode
289-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
290-from django.db.backends.postgresql.operations import DatabaseOperations
291+
292 try:
293     import psycopg as Database
294 except ImportError, e:
295@@ -59,12 +63,7 @@
296     def __iter__(self):
297         return iter(self.cursor)
298 
299-class DatabaseFeatures(BaseDatabaseFeatures):
300-    pass # This backend uses all the defaults.
301-
302 class DatabaseWrapper(BaseDatabaseWrapper):
303-    features = DatabaseFeatures()
304-    ops = DatabaseOperations()
305     operators = {
306         'exact': '= %s',
307         'iexact': 'ILIKE %s',
308@@ -82,6 +81,16 @@
309         'iendswith': 'ILIKE %s',
310     }
311 
312+    def __init__(self, *args, **kwargs):
313+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
314+       
315+        self.features = BaseDatabaseFeatures()
316+        self.ops = DatabaseOperations()
317+        self.client = DatabaseClient()
318+        self.creation = DatabaseCreation(self)
319+        self.introspection = DatabaseIntrospection(self)
320+        self.validation = BaseDatabaseValidation()
321+
322     def _cursor(self, settings):
323         set_tz = False
324         if self.connection is None:
325Index: django/db/backends/postgresql/client.py
326===================================================================
327--- django/db/backends/postgresql/client.py     (revision 8280)
328+++ django/db/backends/postgresql/client.py     (working copy)
329@@ -1,15 +1,17 @@
330+from django.db.backends import BaseDatabaseClient
331 from django.conf import settings
332 import os
333 
334-def runshell():
335-    args = ['psql']
336-    if settings.DATABASE_USER:
337-        args += ["-U", settings.DATABASE_USER]
338-    if settings.DATABASE_PASSWORD:
339-        args += ["-W"]
340-    if settings.DATABASE_HOST:
341-        args.extend(["-h", settings.DATABASE_HOST])
342-    if settings.DATABASE_PORT:
343-        args.extend(["-p", str(settings.DATABASE_PORT)])
344-    args += [settings.DATABASE_NAME]
345-    os.execvp('psql', args)
346+class DatabaseClient(BaseDatabaseClient):
347+    def runshell(self):
348+        args = ['psql']
349+        if settings.DATABASE_USER:
350+            args += ["-U", settings.DATABASE_USER]
351+        if settings.DATABASE_PASSWORD:
352+            args += ["-W"]
353+        if settings.DATABASE_HOST:
354+            args.extend(["-h", settings.DATABASE_HOST])
355+        if settings.DATABASE_PORT:
356+            args.extend(["-p", str(settings.DATABASE_PORT)])
357+        args += [settings.DATABASE_NAME]
358+        os.execvp('psql', args)
359Index: django/db/backends/postgresql/introspection.py
360===================================================================
361--- django/db/backends/postgresql/introspection.py      (revision 8280)
362+++ django/db/backends/postgresql/introspection.py      (working copy)
363@@ -1,86 +1,86 @@
364-from django.db.backends.postgresql.base import DatabaseOperations
365+from django.db.backends import BaseDatabaseIntrospection
366 
367-quote_name = DatabaseOperations().quote_name
368+class DatabaseIntrospection(BaseDatabaseIntrospection):
369+    # Maps type codes to Django Field types.
370+    data_types_reverse = {
371+        16: 'BooleanField',
372+        21: 'SmallIntegerField',
373+        23: 'IntegerField',
374+        25: 'TextField',
375+        701: 'FloatField',
376+        869: 'IPAddressField',
377+        1043: 'CharField',
378+        1082: 'DateField',
379+        1083: 'TimeField',
380+        1114: 'DateTimeField',
381+        1184: 'DateTimeField',
382+        1266: 'TimeField',
383+        1700: 'DecimalField',
384+    }
385+       
386+    def get_table_list(self, cursor):
387+        "Returns a list of table names in the current database."
388+        cursor.execute("""
389+            SELECT c.relname
390+            FROM pg_catalog.pg_class c
391+            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
392+            WHERE c.relkind IN ('r', 'v', '')
393+                AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
394+                AND pg_catalog.pg_table_is_visible(c.oid)""")
395+        return [row[0] for row in cursor.fetchall()]
396 
397-def get_table_list(cursor):
398-    "Returns a list of table names in the current database."
399-    cursor.execute("""
400-        SELECT c.relname
401-        FROM pg_catalog.pg_class c
402-        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
403-        WHERE c.relkind IN ('r', 'v', '')
404-            AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
405-            AND pg_catalog.pg_table_is_visible(c.oid)""")
406-    return [row[0] for row in cursor.fetchall()]
407+    def get_table_description(self, cursor, table_name):
408+        "Returns a description of the table, with the DB-API cursor.description interface."
409+        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
410+        return cursor.description
411 
412-def get_table_description(cursor, table_name):
413-    "Returns a description of the table, with the DB-API cursor.description interface."
414-    cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
415-    return cursor.description
416+    def get_relations(self, cursor, table_name):
417+        """
418+        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
419+        representing all relationships to the given table. Indexes are 0-based.
420+        """
421+        cursor.execute("""
422+            SELECT con.conkey, con.confkey, c2.relname
423+            FROM pg_constraint con, pg_class c1, pg_class c2
424+            WHERE c1.oid = con.conrelid
425+                AND c2.oid = con.confrelid
426+                AND c1.relname = %s
427+                AND con.contype = 'f'""", [table_name])
428+        relations = {}
429+        for row in cursor.fetchall():
430+            try:
431+                # row[0] and row[1] are like "{2}", so strip the curly braces.
432+                relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2])
433+            except ValueError:
434+                continue
435+        return relations
436 
437-def get_relations(cursor, table_name):
438-    """
439-    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
440-    representing all relationships to the given table. Indexes are 0-based.
441-    """
442-    cursor.execute("""
443-        SELECT con.conkey, con.confkey, c2.relname
444-        FROM pg_constraint con, pg_class c1, pg_class c2
445-        WHERE c1.oid = con.conrelid
446-            AND c2.oid = con.confrelid
447-            AND c1.relname = %s
448-            AND con.contype = 'f'""", [table_name])
449-    relations = {}
450-    for row in cursor.fetchall():
451-        try:
452-            # row[0] and row[1] are like "{2}", so strip the curly braces.
453-            relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2])
454-        except ValueError:
455-            continue
456-    return relations
457+    def get_indexes(self, cursor, table_name):
458+        """
459+        Returns a dictionary of fieldname -> infodict for the given table,
460+        where each infodict is in the format:
461+            {'primary_key': boolean representing whether it's the primary key,
462+             'unique': boolean representing whether it's a unique index}
463+        """
464+        # This query retrieves each index on the given table, including the
465+        # first associated field name
466+        cursor.execute("""
467+            SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
468+            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
469+                pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
470+            WHERE c.oid = idx.indrelid
471+                AND idx.indexrelid = c2.oid
472+                AND attr.attrelid = c.oid
473+                AND attr.attnum = idx.indkey[0]
474+                AND c.relname = %s""", [table_name])
475+        indexes = {}
476+        for row in cursor.fetchall():
477+            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
478+            # a string of space-separated integers. This designates the field
479+            # indexes (1-based) of the fields that have indexes on the table.
480+            # Here, we skip any indexes across multiple fields.
481+            if ' ' in row[1]:
482+                continue
483+            indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
484+        return indexes
485 
486-def get_indexes(cursor, table_name):
487-    """
488-    Returns a dictionary of fieldname -> infodict for the given table,
489-    where each infodict is in the format:
490-        {'primary_key': boolean representing whether it's the primary key,
491-         'unique': boolean representing whether it's a unique index}
492-    """
493-    # This query retrieves each index on the given table, including the
494-    # first associated field name
495-    cursor.execute("""
496-        SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
497-        FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
498-            pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
499-        WHERE c.oid = idx.indrelid
500-            AND idx.indexrelid = c2.oid
501-            AND attr.attrelid = c.oid
502-            AND attr.attnum = idx.indkey[0]
503-            AND c.relname = %s""", [table_name])
504-    indexes = {}
505-    for row in cursor.fetchall():
506-        # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
507-        # a string of space-separated integers. This designates the field
508-        # indexes (1-based) of the fields that have indexes on the table.
509-        # Here, we skip any indexes across multiple fields.
510-        if ' ' in row[1]:
511-            continue
512-        indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
513-    return indexes
514-
515-# Maps type codes to Django Field types.
516-DATA_TYPES_REVERSE = {
517-    16: 'BooleanField',
518-    21: 'SmallIntegerField',
519-    23: 'IntegerField',
520-    25: 'TextField',
521-    701: 'FloatField',
522-    869: 'IPAddressField',
523-    1043: 'CharField',
524-    1082: 'DateField',
525-    1083: 'TimeField',
526-    1114: 'DateTimeField',
527-    1184: 'DateTimeField',
528-    1266: 'TimeField',
529-    1700: 'DecimalField',
530-}
531Index: django/db/backends/postgresql/creation.py
532===================================================================
533--- django/db/backends/postgresql/creation.py   (revision 8280)
534+++ django/db/backends/postgresql/creation.py   (working copy)
535@@ -1,28 +1,38 @@
536-# This dictionary maps Field objects to their associated PostgreSQL column
537-# types, as strings. Column-type strings can contain format strings; they'll
538-# be interpolated against the values of Field.__dict__ before being output.
539-# If a column type is set to None, it won't be included in the output.
540-DATA_TYPES = {
541-    'AutoField':         'serial',
542-    'BooleanField':      'boolean',
543-    'CharField':         'varchar(%(max_length)s)',
544-    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
545-    'DateField':         'date',
546-    'DateTimeField':     'timestamp with time zone',
547-    'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
548-    'FileField':         'varchar(%(max_length)s)',
549-    'FilePathField':     'varchar(%(max_length)s)',
550-    'FloatField':        'double precision',
551-    'IntegerField':      'integer',
552-    'IPAddressField':    'inet',
553-    'NullBooleanField':  'boolean',
554-    'OneToOneField':     'integer',
555-    'PhoneNumberField':  'varchar(20)',
556-    'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
557-    'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
558-    'SlugField':         'varchar(%(max_length)s)',
559-    'SmallIntegerField': 'smallint',
560-    'TextField':         'text',
561-    'TimeField':         'time',
562-    'USStateField':      'varchar(2)',
563-}
564+from django.conf import settings
565+from django.db.backends.creation import BaseDatabaseCreation
566+
567+class DatabaseCreation(BaseDatabaseCreation):
568+    # This dictionary maps Field objects to their associated PostgreSQL column
569+    # types, as strings. Column-type strings can contain format strings; they'll
570+    # be interpolated against the values of Field.__dict__ before being output.
571+    # If a column type is set to None, it won't be included in the output.
572+    data_types = {
573+        'AutoField':         'serial',
574+        'BooleanField':      'boolean',
575+        'CharField':         'varchar(%(max_length)s)',
576+        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
577+        'DateField':         'date',
578+        'DateTimeField':     'timestamp with time zone',
579+        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
580+        'FileField':         'varchar(%(max_length)s)',
581+        'FilePathField':     'varchar(%(max_length)s)',
582+        'FloatField':        'double precision',
583+        'IntegerField':      'integer',
584+        'IPAddressField':    'inet',
585+        'NullBooleanField':  'boolean',
586+        'OneToOneField':     'integer',
587+        'PhoneNumberField':  'varchar(20)',
588+        'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
589+        'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
590+        'SlugField':         'varchar(%(max_length)s)',
591+        'SmallIntegerField': 'smallint',
592+        'TextField':         'text',
593+        'TimeField':         'time',
594+        'USStateField':      'varchar(2)',
595+    }
596+
597+    def sql_table_creation_suffix(self):
598+        assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
599+        if settings.TEST_DATABASE_CHARSET:
600+            return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
601+        return ''
602Index: django/db/backends/sqlite3/base.py
603===================================================================
604--- django/db/backends/sqlite3/base.py  (revision 8280)
605+++ django/db/backends/sqlite3/base.py  (working copy)
606@@ -6,7 +6,11 @@
607 Python 2.5 and later use the sqlite3 module in the standard library.
608 """
609 
610-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
611+from django.db.backends import *
612+from django.db.backends.sqlite3.client import DatabaseClient
613+from django.db.backends.sqlite3.creation import DatabaseCreation
614+from django.db.backends.sqlite3.introspection import DatabaseIntrospection
615+
616 try:
617     try:
618         from sqlite3 import dbapi2 as Database
619@@ -46,7 +50,6 @@
620     Database.register_adapter(str, lambda s:s.decode('utf-8'))
621 
622 class DatabaseFeatures(BaseDatabaseFeatures):
623-    supports_constraints = False
624     # SQLite cannot handle us only partially reading from a cursor's result set
625     # and then writing the same rows to the database in another cursor. This
626     # setting ensures we always read result sets fully into memory all in one
627@@ -96,11 +99,8 @@
628         second = '%s-12-31 23:59:59.999999'
629         return [first % value, second % value]
630 
631-
632 class DatabaseWrapper(BaseDatabaseWrapper):
633-    features = DatabaseFeatures()
634-    ops = DatabaseOperations()
635-
636+   
637     # SQLite requires LIKE statements to include an ESCAPE clause if the value
638     # being escaped has a percent or underscore in it.
639     # See http://www.sqlite.org/lang_expr.html for an explanation.
640@@ -121,6 +121,16 @@
641         'iendswith': "LIKE %s ESCAPE '\\'",
642     }
643 
644+    def __init__(self, *args, **kwargs):
645+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
646+       
647+        self.features = DatabaseFeatures()
648+        self.ops = DatabaseOperations()
649+        self.client = DatabaseClient()
650+        self.creation = DatabaseCreation(self)
651+        self.introspection = DatabaseIntrospection(self)
652+        self.validation = BaseDatabaseValidation()
653+
654     def _cursor(self, settings):
655         if self.connection is None:
656             if not settings.DATABASE_NAME:
657Index: django/db/backends/sqlite3/client.py
658===================================================================
659--- django/db/backends/sqlite3/client.py        (revision 8280)
660+++ django/db/backends/sqlite3/client.py        (working copy)
661@@ -1,6 +1,8 @@
662+from django.db.backends import BaseDatabaseClient
663 from django.conf import settings
664 import os
665 
666-def runshell():
667-    args = ['', settings.DATABASE_NAME]
668-    os.execvp('sqlite3', args)
669+class DatabaseClient(BaseDatabaseClient):
670+    def runshell(self):
671+        args = ['', settings.DATABASE_NAME]
672+        os.execvp('sqlite3', args)
673Index: django/db/backends/sqlite3/introspection.py
674===================================================================
675--- django/db/backends/sqlite3/introspection.py (revision 8280)
676+++ django/db/backends/sqlite3/introspection.py (working copy)
677@@ -1,84 +1,30 @@
678-from django.db.backends.sqlite3.base import DatabaseOperations
679+from django.db.backends import BaseDatabaseIntrospection
680 
681-quote_name = DatabaseOperations().quote_name
682-
683-def get_table_list(cursor):
684-    "Returns a list of table names in the current database."
685-    # Skip the sqlite_sequence system table used for autoincrement key
686-    # generation.
687-    cursor.execute("""
688-        SELECT name FROM sqlite_master
689-        WHERE type='table' AND NOT name='sqlite_sequence'
690-        ORDER BY name""")
691-    return [row[0] for row in cursor.fetchall()]
692-
693-def get_table_description(cursor, table_name):
694-    "Returns a description of the table, with the DB-API cursor.description interface."
695-    return [(info['name'], info['type'], None, None, None, None,
696-             info['null_ok']) for info in _table_info(cursor, table_name)]
697-
698-def get_relations(cursor, table_name):
699-    raise NotImplementedError
700-
701-def get_indexes(cursor, table_name):
702-    """
703-    Returns a dictionary of fieldname -> infodict for the given table,
704-    where each infodict is in the format:
705-        {'primary_key': boolean representing whether it's the primary key,
706-         'unique': boolean representing whether it's a unique index}
707-    """
708-    indexes = {}
709-    for info in _table_info(cursor, table_name):
710-        indexes[info['name']] = {'primary_key': info['pk'] != 0,
711-                                 'unique': False}
712-    cursor.execute('PRAGMA index_list(%s)' % quote_name(table_name))
713-    # seq, name, unique
714-    for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
715-        if not unique:
716-            continue
717-        cursor.execute('PRAGMA index_info(%s)' % quote_name(index))
718-        info = cursor.fetchall()
719-        # Skip indexes across multiple fields
720-        if len(info) != 1:
721-            continue
722-        name = info[0][2] # seqno, cid, name
723-        indexes[name]['unique'] = True
724-    return indexes
725-
726-def _table_info(cursor, name):
727-    cursor.execute('PRAGMA table_info(%s)' % quote_name(name))
728-    # cid, name, type, notnull, dflt_value, pk
729-    return [{'name': field[1],
730-             'type': field[2],
731-             'null_ok': not field[3],
732-             'pk': field[5]     # undocumented
733-             } for field in cursor.fetchall()]
734-
735-# Maps SQL types to Django Field types. Some of the SQL types have multiple
736-# entries here because SQLite allows for anything and doesn't normalize the
737-# field type; it uses whatever was given.
738-BASE_DATA_TYPES_REVERSE = {
739-    'bool': 'BooleanField',
740-    'boolean': 'BooleanField',
741-    'smallint': 'SmallIntegerField',
742-    'smallinteger': 'SmallIntegerField',
743-    'int': 'IntegerField',
744-    'integer': 'IntegerField',
745-    'text': 'TextField',
746-    'char': 'CharField',
747-    'date': 'DateField',
748-    'datetime': 'DateTimeField',
749-    'time': 'TimeField',
750-}
751-
752 # This light wrapper "fakes" a dictionary interface, because some SQLite data
753 # types include variables in them -- e.g. "varchar(30)" -- and can't be matched
754 # as a simple dictionary lookup.
755 class FlexibleFieldLookupDict:
756+    # Maps SQL types to Django Field types. Some of the SQL types have multiple
757+    # entries here because SQLite allows for anything and doesn't normalize the
758+    # field type; it uses whatever was given.
759+    base_data_types_reverse = {
760+        'bool': 'BooleanField',
761+        'boolean': 'BooleanField',
762+        'smallint': 'SmallIntegerField',
763+        'smallinteger': 'SmallIntegerField',
764+        'int': 'IntegerField',
765+        'integer': 'IntegerField',
766+        'text': 'TextField',
767+        'char': 'CharField',
768+        'date': 'DateField',
769+        'datetime': 'DateTimeField',
770+        'time': 'TimeField',
771+    }
772+
773     def __getitem__(self, key):
774         key = key.lower()
775         try:
776-            return BASE_DATA_TYPES_REVERSE[key]
777+            return self.base_data_types_reverse[key]
778         except KeyError:
779             import re
780             m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
781@@ -86,4 +32,58 @@
782                 return ('CharField', {'max_length': int(m.group(1))})
783             raise KeyError
784 
785-DATA_TYPES_REVERSE = FlexibleFieldLookupDict()
786+class DatabaseIntrospection(BaseDatabaseIntrospection):
787+    data_types_reverse = FlexibleFieldLookupDict()
788+       
789+    def get_table_list(self, cursor):
790+        "Returns a list of table names in the current database."
791+        # Skip the sqlite_sequence system table used for autoincrement key
792+        # generation.
793+        cursor.execute("""
794+            SELECT name FROM sqlite_master
795+            WHERE type='table' AND NOT name='sqlite_sequence'
796+            ORDER BY name""")
797+        return [row[0] for row in cursor.fetchall()]
798+
799+    def get_table_description(self, cursor, table_name):
800+        "Returns a description of the table, with the DB-API cursor.description interface."
801+        return [(info['name'], info['type'], None, None, None, None,
802+                 info['null_ok']) for info in self._table_info(cursor, table_name)]
803+
804+    def get_relations(self, cursor, table_name):
805+        raise NotImplementedError
806+
807+    def get_indexes(self, cursor, table_name):
808+        """
809+        Returns a dictionary of fieldname -> infodict for the given table,
810+        where each infodict is in the format:
811+            {'primary_key': boolean representing whether it's the primary key,
812+             'unique': boolean representing whether it's a unique index}
813+        """
814+        indexes = {}
815+        for info in self._table_info(cursor, table_name):
816+            indexes[info['name']] = {'primary_key': info['pk'] != 0,
817+                                     'unique': False}
818+        cursor.execute('PRAGMA index_list(%s)' % self.ops.quote_name(table_name))
819+        # seq, name, unique
820+        for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
821+            if not unique:
822+                continue
823+            cursor.execute('PRAGMA index_info(%s)' % self.ops.quote_name(index))
824+            info = cursor.fetchall()
825+            # Skip indexes across multiple fields
826+            if len(info) != 1:
827+                continue
828+            name = info[0][2] # seqno, cid, name
829+            indexes[name]['unique'] = True
830+        return indexes
831+
832+    def _table_info(self, cursor, name):
833+        cursor.execute('PRAGMA table_info(%s)' % self.ops.quote_name(name))
834+        # cid, name, type, notnull, dflt_value, pk
835+        return [{'name': field[1],
836+                 'type': field[2],
837+                 'null_ok': not field[3],
838+                 'pk': field[5]     # undocumented
839+                 } for field in cursor.fetchall()]
840+
841Index: django/db/backends/sqlite3/creation.py
842===================================================================
843--- django/db/backends/sqlite3/creation.py      (revision 8280)
844+++ django/db/backends/sqlite3/creation.py      (working copy)
845@@ -1,27 +1,73 @@
846-# SQLite doesn't actually support most of these types, but it "does the right
847-# thing" given more verbose field definitions, so leave them as is so that
848-# schema inspection is more useful.
849-DATA_TYPES = {
850-    'AutoField':                    'integer',
851-    'BooleanField':                 'bool',
852-    'CharField':                    'varchar(%(max_length)s)',
853-    'CommaSeparatedIntegerField':   'varchar(%(max_length)s)',
854-    'DateField':                    'date',
855-    'DateTimeField':                'datetime',
856-    'DecimalField':                 'decimal',
857-    'FileField':                    'varchar(%(max_length)s)',
858-    'FilePathField':                'varchar(%(max_length)s)',
859-    'FloatField':                   'real',
860-    'IntegerField':                 'integer',
861-    'IPAddressField':               'char(15)',
862-    'NullBooleanField':             'bool',
863-    'OneToOneField':                'integer',
864-    'PhoneNumberField':             'varchar(20)',
865-    'PositiveIntegerField':         'integer unsigned',
866-    'PositiveSmallIntegerField':    'smallint unsigned',
867-    'SlugField':                    'varchar(%(max_length)s)',
868-    'SmallIntegerField':            'smallint',
869-    'TextField':                    'text',
870-    'TimeField':                    'time',
871-    'USStateField':                 'varchar(2)',
872-}
873+import os
874+import sys
875+from django.conf import settings
876+from django.db.backends.creation import BaseDatabaseCreation
877+
878+class DatabaseCreation(BaseDatabaseCreation):
879+    # SQLite doesn't actually support most of these types, but it "does the right
880+    # thing" given more verbose field definitions, so leave them as is so that
881+    # schema inspection is more useful.
882+    data_types = {
883+        'AutoField':                    'integer',
884+        'BooleanField':                 'bool',
885+        'CharField':                    'varchar(%(max_length)s)',
886+        'CommaSeparatedIntegerField':   'varchar(%(max_length)s)',
887+        'DateField':                    'date',
888+        'DateTimeField':                'datetime',
889+        'DecimalField':                 'decimal',
890+        'FileField':                    'varchar(%(max_length)s)',
891+        'FilePathField':                'varchar(%(max_length)s)',
892+        'FloatField':                   'real',
893+        'IntegerField':                 'integer',
894+        'IPAddressField':               'char(15)',
895+        'NullBooleanField':             'bool',
896+        'OneToOneField':                'integer',
897+        'PhoneNumberField':             'varchar(20)',
898+        'PositiveIntegerField':         'integer unsigned',
899+        'PositiveSmallIntegerField':    'smallint unsigned',
900+        'SlugField':                    'varchar(%(max_length)s)',
901+        'SmallIntegerField':            'smallint',
902+        'TextField':                    'text',
903+        'TimeField':                    'time',
904+        'USStateField':                 'varchar(2)',
905+    }
906+   
907+    def sql_for_pending_references(self, model, style, pending_references):
908+        "SQLite3 doesn't support constraints"
909+        return []
910+
911+    def sql_remove_table_constraints(self, model, references_to_delete):
912+        "SQLite3 doesn't support constraints"
913+        return []
914+       
915+    def _create_test_db(self, verbosity, autoclobber):
916+        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
917+            test_database_name = settings.TEST_DATABASE_NAME
918+            # Erase the old test database
919+            if verbosity >= 1:
920+                print "Destroying old test database..."
921+            if os.access(test_database_name, os.F_OK):
922+                if not autoclobber:
923+                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
924+                if autoclobber or confirm == 'yes':
925+                  try:
926+                      if verbosity >= 1:
927+                          print "Destroying old test database..."
928+                      os.remove(test_database_name)
929+                  except Exception, e:
930+                      sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
931+                      sys.exit(2)
932+                else:
933+                    print "Tests cancelled."
934+                    sys.exit(1)
935+            if verbosity >= 1:
936+                print "Creating test database..."
937+        else:
938+            test_database_name = ":memory:"
939+        return test_database_name
940+       
941+    def _destroy_test_db(self, test_database_name, verbosity):
942+        if test_database_name and test_database_name != ":memory:":
943+            # Remove the SQLite database file
944+            os.remove(test_database_name)       
945+                   
946\ No newline at end of file
947Index: django/db/backends/mysql/base.py
948===================================================================
949--- django/db/backends/mysql/base.py    (revision 8280)
950+++ django/db/backends/mysql/base.py    (working copy)
951@@ -4,7 +4,12 @@
952 Requires MySQLdb: http://sourceforge.net/projects/mysql-python
953 """
954 
955-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
956+from django.db.backends import *
957+from django.db.backends.mysql.client import DatabaseClient
958+from django.db.backends.mysql.creation import DatabaseCreation
959+from django.db.backends.mysql.introspection import DatabaseIntrospection
960+from django.db.backends.mysql.validation import DatabaseValidation
961+
962 try:
963     import MySQLdb as Database
964 except ImportError, e:
965@@ -60,7 +65,6 @@
966 # TRADITIONAL will automatically cause most warnings to be treated as errors.
967 
968 class DatabaseFeatures(BaseDatabaseFeatures):
969-    inline_fk_references = False
970     empty_fetchmany_value = ()
971     update_can_self_select = False
972 
973@@ -142,8 +146,7 @@
974         return [first % value, second % value]
975 
976 class DatabaseWrapper(BaseDatabaseWrapper):
977-    features = DatabaseFeatures()
978-    ops = DatabaseOperations()
979+   
980     operators = {
981         'exact': '= BINARY %s',
982         'iexact': 'LIKE %s',
983@@ -164,6 +167,13 @@
984     def __init__(self, **kwargs):
985         super(DatabaseWrapper, self).__init__(**kwargs)
986         self.server_version = None
987+       
988+        self.features = DatabaseFeatures()
989+        self.ops = DatabaseOperations()
990+        self.client = DatabaseClient()
991+        self.creation = DatabaseCreation(self)
992+        self.introspection = DatabaseIntrospection(self)
993+        self.validation = DatabaseValidation()
994 
995     def _valid_connection(self):
996         if self.connection is not None:
997Index: django/db/backends/mysql/validation.py
998===================================================================
999--- django/db/backends/mysql/validation.py      (revision 0)
1000+++ django/db/backends/mysql/validation.py      (revision 0)
1001@@ -0,0 +1,13 @@
1002+from django.db.backends import BaseDatabaseValidation
1003+
1004+class DatabaseValidation(BaseDatabaseValidation):
1005+    def validate_field(self, errors, opts, f):
1006+        "Prior to MySQL 5.0.3, character fields could not exceed 255 characters"
1007+        from django.db import models
1008+        from django.db import connection
1009+        db_version = connection.get_server_version()
1010+        if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
1011+            errors.add(opts,
1012+                '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' %
1013+                (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
1014+   
1015\ No newline at end of file
1016Index: django/db/backends/mysql/client.py
1017===================================================================
1018--- django/db/backends/mysql/client.py  (revision 8280)
1019+++ django/db/backends/mysql/client.py  (working copy)
1020@@ -1,27 +1,29 @@
1021+from django.db.backends import BaseDatabaseClient
1022 from django.conf import settings
1023 import os
1024 
1025-def runshell():
1026-    args = ['']
1027-    db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
1028-    user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
1029-    passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
1030-    host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
1031-    port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
1032-    defaults_file = settings.DATABASE_OPTIONS.get('read_default_file')
1033-    # Seems to be no good way to set sql_mode with CLI
1034+class DatabaseClient(BaseDatabaseClient):
1035+    def runshell(self):
1036+        args = ['']
1037+        db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
1038+        user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
1039+        passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
1040+        host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
1041+        port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
1042+        defaults_file = settings.DATABASE_OPTIONS.get('read_default_file')
1043+        # Seems to be no good way to set sql_mode with CLI
1044     
1045-    if defaults_file:
1046-        args += ["--defaults-file=%s" % defaults_file]
1047-    if user:
1048-        args += ["--user=%s" % user]
1049-    if passwd:
1050-        args += ["--password=%s" % passwd]
1051-    if host:
1052-        args += ["--host=%s" % host]
1053-    if port:
1054-        args += ["--port=%s" % port]
1055-    if db:
1056-        args += [db]
1057+        if defaults_file:
1058+            args += ["--defaults-file=%s" % defaults_file]
1059+        if user:
1060+            args += ["--user=%s" % user]
1061+        if passwd:
1062+            args += ["--password=%s" % passwd]
1063+        if host:
1064+            args += ["--host=%s" % host]
1065+        if port:
1066+            args += ["--port=%s" % port]
1067+        if db:
1068+            args += [db]
1069 
1070-    os.execvp('mysql', args)
1071+        os.execvp('mysql', args)
1072Index: django/db/backends/mysql/introspection.py
1073===================================================================
1074--- django/db/backends/mysql/introspection.py   (revision 8280)
1075+++ django/db/backends/mysql/introspection.py   (working copy)
1076@@ -1,96 +1,97 @@
1077-from django.db.backends.mysql.base import DatabaseOperations
1078+from django.db.backends import BaseDatabaseIntrospection
1079 from MySQLdb import ProgrammingError, OperationalError
1080 from MySQLdb.constants import FIELD_TYPE
1081 import re
1082 
1083-quote_name = DatabaseOperations().quote_name
1084 foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
1085 
1086-def get_table_list(cursor):
1087-    "Returns a list of table names in the current database."
1088-    cursor.execute("SHOW TABLES")
1089-    return [row[0] for row in cursor.fetchall()]
1090+class DatabaseIntrospection(BaseDatabaseIntrospection):
1091+    data_types_reverse = {
1092+        FIELD_TYPE.BLOB: 'TextField',
1093+        FIELD_TYPE.CHAR: 'CharField',
1094+        FIELD_TYPE.DECIMAL: 'DecimalField',
1095+        FIELD_TYPE.DATE: 'DateField',
1096+        FIELD_TYPE.DATETIME: 'DateTimeField',
1097+        FIELD_TYPE.DOUBLE: 'FloatField',
1098+        FIELD_TYPE.FLOAT: 'FloatField',
1099+        FIELD_TYPE.INT24: 'IntegerField',
1100+        FIELD_TYPE.LONG: 'IntegerField',
1101+        FIELD_TYPE.LONGLONG: 'IntegerField',
1102+        FIELD_TYPE.SHORT: 'IntegerField',
1103+        FIELD_TYPE.STRING: 'CharField',
1104+        FIELD_TYPE.TIMESTAMP: 'DateTimeField',
1105+        FIELD_TYPE.TINY: 'IntegerField',
1106+        FIELD_TYPE.TINY_BLOB: 'TextField',
1107+        FIELD_TYPE.MEDIUM_BLOB: 'TextField',
1108+        FIELD_TYPE.LONG_BLOB: 'TextField',
1109+        FIELD_TYPE.VAR_STRING: 'CharField',
1110+    }
1111 
1112-def get_table_description(cursor, table_name):
1113-    "Returns a description of the table, with the DB-API cursor.description interface."
1114-    cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
1115-    return cursor.description
1116+    def get_table_list(self, cursor):
1117+        "Returns a list of table names in the current database."
1118+        cursor.execute("SHOW TABLES")
1119+        return [row[0] for row in cursor.fetchall()]
1120 
1121-def _name_to_index(cursor, table_name):
1122-    """
1123-    Returns a dictionary of {field_name: field_index} for the given table.
1124-    Indexes are 0-based.
1125-    """
1126-    return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
1127+    def get_table_description(self, cursor, table_name):
1128+        "Returns a description of the table, with the DB-API cursor.description interface."
1129+        cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
1130+        return cursor.description
1131 
1132-def get_relations(cursor, table_name):
1133-    """
1134-    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1135-    representing all relationships to the given table. Indexes are 0-based.
1136-    """
1137-    my_field_dict = _name_to_index(cursor, table_name)
1138-    constraints = []
1139-    relations = {}
1140-    try:
1141-        # This should work for MySQL 5.0.
1142-        cursor.execute("""
1143-            SELECT column_name, referenced_table_name, referenced_column_name
1144-            FROM information_schema.key_column_usage
1145-            WHERE table_name = %s
1146-                AND table_schema = DATABASE()
1147-                AND referenced_table_name IS NOT NULL
1148-                AND referenced_column_name IS NOT NULL""", [table_name])
1149-        constraints.extend(cursor.fetchall())
1150-    except (ProgrammingError, OperationalError):
1151-        # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
1152-        # Go through all constraints and save the equal matches.
1153-        cursor.execute("SHOW CREATE TABLE %s" % quote_name(table_name))
1154-        for row in cursor.fetchall():
1155-            pos = 0
1156-            while True:
1157-                match = foreign_key_re.search(row[1], pos)
1158-                if match == None:
1159-                    break
1160-                pos = match.end()
1161-                constraints.append(match.groups())
1162+    def _name_to_index(self, cursor, table_name):
1163+        """
1164+        Returns a dictionary of {field_name: field_index} for the given table.
1165+        Indexes are 0-based.
1166+        """
1167+        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
1168 
1169-    for my_fieldname, other_table, other_field in constraints:
1170-        other_field_index = _name_to_index(cursor, other_table)[other_field]
1171-        my_field_index = my_field_dict[my_fieldname]
1172-        relations[my_field_index] = (other_field_index, other_table)
1173+    def get_relations(self, cursor, table_name):
1174+        """
1175+        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1176+        representing all relationships to the given table. Indexes are 0-based.
1177+        """
1178+        my_field_dict = self._name_to_index(cursor, table_name)
1179+        constraints = []
1180+        relations = {}
1181+        try:
1182+            # This should work for MySQL 5.0.
1183+            cursor.execute("""
1184+                SELECT column_name, referenced_table_name, referenced_column_name
1185+                FROM information_schema.key_column_usage
1186+                WHERE table_name = %s
1187+                    AND table_schema = DATABASE()
1188+                    AND referenced_table_name IS NOT NULL
1189+                    AND referenced_column_name IS NOT NULL""", [table_name])
1190+            constraints.extend(cursor.fetchall())
1191+        except (ProgrammingError, OperationalError):
1192+            # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
1193+            # Go through all constraints and save the equal matches.
1194+            cursor.execute("SHOW CREATE TABLE %s" % self.connection.ops.quote_name(table_name))
1195+            for row in cursor.fetchall():
1196+                pos = 0
1197+                while True:
1198+                    match = foreign_key_re.search(row[1], pos)
1199+                    if match == None:
1200+                        break
1201+                    pos = match.end()
1202+                    constraints.append(match.groups())
1203 
1204-    return relations
1205+        for my_fieldname, other_table, other_field in constraints:
1206+            other_field_index = self._name_to_index(cursor, other_table)[other_field]
1207+            my_field_index = my_field_dict[my_fieldname]
1208+            relations[my_field_index] = (other_field_index, other_table)
1209 
1210-def get_indexes(cursor, table_name):
1211-    """
1212-    Returns a dictionary of fieldname -> infodict for the given table,
1213-    where each infodict is in the format:
1214-        {'primary_key': boolean representing whether it's the primary key,
1215-         'unique': boolean representing whether it's a unique index}
1216-    """
1217-    cursor.execute("SHOW INDEX FROM %s" % quote_name(table_name))
1218-    indexes = {}
1219-    for row in cursor.fetchall():
1220-        indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
1221-    return indexes
1222+        return relations
1223 
1224-DATA_TYPES_REVERSE = {
1225-    FIELD_TYPE.BLOB: 'TextField',
1226-    FIELD_TYPE.CHAR: 'CharField',
1227-    FIELD_TYPE.DECIMAL: 'DecimalField',
1228-    FIELD_TYPE.DATE: 'DateField',
1229-    FIELD_TYPE.DATETIME: 'DateTimeField',
1230-    FIELD_TYPE.DOUBLE: 'FloatField',
1231-    FIELD_TYPE.FLOAT: 'FloatField',
1232-    FIELD_TYPE.INT24: 'IntegerField',
1233-    FIELD_TYPE.LONG: 'IntegerField',
1234-    FIELD_TYPE.LONGLONG: 'IntegerField',
1235-    FIELD_TYPE.SHORT: 'IntegerField',
1236-    FIELD_TYPE.STRING: 'CharField',
1237-    FIELD_TYPE.TIMESTAMP: 'DateTimeField',
1238-    FIELD_TYPE.TINY: 'IntegerField',
1239-    FIELD_TYPE.TINY_BLOB: 'TextField',
1240-    FIELD_TYPE.MEDIUM_BLOB: 'TextField',
1241-    FIELD_TYPE.LONG_BLOB: 'TextField',
1242-    FIELD_TYPE.VAR_STRING: 'CharField',
1243-}
1244+    def get_indexes(self, cursor, table_name):
1245+        """
1246+        Returns a dictionary of fieldname -> infodict for the given table,
1247+        where each infodict is in the format:
1248+            {'primary_key': boolean representing whether it's the primary key,
1249+             'unique': boolean representing whether it's a unique index}
1250+        """
1251+        cursor.execute("SHOW INDEX FROM %s" % self.connection.ops.quote_name(table_name))
1252+        indexes = {}
1253+        for row in cursor.fetchall():
1254+            indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
1255+        return indexes
1256+
1257Index: django/db/backends/mysql/creation.py
1258===================================================================
1259--- django/db/backends/mysql/creation.py        (revision 8280)
1260+++ django/db/backends/mysql/creation.py        (working copy)
1261@@ -1,28 +1,68 @@
1262-# This dictionary maps Field objects to their associated MySQL column
1263-# types, as strings. Column-type strings can contain format strings; they'll
1264-# be interpolated against the values of Field.__dict__ before being output.
1265-# If a column type is set to None, it won't be included in the output.
1266-DATA_TYPES = {
1267-    'AutoField':         'integer AUTO_INCREMENT',
1268-    'BooleanField':      'bool',
1269-    'CharField':         'varchar(%(max_length)s)',
1270-    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
1271-    'DateField':         'date',
1272-    'DateTimeField':     'datetime',
1273-    'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
1274-    'FileField':         'varchar(%(max_length)s)',
1275-    'FilePathField':     'varchar(%(max_length)s)',
1276-    'FloatField':        'double precision',
1277-    'IntegerField':      'integer',
1278-    'IPAddressField':    'char(15)',
1279-    'NullBooleanField':  'bool',
1280-    'OneToOneField':     'integer',
1281-    'PhoneNumberField':  'varchar(20)',
1282-    'PositiveIntegerField': 'integer UNSIGNED',
1283-    'PositiveSmallIntegerField': 'smallint UNSIGNED',
1284-    'SlugField':         'varchar(%(max_length)s)',
1285-    'SmallIntegerField': 'smallint',
1286-    'TextField':         'longtext',
1287-    'TimeField':         'time',
1288-    'USStateField':      'varchar(2)',
1289-}
1290+from django.conf import settings
1291+from django.db.backends.creation import BaseDatabaseCreation
1292+
1293+class DatabaseCreation(BaseDatabaseCreation):
1294+    # This dictionary maps Field objects to their associated MySQL column
1295+    # types, as strings. Column-type strings can contain format strings; they'll
1296+    # be interpolated against the values of Field.__dict__ before being output.
1297+    # If a column type is set to None, it won't be included in the output.
1298+    data_types = {
1299+        'AutoField':         'integer AUTO_INCREMENT',
1300+        'BooleanField':      'bool',
1301+        'CharField':         'varchar(%(max_length)s)',
1302+        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
1303+        'DateField':         'date',
1304+        'DateTimeField':     'datetime',
1305+        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
1306+        'FileField':         'varchar(%(max_length)s)',
1307+        'FilePathField':     'varchar(%(max_length)s)',
1308+        'FloatField':        'double precision',
1309+        'IntegerField':      'integer',
1310+        'IPAddressField':    'char(15)',
1311+        'NullBooleanField':  'bool',
1312+        'OneToOneField':     'integer',
1313+        'PhoneNumberField':  'varchar(20)',
1314+        'PositiveIntegerField': 'integer UNSIGNED',
1315+        'PositiveSmallIntegerField': 'smallint UNSIGNED',
1316+        'SlugField':         'varchar(%(max_length)s)',
1317+        'SmallIntegerField': 'smallint',
1318+        'TextField':         'longtext',
1319+        'TimeField':         'time',
1320+        'USStateField':      'varchar(2)',
1321+    }
1322+
1323+    def sql_table_creation_suffix(self):
1324+        suffix = []
1325+        if settings.TEST_DATABASE_CHARSET:
1326+            suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
1327+        if settings.TEST_DATABASE_COLLATION:
1328+            suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
1329+        return ' '.join(suffix)
1330+
1331+    def sql_for_inline_foreign_key_references(self, field, known_models, style):
1332+        "All inline references are pending under MySQL"
1333+        return [], True
1334+       
1335+    def sql_for_inline_many_to_many_references(self, model, field, style):
1336+        from django.db import models
1337+        opts = model._meta
1338+        qn = self.connection.ops.quote_name
1339+       
1340+        table_output = [
1341+            '    %s %s %s,' %
1342+                (style.SQL_FIELD(qn(field.m2m_column_name())),
1343+                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
1344+                style.SQL_KEYWORD('NOT NULL')),
1345+            '    %s %s %s,' %
1346+            (style.SQL_FIELD(qn(field.m2m_reverse_name())),
1347+            style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()),
1348+            style.SQL_KEYWORD('NOT NULL'))
1349+        ]
1350+        deferred = [
1351+            (field.m2m_db_table(), field.m2m_column_name(), opts.db_table,
1352+                opts.pk.column),
1353+            (field.m2m_db_table(), field.m2m_reverse_name(),
1354+                field.rel.to._meta.db_table, field.rel.to._meta.pk.column)
1355+            ]
1356+        return table_output, deferred
1357+       
1358\ No newline at end of file
1359Index: django/db/backends/oracle/base.py
1360===================================================================
1361--- django/db/backends/oracle/base.py   (revision 8280)
1362+++ django/db/backends/oracle/base.py   (working copy)
1363@@ -8,8 +8,11 @@
1364 import datetime
1365 import time
1366 
1367-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
1368+from django.db.backends import *
1369 from django.db.backends.oracle import query
1370+from django.db.backends.oracle.client import DatabaseClient
1371+from django.db.backends.oracle.creation import DatabaseCreation
1372+from django.db.backends.oracle.introspection import DatabaseIntrospection
1373 from django.utils.encoding import smart_str, force_unicode
1374 
1375 # Oracle takes client-side character set encoding from the environment.
1376@@ -24,11 +27,8 @@
1377 IntegrityError = Database.IntegrityError
1378 
1379 class DatabaseFeatures(BaseDatabaseFeatures):
1380-    allows_group_by_ordinal = False
1381     empty_fetchmany_value = ()
1382     needs_datetime_string_cast = False
1383-    supports_tablespaces = True
1384-    uses_case_insensitive_names = True
1385     uses_custom_query_class = True
1386     interprets_empty_strings_as_nulls = True
1387 
1388@@ -194,10 +194,8 @@
1389         return [first % value, second % value]
1390 
1391 
1392-
1393 class DatabaseWrapper(BaseDatabaseWrapper):
1394-    features = DatabaseFeatures()
1395-    ops = DatabaseOperations()
1396+   
1397     operators = {
1398         'exact': '= %s',
1399         'iexact': '= UPPER(%s)',
1400@@ -214,6 +212,16 @@
1401     }
1402     oracle_version = None
1403 
1404+    def __init__(self, *args, **kwargs):       
1405+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
1406+
1407+        self.features = DatabaseFeatures()
1408+        self.ops = DatabaseOperations()
1409+        self.client = DatabaseClient()
1410+        self.creation = DatabaseCreation(self)
1411+        self.introspection = DatabaseIntrospection(self)
1412+        self.validation = BaseDatabaseValidation()
1413+
1414     def _valid_connection(self):
1415         return self.connection is not None
1416 
1417Index: django/db/backends/oracle/client.py
1418===================================================================
1419--- django/db/backends/oracle/client.py (revision 8280)
1420+++ django/db/backends/oracle/client.py (working copy)
1421@@ -1,11 +1,13 @@
1422+from django.db.backends import BaseDatabaseClient
1423 from django.conf import settings
1424 import os
1425 
1426-def runshell():
1427-    dsn = settings.DATABASE_USER
1428-    if settings.DATABASE_PASSWORD:
1429-        dsn += "/%s" % settings.DATABASE_PASSWORD
1430-    if settings.DATABASE_NAME:
1431-        dsn += "@%s" % settings.DATABASE_NAME
1432-    args = ["sqlplus", "-L", dsn]
1433-    os.execvp("sqlplus", args)
1434+class DatabaseClient(BaseDatabaseClient):
1435+    def runshell(self):
1436+        dsn = settings.DATABASE_USER
1437+        if settings.DATABASE_PASSWORD:
1438+            dsn += "/%s" % settings.DATABASE_PASSWORD
1439+        if settings.DATABASE_NAME:
1440+            dsn += "@%s" % settings.DATABASE_NAME
1441+        args = ["sqlplus", "-L", dsn]
1442+        os.execvp("sqlplus", args)
1443Index: django/db/backends/oracle/introspection.py
1444===================================================================
1445--- django/db/backends/oracle/introspection.py  (revision 8280)
1446+++ django/db/backends/oracle/introspection.py  (working copy)
1447@@ -1,98 +1,103 @@
1448-from django.db.backends.oracle.base import DatabaseOperations
1449+from django.db.backends import BaseDatabaseIntrospection
1450+import cx_Oracle
1451 import re
1452-import cx_Oracle
1453 
1454-quote_name = DatabaseOperations().quote_name
1455 foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
1456 
1457-def get_table_list(cursor):
1458-    "Returns a list of table names in the current database."
1459-    cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
1460-    return [row[0].upper() for row in cursor.fetchall()]
1461+class DatabaseIntrospection(BaseDatabaseIntrospection):
1462+    # Maps type objects to Django Field types.
1463+    data_types_reverse = {
1464+        cx_Oracle.CLOB: 'TextField',
1465+        cx_Oracle.DATETIME: 'DateTimeField',
1466+        cx_Oracle.FIXED_CHAR: 'CharField',
1467+        cx_Oracle.NCLOB: 'TextField',
1468+        cx_Oracle.NUMBER: 'DecimalField',
1469+        cx_Oracle.STRING: 'CharField',
1470+        cx_Oracle.TIMESTAMP: 'DateTimeField',
1471+    }
1472 
1473-def get_table_description(cursor, table_name):
1474-    "Returns a description of the table, with the DB-API cursor.description interface."
1475-    cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % quote_name(table_name))
1476-    return cursor.description
1477+    def get_table_list(self, cursor):
1478+        "Returns a list of table names in the current database."
1479+        cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
1480+        return [row[0].upper() for row in cursor.fetchall()]
1481 
1482-def _name_to_index(cursor, table_name):
1483-    """
1484-    Returns a dictionary of {field_name: field_index} for the given table.
1485-    Indexes are 0-based.
1486-    """
1487-    return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
1488+    def get_table_description(self, cursor, table_name):
1489+        "Returns a description of the table, with the DB-API cursor.description interface."
1490+        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.connection.ops.quote_name(table_name))
1491+        return cursor.description
1492 
1493-def get_relations(cursor, table_name):
1494-    """
1495-    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1496-    representing all relationships to the given table. Indexes are 0-based.
1497-    """
1498-    cursor.execute("""
1499-SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
1500-FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
1501-       user_tab_cols ta, user_tab_cols tb
1502-WHERE  user_constraints.table_name = %s AND
1503-       ta.table_name = %s AND
1504-       ta.column_name = ca.column_name AND
1505-       ca.table_name = %s AND
1506-       user_constraints.constraint_name = ca.constraint_name AND
1507-       user_constraints.r_constraint_name = cb.constraint_name AND
1508-       cb.table_name = tb.table_name AND
1509-       cb.column_name = tb.column_name AND
1510-       ca.position = cb.position""", [table_name, table_name, table_name])
1511+    def table_name_converter(self, name):
1512+        "Table name comparison is case insensitive under Oracle"
1513+        return name.upper()
1514+       
1515+    def _name_to_index(self, cursor, table_name):
1516+        """
1517+        Returns a dictionary of {field_name: field_index} for the given table.
1518+        Indexes are 0-based.
1519+        """
1520+        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
1521 
1522-    relations = {}
1523-    for row in cursor.fetchall():
1524-        relations[row[0]] = (row[2], row[1])
1525-    return relations
1526+    def get_relations(self, cursor, table_name):
1527+        """
1528+        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
1529+        representing all relationships to the given table. Indexes are 0-based.
1530+        """
1531+        cursor.execute("""
1532+    SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
1533+    FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
1534+           user_tab_cols ta, user_tab_cols tb
1535+    WHERE  user_constraints.table_name = %s AND
1536+           ta.table_name = %s AND
1537+           ta.column_name = ca.column_name AND
1538+           ca.table_name = %s AND
1539+           user_constraints.constraint_name = ca.constraint_name AND
1540+           user_constraints.r_constraint_name = cb.constraint_name AND
1541+           cb.table_name = tb.table_name AND
1542+           cb.column_name = tb.column_name AND
1543+           ca.position = cb.position""", [table_name, table_name, table_name])
1544 
1545-def get_indexes(cursor, table_name):
1546-    """
1547-    Returns a dictionary of fieldname -> infodict for the given table,
1548-    where each infodict is in the format:
1549-        {'primary_key': boolean representing whether it's the primary key,
1550-         'unique': boolean representing whether it's a unique index}
1551-    """
1552-    # This query retrieves each index on the given table, including the
1553-    # first associated field name
1554-    # "We were in the nick of time; you were in great peril!"
1555-    sql = """
1556-WITH primarycols AS (
1557- SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL
1558- FROM   user_cons_columns, user_constraints
1559- WHERE  user_cons_columns.constraint_name = user_constraints.constraint_name AND
1560-        user_constraints.constraint_type = 'P' AND
1561-        user_cons_columns.table_name = %s),
1562- uniquecols AS (
1563- SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL
1564- FROM   user_indexes, user_ind_columns
1565- WHERE  uniqueness = 'UNIQUE' AND
1566-        user_indexes.index_name = user_ind_columns.index_name AND
1567-        user_ind_columns.table_name = %s)
1568-SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL
1569-FROM   (SELECT column_name FROM primarycols UNION SELECT column_name FROM
1570-uniquecols) allcols,
1571-      primarycols, uniquecols
1572-WHERE  allcols.column_name = primarycols.column_name (+) AND
1573-      allcols.column_name = uniquecols.column_name (+)
1574-    """
1575-    cursor.execute(sql, [table_name, table_name])
1576-    indexes = {}
1577-    for row in cursor.fetchall():
1578-        # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
1579-        # a string of space-separated integers. This designates the field
1580-        # indexes (1-based) of the fields that have indexes on the table.
1581-        # Here, we skip any indexes across multiple fields.
1582-        indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
1583-    return indexes
1584+        relations = {}
1585+        for row in cursor.fetchall():
1586+            relations[row[0]] = (row[2], row[1])
1587+        return relations
1588 
1589-# Maps type objects to Django Field types.
1590-DATA_TYPES_REVERSE = {
1591-    cx_Oracle.CLOB: 'TextField',
1592-    cx_Oracle.DATETIME: 'DateTimeField',
1593-    cx_Oracle.FIXED_CHAR: 'CharField',
1594-    cx_Oracle.NCLOB: 'TextField',
1595-    cx_Oracle.NUMBER: 'DecimalField',
1596-    cx_Oracle.STRING: 'CharField',
1597-    cx_Oracle.TIMESTAMP: 'DateTimeField',
1598-}
1599+    def get_indexes(self, cursor, table_name):
1600+        """
1601+        Returns a dictionary of fieldname -> infodict for the given table,
1602+        where each infodict is in the format:
1603+            {'primary_key': boolean representing whether it's the primary key,
1604+             'unique': boolean representing whether it's a unique index}
1605+        """
1606+        # This query retrieves each index on the given table, including the
1607+        # first associated field name
1608+        # "We were in the nick of time; you were in great peril!"
1609+        sql = """
1610+    WITH primarycols AS (
1611+     SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL
1612+     FROM   user_cons_columns, user_constraints
1613+     WHERE  user_cons_columns.constraint_name = user_constraints.constraint_name AND
1614+            user_constraints.constraint_type = 'P' AND
1615+            user_cons_columns.table_name = %s),
1616+     uniquecols AS (
1617+     SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL
1618+     FROM   user_indexes, user_ind_columns
1619+     WHERE  uniqueness = 'UNIQUE' AND
1620+            user_indexes.index_name = user_ind_columns.index_name AND
1621+            user_ind_columns.table_name = %s)
1622+    SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL
1623+    FROM   (SELECT column_name FROM primarycols UNION SELECT column_name FROM
1624+    uniquecols) allcols,
1625+          primarycols, uniquecols
1626+    WHERE  allcols.column_name = primarycols.column_name (+) AND
1627+          allcols.column_name = uniquecols.column_name (+)
1628+        """
1629+        cursor.execute(sql, [table_name, table_name])
1630+        indexes = {}
1631+        for row in cursor.fetchall():
1632+            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
1633+            # a string of space-separated integers. This designates the field
1634+            # indexes (1-based) of the fields that have indexes on the table.
1635+            # Here, we skip any indexes across multiple fields.
1636+            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
1637+        return indexes
1638+
1639Index: django/db/backends/oracle/creation.py
1640===================================================================
1641--- django/db/backends/oracle/creation.py       (revision 8280)
1642+++ django/db/backends/oracle/creation.py       (working copy)
1643@@ -1,291 +1,289 @@
1644 import sys, time
1645+from django.conf import settings
1646 from django.core import management
1647+from django.db.backends.creation import BaseDatabaseCreation
1648 
1649-# This dictionary maps Field objects to their associated Oracle column
1650-# types, as strings. Column-type strings can contain format strings; they'll
1651-# be interpolated against the values of Field.__dict__ before being output.
1652-# If a column type is set to None, it won't be included in the output.
1653-#
1654-# Any format strings starting with "qn_" are quoted before being used in the
1655-# output (the "qn_" prefix is stripped before the lookup is performed.
1656-
1657-DATA_TYPES = {
1658-    'AutoField':                    'NUMBER(11)',
1659-    'BooleanField':                 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
1660-    'CharField':                    'NVARCHAR2(%(max_length)s)',
1661-    'CommaSeparatedIntegerField':   'VARCHAR2(%(max_length)s)',
1662-    'DateField':                    'DATE',
1663-    'DateTimeField':                'TIMESTAMP',
1664-    'DecimalField':                 'NUMBER(%(max_digits)s, %(decimal_places)s)',
1665-    'FileField':                    'NVARCHAR2(%(max_length)s)',
1666-    'FilePathField':                'NVARCHAR2(%(max_length)s)',
1667-    'FloatField':                   'DOUBLE PRECISION',
1668-    'IntegerField':                 'NUMBER(11)',
1669-    'IPAddressField':               'VARCHAR2(15)',
1670-    'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
1671-    'OneToOneField':                'NUMBER(11)',
1672-    'PhoneNumberField':             'VARCHAR2(20)',
1673-    'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
1674-    'PositiveSmallIntegerField':    'NUMBER(11) CHECK (%(qn_column)s >= 0)',
1675-    'SlugField':                    'NVARCHAR2(50)',
1676-    'SmallIntegerField':            'NUMBER(11)',
1677-    'TextField':                    'NCLOB',
1678-    'TimeField':                    'TIMESTAMP',
1679-    'URLField':                     'VARCHAR2(%(max_length)s)',
1680-    'USStateField':                 'CHAR(2)',
1681-}
1682-
1683 TEST_DATABASE_PREFIX = 'test_'
1684 PASSWORD = 'Im_a_lumberjack'
1685-REMEMBER = {}
1686 
1687-def create_test_db(settings, connection, verbosity=1, autoclobber=False):
1688-    TEST_DATABASE_NAME = _test_database_name(settings)
1689-    TEST_DATABASE_USER = _test_database_user(settings)
1690-    TEST_DATABASE_PASSWD = _test_database_passwd(settings)
1691-    TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
1692-    TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
1693+class DatabaseCreation(BaseDatabaseCreation):
1694+    # This dictionary maps Field objects to their associated Oracle column
1695+    # types, as strings. Column-type strings can contain format strings; they'll
1696+    # be interpolated against the values of Field.__dict__ before being output.
1697+    # If a column type is set to None, it won't be included in the output.
1698+    #
1699+    # Any format strings starting with "qn_" are quoted before being used in the
1700+    # output (the "qn_" prefix is stripped before the lookup is performed.
1701 
1702-    parameters = {
1703-        'dbname': TEST_DATABASE_NAME,
1704-        'user': TEST_DATABASE_USER,
1705-        'password': TEST_DATABASE_PASSWD,
1706-        'tblspace': TEST_DATABASE_TBLSPACE,
1707-        'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
1708-       }
1709+    data_types = {
1710+        'AutoField':                    'NUMBER(11)',
1711+        'BooleanField':                 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
1712+        'CharField':                    'NVARCHAR2(%(max_length)s)',
1713+        'CommaSeparatedIntegerField':   'VARCHAR2(%(max_length)s)',
1714+        'DateField':                    'DATE',
1715+        'DateTimeField':                'TIMESTAMP',
1716+        'DecimalField':                 'NUMBER(%(max_digits)s, %(decimal_places)s)',
1717+        'FileField':                    'NVARCHAR2(%(max_length)s)',
1718+        'FilePathField':                'NVARCHAR2(%(max_length)s)',
1719+        'FloatField':                   'DOUBLE PRECISION',
1720+        'IntegerField':                 'NUMBER(11)',
1721+        'IPAddressField':               'VARCHAR2(15)',
1722+        'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
1723+        'OneToOneField':                'NUMBER(11)',
1724+        'PhoneNumberField':             'VARCHAR2(20)',
1725+        'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
1726+        'PositiveSmallIntegerField':    'NUMBER(11) CHECK (%(qn_column)s >= 0)',
1727+        'SlugField':                    'NVARCHAR2(50)',
1728+        'SmallIntegerField':            'NUMBER(11)',
1729+        'TextField':                    'NCLOB',
1730+        'TimeField':                    'TIMESTAMP',
1731+        'URLField':                     'VARCHAR2(%(max_length)s)',
1732+        'USStateField':                 'CHAR(2)',
1733+    }
1734+   
1735+    def _create_test_db(self, verbosity, autoclobber):
1736+        TEST_DATABASE_NAME = self._test_database_name(settings)
1737+        TEST_DATABASE_USER = self._test_database_user(settings)
1738+        TEST_DATABASE_PASSWD = self._test_database_passwd(settings)
1739+        TEST_DATABASE_TBLSPACE = self._test_database_tblspace(settings)
1740+        TEST_DATABASE_TBLSPACE_TMP = self._test_database_tblspace_tmp(settings)
1741 
1742-    REMEMBER['user'] = settings.DATABASE_USER
1743-    REMEMBER['passwd'] = settings.DATABASE_PASSWORD
1744+        parameters = {
1745+            'dbname': TEST_DATABASE_NAME,
1746+            'user': TEST_DATABASE_USER,
1747+            'password': TEST_DATABASE_PASSWD,
1748+            'tblspace': TEST_DATABASE_TBLSPACE,
1749+            'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
1750+       }
1751 
1752-    cursor = connection.cursor()
1753-    if _test_database_create(settings):
1754-        if verbosity >= 1:
1755-            print 'Creating test database...'
1756-        try:
1757-            _create_test_db(cursor, parameters, verbosity)
1758-        except Exception, e:
1759-            sys.stderr.write("Got an error creating the test database: %s\n" % e)
1760-            if not autoclobber:
1761-                confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
1762-            if autoclobber or confirm == 'yes':
1763-                try:
1764-                    if verbosity >= 1:
1765-                        print "Destroying old test database..."
1766-                    _destroy_test_db(cursor, parameters, verbosity)
1767-                    if verbosity >= 1:
1768-                        print "Creating test database..."
1769-                    _create_test_db(cursor, parameters, verbosity)
1770-                except Exception, e:
1771-                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
1772-                    sys.exit(2)
1773-            else:
1774-                print "Tests cancelled."
1775-                sys.exit(1)
1776+        self.remember['user'] = settings.DATABASE_USER
1777+        self.remember['passwd'] = settings.DATABASE_PASSWORD
1778 
1779-    if _test_user_create(settings):
1780-        if verbosity >= 1:
1781-            print "Creating test user..."
1782-        try:
1783-            _create_test_user(cursor, parameters, verbosity)
1784-        except Exception, e:
1785-            sys.stderr.write("Got an error creating the test user: %s\n" % e)
1786-            if not autoclobber:
1787-                confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER)
1788-            if autoclobber or confirm == 'yes':
1789-                try:
1790-                    if verbosity >= 1:
1791-                        print "Destroying old test user..."
1792-                    _destroy_test_user(cursor, parameters, verbosity)
1793-                    if verbosity >= 1:
1794-                        print "Creating test user..."
1795-                    _create_test_user(cursor, parameters, verbosity)
1796-                except Exception, e:
1797-                    sys.stderr.write("Got an error recreating the test user: %s\n" % e)
1798-                    sys.exit(2)
1799-            else:
1800-                print "Tests cancelled."
1801-                sys.exit(1)
1802+        cursor = self.connection.cursor()
1803+        if self._test_database_create(settings):
1804+            if verbosity >= 1:
1805+                print 'Creating test database...'
1806+            try:
1807+                self._execute_test_db_creation(cursor, parameters, verbosity)
1808+            except Exception, e:
1809+                sys.stderr.write("Got an error creating the test database: %s\n" % e)
1810+                if not autoclobber:
1811+                    confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
1812+                if autoclobber or confirm == 'yes':
1813+                    try:
1814+                        if verbosity >= 1:
1815+                            print "Destroying old test database..."
1816+                        self._execute_test_db_destruction(cursor, parameters, verbosity)
1817+                        if verbosity >= 1:
1818+                            print "Creating test database..."
1819+                        self._execute_test_db_creation(cursor, parameters, verbosity)
1820+                    except Exception, e:
1821+                        sys.stderr.write("Got an error recreating the test database: %s\n" % e)
1822+                        sys.exit(2)
1823+                else:
1824+                    print "Tests cancelled."
1825+                    sys.exit(1)
1826 
1827-    connection.close()
1828-    settings.DATABASE_USER = TEST_DATABASE_USER
1829-    settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD
1830+        if self._test_user_create(settings):
1831+            if verbosity >= 1:
1832+                print "Creating test user..."
1833+            try:
1834+                self._create_test_user(cursor, parameters, verbosity)
1835+            except Exception, e:
1836+                sys.stderr.write("Got an error creating the test user: %s\n" % e)
1837+                if not autoclobber:
1838+                    confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER)
1839+                if autoclobber or confirm == 'yes':
1840+                    try:
1841+                        if verbosity >= 1:
1842+                            print "Destroying old test user..."
1843+                        self._destroy_test_user(cursor, parameters, verbosity)
1844+                        if verbosity >= 1:
1845+                            print "Creating test user..."
1846+                        self._create_test_user(cursor, parameters, verbosity)
1847+                    except Exception, e:
1848+                        sys.stderr.write("Got an error recreating the test user: %s\n" % e)
1849+                        sys.exit(2)
1850+                else:
1851+                    print "Tests cancelled."
1852+                    sys.exit(1)
1853 
1854-    management.call_command('syncdb', verbosity=verbosity, interactive=False)
1855+        settings.DATABASE_USER = TEST_DATABASE_USER
1856+        settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD
1857 
1858-    # Get a cursor (even though we don't need one yet). This has
1859-    # the side effect of initializing the test database.
1860-    cursor = connection.cursor()
1861+        return TEST_DATABASE_NAME
1862+       
1863+    def _destroy_test_db(self, test_database_name, verbosity=1):
1864+        """
1865+        Destroy a test database, prompting the user for confirmation if the
1866+        database already exists. Returns the name of the test database created.
1867+        """
1868+        TEST_DATABASE_NAME = self._test_database_name(settings)
1869+        TEST_DATABASE_USER = self._test_database_user(settings)
1870+        TEST_DATABASE_PASSWD = self._test_database_passwd(settings)
1871+        TEST_DATABASE_TBLSPACE = self._test_database_tblspace(settings)
1872+        TEST_DATABASE_TBLSPACE_TMP = self._test_database_tblspace_tmp(settings)
1873 
1874-def destroy_test_db(settings, connection, old_database_name, verbosity=1):
1875-    connection.close()
1876+        settings.DATABASE_USER = self.remember['user']
1877+        settings.DATABASE_PASSWORD = self.remember['passwd']
1878 
1879-    TEST_DATABASE_NAME = _test_database_name(settings)
1880-    TEST_DATABASE_USER = _test_database_user(settings)
1881-    TEST_DATABASE_PASSWD = _test_database_passwd(settings)
1882-    TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
1883-    TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
1884+        parameters = {
1885+            'dbname': TEST_DATABASE_NAME,
1886+            'user': TEST_DATABASE_USER,
1887+            'password': TEST_DATABASE_PASSWD,
1888+            'tblspace': TEST_DATABASE_TBLSPACE,
1889+            'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
1890+       }
1891 
1892-    settings.DATABASE_NAME = old_database_name
1893-    settings.DATABASE_USER = REMEMBER['user']
1894-    settings.DATABASE_PASSWORD = REMEMBER['passwd']
1895+        self.remember['user'] = settings.DATABASE_USER
1896+        self.remember['passwd'] = settings.DATABASE_PASSWORD
1897 
1898-    parameters = {
1899-        'dbname': TEST_DATABASE_NAME,
1900-        'user': TEST_DATABASE_USER,
1901-        'password': TEST_DATABASE_PASSWD,
1902-        'tblspace': TEST_DATABASE_TBLSPACE,
1903-        'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
1904-       }
1905+        cursor = self.connection.cursor()
1906+        time.sleep(1) # To avoid "database is being accessed by other users" errors.
1907+        if self._test_user_create(settings):
1908+            if verbosity >= 1:
1909+                print 'Destroying test user...'
1910+            self._destroy_test_user(cursor, parameters, verbosity)
1911+        if self._test_database_create(settings):
1912+            if verbosity >= 1:
1913+                print 'Destroying test database tables...'
1914+            self._execute_test_db_destruction(cursor, parameters, verbosity)
1915+        self.connection.close()
1916 
1917-    REMEMBER['user'] = settings.DATABASE_USER
1918-    REMEMBER['passwd'] = settings.DATABASE_PASSWORD
1919+    def _execute_test_db_creation(cursor, parameters, verbosity):
1920+        if verbosity >= 2:
1921+            print "_create_test_db(): dbname = %s" % parameters['dbname']
1922+        statements = [
1923+            """CREATE TABLESPACE %(tblspace)s
1924+               DATAFILE '%(tblspace)s.dbf' SIZE 20M
1925+               REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
1926+            """,
1927+            """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s
1928+               TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M
1929+               REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
1930+            """,
1931+        ]
1932+        _execute_statements(cursor, statements, parameters, verbosity)
1933 
1934-    cursor = connection.cursor()
1935-    time.sleep(1) # To avoid "database is being accessed by other users" errors.
1936-    if _test_user_create(settings):
1937-        if verbosity >= 1:
1938-            print 'Destroying test user...'
1939-        _destroy_test_user(cursor, parameters, verbosity)
1940-    if _test_database_create(settings):
1941-        if verbosity >= 1:
1942-            print 'Destroying test database...'
1943-        _destroy_test_db(cursor, parameters, verbosity)
1944-    connection.close()
1945+    def _create_test_user(cursor, parameters, verbosity):
1946+        if verbosity >= 2:
1947+            print "_create_test_user(): username = %s" % parameters['user']
1948+        statements = [
1949+            """CREATE USER %(user)s
1950+               IDENTIFIED BY %(password)s
1951+               DEFAULT TABLESPACE %(tblspace)s
1952+               TEMPORARY TABLESPACE %(tblspace_temp)s
1953+            """,
1954+            """GRANT CONNECT, RESOURCE TO %(user)s""",
1955+        ]
1956+        _execute_statements(cursor, statements, parameters, verbosity)
1957 
1958-def _create_test_db(cursor, parameters, verbosity):
1959-    if verbosity >= 2:
1960-        print "_create_test_db(): dbname = %s" % parameters['dbname']
1961-    statements = [
1962-        """CREATE TABLESPACE %(tblspace)s
1963-           DATAFILE '%(tblspace)s.dbf' SIZE 20M
1964-           REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
1965-        """,
1966-        """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s
1967-           TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M
1968-           REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
1969-        """,
1970-    ]
1971-    _execute_statements(cursor, statements, parameters, verbosity)
1972+    def _execute_test_db_destruction(cursor, parameters, verbosity):
1973+        if verbosity >= 2:
1974+            print "_execute_test_db_destruction(): dbname=%s" % parameters['dbname']
1975+        statements = [
1976+            'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
1977+            'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
1978+            ]
1979+        _execute_statements(cursor, statements, parameters, verbosity)
1980 
1981-def _create_test_user(cursor, parameters, verbosity):
1982-    if verbosity >= 2:
1983-        print "_create_test_user(): username = %s" % parameters['user']
1984-    statements = [
1985-        """CREATE USER %(user)s
1986-           IDENTIFIED BY %(password)s
1987-           DEFAULT TABLESPACE %(tblspace)s
1988-           TEMPORARY TABLESPACE %(tblspace_temp)s
1989-        """,
1990-        """GRANT CONNECT, RESOURCE TO %(user)s""",
1991-    ]
1992-    _execute_statements(cursor, statements, parameters, verbosity)
1993-
1994-def _destroy_test_db(cursor, parameters, verbosity):
1995-    if verbosity >= 2:
1996-        print "_destroy_test_db(): dbname=%s" % parameters['dbname']
1997-    statements = [
1998-        'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
1999-        'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
2000+    def _destroy_test_user(cursor, parameters, verbosity):
2001+        if verbosity >= 2:
2002+            print "_destroy_test_user(): user=%s" % parameters['user']
2003+            print "Be patient.  This can take some time..."
2004+        statements = [
2005+            'DROP USER %(user)s CASCADE',
2006         ]
2007-    _execute_statements(cursor, statements, parameters, verbosity)
2008+        _execute_statements(cursor, statements, parameters, verbosity)
2009 
2010-def _destroy_test_user(cursor, parameters, verbosity):
2011-    if verbosity >= 2:
2012-        print "_destroy_test_user(): user=%s" % parameters['user']
2013-        print "Be patient.  This can take some time..."
2014-    statements = [
2015-        'DROP USER %(user)s CASCADE',
2016-    ]
2017-    _execute_statements(cursor, statements, parameters, verbosity)
2018+    def _execute_statements(cursor, statements, parameters, verbosity):
2019+        for template in statements:
2020+            stmt = template % parameters
2021+            if verbosity >= 2:
2022+                print stmt
2023+            try:
2024+                cursor.execute(stmt)
2025+            except Exception, err:
2026+                sys.stderr.write("Failed (%s)\n" % (err))
2027+                raise
2028 
2029-def _execute_statements(cursor, statements, parameters, verbosity):
2030-    for template in statements:
2031-        stmt = template % parameters
2032-        if verbosity >= 2:
2033-            print stmt
2034+    def _test_database_name(settings):
2035+        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2036         try:
2037-            cursor.execute(stmt)
2038-        except Exception, err:
2039-            sys.stderr.write("Failed (%s)\n" % (err))
2040+            if settings.TEST_DATABASE_NAME:
2041+                name = settings.TEST_DATABASE_NAME
2042+        except AttributeError:
2043+            pass
2044+        except:
2045             raise
2046+        return name
2047 
2048-def _test_database_name(settings):
2049-    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2050-    try:
2051-        if settings.TEST_DATABASE_NAME:
2052-            name = settings.TEST_DATABASE_NAME
2053-    except AttributeError:
2054-        pass
2055-    except:
2056-        raise
2057-    return name
2058+    def _test_database_create(settings):
2059+        name = True
2060+        try:
2061+            if settings.TEST_DATABASE_CREATE:
2062+                name = True
2063+            else:
2064+                name = False
2065+        except AttributeError:
2066+            pass
2067+        except:
2068+            raise
2069+        return name
2070 
2071-def _test_database_create(settings):
2072-    name = True
2073-    try:
2074-        if settings.TEST_DATABASE_CREATE:
2075-            name = True
2076-        else:
2077-            name = False
2078-    except AttributeError:
2079-        pass
2080-    except:
2081-        raise
2082-    return name
2083+    def _test_user_create(settings):
2084+        name = True
2085+        try:
2086+            if settings.TEST_USER_CREATE:
2087+                name = True
2088+            else:
2089+                name = False
2090+        except AttributeError:
2091+            pass
2092+        except:
2093+            raise
2094+        return name
2095 
2096-def _test_user_create(settings):
2097-    name = True
2098-    try:
2099-        if settings.TEST_USER_CREATE:
2100-            name = True
2101-        else:
2102-            name = False
2103-    except AttributeError:
2104-        pass
2105-    except:
2106-        raise
2107-    return name
2108+    def _test_database_user(settings):
2109+        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2110+        try:
2111+            if settings.TEST_DATABASE_USER:
2112+                name = settings.TEST_DATABASE_USER
2113+        except AttributeError:
2114+            pass
2115+        except:
2116+            raise
2117+        return name
2118 
2119-def _test_database_user(settings):
2120-    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2121-    try:
2122-        if settings.TEST_DATABASE_USER:
2123-            name = settings.TEST_DATABASE_USER
2124-    except AttributeError:
2125-        pass
2126-    except:
2127-        raise
2128-    return name
2129+    def _test_database_passwd(settings):
2130+        name = PASSWORD
2131+        try:
2132+            if settings.TEST_DATABASE_PASSWD:
2133+                name = settings.TEST_DATABASE_PASSWD
2134+        except AttributeError:
2135+            pass
2136+        except:
2137+            raise
2138+        return name
2139 
2140-def _test_database_passwd(settings):
2141-    name = PASSWORD
2142-    try:
2143-        if settings.TEST_DATABASE_PASSWD:
2144-            name = settings.TEST_DATABASE_PASSWD
2145-    except AttributeError:
2146-        pass
2147-    except:
2148-        raise
2149-    return name
2150+    def _test_database_tblspace(settings):
2151+        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2152+        try:
2153+            if settings.TEST_DATABASE_TBLSPACE:
2154+                name = settings.TEST_DATABASE_TBLSPACE
2155+        except AttributeError:
2156+            pass
2157+        except:
2158+            raise
2159+        return name
2160 
2161-def _test_database_tblspace(settings):
2162-    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2163-    try:
2164-        if settings.TEST_DATABASE_TBLSPACE:
2165-            name = settings.TEST_DATABASE_TBLSPACE
2166-    except AttributeError:
2167-        pass
2168-    except:
2169-        raise
2170-    return name
2171-
2172-def _test_database_tblspace_tmp(settings):
2173-    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
2174-    try:
2175-        if settings.TEST_DATABASE_TBLSPACE_TMP:
2176-            name = settings.TEST_DATABASE_TBLSPACE_TMP
2177-    except AttributeError:
2178-        pass
2179-    except:
2180-        raise
2181-    return name
2182+    def _test_database_tblspace_tmp(settings):
2183+        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
2184+        try:
2185+            if settings.TEST_DATABASE_TBLSPACE_TMP:
2186+                name = settings.TEST_DATABASE_TBLSPACE_TMP
2187+        except AttributeError:
2188+            pass
2189+        except:
2190+            raise
2191+        return name
2192Index: django/db/backends/__init__.py
2193===================================================================
2194--- django/db/backends/__init__.py      (revision 8280)
2195+++ django/db/backends/__init__.py      (working copy)
2196@@ -42,14 +42,9 @@
2197         return util.CursorDebugWrapper(cursor, self)
2198 
2199 class BaseDatabaseFeatures(object):
2200-    allows_group_by_ordinal = True
2201-    inline_fk_references = True
2202     # True if django.db.backend.utils.typecast_timestamp is used on values
2203     # returned from dates() calls.
2204     needs_datetime_string_cast = True
2205-    supports_constraints = True
2206-    supports_tablespaces = False
2207-    uses_case_insensitive_names = False
2208     uses_custom_query_class = False
2209     empty_fetchmany_value = []
2210     update_can_self_select = True
2211@@ -253,13 +248,13 @@
2212         """
2213         return "BEGIN;"
2214 
2215-    def tablespace_sql(self, tablespace, inline=False):
2216+    def sql_for_tablespace(self, tablespace, inline=False):
2217         """
2218-        Returns the tablespace SQL, or None if the backend doesn't use
2219-        tablespaces.
2220+        Returns the SQL that will be appended to tables or rows to define
2221+        a tablespace. Returns '' if the backend doesn't use tablespaces.
2222         """
2223-        return None
2224-
2225+        return ''
2226+           
2227     def prep_for_like_query(self, x):
2228         """Prepares a value for use in a LIKE query."""
2229         from django.utils.encoding import smart_unicode
2230@@ -325,3 +320,89 @@
2231         """
2232         return self.year_lookup_bounds(value)
2233 
2234+class BaseDatabaseIntrospection(object):
2235+    """
2236+    This class encapsulates all backend-specific introspection utilities
2237+    """
2238+    data_types_reverse = {}
2239+
2240+    def __init__(self, connection):
2241+        self.connection = connection
2242+
2243+    def table_name_converter(self, name):
2244+        """Apply a conversion to the name for the purposes of comparison.
2245+       
2246+        The default table name converter is for case sensitive comparison.
2247+        """
2248+        return name
2249+       
2250+    def table_names(self):
2251+        "Returns a list of names of all tables that exist in the database."
2252+        cursor = self.connection.cursor()
2253+        return self.get_table_list(cursor)
2254+
2255+    def django_table_names(self, only_existing=False):
2256+        """
2257+        Returns a list of all table names that have associated Django models and
2258+        are in INSTALLED_APPS.
2259+
2260+        If only_existing is True, the resulting list will only include the tables
2261+        that actually exist in the database.
2262+        """
2263+        from django.db import models
2264+        tables = set()
2265+        for app in models.get_apps():
2266+            for model in models.get_models(app):
2267+                tables.add(model._meta.db_table)
2268+                tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
2269+        if only_existing:
2270+            tables = [t for t in tables if t in self.table_names()]
2271+        return tables
2272+
2273+    def installed_models(self, tables):
2274+        "Returns a set of all models represented by the provided list of table names."
2275+        from django.db import models
2276+        all_models = []
2277+        for app in models.get_apps():
2278+            for model in models.get_models(app):
2279+                all_models.append(model)
2280+        return set([m for m in all_models
2281+            if self.table_name_converter(m._meta.db_table) in map(self.table_name_converter, tables)
2282+        ])
2283+       
2284+    def sequence_list(self):
2285+        "Returns a list of information about all DB sequences for all models in all apps."
2286+        from django.db import models
2287+
2288+        apps = models.get_apps()
2289+        sequence_list = []
2290+
2291+        for app in apps:
2292+            for model in models.get_models(app):
2293+                for f in model._meta.local_fields:
2294+                    if isinstance(f, models.AutoField):
2295+                        sequence_list.append({'table': model._meta.db_table, 'column': f.column})
2296+                        break # Only one AutoField is allowed per model, so don't bother continuing.
2297+
2298+                for f in model._meta.local_many_to_many:
2299+                    sequence_list.append({'table': f.m2m_db_table(), 'column': None})
2300+
2301+        return sequence_list
2302+       
2303+       
2304+class BaseDatabaseClient(object):
2305+    """
2306+    This class encapsualtes all backend-specific methods for opening a
2307+    client shell
2308+    """
2309+    def runshell(self):
2310+        raise NotImplementedError()
2311+
2312+class BaseDatabaseValidation(object):
2313+    """
2314+    This class encapsualtes all backend-specific model validation.
2315+    """
2316+    def validate_field(self, errors, opts, f):
2317+        "By default, there is no backend-specific validation"
2318+        pass
2319+
2320Index: django/db/backends/postgresql_psycopg2/base.py
2321===================================================================
2322--- django/db/backends/postgresql_psycopg2/base.py      (revision 8280)
2323+++ django/db/backends/postgresql_psycopg2/base.py      (working copy)
2324@@ -4,8 +4,12 @@
2325 Requires psycopg 2: http://initd.org/projects/psycopg2
2326 """
2327 
2328-from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
2329+from django.db.backends import *
2330 from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
2331+from django.db.backends.postgresql.client import DatabaseClient
2332+from django.db.backends.postgresql.creation import DatabaseCreation
2333+from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
2334+
2335 from django.utils.safestring import SafeUnicode
2336 try:
2337     import psycopg2 as Database
2338@@ -31,8 +35,6 @@
2339         return cursor.query
2340 
2341 class DatabaseWrapper(BaseDatabaseWrapper):
2342-    features = DatabaseFeatures()
2343-    ops = DatabaseOperations()
2344     operators = {
2345         'exact': '= %s',
2346         'iexact': 'ILIKE %s',
2347@@ -50,6 +52,16 @@
2348         'iendswith': 'ILIKE %s',
2349     }
2350 
2351+    def __init__(self, *args, **kwargs):
2352+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
2353+       
2354+        self.features = DatabaseFeatures()
2355+        self.ops = DatabaseOperations()
2356+        self.client = DatabaseClient()
2357+        self.creation = DatabaseCreation(self)
2358+        self.introspection = DatabaseIntrospection(self)
2359+        self.validation = BaseDatabaseValidation()
2360+
2361     def _cursor(self, settings):
2362         set_tz = False
2363         if self.connection is None:
2364Index: django/db/backends/postgresql_psycopg2/client.py
2365===================================================================
2366--- django/db/backends/postgresql_psycopg2/client.py    (revision 8280)
2367+++ django/db/backends/postgresql_psycopg2/client.py    (working copy)
2368@@ -1 +0,0 @@
2369-from django.db.backends.postgresql.client import *
2370Index: django/db/backends/postgresql_psycopg2/introspection.py
2371===================================================================
2372--- django/db/backends/postgresql_psycopg2/introspection.py     (revision 8280)
2373+++ django/db/backends/postgresql_psycopg2/introspection.py     (working copy)
2374@@ -1,83 +1,21 @@
2375-from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
2376+from django.db.backends.postgresql.introspection import DatabaseIntrospection as PostgresDatabaseIntrospection
2377 
2378-quote_name = DatabaseOperations().quote_name
2379+class DatabaseIntrospection(PostgresDatabaseIntrospection):
2380 
2381-def get_table_list(cursor):
2382-    "Returns a list of table names in the current database."
2383-    cursor.execute("""
2384-        SELECT c.relname
2385-        FROM pg_catalog.pg_class c
2386-        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
2387-        WHERE c.relkind IN ('r', 'v', '')
2388-            AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
2389-            AND pg_catalog.pg_table_is_visible(c.oid)""")
2390-    return [row[0] for row in cursor.fetchall()]
2391-
2392-def get_table_description(cursor, table_name):
2393-    "Returns a description of the table, with the DB-API cursor.description interface."
2394-    cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
2395-    return cursor.description
2396-
2397-def get_relations(cursor, table_name):
2398-    """
2399-    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
2400-    representing all relationships to the given table. Indexes are 0-based.
2401-    """
2402-    cursor.execute("""
2403-        SELECT con.conkey, con.confkey, c2.relname
2404-        FROM pg_constraint con, pg_class c1, pg_class c2
2405-        WHERE c1.oid = con.conrelid
2406-            AND c2.oid = con.confrelid
2407-            AND c1.relname = %s
2408-            AND con.contype = 'f'""", [table_name])
2409-    relations = {}
2410-    for row in cursor.fetchall():
2411-        # row[0] and row[1] are single-item lists, so grab the single item.
2412-        relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
2413-    return relations
2414-
2415-def get_indexes(cursor, table_name):
2416-    """
2417-    Returns a dictionary of fieldname -> infodict for the given table,
2418-    where each infodict is in the format:
2419-        {'primary_key': boolean representing whether it's the primary key,
2420-         'unique': boolean representing whether it's a unique index}
2421-    """
2422-    # This query retrieves each index on the given table, including the
2423-    # first associated field name
2424-    cursor.execute("""
2425-        SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
2426-        FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
2427-            pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
2428-        WHERE c.oid = idx.indrelid
2429-            AND idx.indexrelid = c2.oid
2430-            AND attr.attrelid = c.oid
2431-            AND attr.attnum = idx.indkey[0]
2432-            AND c.relname = %s""", [table_name])
2433-    indexes = {}
2434-    for row in cursor.fetchall():
2435-        # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
2436-        # a string of space-separated integers. This designates the field
2437-        # indexes (1-based) of the fields that have indexes on the table.
2438-        # Here, we skip any indexes across multiple fields.
2439-        if ' ' in row[1]:
2440-            continue
2441-        indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
2442-    return indexes
2443-
2444-# Maps type codes to Django Field types.
2445-DATA_TYPES_REVERSE = {
2446-    16: 'BooleanField',
2447-    21: 'SmallIntegerField',
2448-    23: 'IntegerField',
2449-    25: 'TextField',
2450-    701: 'FloatField',
2451-    869: 'IPAddressField',
2452-    1043: 'CharField',
2453-    1082: 'DateField',
2454-    1083: 'TimeField',
2455-    1114: 'DateTimeField',
2456-    1184: 'DateTimeField',
2457-    1266: 'TimeField',
2458-    1700: 'DecimalField',
2459-}
2460+    def get_relations(self, cursor, table_name):
2461+        """
2462+        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
2463+        representing all relationships to the given table. Indexes are 0-based.
2464+        """
2465+        cursor.execute("""
2466+            SELECT con.conkey, con.confkey, c2.relname
2467+            FROM pg_constraint con, pg_class c1, pg_class c2
2468+            WHERE c1.oid = con.conrelid
2469+                AND c2.oid = con.confrelid
2470+                AND c1.relname = %s
2471+                AND con.contype = 'f'""", [table_name])
2472+        relations = {}
2473+        for row in cursor.fetchall():
2474+            # row[0] and row[1] are single-item lists, so grab the single item.
2475+            relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
2476+        return relations
2477Index: django/db/backends/postgresql_psycopg2/creation.py
2478===================================================================
2479--- django/db/backends/postgresql_psycopg2/creation.py  (revision 8280)
2480+++ django/db/backends/postgresql_psycopg2/creation.py  (working copy)
2481@@ -1 +0,0 @@
2482-from django.db.backends.postgresql.creation import *
2483Index: django/db/backends/dummy/base.py
2484===================================================================
2485--- django/db/backends/dummy/base.py    (revision 8280)
2486+++ django/db/backends/dummy/base.py    (working copy)
2487@@ -8,7 +8,8 @@
2488 """
2489 
2490 from django.core.exceptions import ImproperlyConfigured
2491-from django.db.backends import BaseDatabaseFeatures, BaseDatabaseOperations
2492+from django.db.backends import *
2493+from django.db.backends.creation import BaseDatabaseCreation
2494 
2495 def complain(*args, **kwargs):
2496     raise ImproperlyConfigured, "You haven't set the DATABASE_ENGINE setting yet."
2497@@ -25,16 +26,30 @@
2498 class DatabaseOperations(BaseDatabaseOperations):
2499     quote_name = complain
2500 
2501-class DatabaseWrapper(object):
2502-    features = BaseDatabaseFeatures()
2503-    ops = DatabaseOperations()
2504+class DatabaseClient(BaseDatabaseClient):
2505+    runshell = complain
2506+   
2507+class DatabaseIntrospection(BaseDatabaseIntrospection):
2508+    get_table_list = complain
2509+    get_table_description = complain
2510+    get_relations = complain
2511+    get_indexes = complain
2512+   
2513+class DatabaseWrapper(object):   
2514     operators = {}
2515     cursor = complain
2516     _commit = complain
2517     _rollback = ignore
2518 
2519-    def __init__(self, **kwargs):
2520-        pass
2521+    def __init__(self, *args, **kwargs):
2522+        super(DatabaseWrapper, self).__init__(*args, **kwargs)
2523 
2524+        self.features = BaseDatabaseFeatures()
2525+        self.ops = DatabaseOperations()
2526+        self.client = DatabaseClient()
2527+        self.creation = BaseDatabaseCreation(self)
2528+        self.introspection = DatabaseIntrospection(self)
2529+        self.validation = BaseDatabaseValidation()
2530+
2531     def close(self):
2532         pass
2533Index: django/db/backends/dummy/client.py
2534===================================================================
2535--- django/db/backends/dummy/client.py  (revision 8280)
2536+++ django/db/backends/dummy/client.py  (working copy)
2537@@ -1,3 +0,0 @@
2538-from django.db.backends.dummy.base import complain
2539-
2540-runshell = complain
2541Index: django/db/backends/dummy/introspection.py
2542===================================================================
2543--- django/db/backends/dummy/introspection.py   (revision 8280)
2544+++ django/db/backends/dummy/introspection.py   (working copy)
2545@@ -1,8 +0,0 @@
2546-from django.db.backends.dummy.base import complain
2547-
2548-get_table_list = complain
2549-get_table_description = complain
2550-get_relations = complain
2551-get_indexes = complain
2552-
2553-DATA_TYPES_REVERSE = {}
2554Index: django/db/backends/dummy/creation.py
2555===================================================================
2556--- django/db/backends/dummy/creation.py        (revision 8280)
2557+++ django/db/backends/dummy/creation.py        (working copy)
2558@@ -1 +0,0 @@
2559-DATA_TYPES = {}
2560Index: django/db/backends/creation.py
2561===================================================================
2562--- django/db/backends/creation.py      (revision 8280)
2563+++ django/db/backends/creation.py      (working copy)
2564@@ -1,7 +1,395 @@
2565-class BaseCreation(object):
2566+import sys
2567+import time
2568+
2569+from django.conf import settings
2570+from django.core.management import call_command
2571+
2572+# The prefix to put on the default database name when creating
2573+# the test database.
2574+TEST_DATABASE_PREFIX = 'test_'
2575+
2576+class BaseDatabaseCreation(object):
2577     """
2578     This class encapsulates all backend-specific differences that pertain to
2579     database *creation*, such as the column types to use for particular Django
2580     Fields.
2581     """
2582-    pass
2583+    data_types = {}
2584+   
2585+    def __init__(self, connection):
2586+        self.connection = connection
2587+       
2588+    def sql_create_model(self, model, style, known_models=set()):
2589+        """
2590+        Returns the SQL required to create a single model, as a tuple of:
2591+            (list_of_sql, pending_references_dict)
2592+        """
2593+        from django.db import models
2594+
2595+        opts = model._meta
2596+        final_output = []
2597+        table_output = []
2598+        pending_references = {}
2599+        qn = self.connection.ops.quote_name
2600+        for f in opts.local_fields:
2601+            col_type = f.db_type()
2602+            tablespace = f.db_tablespace or opts.db_tablespace
2603+            if col_type is None:
2604+                # Skip ManyToManyFields, because they're not represented as
2605+                # database columns in this table.
2606+                continue
2607+            # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
2608+            field_output = [style.SQL_FIELD(qn(f.column)),
2609+                style.SQL_COLTYPE(col_type)]
2610+            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
2611+            if f.primary_key:
2612+                field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
2613+            elif f.unique:
2614+                field_output.append(style.SQL_KEYWORD('UNIQUE'))
2615+            if tablespace and f.unique:
2616+                # We must specify the index tablespace inline, because we
2617+                # won't be generating a CREATE INDEX statement for this field.
2618+                field_output.append(self.connection.ops.tablespace_sql(tablespace, inline=True))
2619+            if f.rel:
2620+                ref_output, pending = self.sql_for_inline_foreign_key_references(f, known_models, style)
2621+                if pending:
2622+                    pr = pending_references.setdefault(f.rel.to, []).append((model, f))
2623+                else:
2624+                    field_output.extend(ref_output)
2625+            table_output.append(' '.join(field_output))
2626+        if opts.order_with_respect_to:
2627+            table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
2628+                style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
2629+                style.SQL_KEYWORD('NULL'))
2630+        for field_constraints in opts.unique_together:
2631+            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
2632+                ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
2633+
2634+        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
2635+        for i, line in enumerate(table_output): # Combine and add commas.
2636+            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
2637+        full_statement.append(')')
2638+        if opts.db_tablespace:
2639+            full_statement.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
2640+        full_statement.append(';')
2641+        final_output.append('\n'.join(full_statement))
2642+
2643+        if opts.has_auto_field:
2644+            # Add any extra SQL needed to support auto-incrementing primary keys.
2645+            auto_column = opts.auto_field.db_column or opts.auto_field.name
2646+            autoinc_sql = self.connection.ops.autoinc_sql(opts.db_table, auto_column)
2647+            if autoinc_sql:
2648+                for stmt in autoinc_sql:
2649+                    final_output.append(stmt)
2650+
2651+        return final_output, pending_references
2652+       
2653+    def sql_for_inline_foreign_key_references(self, field, known_models, style):
2654+        "Return the SQL snippet defining the foreign key reference for a field"
2655+        qn = self.connection.ops.quote_name
2656+        if field.rel.to in known_models:
2657+            output = [style.SQL_KEYWORD('REFERENCES') + ' ' + \
2658+                style.SQL_TABLE(qn(field.rel.to._meta.db_table)) + ' (' + \
2659+                style.SQL_FIELD(qn(field.rel.to._meta.get_field(field.rel.field_name).column)) + ')' +
2660+                self.connection.ops.deferrable_sql()
2661+            ]
2662+            pending = False
2663+        else:
2664+            # We haven't yet created the table to which this field
2665+            # is related, so save it for later.
2666+            output = []
2667+            pending = True
2668+       
2669+        return output, pending
2670+       
2671+    def sql_for_pending_references(self, model, style, pending_references):
2672+        "Returns any ALTER TABLE statements to add constraints after the fact."
2673+        from django.db.backends.util import truncate_name
2674+
2675+        qn = self.connection.ops.quote_name
2676+        final_output = []
2677+        opts = model._meta
2678+        if model in pending_references:
2679+            for rel_class, f in pending_references[model]:
2680+                rel_opts = rel_class._meta
2681+                r_table = rel_opts.db_table
2682+                r_col = f.column
2683+                table = opts.db_table
2684+                col = opts.get_field(f.rel.field_name).column
2685+                # For MySQL, r_name must be unique in the first 64 characters.
2686+                # So we are careful with character usage here.
2687+                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
2688+                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
2689+                    (qn(r_table), truncate_name(r_name, self.connection.ops.max_name_length()),
2690+                    qn(r_col), qn(table), qn(col),
2691+                    self.connection.ops.deferrable_sql()))
2692+            del pending_references[model]
2693+        return final_output
2694+   
2695+    def sql_for_many_to_many(self, model, style):
2696+        "Return the CREATE TABLE statments for all the many-to-many tables defined on a model"
2697+        output = []
2698+        for f in model._meta.local_many_to_many:
2699+            output.extend(self.sql_for_many_to_many_field(model, f, style))
2700+        return output
2701+
2702+    def sql_for_many_to_many_field(self, model, f, style):
2703+        "Return the CREATE TABLE statements for a single m2m field"
2704+        from django.db import models
2705+        from django.db.backends.util import truncate_name
2706+
2707+        output = []
2708+        if f.creates_table:
2709+            opts = model._meta
2710+            qn = self.connection.ops.quote_name
2711+            tablespace = f.db_tablespace or opts.db_tablespace
2712+            if tablespace:
2713+                sql = self.connection.ops.tablespace_sql(tablespace, inline=True)
2714+                if sql:
2715+                    tablespace_sql = ' ' + sql
2716+                else:
2717+                    tablespace_sql = ''
2718+            else:
2719+                tablespace_sql = ''
2720+            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
2721+                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
2722+            table_output.append('    %s %s %s%s,' %
2723+                (style.SQL_FIELD(qn('id')),
2724+                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
2725+                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
2726+                tablespace_sql))
2727+
2728+            deferred = []
2729+            inline_output, deferred = self.sql_for_inline_many_to_many_references(model, f, style)
2730+            table_output.extend(inline_output)
2731+
2732+            table_output.append('    %s (%s, %s)%s' %
2733+                (style.SQL_KEYWORD('UNIQUE'),
2734+                style.SQL_FIELD(qn(f.m2m_column_name())),
2735+                style.SQL_FIELD(qn(f.m2m_reverse_name())),
2736+                tablespace_sql))
2737+            table_output.append(')')
2738+            if opts.db_tablespace:
2739+                # f.db_tablespace is only for indices, so ignore its value here.
2740+                table_output.append(self.connection.ops.tablespace_sql(opts.db_tablespace))
2741+            table_output.append(';')
2742+            output.append('\n'.join(table_output))
2743+
2744+            for r_table, r_col, table, col in deferred:
2745+                r_name = '%s_refs_%s_%x' % (r_col, col,
2746+                        abs(hash((r_table, table))))
2747+                output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
2748+                (qn(r_table),
2749+                truncate_name(r_name, self.connection.ops.max_name_length()),
2750+                qn(r_col), qn(table), qn(col),
2751+                self.connection.ops.deferrable_sql()))
2752+
2753+            # Add any extra SQL needed to support auto-incrementing PKs
2754+            autoinc_sql = self.connection.ops.autoinc_sql(f.m2m_db_table(), 'id')
2755+            if autoinc_sql:
2756+                for stmt in autoinc_sql:
2757+                    output.append(stmt)
2758+        return output       
2759+
2760+    def sql_for_inline_many_to_many_references(self, model, field, style):
2761+        "Create the references to other tables required by a many-to-many table"
2762+        from django.db import models
2763+        opts = model._meta
2764+        qn = self.connection.ops.quote_name
2765+
2766+        table_output = [
2767+            '    %s %s %s %s (%s)%s,' %
2768+                (style.SQL_FIELD(qn(field.m2m_column_name())),
2769+                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
2770+                style.SQL_KEYWORD('NOT NULL REFERENCES'),
2771+                style.SQL_TABLE(qn(opts.db_table)),
2772+                style.SQL_FIELD(qn(opts.pk.column)),
2773+                self.connection.ops.deferrable_sql()),
2774+            '    %s %s %s %s (%s)%s,' %
2775+                (style.SQL_FIELD(qn(field.m2m_reverse_name())),
2776+                style.SQL_COLTYPE(models.ForeignKey(field.rel.to).db_type()),
2777+                style.SQL_KEYWORD('NOT NULL REFERENCES'),
2778+                style.SQL_TABLE(qn(field.rel.to._meta.db_table)),
2779+                style.SQL_FIELD(qn(field.rel.to._meta.pk.column)),
2780+                self.connection.ops.deferrable_sql())
2781+        ]
2782+        deferred = []
2783+
2784+        return table_output, deferred
2785+       
2786+    def sql_indexes_for_model(self, model, style):
2787+        "Returns the CREATE INDEX SQL statements for a single model"
2788+        output = []
2789+        for f in model._meta.local_fields:
2790+            output.extend(self.sql_indexes_for_field(model, f, style))
2791+        return output
2792+       
2793+    def sql_indexes_for_field(self, model, f, style):
2794+        "Return the CREATE INDEX SQL statements for a single model field"
2795+        if f.db_index and not f.unique:
2796+            qn = self.connection.ops.quote_name
2797+            tablespace = f.db_tablespace or model._meta.db_tablespace
2798+            if tablespace:
2799+                sql = self.connection.ops.tablespace_sql(tablespace)
2800+                if sql:
2801+                    tablespace_sql = ' ' + sql
2802+                else:
2803+                    tablespace_sql = ''
2804+            else:
2805+                tablespace_sql = ''
2806+            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
2807+                style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' +
2808+                style.SQL_KEYWORD('ON') + ' ' +
2809+                style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
2810+                "(%s)" % style.SQL_FIELD(qn(f.column)) +
2811+                "%s;" % tablespace_sql]
2812+        else:
2813+            output = []
2814+        return output
2815+
2816+    def sql_destroy_model(self, model, references_to_delete, style):
2817+        "Return the DROP TABLE and restraint dropping statements for a single model"
2818+        # Drop the table now
2819+        qn = self.connection.ops.quote_name
2820+        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
2821+                              style.SQL_TABLE(qn(model._meta.db_table)))]
2822+        if model in references_to_delete:
2823+            output.extend(self.sql_remove_table_constraints(model, references_to_delete))
2824+           
2825+        if model._meta.has_auto_field:
2826+            ds = self.connection.ops.drop_sequence_sql(model._meta.db_table)
2827+            if ds:
2828+                output.append(ds)
2829+        return output
2830+
2831+    def sql_remove_table_constraints(self, model, references_to_delete):
2832+        output = []
2833+        for rel_class, f in references_to_delete[model]:
2834+            table = rel_class._meta.db_table
2835+            col = f.column
2836+            r_table = model._meta.db_table
2837+            r_col = model._meta.get_field(f.rel.field_name).column
2838+            r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
2839+            output.append('%s %s %s %s;' % \
2840+                (style.SQL_KEYWORD('ALTER TABLE'),
2841+                style.SQL_TABLE(qn(table)),
2842+                style.SQL_KEYWORD(self.connection.ops.drop_foreignkey_sql()),
2843+                style.SQL_FIELD(truncate_name(r_name, self.connection.ops.max_name_length()))))
2844+        del references_to_delete[model]
2845+        return output
2846+       
2847+    def sql_destroy_many_to_many(self, model, f, style):
2848+        "Returns the DROP TABLE statements for a single m2m field"
2849+        qn = self.connection.ops.quote_name
2850+        output = []
2851+        if f.creates_table:
2852+            output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
2853+                style.SQL_TABLE(qn(f.m2m_db_table()))))
2854+            ds = self.connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
2855+            if ds:
2856+                output.append(ds)
2857+        return output
2858+       
2859+    def create_test_db(self, verbosity=1, autoclobber=False):
2860+        """
2861+        Creates a test database, prompting the user for confirmation if the
2862+        database already exists. Returns the name of the test database created.
2863+        """
2864+        if verbosity >= 1:
2865+            print "Creating test database..."
2866+           
2867+        test_database_name = self._create_test_db(verbosity, autoclobber)
2868+
2869+        self.connection.close()
2870+        settings.DATABASE_NAME = test_database_name
2871+
2872+        call_command('syncdb', verbosity=verbosity, interactive=False)
2873+
2874+        if settings.CACHE_BACKEND.startswith('db://'):
2875+            cache_name = settings.CACHE_BACKEND[len('db://'):]
2876+            call_command('createcachetable', cache_name)
2877+
2878+        # Get a cursor (even though we don't need one yet). This has
2879+        # the side effect of initializing the test database.
2880+        cursor = self.connection.cursor()
2881+
2882+        return test_database_name
2883+
2884+    def _create_test_db(self, verbosity, autoclobber):
2885+        "Internal implementation - creates the test db tables."
2886+        suffix = self.sql_table_creation_suffix()
2887+       
2888+        if settings.TEST_DATABASE_NAME:
2889+            test_database_name = settings.TEST_DATABASE_NAME
2890+        else:
2891+            test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
2892+
2893+        qn = self.connection.ops.quote_name
2894+
2895+        # Create the test database and connect to it. We need to autocommit
2896+        # if the database supports it because PostgreSQL doesn't allow
2897+        # CREATE/DROP DATABASE statements within transactions.
2898+        cursor = self.connection.cursor()
2899+        self.set_autocommit()
2900+        try:
2901+            cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
2902+        except Exception, e:
2903+            sys.stderr.write("Got an error creating the test database: %s\n" % e)
2904+            if not autoclobber:
2905+                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
2906+            if autoclobber or confirm == 'yes':
2907+                try:
2908+                    if verbosity >= 1:
2909+                        print "Destroying old test database..."
2910+                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
2911+                    if verbosity >= 1:
2912+                        print "Creating test database..."
2913+                    cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
2914+                except Exception, e:
2915+                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
2916+                    sys.exit(2)
2917+            else:
2918+                print "Tests cancelled."
2919+                sys.exit(1)
2920+               
2921+        return test_database_name
2922+       
2923+    def destroy_test_db(self, old_database_name, verbosity=1):
2924+        """
2925+        Destroy a test database, prompting the user for confirmation if the
2926+        database already exists. Returns the name of the test database created.
2927+        """
2928+        if verbosity >= 1:
2929+            print "Destroying test database..."
2930+        self.connection.close()
2931+        test_database_name = settings.DATABASE_NAME
2932+        settings.DATABASE_NAME = old_database_name
2933+       
2934+        self._destroy_test_db(test_database_name, verbosity)
2935+       
2936+    def _destroy_test_db(self, test_database_name, verbosity):
2937+        "Internal implementation - remove the test db tables."
2938+        # Remove the test database to clean up after
2939+        # ourselves. Connect to the previous database (not the test database)
2940+        # to do so, because it's not allowed to delete a database while being
2941+        # connected to it.
2942+        cursor = self.connection.cursor()
2943+        self.set_autocommit()
2944+        time.sleep(1) # To avoid "database is being accessed by other users" errors.
2945+        cursor.execute("DROP DATABASE %s" % self.connection.ops.quote_name(test_database_name))
2946+        self.connection.close()
2947+
2948+    def set_autocommit(self):
2949+        "Make sure a connection is in autocommit mode."
2950+        if hasattr(self.connection.connection, "autocommit"):
2951+            if callable(self.connection.connection.autocommit):
2952+                self.connection.connection.autocommit(True)
2953+            else:
2954+                self.connection.connection.autocommit = True
2955+        elif hasattr(self.connection.connection, "set_isolation_level"):
2956+            self.connection.connection.set_isolation_level(0)
2957+
2958+    def sql_table_creation_suffix(self):
2959+        "SQL to append to the end of the test table creation statements"
2960+        return ''
2961+       
2962Index: django/core/management/commands/syncdb.py
2963===================================================================
2964--- django/core/management/commands/syncdb.py   (revision 8280)
2965+++ django/core/management/commands/syncdb.py   (working copy)
2966@@ -21,7 +21,7 @@
2967     def handle_noargs(self, **options):
2968         from django.db import connection, transaction, models
2969         from django.conf import settings
2970-        from django.core.management.sql import table_names, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
2971+        from django.core.management.sql import custom_sql_for_model, emit_post_sync_signal
2972 
2973         verbosity = int(options.get('verbosity', 1))
2974         interactive = options.get('interactive')
2975@@ -50,16 +50,9 @@
2976 
2977         cursor = connection.cursor()
2978 
2979-        if connection.features.uses_case_insensitive_names:
2980-            table_name_converter = lambda x: x.upper()
2981-        else:
2982-            table_name_converter = lambda x: x
2983-        # Get a list of all existing database tables, so we know what needs to
2984-        # be added.
2985-        tables = [table_name_converter(name) for name in table_names()]
2986-
2987         # Get a list of already installed *models* so that references work right.
2988-        seen_models = installed_models(tables)
2989+        tables = connection.introspection.table_names()
2990+        seen_models = connection.introspection.installed_models(tables)
2991         created_models = set()
2992         pending_references = {}
2993 
2994@@ -71,21 +64,21 @@
2995                 # Create the model's database table, if it doesn't already exist.
2996                 if verbosity >= 2:
2997                     print "Processing %s.%s model" % (app_name, model._meta.object_name)
2998-                if table_name_converter(model._meta.db_table) in tables:
2999+                if connection.introspection.table_name_converter(model._meta.db_table) in tables:
3000                     continue
3001-                sql, references = sql_model_create(model, self.style, seen_models)
3002+                sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
3003                 seen_models.add(model)
3004                 created_models.add(model)
3005                 for refto, refs in references.items():
3006                     pending_references.setdefault(refto, []).extend(refs)
3007                     if refto in seen_models:
3008-                        sql.extend(sql_for_pending_references(refto, self.style, pending_references))
3009-                sql.extend(sql_for_pending_references(model, self.style, pending_references))
3010+                        sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
3011+                sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
3012                 if verbosity >= 1:
3013                     print "Creating table %s" % model._meta.db_table
3014                 for statement in sql:
3015                     cursor.execute(statement)
3016-                tables.append(table_name_converter(model._meta.db_table))
3017+                tables.append(connection.introspection.table_name_converter(model._meta.db_table))
3018 
3019         # Create the m2m tables. This must be done after all tables have been created
3020         # to ensure that all referred tables will exist.
3021@@ -94,7 +87,7 @@
3022             model_list = models.get_models(app)
3023             for model in model_list:
3024                 if model in created_models:
3025-                    sql = many_to_many_sql_for_model(model, self.style)
3026+                    sql = connection.creation.sql_for_many_to_many(model, self.style)
3027                     if sql:
3028                         if verbosity >= 2:
3029                             print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name)
3030@@ -140,7 +133,7 @@
3031             app_name = app.__name__.split('.')[-2]
3032             for model in models.get_models(app):
3033                 if model in created_models:
3034-                    index_sql = sql_indexes_for_model(model, self.style)
3035+                    index_sql = connection.creation.sql_indexes_for_model(model, self.style)
3036                     if index_sql:
3037                         if verbosity >= 1:
3038                             print "Installing index for %s.%s model" % (app_name, model._meta.object_name)
3039Index: django/core/management/commands/testserver.py
3040===================================================================
3041--- django/core/management/commands/testserver.py       (revision 8280)
3042+++ django/core/management/commands/testserver.py       (working copy)
3043@@ -18,13 +18,13 @@
3044 
3045     def handle(self, *fixture_labels, **options):
3046         from django.core.management import call_command
3047-        from django.test.utils import create_test_db
3048+        from django.db import connection
3049 
3050         verbosity = int(options.get('verbosity', 1))
3051         addrport = options.get('addrport')
3052 
3053         # Create a test database.
3054-        db_name = create_test_db(verbosity=verbosity)
3055+        db_name = connection.creation.create_test_db(verbosity=verbosity)
3056 
3057         # Import the fixture data into the test database.
3058         call_command('loaddata', *fixture_labels, **{'verbosity': verbosity})
3059Index: django/core/management/commands/inspectdb.py
3060===================================================================
3061--- django/core/management/commands/inspectdb.py        (revision 8280)
3062+++ django/core/management/commands/inspectdb.py        (working copy)
3063@@ -13,11 +13,9 @@
3064             raise CommandError("Database inspection isn't supported for the currently selected database backend.")
3065 
3066     def handle_inspection(self):
3067-        from django.db import connection, get_introspection_module
3068+        from django.db import connection
3069         import keyword
3070 
3071-        introspection_module = get_introspection_module()
3072-
3073         table2model = lambda table_name: table_name.title().replace('_', '')
3074 
3075         cursor = connection.cursor()
3076@@ -32,17 +30,17 @@
3077         yield ''
3078         yield 'from django.db import models'
3079         yield ''
3080-        for table_name in introspection_module.get_table_list(cursor):
3081+        for table_name in connection.introspection.get_table_list(cursor):
3082             yield 'class %s(models.Model):' % table2model(table_name)
3083             try:
3084-                relations = introspection_module.get_relations(cursor, table_name)
3085+                relations = connection.introspection.get_relations(cursor, table_name)
3086             except NotImplementedError:
3087                 relations = {}
3088             try:
3089-                indexes = introspection_module.get_indexes(cursor, table_name)
3090+                indexes = connection.introspection.get_indexes(cursor, table_name)
3091             except NotImplementedError:
3092                 indexes = {}
3093-            for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
3094+            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
3095                 att_name = row[0].lower()
3096                 comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
3097                 extra_params = {}  # Holds Field parameters such as 'db_column'.
3098@@ -65,7 +63,7 @@
3099                         extra_params['db_column'] = att_name
3100                 else:
3101                     try:
3102-                        field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
3103+                        field_type = connection.introspection.data_types_reverse[row[1]]
3104                     except KeyError:
3105                         field_type = 'TextField'
3106                         comment_notes.append('This field type is a guess.')
3107Index: django/core/management/commands/dbshell.py
3108===================================================================
3109--- django/core/management/commands/dbshell.py  (revision 8280)
3110+++ django/core/management/commands/dbshell.py  (working copy)
3111@@ -6,5 +6,5 @@
3112     requires_model_validation = False
3113 
3114     def handle_noargs(self, **options):
3115-        from django.db import runshell
3116-        runshell()
3117+        from django.db import connection
3118+        connection.client.runshell()
3119Index: django/core/management/validation.py
3120===================================================================
3121--- django/core/management/validation.py        (revision 8280)
3122+++ django/core/management/validation.py        (working copy)
3123@@ -61,11 +61,8 @@
3124             if f.db_index not in (None, True, False):
3125                 e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
3126 
3127-            # Check that max_length <= 255 if using older MySQL versions.
3128-            if settings.DATABASE_ENGINE == 'mysql':
3129-                db_version = connection.get_server_version()
3130-                if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
3131-                    e.add(opts, '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
3132+            # Perform any backend-specific field validation.
3133+            connection.validation.validate_field(e, opts, f)
3134 
3135             # Check to see if the related field will clash with any existing
3136             # fields, m2m fields, m2m related objects or related objects
3137Index: django/core/management/sql.py
3138===================================================================
3139--- django/core/management/sql.py       (revision 8280)
3140+++ django/core/management/sql.py       (working copy)
3141@@ -7,65 +7,9 @@
3142 except NameError:
3143     from sets import Set as set   # Python 2.3 fallback
3144 
3145-def table_names():
3146-    "Returns a list of all table names that exist in the database."
3147-    from django.db import connection, get_introspection_module
3148-    cursor = connection.cursor()
3149-    return set(get_introspection_module().get_table_list(cursor))
3150-
3151-def django_table_names(only_existing=False):
3152-    """
3153-    Returns a list of all table names that have associated Django models and
3154-    are in INSTALLED_APPS.
3155-
3156-    If only_existing is True, the resulting list will only include the tables
3157-    that actually exist in the database.
3158-    """
3159-    from django.db import models
3160-    tables = set()
3161-    for app in models.get_apps():
3162-        for model in models.get_models(app):
3163-            tables.add(model._meta.db_table)
3164-            tables.update([f.m2m_db_table() for f in model._meta.local_many_to_many])
3165-    if only_existing:
3166-        tables = [t for t in tables if t in table_names()]
3167-    return tables
3168-
3169-def installed_models(table_list):
3170-    "Returns a set of all models that are installed, given a list of existing table names."
3171-    from django.db import connection, models
3172-    all_models = []
3173-    for app in models.get_apps():
3174-        for model in models.get_models(app):
3175-            all_models.append(model)
3176-    if connection.features.uses_case_insensitive_names:
3177-        converter = lambda x: x.upper()
3178-    else:
3179-        converter = lambda x: x
3180-    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)])
3181-
3182-def sequence_list():
3183-    "Returns a list of information about all DB sequences for all models in all apps."
3184-    from django.db import models
3185-
3186-    apps = models.get_apps()
3187-    sequence_list = []
3188-
3189-    for app in apps:
3190-        for model in models.get_models(app):
3191-            for f in model._meta.local_fields:
3192-                if isinstance(f, models.AutoField):
3193-                    sequence_list.append({'table': model._meta.db_table, 'column': f.column})
3194-                    break # Only one AutoField is allowed per model, so don't bother continuing.
3195-
3196-            for f in model._meta.local_many_to_many:
3197-                sequence_list.append({'table': f.m2m_db_table(), 'column': None})
3198-
3199-    return sequence_list
3200-
3201 def sql_create(app, style):
3202     "Returns a list of the CREATE TABLE SQL statements for the given app."
3203-    from django.db import models
3204+    from django.db import connection, models
3205     from django.conf import settings
3206 
3207     if settings.DATABASE_ENGINE == 'dummy':
3208@@ -81,23 +25,24 @@
3209     # we can be conservative).
3210     app_models = models.get_models(app)
3211     final_output = []
3212-    known_models = set([model for model in installed_models(table_names()) if model not in app_models])
3213+    tables = connection.introspection.table_names()
3214+    known_models = set([model for model in connection.introspection.installed_models(tables) if model not in app_models])
3215     pending_references = {}
3216 
3217     for model in app_models:
3218-        output, references = sql_model_create(model, style, known_models)
3219+        output, references = connection.creation.sql_create_model(model, style, known_models)
3220         final_output.extend(output)
3221         for refto, refs in references.items():
3222             pending_references.setdefault(refto, []).extend(refs)
3223             if refto in known_models:
3224-                final_output.extend(sql_for_pending_references(refto, style, pending_references))
3225-        final_output.extend(sql_for_pending_references(model, style, pending_references))
3226+                final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
3227+        final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
3228         # Keep track of the fact that we've created the table for this model.
3229         known_models.add(model)
3230 
3231     # Create the many-to-many join tables.
3232     for model in app_models:
3233-        final_output.extend(many_to_many_sql_for_model(model, style))
3234+        final_output.extend(connection.creation.sql_for_many_to_many(model, style))
3235 
3236     # Handle references to tables that are from other apps
3237     # but don't exist physically.
3238@@ -106,7 +51,7 @@
3239         alter_sql = []
3240         for model in not_installed_models:
3241             alter_sql.extend(['-- ' + sql for sql in
3242-                sql_for_pending_references(model, style, pending_references)])
3243+                connection.creation.sql_for_pending_references(model, style, pending_references)])
3244         if alter_sql:
3245             final_output.append('-- The following references should be added but depend on non-existent tables:')
3246             final_output.extend(alter_sql)
3247@@ -115,10 +60,9 @@
3248 
3249 def sql_delete(app, style):
3250     "Returns a list of the DROP TABLE SQL statements for the given app."
3251-    from django.db import connection, models, get_introspection_module
3252+    from django.db import connection, models
3253     from django.db.backends.util import truncate_name
3254     from django.contrib.contenttypes import generic
3255-    introspection = get_introspection_module()
3256 
3257     # This should work even if a connection isn't available
3258     try:
3259@@ -128,16 +72,11 @@
3260 
3261     # Figure out which tables already exist
3262     if cursor:
3263-        table_names = introspection.get_table_list(cursor)
3264+        table_names = connection.introspection.get_table_list(cursor)
3265     else:
3266         table_names = []
3267-    if connection.features.uses_case_insensitive_names:
3268-        table_name_converter = lambda x: x.upper()
3269-    else:
3270-        table_name_converter = lambda x: x
3271 
3272     output = []
3273-    qn = connection.ops.quote_name
3274 
3275     # Output DROP TABLE statements for standard application tables.
3276     to_delete = set()
3277@@ -145,7 +84,7 @@
3278     references_to_delete = {}
3279     app_models = models.get_models(app)
3280     for model in app_models:
3281-        if cursor and table_name_converter(model._meta.db_table) in table_names:
3282+        if cursor and connection.introspection.table_name_converter(model._meta.db_table) in table_names:
3283             # The table exists, so it needs to be dropped
3284             opts = model._meta
3285             for f in opts.local_fields:
3286@@ -155,40 +94,15 @@
3287             to_delete.add(model)
3288 
3289     for model in app_models:
3290-        if cursor and table_name_converter(model._meta.db_table) in table_names:
3291-            # Drop the table now
3292-            output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
3293-                style.SQL_TABLE(qn(model._meta.db_table))))
3294-            if connection.features.supports_constraints and model in references_to_delete:
3295-                for rel_class, f in references_to_delete[model]:
3296-                    table = rel_class._meta.db_table
3297-                    col = f.column
3298-                    r_table = model._meta.db_table
3299-                    r_col = model._meta.get_field(f.rel.field_name).column
3300-                    r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
3301-                    output.append('%s %s %s %s;' % \
3302-                        (style.SQL_KEYWORD('ALTER TABLE'),
3303-                        style.SQL_TABLE(qn(table)),
3304-                        style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
3305-                        style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
3306-                del references_to_delete[model]
3307-            if model._meta.has_auto_field:
3308-                ds = connection.ops.drop_sequence_sql(model._meta.db_table)
3309-                if ds:
3310-                    output.append(ds)
3311+        if connection.introspection.table_name_converter(model._meta.db_table) in table_names:
3312+            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
3313 
3314     # Output DROP TABLE statements for many-to-many tables.
3315     for model in app_models:
3316         opts = model._meta
3317         for f in opts.local_many_to_many:
3318-            if not f.creates_table:
3319-                continue
3320-            if cursor and table_name_converter(f.m2m_db_table()) in table_names:
3321-                output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
3322-                    style.SQL_TABLE(qn(f.m2m_db_table()))))
3323-                ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
3324-                if ds:
3325-                    output.append(ds)
3326+            if cursor and connection.introspection.table_name_converter(f.m2m_db_table()) in table_names:
3327+                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
3328 
3329     app_label = app_models[0]._meta.app_label
3330 
3331@@ -213,10 +127,10 @@
3332     """
3333     from django.db import connection
3334     if only_django:
3335-        tables = django_table_names()
3336+        tables = connection.introspection.django_table_names()
3337     else:
3338-        tables = table_names()
3339-    statements = connection.ops.sql_flush(style, tables, sequence_list())
3340+        tables = connection.introspection.table_names()
3341+    statements = connection.ops.sql_flush(style, tables, connection.introspection.sequence_list())
3342     return statements
3343 
3344 def sql_custom(app, style):
3345@@ -234,198 +148,16 @@
3346 
3347 def sql_indexes(app, style):
3348     "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
3349-    from django.db import models
3350+    from django.db import connection, models
3351     output = []
3352     for model in models.get_models(app):
3353-        output.extend(sql_indexes_for_model(model, style))
3354+        output.extend(connection.creation.sql_indexes_for_model(model, style))
3355     return output
3356 
3357 def sql_all(app, style):
3358     "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
3359     return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style)
3360 
3361-def sql_model_create(model, style, known_models=set()):
3362-    """
3363-    Returns the SQL required to create a single model, as a tuple of:
3364-        (list_of_sql, pending_references_dict)
3365-    """
3366-    from django.db import connection, models
3367-
3368-    opts = model._meta
3369-    final_output = []
3370-    table_output = []
3371-    pending_references = {}
3372-    qn = connection.ops.quote_name
3373-    inline_references = connection.features.inline_fk_references
3374-    for f in opts.local_fields:
3375-        col_type = f.db_type()
3376-        tablespace = f.db_tablespace or opts.db_tablespace
3377-        if col_type is None:
3378-            # Skip ManyToManyFields, because they're not represented as
3379-            # database columns in this table.
3380-            continue
3381-        # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
3382-        field_output = [style.SQL_FIELD(qn(f.column)),
3383-            style.SQL_COLTYPE(col_type)]
3384-        field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
3385-        if f.primary_key:
3386-            field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
3387-        elif f.unique:
3388-            field_output.append(style.SQL_KEYWORD('UNIQUE'))
3389-        if tablespace and connection.features.supports_tablespaces and f.unique:
3390-            # We must specify the index tablespace inline, because we
3391-            # won't be generating a CREATE INDEX statement for this field.
3392-            field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
3393-        if f.rel:
3394-            if inline_references and f.rel.to in known_models:
3395-                field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
3396-                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
3397-                    style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
3398-                    connection.ops.deferrable_sql()
3399-                )
3400-            else:
3401-                # We haven't yet created the table to which this field
3402-                # is related, so save it for later.
3403-                pr = pending_references.setdefault(f.rel.to, []).append((model, f))
3404-        table_output.append(' '.join(field_output))
3405-    if opts.order_with_respect_to:
3406-        table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
3407-            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
3408-            style.SQL_KEYWORD('NULL'))
3409-    for field_constraints in opts.unique_together:
3410-        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
3411-            ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
3412-
3413-    full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
3414-    for i, line in enumerate(table_output): # Combine and add commas.
3415-        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
3416-    full_statement.append(')')
3417-    if opts.db_tablespace and connection.features.supports_tablespaces:
3418-        full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
3419-    full_statement.append(';')
3420-    final_output.append('\n'.join(full_statement))
3421-
3422-    if opts.has_auto_field:
3423-        # Add any extra SQL needed to support auto-incrementing primary keys.
3424-        auto_column = opts.auto_field.db_column or opts.auto_field.name
3425-        autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column)
3426-        if autoinc_sql:
3427-            for stmt in autoinc_sql:
3428-                final_output.append(stmt)
3429-
3430-    return final_output, pending_references
3431-
3432-def sql_for_pending_references(model, style, pending_references):
3433-    """
3434-    Returns any ALTER TABLE statements to add constraints after the fact.
3435-    """
3436-    from django.db import connection
3437-    from django.db.backends.util import truncate_name
3438-
3439-    qn = connection.ops.quote_name
3440-    final_output = []
3441-    if connection.features.supports_constraints:
3442-        opts = model._meta
3443-        if model in pending_references:
3444-            for rel_class, f in pending_references[model]:
3445-                rel_opts = rel_class._meta
3446-                r_table = rel_opts.db_table
3447-                r_col = f.column
3448-                table = opts.db_table
3449-                col = opts.get_field(f.rel.field_name).column
3450-                # For MySQL, r_name must be unique in the first 64 characters.
3451-                # So we are careful with character usage here.
3452-                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
3453-                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
3454-                    (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
3455-                    qn(r_col), qn(table), qn(col),
3456-                    connection.ops.deferrable_sql()))
3457-            del pending_references[model]
3458-    return final_output
3459-
3460-def many_to_many_sql_for_model(model, style):
3461-    from django.db import connection, models
3462-    from django.contrib.contenttypes import generic
3463-    from django.db.backends.util import truncate_name
3464-
3465-    opts = model._meta
3466-    final_output = []
3467-    qn = connection.ops.quote_name
3468-    inline_references = connection.features.inline_fk_references
3469-    for f in opts.local_many_to_many:
3470-        if f.creates_table:
3471-            tablespace = f.db_tablespace or opts.db_tablespace
3472-            if tablespace and connection.features.supports_tablespaces:
3473-                tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
3474-            else:
3475-                tablespace_sql = ''
3476-            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
3477-                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
3478-            table_output.append('    %s %s %s%s,' %
3479-                (style.SQL_FIELD(qn('id')),
3480-                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
3481-                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
3482-                tablespace_sql))
3483-            if inline_references:
3484-                deferred = []
3485-                table_output.append('    %s %s %s %s (%s)%s,' %
3486-                    (style.SQL_FIELD(qn(f.m2m_column_name())),
3487-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
3488-                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
3489-                    style.SQL_TABLE(qn(opts.db_table)),
3490-                    style.SQL_FIELD(qn(opts.pk.column)),
3491-                    connection.ops.deferrable_sql()))
3492-                table_output.append('    %s %s %s %s (%s)%s,' %
3493-                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
3494-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
3495-                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
3496-                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
3497-                    style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
3498-                    connection.ops.deferrable_sql()))
3499-            else:
3500-                table_output.append('    %s %s %s,' %
3501-                    (style.SQL_FIELD(qn(f.m2m_column_name())),
3502-                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
3503-                    style.SQL_KEYWORD('NOT NULL')))
3504-                table_output.append('    %s %s %s,' %
3505-                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
3506-                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
3507-                    style.SQL_KEYWORD('NOT NULL')))
3508-                deferred = [
3509-                    (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
3510-                        opts.pk.column),
3511-                    ( f.m2m_db_table(), f.m2m_reverse_name(),
3512-                        f.rel.to._meta.db_table, f.rel.to._meta.pk.column)
3513-                    ]
3514-            table_output.append('    %s (%s, %s)%s' %
3515-                (style.SQL_KEYWORD('UNIQUE'),
3516-                style.SQL_FIELD(qn(f.m2m_column_name())),
3517-                style.SQL_FIELD(qn(f.m2m_reverse_name())),
3518-                tablespace_sql))
3519-            table_output.append(')')
3520-            if opts.db_tablespace and connection.features.supports_tablespaces:
3521-                # f.db_tablespace is only for indices, so ignore its value here.
3522-                table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
3523-            table_output.append(';')
3524-            final_output.append('\n'.join(table_output))
3525-
3526-            for r_table, r_col, table, col in deferred:
3527-                r_name = '%s_refs_%s_%x' % (r_col, col,
3528-                        abs(hash((r_table, table))))
3529-                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
3530-                (qn(r_table),
3531-                truncate_name(r_name, connection.ops.max_name_length()),
3532-                qn(r_col), qn(table), qn(col),
3533-                connection.ops.deferrable_sql()))
3534-
3535-            # Add any extra SQL needed to support auto-incrementing PKs
3536-            autoinc_sql = connection.ops.autoinc_sql(f.m2m_db_table(), 'id')
3537-            if autoinc_sql:
3538-                for stmt in autoinc_sql:
3539-                    final_output.append(stmt)
3540-
3541-    return final_output
3542-
3543 def custom_sql_for_model(model, style):
3544     from django.db import models
3545     from django.conf import settings
3546@@ -461,29 +193,7 @@
3547 
3548     return output
3549 
3550-def sql_indexes_for_model(model, style):
3551-    "Returns the CREATE INDEX SQL statements for a single model"
3552-    from django.db import connection
3553-    output = []
3554 
3555-    qn = connection.ops.quote_name
3556-    for f in model._meta.local_fields:
3557-        if f.db_index and not f.unique:
3558-            tablespace = f.db_tablespace or model._meta.db_tablespace
3559-            if tablespace and connection.features.supports_tablespaces:
3560-                tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
3561-            else:
3562-                tablespace_sql = ''
3563-            output.append(
3564-                style.SQL_KEYWORD('CREATE INDEX') + ' ' + \
3565-                style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
3566-                style.SQL_KEYWORD('ON') + ' ' + \
3567-                style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
3568-                "(%s)" % style.SQL_FIELD(qn(f.column)) + \
3569-                "%s;" % tablespace_sql
3570-            )
3571-    return output
3572-
3573 def emit_post_sync_signal(created_models, verbosity, interactive):
3574     from django.db import models
3575     from django.dispatch import dispatcher
3576Index: django/contrib/gis/db/backend/mysql/creation.py
3577===================================================================
3578--- django/contrib/gis/db/backend/mysql/creation.py     (revision 8280)
3579+++ django/contrib/gis/db/backend/mysql/creation.py     (working copy)
3580@@ -1,5 +1,5 @@
3581-from django.test.utils import create_test_db
3582 
3583 def create_spatial_db(test=True, verbosity=1, autoclobber=False):
3584     if not test: raise NotImplementedError('This uses `create_test_db` from test/utils.py')
3585-    create_test_db(verbosity, autoclobber)
3586+    from django.db import connection
3587+    connection.creation.create_test_db(verbosity, autoclobber)
3588Index: django/contrib/gis/db/backend/oracle/creation.py
3589===================================================================
3590--- django/contrib/gis/db/backend/oracle/creation.py    (revision 8280)
3591+++ django/contrib/gis/db/backend/oracle/creation.py    (working copy)
3592@@ -1,8 +1,6 @@
3593-from django.db.backends.oracle.creation import create_test_db
3594 
3595 def create_spatial_db(test=True, verbosity=1, autoclobber=False):
3596     "A wrapper over the Oracle `create_test_db` routine."
3597     if not test: raise NotImplementedError('This uses `create_test_db` from db/backends/oracle/creation.py')
3598-    from django.conf import settings
3599     from django.db import connection
3600-    create_test_db(settings, connection, verbosity, autoclobber)
3601+    connection.creation.create_test_db(verbosity, autoclobber)
3602Index: django/contrib/gis/db/backend/postgis/creation.py
3603===================================================================
3604--- django/contrib/gis/db/backend/postgis/creation.py   (revision 8280)
3605+++ django/contrib/gis/db/backend/postgis/creation.py   (working copy)
3606@@ -1,7 +1,7 @@
3607 from django.conf import settings
3608 from django.core.management import call_command
3609 from django.db import connection
3610-from django.test.utils import _set_autocommit, TEST_DATABASE_PREFIX
3611+from django.db.backends.creation import TEST_DATABASE_PREFIX
3612 import os, re, sys
3613 
3614 def getstatusoutput(cmd):
3615@@ -38,9 +38,9 @@
3616     create_sql = 'CREATE DATABASE %s' % connection.ops.quote_name(db_name)
3617     if settings.DATABASE_USER:
3618         create_sql += ' OWNER %s' % settings.DATABASE_USER
3619-       
3620+
3621     cursor = connection.cursor()
3622-    _set_autocommit(connection)
3623+    connection.creation.set_autocommit(connection)
3624 
3625     try:
3626         # Trying to create the database first.
3627@@ -58,12 +58,12 @@
3628         else:
3629             raise Exception('Spatial Database Creation canceled.')
3630 foo = _create_with_cursor
3631-   
3632+
3633 created_regex = re.compile(r'^createdb: database creation failed: ERROR:  database ".+" already exists')
3634 def _create_with_shell(db_name, verbosity=1, autoclobber=False):
3635     """
3636-    If no spatial database already exists, then using a cursor will not work. 
3637-     Thus, a `createdb` command will be issued through the shell to bootstrap
3638+    If no spatial database already exists, then using a cursor will not work.
3639+     Thus, a `createdb` command will be issued through the shell to bootstrap
3640      creation of the spatial database.
3641     """
3642 
3643@@ -83,7 +83,7 @@
3644                 if verbosity >= 1: print 'Destroying old spatial database...'
3645                 drop_cmd = 'dropdb %s%s' % (options, db_name)
3646                 status, output = getstatusoutput(drop_cmd)
3647-                if status != 0:
3648+                if status != 0:
3649                     raise Exception('Could not drop database %s: %s' % (db_name, output))
3650                 if verbosity >= 1: print 'Creating new spatial database...'
3651                 status, output = getstatusoutput(create_cmd)
3652@@ -102,10 +102,10 @@
3653         raise Exception('Spatial database creation only supported postgresql_psycopg2 platform.')
3654 
3655     # Getting the spatial database name
3656-    if test:
3657+    if test:
3658         db_name = get_spatial_db(test=True)
3659         _create_with_cursor(db_name, verbosity=verbosity, autoclobber=autoclobber)
3660-    else:
3661+    else:
3662         db_name = get_spatial_db()
3663         _create_with_shell(db_name, verbosity=verbosity, autoclobber=autoclobber)
3664 
3665@@ -125,7 +125,7 @@
3666 
3667     # Syncing the database
3668     call_command('syncdb', verbosity=verbosity, interactive=interactive)
3669-   
3670+
3671 def drop_db(db_name=False, test=False):
3672     """
3673     Drops the given database (defaults to what is returned from
3674@@ -151,7 +151,7 @@
3675 
3676 def get_spatial_db(test=False):
3677     """
3678-    Returns the name of the spatial database.  The 'test' keyword may be set
3679+    Returns the name of the spatial database.  The 'test' keyword may be set
3680      to return the test spatial database name.
3681     """
3682     if test:
3683@@ -167,13 +167,13 @@
3684 
3685 def load_postgis_sql(db_name, verbosity=1):
3686     """
3687-    This routine loads up the PostGIS SQL files lwpostgis.sql and
3688+    This routine loads up the PostGIS SQL files lwpostgis.sql and
3689      spatial_ref_sys.sql.
3690     """
3691 
3692     # Getting the path to the PostGIS SQL
3693     try:
3694-        # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
3695+        # POSTGIS_SQL_PATH may be placed in settings to tell GeoDjango where the
3696         #  PostGIS SQL files are located.  This is especially useful on Win32
3697         #  platforms since the output of pg_config looks like "C:/PROGRA~1/..".
3698         sql_path = settings.POSTGIS_SQL_PATH
3699@@ -193,7 +193,7 @@
3700     # Getting the psql command-line options, and command format.
3701     options = get_cmd_options(db_name)
3702     cmd_fmt = 'psql %s-f "%%s"' % options
3703-   
3704+
3705     # Now trying to load up the PostGIS functions
3706     cmd = cmd_fmt % lwpostgis_file
3707     if verbosity >= 1: print cmd
3708@@ -211,8 +211,8 @@
3709     # Setting the permissions because on Windows platforms the owner
3710     #  of the spatial_ref_sys and geometry_columns tables is always
3711     #  the postgres user, regardless of how the db is created.
3712-    if os.name == 'nt': set_permissions(db_name)
3713-   
3714+    if os.name == 'nt': set_permissions(db_name)
3715+
3716 def set_permissions(db_name):
3717     """
3718     Sets the permissions on the given database to that of the user specified
3719Index: django/contrib/gis/management/commands/inspectdb.py
3720===================================================================
3721--- django/contrib/gis/management/commands/inspectdb.py (revision 8280)
3722+++ django/contrib/gis/management/commands/inspectdb.py (working copy)
3723@@ -7,7 +7,7 @@
3724 from django.contrib.gis.db.backend import SpatialBackend
3725 
3726 class Command(InspectCommand):
3727-   
3728+
3729     # Mapping from lower-case OGC type to the corresponding GeoDjango field.
3730     geofield_mapping = {'point' : 'PointField',
3731                         'linestring' : 'LineStringField',
3732@@ -21,11 +21,11 @@
3733 
3734     def geometry_columns(self):
3735         """
3736-        Returns a datastructure of metadata information associated with the
3737+        Returns a datastructure of metadata information associated with the
3738         `geometry_columns` (or equivalent) table.
3739         """
3740         # The `geo_cols` is a dictionary data structure that holds information
3741-        # about any geographic columns in the database.
3742+        # about any geographic columns in the database.
3743         geo_cols = {}
3744         def add_col(table, column, coldata):
3745             if table in geo_cols:
3746@@ -47,7 +47,7 @@
3747         elif SpatialBackend.name == 'mysql':
3748             # On MySQL have to get all table metadata before hand; this means walking through
3749             # each table and seeing if any column types are spatial.  Can't detect this with
3750-            # `cursor.description` (what the introspection module does) because all spatial types
3751+            # `cursor.description` (what the introspection module does) because all spatial types
3752             # have the same integer type (255 for GEOMETRY).
3753             from django.db import connection
3754             cursor = connection.cursor()
3755@@ -67,13 +67,11 @@
3756 
3757     def handle_inspection(self):
3758         "Overloaded from Django's version to handle geographic database tables."
3759-        from django.db import connection, get_introspection_module
3760+        from django.db import connection
3761         import keyword
3762 
3763-        introspection_module = get_introspection_module()
3764-
3765         geo_cols = self.geometry_columns()
3766-       
3767+
3768         table2model = lambda table_name: table_name.title().replace('_', '')
3769 
3770         cursor = connection.cursor()
3771@@ -88,20 +86,20 @@
3772         yield ''
3773         yield 'from django.contrib.gis.db import models'
3774         yield ''
3775-        for table_name in introspection_module.get_table_list(cursor):
3776+        for table_name in connection.introspection.get_table_list(cursor):
3777             # Getting the geographic table dictionary.
3778             geo_table = geo_cols.get(table_name, {})
3779 
3780             yield 'class %s(models.Model):' % table2model(table_name)
3781             try:
3782-                relations = introspection_module.get_relations(cursor, table_name)
3783+                relations = connection.introspection.get_relations(cursor, table_name)
3784             except NotImplementedError:
3785                 relations = {}
3786             try:
3787-                indexes = introspection_module.get_indexes(cursor, table_name)
3788+                indexes = connection.introspection.get_indexes(cursor, table_name)
3789             except NotImplementedError:
3790                 indexes = {}
3791-            for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
3792+            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
3793                 att_name, iatt_name = row[0].lower(), row[0]
3794                 comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
3795                 extra_params = {}  # Holds Field parameters such as 'db_column'.
3796@@ -133,12 +131,12 @@
3797                         if srid != 4326: extra_params['srid'] = srid
3798                     else:
3799                         try:
3800-                            field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
3801+                            field_type = connection.introspection.data_types_reverse[row[1]]
3802                         except KeyError:
3803                             field_type = 'TextField'
3804                             comment_notes.append('This field type is a guess.')
3805 
3806-                    # This is a hook for DATA_TYPES_REVERSE to return a tuple of
3807+                    # This is a hook for data_types_reverse to return a tuple of
3808                     # (field_type, extra_params_dict).
3809                     if type(field_type) is tuple:
3810                         field_type, new_params = field_type
3811Index: tests/regressiontests/backends/models.py
3812===================================================================
3813--- tests/regressiontests/backends/models.py    (revision 8280)
3814+++ tests/regressiontests/backends/models.py    (working copy)
3815@@ -15,10 +15,6 @@
3816     def __unicode__(self):
3817         return u'%s %s' % (self.first_name, self.last_name)
3818 
3819-if connection.features.uses_case_insensitive_names:
3820-    t_convert = lambda x: x.upper()
3821-else:
3822-    t_convert = lambda x: x
3823 qn = connection.ops.quote_name
3824 
3825 __test__ = {'API_TESTS': """
3826@@ -29,7 +25,7 @@
3827 >>> opts = Square._meta
3828 >>> f1, f2 = opts.get_field('root'), opts.get_field('square')
3829 >>> query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)'
3830-...         % (t_convert(opts.db_table), qn(f1.column), qn(f2.column)))
3831+...         % (connection.introspection.table_name_converter(opts.db_table), qn(f1.column), qn(f2.column)))
3832 >>> cursor.executemany(query, [(i, i**2) for i in range(-5, 6)]) and None or None
3833 >>> Square.objects.order_by('root')
3834 [<Square: -5 ** 2 == 25>, <Square: -4 ** 2 == 16>, <Square: -3 ** 2 == 9>, <Square: -2 ** 2 == 4>, <Square: -1 ** 2 == 1>, <Square: 0 ** 2 == 0>, <Square: 1 ** 2 == 1>, <Square: 2 ** 2 == 4>, <Square: 3 ** 2 == 9>, <Square: 4 ** 2 == 16>, <Square: 5 ** 2 == 25>]
3835@@ -48,7 +44,7 @@
3836 >>> opts2 = Person._meta
3837 >>> f3, f4 = opts2.get_field('first_name'), opts2.get_field('last_name')
3838 >>> query2 = ('SELECT %s, %s FROM %s ORDER BY %s'
3839-...          % (qn(f3.column), qn(f4.column), t_convert(opts2.db_table),
3840+...          % (qn(f3.column), qn(f4.column), connection.introspection.table_name_converter(opts2.db_table),
3841 ...             qn(f3.column)))
3842 >>> cursor.execute(query2) and None or None
3843 >>> cursor.fetchone()
3844Index: docs/testing.txt
3845===================================================================
3846--- docs/testing.txt    (revision 8280)
3847+++ docs/testing.txt    (working copy)
3848@@ -1026,6 +1026,9 @@
3849     black magic hooks into the template system and restoring normal e-mail
3850     services.
3851 
3852+The creation module of the database backend (``connection.creation``) also
3853+provides some utilities that can be useful during testing.
3854+
3855 ``create_test_db(verbosity=1, autoclobber=False)``
3856     Creates a new test database and runs ``syncdb`` against it.
3857 
3858@@ -1044,7 +1047,7 @@
3859     ``create_test_db()`` has the side effect of modifying
3860     ``settings.DATABASE_NAME`` to match the name of the test database.
3861 
3862-    New in the Django development version, this function returns the name of
3863+    **New in Django development version:** This function returns the name of
3864     the test database that it created.
3865 
3866 ``destroy_test_db(old_database_name, verbosity=1)``