Ticket #5461: 5461-r8156.diff

File 5461-r8156.diff, 90.3 KB (added by russellm, 7 years ago)

Refactored Creation, Introspection and Client for backend

  • django/test/utils.py

     
    11import sys, time, os
    22from django.conf import settings
    3 from django.db import connection, get_creation_module
     3from django.db import connection
    44from django.core import mail
    55from django.core.management import call_command
    66from django.dispatch import dispatcher
     
    101101    database already exists. Returns the name of the test database created.
    102102    """
    103103    # If the database backend wants to create the test DB itself, let it
    104     creation_module = get_creation_module()
    105     if hasattr(creation_module, "create_test_db"):
    106         creation_module.create_test_db(settings, connection, verbosity, autoclobber)
     104    if hasattr(connection.creation, "create_test_db"):
     105        connection.creation.create_test_db(settings, connection, verbosity, autoclobber)
    107106        return
    108107
    109108    if verbosity >= 1:
     
    191190
    192191def destroy_test_db(old_database_name, verbosity=1):
    193192    # If the database wants to drop the test DB itself, let it
    194     creation_module = get_creation_module()
    195     if hasattr(creation_module, "destroy_test_db"):
    196         creation_module.destroy_test_db(settings, connection, old_database_name, verbosity)
     193    if hasattr(connection.creation, "destroy_test_db"):
     194        connection.creation.destroy_test_db(settings, connection, old_database_name, verbosity)
    197195        return
    198196
    199197    if verbosity >= 1:
  • django/db/models/fields/__init__.py

     
    77except ImportError:
    88    from django.utils import _decimal as decimal    # for Python 2.3
    99
    10 from django.db import connection, get_creation_module
     10from django.db import connection
    1111from django.db.models import signals
    1212from django.db.models.query_utils import QueryWrapper
    1313from django.dispatch import dispatcher
     
    150150        # as the TextField Django field type, which means XMLField's
    151151        # get_internal_type() returns 'TextField'.
    152152        #
    153         # But the limitation of the get_internal_type() / DATA_TYPES approach
     153        # But the limitation of the get_internal_type() / data_types approach
    154154        # is that it cannot handle database column types that aren't already
    155155        # mapped to one of the built-in Django field types. In this case, you
    156156        # can implement db_type() instead of get_internal_type() to specify
    157157        # exactly which wacky database column type you want to use.
    158158        data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
    159159        try:
    160             return get_creation_module().DATA_TYPES[self.get_internal_type()] % data
     160            return connection.creation.data_types[self.get_internal_type()] % data
    161161        except KeyError:
    162162            return None
    163163
  • django/db/__init__.py

     
    1515    # backends that ships with Django, so look there first.
    1616    _import_path = 'django.db.backends.'
    1717    backend = __import__('%s%s.base' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
    18     creation = __import__('%s%s.creation' % (_import_path, settings.DATABASE_ENGINE), {}, {}, [''])
    1918except ImportError, e:
     19    import traceback
     20    traceback.print_exc()
    2021    # If the import failed, we might be looking for a database backend
    2122    # distributed external to Django. So we'll try that next.
    2223    try:
    2324        _import_path = ''
    2425        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
    25         creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
    2626    except ImportError, e_user:
    2727        # The database backend wasn't found. Display a helpful error message
    2828        # listing all possible (built-in) database backends.
     
    3535        else:
    3636            raise # If there's some other error, this must be an error in Django itself.
    3737
    38 def _import_database_module(import_path='', module_name=''):
    39     """Lazily import a database module when requested."""
    40     return __import__('%s%s.%s' % (import_path, settings.DATABASE_ENGINE, module_name), {}, {}, [''])
    41 
    42 # We don't want to import the introspect module unless someone asks for it, so
    43 # lazily load it on demmand.
    44 get_introspection_module = curry(_import_database_module, _import_path, 'introspection')
    45 
    46 def get_creation_module():
    47     return creation
    48 
    49 # We want runshell() to work the same way, but we have to treat it a
    50 # little differently (since it just runs instead of returning a module like
    51 # the above) and wrap the lazily-loaded runshell() method.
    52 runshell = lambda: _import_database_module(_import_path, "client").runshell()
    53 
    5438# Convenient aliases for backend bits.
    5539connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
    5640DatabaseError = backend.DatabaseError
  • django/db/backends/postgresql/base.py

     
    77from django.utils.encoding import smart_str, smart_unicode
    88from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
    99from django.db.backends.postgresql.operations import DatabaseOperations
     10from django.db.backends.postgresql.client import DatabaseClient
     11from django.db.backends.postgresql.creation import DatabaseCreation
     12from django.db.backends.postgresql.introspection import DatabaseIntrospection
     13
    1014try:
    1115    import psycopg as Database
    1216except ImportError, e:
     
    6569class DatabaseWrapper(BaseDatabaseWrapper):
    6670    features = DatabaseFeatures()
    6771    ops = DatabaseOperations()
     72    client = DatabaseClient()
     73    creation = DatabaseCreation()
     74    introspection = DatabaseIntrospection(ops)
     75
    6876    operators = {
    6977        'exact': '= %s',
    7078        'iexact': 'ILIKE %s',
  • django/db/backends/postgresql/client.py

     
     1from django.db.backends import BaseDatabaseClient
    12from django.conf import settings
    23import os
    34
    4 def runshell():
    5     args = ['psql']
    6     if settings.DATABASE_USER:
    7         args += ["-U", settings.DATABASE_USER]
    8     if settings.DATABASE_PASSWORD:
    9         args += ["-W"]
    10     if settings.DATABASE_HOST:
    11         args.extend(["-h", settings.DATABASE_HOST])
    12     if settings.DATABASE_PORT:
    13         args.extend(["-p", str(settings.DATABASE_PORT)])
    14     args += [settings.DATABASE_NAME]
    15     os.execvp('psql', args)
     5class DatabaseClient(BaseDatabaseClient):
     6    def runshell(self):
     7        args = ['psql']
     8        if settings.DATABASE_USER:
     9            args += ["-U", settings.DATABASE_USER]
     10        if settings.DATABASE_PASSWORD:
     11            args += ["-W"]
     12        if settings.DATABASE_HOST:
     13            args.extend(["-h", settings.DATABASE_HOST])
     14        if settings.DATABASE_PORT:
     15            args.extend(["-p", str(settings.DATABASE_PORT)])
     16        args += [settings.DATABASE_NAME]
     17        os.execvp('psql', args)
  • django/db/backends/postgresql/introspection.py

     
    1 from django.db.backends.postgresql.base import DatabaseOperations
     1from django.db.backends import BaseDatabaseIntrospection
    22
    3 quote_name = DatabaseOperations().quote_name
     3class DatabaseIntrospection(BaseDatabaseIntrospection):
     4    # Maps type codes to Django Field types.
     5    data_types_reverse = {
     6        16: 'BooleanField',
     7        21: 'SmallIntegerField',
     8        23: 'IntegerField',
     9        25: 'TextField',
     10        701: 'FloatField',
     11        869: 'IPAddressField',
     12        1043: 'CharField',
     13        1082: 'DateField',
     14        1083: 'TimeField',
     15        1114: 'DateTimeField',
     16        1184: 'DateTimeField',
     17        1266: 'TimeField',
     18        1700: 'DecimalField',
     19    }
     20   
     21    def __init__(self, ops):
     22        self.ops = ops
     23       
     24    def get_table_list(self, cursor):
     25        "Returns a list of table names in the current database."
     26        cursor.execute("""
     27            SELECT c.relname
     28            FROM pg_catalog.pg_class c
     29            LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
     30            WHERE c.relkind IN ('r', 'v', '')
     31                AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
     32                AND pg_catalog.pg_table_is_visible(c.oid)""")
     33        return [row[0] for row in cursor.fetchall()]
    434
    5 def get_table_list(cursor):
    6     "Returns a list of table names in the current database."
    7     cursor.execute("""
    8         SELECT c.relname
    9         FROM pg_catalog.pg_class c
    10         LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    11         WHERE c.relkind IN ('r', 'v', '')
    12             AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
    13             AND pg_catalog.pg_table_is_visible(c.oid)""")
    14     return [row[0] for row in cursor.fetchall()]
     35    def get_table_description(self, cursor, table_name):
     36        "Returns a description of the table, with the DB-API cursor.description interface."
     37        cursor.execute("SELECT * FROM %s LIMIT 1" % self.ops.quote_name(table_name))
     38        return cursor.description
    1539
    16 def get_table_description(cursor, table_name):
    17     "Returns a description of the table, with the DB-API cursor.description interface."
    18     cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
    19     return cursor.description
     40    def get_relations(self, cursor, table_name):
     41        """
     42        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     43        representing all relationships to the given table. Indexes are 0-based.
     44        """
     45        cursor.execute("""
     46            SELECT con.conkey, con.confkey, c2.relname
     47            FROM pg_constraint con, pg_class c1, pg_class c2
     48            WHERE c1.oid = con.conrelid
     49                AND c2.oid = con.confrelid
     50                AND c1.relname = %s
     51                AND con.contype = 'f'""", [table_name])
     52        relations = {}
     53        for row in cursor.fetchall():
     54            try:
     55                # row[0] and row[1] are like "{2}", so strip the curly braces.
     56                relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2])
     57            except ValueError:
     58                continue
     59        return relations
    2060
    21 def get_relations(cursor, table_name):
    22     """
    23     Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    24     representing all relationships to the given table. Indexes are 0-based.
    25     """
    26     cursor.execute("""
    27         SELECT con.conkey, con.confkey, c2.relname
    28         FROM pg_constraint con, pg_class c1, pg_class c2
    29         WHERE c1.oid = con.conrelid
    30             AND c2.oid = con.confrelid
    31             AND c1.relname = %s
    32             AND con.contype = 'f'""", [table_name])
    33     relations = {}
    34     for row in cursor.fetchall():
    35         try:
    36             # row[0] and row[1] are like "{2}", so strip the curly braces.
    37             relations[int(row[0][1:-1]) - 1] = (int(row[1][1:-1]) - 1, row[2])
    38         except ValueError:
    39             continue
    40     return relations
     61    def get_indexes(self, cursor, table_name):
     62        """
     63        Returns a dictionary of fieldname -> infodict for the given table,
     64        where each infodict is in the format:
     65            {'primary_key': boolean representing whether it's the primary key,
     66             'unique': boolean representing whether it's a unique index}
     67        """
     68        # This query retrieves each index on the given table, including the
     69        # first associated field name
     70        cursor.execute("""
     71            SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
     72            FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
     73                pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
     74            WHERE c.oid = idx.indrelid
     75                AND idx.indexrelid = c2.oid
     76                AND attr.attrelid = c.oid
     77                AND attr.attnum = idx.indkey[0]
     78                AND c.relname = %s""", [table_name])
     79        indexes = {}
     80        for row in cursor.fetchall():
     81            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
     82            # a string of space-separated integers. This designates the field
     83            # indexes (1-based) of the fields that have indexes on the table.
     84            # Here, we skip any indexes across multiple fields.
     85            if ' ' in row[1]:
     86                continue
     87            indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
     88        return indexes
    4189
    42 def get_indexes(cursor, table_name):
    43     """
    44     Returns a dictionary of fieldname -> infodict for the given table,
    45     where each infodict is in the format:
    46         {'primary_key': boolean representing whether it's the primary key,
    47          'unique': boolean representing whether it's a unique index}
    48     """
    49     # This query retrieves each index on the given table, including the
    50     # first associated field name
    51     cursor.execute("""
    52         SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
    53         FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
    54             pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
    55         WHERE c.oid = idx.indrelid
    56             AND idx.indexrelid = c2.oid
    57             AND attr.attrelid = c.oid
    58             AND attr.attnum = idx.indkey[0]
    59             AND c.relname = %s""", [table_name])
    60     indexes = {}
    61     for row in cursor.fetchall():
    62         # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
    63         # a string of space-separated integers. This designates the field
    64         # indexes (1-based) of the fields that have indexes on the table.
    65         # Here, we skip any indexes across multiple fields.
    66         if ' ' in row[1]:
    67             continue
    68         indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
    69     return indexes
    70 
    71 # Maps type codes to Django Field types.
    72 DATA_TYPES_REVERSE = {
    73     16: 'BooleanField',
    74     21: 'SmallIntegerField',
    75     23: 'IntegerField',
    76     25: 'TextField',
    77     701: 'FloatField',
    78     869: 'IPAddressField',
    79     1043: 'CharField',
    80     1082: 'DateField',
    81     1083: 'TimeField',
    82     1114: 'DateTimeField',
    83     1184: 'DateTimeField',
    84     1266: 'TimeField',
    85     1700: 'DecimalField',
    86 }
  • django/db/backends/postgresql/creation.py

     
    1 # This dictionary maps Field objects to their associated PostgreSQL column
    2 # types, as strings. Column-type strings can contain format strings; they'll
    3 # be interpolated against the values of Field.__dict__ before being output.
    4 # If a column type is set to None, it won't be included in the output.
    5 DATA_TYPES = {
    6     'AutoField':         'serial',
    7     'BooleanField':      'boolean',
    8     'CharField':         'varchar(%(max_length)s)',
    9     'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    10     'DateField':         'date',
    11     'DateTimeField':     'timestamp with time zone',
    12     'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
    13     'FileField':         'varchar(%(max_length)s)',
    14     'FilePathField':     'varchar(%(max_length)s)',
    15     'FloatField':        'double precision',
    16     'IntegerField':      'integer',
    17     'IPAddressField':    'inet',
    18     'NullBooleanField':  'boolean',
    19     'OneToOneField':     'integer',
    20     'PhoneNumberField':  'varchar(20)',
    21     'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
    22     'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
    23     'SlugField':         'varchar(%(max_length)s)',
    24     'SmallIntegerField': 'smallint',
    25     'TextField':         'text',
    26     'TimeField':         'time',
    27     'USStateField':      'varchar(2)',
    28 }
     1from django.db.backends import BaseDatabaseCreation
     2
     3class DatabaseCreation(BaseDatabaseCreation):
     4    # This dictionary maps Field objects to their associated PostgreSQL column
     5    # types, as strings. Column-type strings can contain format strings; they'll
     6    # be interpolated against the values of Field.__dict__ before being output.
     7    # If a column type is set to None, it won't be included in the output.
     8    data_types = {
     9        'AutoField':         'serial',
     10        'BooleanField':      'boolean',
     11        'CharField':         'varchar(%(max_length)s)',
     12        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
     13        'DateField':         'date',
     14        'DateTimeField':     'timestamp with time zone',
     15        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
     16        'FileField':         'varchar(%(max_length)s)',
     17        'FilePathField':     'varchar(%(max_length)s)',
     18        'FloatField':        'double precision',
     19        'IntegerField':      'integer',
     20        'IPAddressField':    'inet',
     21        'NullBooleanField':  'boolean',
     22        'OneToOneField':     'integer',
     23        'PhoneNumberField':  'varchar(20)',
     24        'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
     25        'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
     26        'SlugField':         'varchar(%(max_length)s)',
     27        'SmallIntegerField': 'smallint',
     28        'TextField':         'text',
     29        'TimeField':         'time',
     30        'USStateField':      'varchar(2)',
     31    }
  • django/db/backends/sqlite3/base.py

     
    77"""
    88
    99from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     10from django.db.backends.sqlite3.client import DatabaseClient
     11from django.db.backends.sqlite3.creation import DatabaseCreation
     12from django.db.backends.sqlite3.introspection import DatabaseIntrospection
     13
    1014try:
    1115    try:
    1216        from sqlite3 import dbapi2 as Database
     
    9397class DatabaseWrapper(BaseDatabaseWrapper):
    9498    features = DatabaseFeatures()
    9599    ops = DatabaseOperations()
     100    client = DatabaseClient()
     101    creation = DatabaseCreation()
     102    introspection = DatabaseIntrospection(ops)
    96103
    97104    # SQLite requires LIKE statements to include an ESCAPE clause if the value
    98105    # being escaped has a percent or underscore in it.
  • django/db/backends/sqlite3/client.py

     
     1from django.db.backends import BaseDatabaseClient
    12from django.conf import settings
    23import os
    34
    4 def runshell():
    5     args = ['', settings.DATABASE_NAME]
    6     os.execvp('sqlite3', args)
     5class DatabaseClient(BaseDatabaseClient):
     6    def runshell(self):
     7        args = ['', settings.DATABASE_NAME]
     8        os.execvp('sqlite3', args)
  • django/db/backends/sqlite3/introspection.py

     
    1 from django.db.backends.sqlite3.base import DatabaseOperations
     1from django.db.backends import BaseDatabaseIntrospection
    22
    3 quote_name = DatabaseOperations().quote_name
    4 
    5 def get_table_list(cursor):
    6     "Returns a list of table names in the current database."
    7     # Skip the sqlite_sequence system table used for autoincrement key
    8     # generation.
    9     cursor.execute("""
    10         SELECT name FROM sqlite_master
    11         WHERE type='table' AND NOT name='sqlite_sequence'
    12         ORDER BY name""")
    13     return [row[0] for row in cursor.fetchall()]
    14 
    15 def get_table_description(cursor, table_name):
    16     "Returns a description of the table, with the DB-API cursor.description interface."
    17     return [(info['name'], info['type'], None, None, None, None,
    18              info['null_ok']) for info in _table_info(cursor, table_name)]
    19 
    20 def get_relations(cursor, table_name):
    21     raise NotImplementedError
    22 
    23 def get_indexes(cursor, table_name):
    24     """
    25     Returns a dictionary of fieldname -> infodict for the given table,
    26     where each infodict is in the format:
    27         {'primary_key': boolean representing whether it's the primary key,
    28          'unique': boolean representing whether it's a unique index}
    29     """
    30     indexes = {}
    31     for info in _table_info(cursor, table_name):
    32         indexes[info['name']] = {'primary_key': info['pk'] != 0,
    33                                  'unique': False}
    34     cursor.execute('PRAGMA index_list(%s)' % quote_name(table_name))
    35     # seq, name, unique
    36     for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
    37         if not unique:
    38             continue
    39         cursor.execute('PRAGMA index_info(%s)' % quote_name(index))
    40         info = cursor.fetchall()
    41         # Skip indexes across multiple fields
    42         if len(info) != 1:
    43             continue
    44         name = info[0][2] # seqno, cid, name
    45         indexes[name]['unique'] = True
    46     return indexes
    47 
    48 def _table_info(cursor, name):
    49     cursor.execute('PRAGMA table_info(%s)' % quote_name(name))
    50     # cid, name, type, notnull, dflt_value, pk
    51     return [{'name': field[1],
    52              'type': field[2],
    53              'null_ok': not field[3],
    54              'pk': field[5]     # undocumented
    55              } for field in cursor.fetchall()]
    56 
    57 # Maps SQL types to Django Field types. Some of the SQL types have multiple
    58 # entries here because SQLite allows for anything and doesn't normalize the
    59 # field type; it uses whatever was given.
    60 BASE_DATA_TYPES_REVERSE = {
    61     'bool': 'BooleanField',
    62     'boolean': 'BooleanField',
    63     'smallint': 'SmallIntegerField',
    64     'smallinteger': 'SmallIntegerField',
    65     'int': 'IntegerField',
    66     'integer': 'IntegerField',
    67     'text': 'TextField',
    68     'char': 'CharField',
    69     'date': 'DateField',
    70     'datetime': 'DateTimeField',
    71     'time': 'TimeField',
    72 }
    73 
    743# This light wrapper "fakes" a dictionary interface, because some SQLite data
    754# types include variables in them -- e.g. "varchar(30)" -- and can't be matched
    765# as a simple dictionary lookup.
    776class FlexibleFieldLookupDict:
     7    # Maps SQL types to Django Field types. Some of the SQL types have multiple
     8    # entries here because SQLite allows for anything and doesn't normalize the
     9    # field type; it uses whatever was given.
     10    base_data_types_reverse = {
     11        'bool': 'BooleanField',
     12        'boolean': 'BooleanField',
     13        'smallint': 'SmallIntegerField',
     14        'smallinteger': 'SmallIntegerField',
     15        'int': 'IntegerField',
     16        'integer': 'IntegerField',
     17        'text': 'TextField',
     18        'char': 'CharField',
     19        'date': 'DateField',
     20        'datetime': 'DateTimeField',
     21        'time': 'TimeField',
     22    }
     23
    7824    def __getitem__(self, key):
    7925        key = key.lower()
    8026        try:
    81             return BASE_DATA_TYPES_REVERSE[key]
     27            return self.base_data_types_reverse[key]
    8228        except KeyError:
    8329            import re
    8430            m = re.search(r'^\s*(?:var)?char\s*\(\s*(\d+)\s*\)\s*$', key)
     
    8632                return ('CharField', {'max_length': int(m.group(1))})
    8733            raise KeyError
    8834
    89 DATA_TYPES_REVERSE = FlexibleFieldLookupDict()
     35class DatabaseIntrospection(BaseDatabaseIntrospection):
     36    data_types_reverse = FlexibleFieldLookupDict()
     37
     38    def __init__(self, ops):
     39        self.ops = ops
     40       
     41    def get_table_list(self, cursor):
     42        "Returns a list of table names in the current database."
     43        # Skip the sqlite_sequence system table used for autoincrement key
     44        # generation.
     45        cursor.execute("""
     46            SELECT name FROM sqlite_master
     47            WHERE type='table' AND NOT name='sqlite_sequence'
     48            ORDER BY name""")
     49        return [row[0] for row in cursor.fetchall()]
     50
     51    def get_table_description(self, cursor, table_name):
     52        "Returns a description of the table, with the DB-API cursor.description interface."
     53        return [(info['name'], info['type'], None, None, None, None,
     54                 info['null_ok']) for info in self._table_info(cursor, table_name)]
     55
     56    def get_relations(self, cursor, table_name):
     57        raise NotImplementedError
     58
     59    def get_indexes(self, cursor, table_name):
     60        """
     61        Returns a dictionary of fieldname -> infodict for the given table,
     62        where each infodict is in the format:
     63            {'primary_key': boolean representing whether it's the primary key,
     64             'unique': boolean representing whether it's a unique index}
     65        """
     66        indexes = {}
     67        for info in self._table_info(cursor, table_name):
     68            indexes[info['name']] = {'primary_key': info['pk'] != 0,
     69                                     'unique': False}
     70        cursor.execute('PRAGMA index_list(%s)' % self.ops.quote_name(table_name))
     71        # seq, name, unique
     72        for index, unique in [(field[1], field[2]) for field in cursor.fetchall()]:
     73            if not unique:
     74                continue
     75            cursor.execute('PRAGMA index_info(%s)' % self.ops.quote_name(index))
     76            info = cursor.fetchall()
     77            # Skip indexes across multiple fields
     78            if len(info) != 1:
     79                continue
     80            name = info[0][2] # seqno, cid, name
     81            indexes[name]['unique'] = True
     82        return indexes
     83
     84    def _table_info(self, cursor, name):
     85        cursor.execute('PRAGMA table_info(%s)' % self.ops.quote_name(name))
     86        # cid, name, type, notnull, dflt_value, pk
     87        return [{'name': field[1],
     88                 'type': field[2],
     89                 'null_ok': not field[3],
     90                 'pk': field[5]     # undocumented
     91                 } for field in cursor.fetchall()]
     92
  • django/db/backends/sqlite3/creation.py

     
    1 # SQLite doesn't actually support most of these types, but it "does the right
    2 # thing" given more verbose field definitions, so leave them as is so that
    3 # schema inspection is more useful.
    4 DATA_TYPES = {
    5     'AutoField':                    'integer',
    6     'BooleanField':                 'bool',
    7     'CharField':                    'varchar(%(max_length)s)',
    8     'CommaSeparatedIntegerField':   'varchar(%(max_length)s)',
    9     'DateField':                    'date',
    10     'DateTimeField':                'datetime',
    11     'DecimalField':                 'decimal',
    12     'FileField':                    'varchar(%(max_length)s)',
    13     'FilePathField':                'varchar(%(max_length)s)',
    14     'FloatField':                   'real',
    15     'IntegerField':                 'integer',
    16     'IPAddressField':               'char(15)',
    17     'NullBooleanField':             'bool',
    18     'OneToOneField':                'integer',
    19     'PhoneNumberField':             'varchar(20)',
    20     'PositiveIntegerField':         'integer unsigned',
    21     'PositiveSmallIntegerField':    'smallint unsigned',
    22     'SlugField':                    'varchar(%(max_length)s)',
    23     'SmallIntegerField':            'smallint',
    24     'TextField':                    'text',
    25     'TimeField':                    'time',
    26     'USStateField':                 'varchar(2)',
    27 }
     1from django.db.backends import BaseDatabaseCreation
     2
     3class DatabaseCreation(BaseDatabaseCreation):
     4    # SQLite doesn't actually support most of these types, but it "does the right
     5    # thing" given more verbose field definitions, so leave them as is so that
     6    # schema inspection is more useful.
     7    data_types = {
     8        'AutoField':                    'integer',
     9        'BooleanField':                 'bool',
     10        'CharField':                    'varchar(%(max_length)s)',
     11        'CommaSeparatedIntegerField':   'varchar(%(max_length)s)',
     12        'DateField':                    'date',
     13        'DateTimeField':                'datetime',
     14        'DecimalField':                 'decimal',
     15        'FileField':                    'varchar(%(max_length)s)',
     16        'FilePathField':                'varchar(%(max_length)s)',
     17        'FloatField':                   'real',
     18        'IntegerField':                 'integer',
     19        'IPAddressField':               'char(15)',
     20        'NullBooleanField':             'bool',
     21        'OneToOneField':                'integer',
     22        'PhoneNumberField':             'varchar(20)',
     23        'PositiveIntegerField':         'integer unsigned',
     24        'PositiveSmallIntegerField':    'smallint unsigned',
     25        'SlugField':                    'varchar(%(max_length)s)',
     26        'SmallIntegerField':            'smallint',
     27        'TextField':                    'text',
     28        'TimeField':                    'time',
     29        'USStateField':                 'varchar(2)',
     30    }
  • django/db/backends/mysql/base.py

     
    55"""
    66
    77from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     8from django.db.backends.mysql.client import DatabaseClient
     9from django.db.backends.mysql.creation import DatabaseCreation
     10from django.db.backends.mysql.introspection import DatabaseIntrospection
     11
    812try:
    913    import MySQLdb as Database
    1014except ImportError, e:
     
    144148class DatabaseWrapper(BaseDatabaseWrapper):
    145149    features = DatabaseFeatures()
    146150    ops = DatabaseOperations()
     151    client = DatabaseClient()
     152    creation = DatabaseCreation()
     153    introspection = DatabaseIntrospection(ops)
     154   
    147155    operators = {
    148156        'exact': '= BINARY %s',
    149157        'iexact': 'LIKE %s',
  • django/db/backends/mysql/client.py

     
     1from django.db.backends import BaseDatabaseClient
    12from django.conf import settings
    23import os
    34
    4 def runshell():
    5     args = ['']
    6     db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
    7     user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
    8     passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
    9     host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
    10     port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
    11     defaults_file = settings.DATABASE_OPTIONS.get('read_default_file')
    12     # Seems to be no good way to set sql_mode with CLI
     5class DatabaseClient(BaseDatabaseClient):
     6    def runshell(self):
     7        args = ['']
     8        db = settings.DATABASE_OPTIONS.get('db', settings.DATABASE_NAME)
     9        user = settings.DATABASE_OPTIONS.get('user', settings.DATABASE_USER)
     10        passwd = settings.DATABASE_OPTIONS.get('passwd', settings.DATABASE_PASSWORD)
     11        host = settings.DATABASE_OPTIONS.get('host', settings.DATABASE_HOST)
     12        port = settings.DATABASE_OPTIONS.get('port', settings.DATABASE_PORT)
     13        defaults_file = settings.DATABASE_OPTIONS.get('read_default_file')
     14        # Seems to be no good way to set sql_mode with CLI
    1315   
    14     if defaults_file:
    15         args += ["--defaults-file=%s" % defaults_file]
    16     if user:
    17         args += ["--user=%s" % user]
    18     if passwd:
    19         args += ["--password=%s" % passwd]
    20     if host:
    21         args += ["--host=%s" % host]
    22     if port:
    23         args += ["--port=%s" % port]
    24     if db:
    25         args += [db]
     16        if defaults_file:
     17            args += ["--defaults-file=%s" % defaults_file]
     18        if user:
     19            args += ["--user=%s" % user]
     20        if passwd:
     21            args += ["--password=%s" % passwd]
     22        if host:
     23            args += ["--host=%s" % host]
     24        if port:
     25            args += ["--port=%s" % port]
     26        if db:
     27            args += [db]
    2628
    27     os.execvp('mysql', args)
     29        os.execvp('mysql', args)
  • django/db/backends/mysql/introspection.py

     
    1 from django.db.backends.mysql.base import DatabaseOperations
     1from django.db.backends import BaseDatabaseIntrospection
    22from MySQLdb import ProgrammingError, OperationalError
    33from MySQLdb.constants import FIELD_TYPE
    44import re
    55
    6 quote_name = DatabaseOperations().quote_name
    76foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
    87
    9 def get_table_list(cursor):
    10     "Returns a list of table names in the current database."
    11     cursor.execute("SHOW TABLES")
    12     return [row[0] for row in cursor.fetchall()]
     8class DatabaseIntrospection(BaseDatabaseIntrospection):
     9    data_types_reverse = {
     10        FIELD_TYPE.BLOB: 'TextField',
     11        FIELD_TYPE.CHAR: 'CharField',
     12        FIELD_TYPE.DECIMAL: 'DecimalField',
     13        FIELD_TYPE.DATE: 'DateField',
     14        FIELD_TYPE.DATETIME: 'DateTimeField',
     15        FIELD_TYPE.DOUBLE: 'FloatField',
     16        FIELD_TYPE.FLOAT: 'FloatField',
     17        FIELD_TYPE.INT24: 'IntegerField',
     18        FIELD_TYPE.LONG: 'IntegerField',
     19        FIELD_TYPE.LONGLONG: 'IntegerField',
     20        FIELD_TYPE.SHORT: 'IntegerField',
     21        FIELD_TYPE.STRING: 'CharField',
     22        FIELD_TYPE.TIMESTAMP: 'DateTimeField',
     23        FIELD_TYPE.TINY: 'IntegerField',
     24        FIELD_TYPE.TINY_BLOB: 'TextField',
     25        FIELD_TYPE.MEDIUM_BLOB: 'TextField',
     26        FIELD_TYPE.LONG_BLOB: 'TextField',
     27        FIELD_TYPE.VAR_STRING: 'CharField',
     28    }
    1329
    14 def get_table_description(cursor, table_name):
    15     "Returns a description of the table, with the DB-API cursor.description interface."
    16     cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
    17     return cursor.description
     30    def __init__(self, ops):
     31        self.ops = ops
     32       
     33    def get_table_list(self, cursor):
     34        "Returns a list of table names in the current database."
     35        cursor.execute("SHOW TABLES")
     36        return [row[0] for row in cursor.fetchall()]
    1837
    19 def _name_to_index(cursor, table_name):
    20     """
    21     Returns a dictionary of {field_name: field_index} for the given table.
    22     Indexes are 0-based.
    23     """
    24     return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
     38    def get_table_description(self, cursor, table_name):
     39        "Returns a description of the table, with the DB-API cursor.description interface."
     40        cursor.execute("SELECT * FROM %s LIMIT 1" % self.ops.quote_name(table_name))
     41        return cursor.description
    2542
    26 def get_relations(cursor, table_name):
    27     """
    28     Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    29     representing all relationships to the given table. Indexes are 0-based.
    30     """
    31     my_field_dict = _name_to_index(cursor, table_name)
    32     constraints = []
    33     relations = {}
    34     try:
    35         # This should work for MySQL 5.0.
    36         cursor.execute("""
    37             SELECT column_name, referenced_table_name, referenced_column_name
    38             FROM information_schema.key_column_usage
    39             WHERE table_name = %s
    40                 AND table_schema = DATABASE()
    41                 AND referenced_table_name IS NOT NULL
    42                 AND referenced_column_name IS NOT NULL""", [table_name])
    43         constraints.extend(cursor.fetchall())
    44     except (ProgrammingError, OperationalError):
    45         # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
    46         # Go through all constraints and save the equal matches.
    47         cursor.execute("SHOW CREATE TABLE %s" % quote_name(table_name))
    48         for row in cursor.fetchall():
    49             pos = 0
    50             while True:
    51                 match = foreign_key_re.search(row[1], pos)
    52                 if match == None:
    53                     break
    54                 pos = match.end()
    55                 constraints.append(match.groups())
     43    def _name_to_index(self, cursor, table_name):
     44        """
     45        Returns a dictionary of {field_name: field_index} for the given table.
     46        Indexes are 0-based.
     47        """
     48        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
    5649
    57     for my_fieldname, other_table, other_field in constraints:
    58         other_field_index = _name_to_index(cursor, other_table)[other_field]
    59         my_field_index = my_field_dict[my_fieldname]
    60         relations[my_field_index] = (other_field_index, other_table)
     50    def get_relations(self, cursor, table_name):
     51        """
     52        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     53        representing all relationships to the given table. Indexes are 0-based.
     54        """
     55        my_field_dict = self._name_to_index(cursor, table_name)
     56        constraints = []
     57        relations = {}
     58        try:
     59            # This should work for MySQL 5.0.
     60            cursor.execute("""
     61                SELECT column_name, referenced_table_name, referenced_column_name
     62                FROM information_schema.key_column_usage
     63                WHERE table_name = %s
     64                    AND table_schema = DATABASE()
     65                    AND referenced_table_name IS NOT NULL
     66                    AND referenced_column_name IS NOT NULL""", [table_name])
     67            constraints.extend(cursor.fetchall())
     68        except (ProgrammingError, OperationalError):
     69            # Fall back to "SHOW CREATE TABLE", for previous MySQL versions.
     70            # Go through all constraints and save the equal matches.
     71            cursor.execute("SHOW CREATE TABLE %s" % self.ops.quote_name(table_name))
     72            for row in cursor.fetchall():
     73                pos = 0
     74                while True:
     75                    match = foreign_key_re.search(row[1], pos)
     76                    if match == None:
     77                        break
     78                    pos = match.end()
     79                    constraints.append(match.groups())
    6180
    62     return relations
     81        for my_fieldname, other_table, other_field in constraints:
     82            other_field_index = self._name_to_index(cursor, other_table)[other_field]
     83            my_field_index = my_field_dict[my_fieldname]
     84            relations[my_field_index] = (other_field_index, other_table)
    6385
    64 def get_indexes(cursor, table_name):
    65     """
    66     Returns a dictionary of fieldname -> infodict for the given table,
    67     where each infodict is in the format:
    68         {'primary_key': boolean representing whether it's the primary key,
    69          'unique': boolean representing whether it's a unique index}
    70     """
    71     cursor.execute("SHOW INDEX FROM %s" % quote_name(table_name))
    72     indexes = {}
    73     for row in cursor.fetchall():
    74         indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
    75     return indexes
     86        return relations
    7687
    77 DATA_TYPES_REVERSE = {
    78     FIELD_TYPE.BLOB: 'TextField',
    79     FIELD_TYPE.CHAR: 'CharField',
    80     FIELD_TYPE.DECIMAL: 'DecimalField',
    81     FIELD_TYPE.DATE: 'DateField',
    82     FIELD_TYPE.DATETIME: 'DateTimeField',
    83     FIELD_TYPE.DOUBLE: 'FloatField',
    84     FIELD_TYPE.FLOAT: 'FloatField',
    85     FIELD_TYPE.INT24: 'IntegerField',
    86     FIELD_TYPE.LONG: 'IntegerField',
    87     FIELD_TYPE.LONGLONG: 'IntegerField',
    88     FIELD_TYPE.SHORT: 'IntegerField',
    89     FIELD_TYPE.STRING: 'CharField',
    90     FIELD_TYPE.TIMESTAMP: 'DateTimeField',
    91     FIELD_TYPE.TINY: 'IntegerField',
    92     FIELD_TYPE.TINY_BLOB: 'TextField',
    93     FIELD_TYPE.MEDIUM_BLOB: 'TextField',
    94     FIELD_TYPE.LONG_BLOB: 'TextField',
    95     FIELD_TYPE.VAR_STRING: 'CharField',
    96 }
     88    def get_indexes(self, cursor, table_name):
     89        """
     90        Returns a dictionary of fieldname -> infodict for the given table,
     91        where each infodict is in the format:
     92            {'primary_key': boolean representing whether it's the primary key,
     93             'unique': boolean representing whether it's a unique index}
     94        """
     95        cursor.execute("SHOW INDEX FROM %s" % self.ops.quote_name(table_name))
     96        indexes = {}
     97        for row in cursor.fetchall():
     98            indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
     99        return indexes
     100
  • django/db/backends/mysql/creation.py

     
    1 # This dictionary maps Field objects to their associated MySQL column
    2 # types, as strings. Column-type strings can contain format strings; they'll
    3 # be interpolated against the values of Field.__dict__ before being output.
    4 # If a column type is set to None, it won't be included in the output.
    5 DATA_TYPES = {
    6     'AutoField':         'integer AUTO_INCREMENT',
    7     'BooleanField':      'bool',
    8     'CharField':         'varchar(%(max_length)s)',
    9     'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    10     'DateField':         'date',
    11     'DateTimeField':     'datetime',
    12     'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
    13     'FileField':         'varchar(%(max_length)s)',
    14     'FilePathField':     'varchar(%(max_length)s)',
    15     'FloatField':        'double precision',
    16     'IntegerField':      'integer',
    17     'IPAddressField':    'char(15)',
    18     'NullBooleanField':  'bool',
    19     'OneToOneField':     'integer',
    20     'PhoneNumberField':  'varchar(20)',
    21     'PositiveIntegerField': 'integer UNSIGNED',
    22     'PositiveSmallIntegerField': 'smallint UNSIGNED',
    23     'SlugField':         'varchar(%(max_length)s)',
    24     'SmallIntegerField': 'smallint',
    25     'TextField':         'longtext',
    26     'TimeField':         'time',
    27     'USStateField':      'varchar(2)',
    28 }
     1from django.db.backends import BaseDatabaseCreation
     2
     3class DatabaseCreation(BaseDatabaseCreation):
     4    # This dictionary maps Field objects to their associated MySQL column
     5    # types, as strings. Column-type strings can contain format strings; they'll
     6    # be interpolated against the values of Field.__dict__ before being output.
     7    # If a column type is set to None, it won't be included in the output.
     8    data_types = {
     9        'AutoField':         'integer AUTO_INCREMENT',
     10        'BooleanField':      'bool',
     11        'CharField':         'varchar(%(max_length)s)',
     12        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
     13        'DateField':         'date',
     14        'DateTimeField':     'datetime',
     15        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
     16        'FileField':         'varchar(%(max_length)s)',
     17        'FilePathField':     'varchar(%(max_length)s)',
     18        'FloatField':        'double precision',
     19        'IntegerField':      'integer',
     20        'IPAddressField':    'char(15)',
     21        'NullBooleanField':  'bool',
     22        'OneToOneField':     'integer',
     23        'PhoneNumberField':  'varchar(20)',
     24        'PositiveIntegerField': 'integer UNSIGNED',
     25        'PositiveSmallIntegerField': 'smallint UNSIGNED',
     26        'SlugField':         'varchar(%(max_length)s)',
     27        'SmallIntegerField': 'smallint',
     28        'TextField':         'longtext',
     29        'TimeField':         'time',
     30        'USStateField':      'varchar(2)',
     31    }
  • django/db/backends/oracle/base.py

     
    1010
    1111from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
    1212from django.db.backends.oracle import query
     13from django.db.backends.oracle.client import DatabaseClient
     14from django.db.backends.oracle.creation import DatabaseCreation
     15from django.db.backends.oracle.introspection import DatabaseIntrospection
    1316from django.utils.encoding import smart_str, force_unicode
    1417
    1518# Oracle takes client-side character set encoding from the environment.
     
    198201class DatabaseWrapper(BaseDatabaseWrapper):
    199202    features = DatabaseFeatures()
    200203    ops = DatabaseOperations()
     204    client = DatabaseClient()
     205    creation = DatabaseCreation()
     206    introspection = DatabaseIntrospection(ops)
     207   
    201208    operators = {
    202209        'exact': '= %s',
    203210        'iexact': '= UPPER(%s)',
  • django/db/backends/oracle/client.py

     
     1from django.db.backends import BaseDatabaseClient
    12from django.conf import settings
    23import os
    34
    4 def runshell():
    5     dsn = settings.DATABASE_USER
    6     if settings.DATABASE_PASSWORD:
    7         dsn += "/%s" % settings.DATABASE_PASSWORD
    8     if settings.DATABASE_NAME:
    9         dsn += "@%s" % settings.DATABASE_NAME
    10     args = ["sqlplus", "-L", dsn]
    11     os.execvp("sqlplus", args)
     5class DatabaseClient(BaseDatabaseClient):
     6    def runshell(self):
     7        dsn = settings.DATABASE_USER
     8        if settings.DATABASE_PASSWORD:
     9            dsn += "/%s" % settings.DATABASE_PASSWORD
     10        if settings.DATABASE_NAME:
     11            dsn += "@%s" % settings.DATABASE_NAME
     12        args = ["sqlplus", "-L", dsn]
     13        os.execvp("sqlplus", args)
  • django/db/backends/oracle/introspection.py

     
    1 from django.db.backends.oracle.base import DatabaseOperations
     1from django.db.backends import BaseDatabaseIntrospection
     2import cx_Oracle
    23import re
    3 import cx_Oracle
    44
    5 quote_name = DatabaseOperations().quote_name
    65foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)")
    76
    8 def get_table_list(cursor):
    9     "Returns a list of table names in the current database."
    10     cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
    11     return [row[0].upper() for row in cursor.fetchall()]
     7class DatabaseIntrospection(BaseDatabaseIntrospection):
     8    # Maps type objects to Django Field types.
     9    data_types_reverse = {
     10        cx_Oracle.CLOB: 'TextField',
     11        cx_Oracle.DATETIME: 'DateTimeField',
     12        cx_Oracle.FIXED_CHAR: 'CharField',
     13        cx_Oracle.NCLOB: 'TextField',
     14        cx_Oracle.NUMBER: 'DecimalField',
     15        cx_Oracle.STRING: 'CharField',
     16        cx_Oracle.TIMESTAMP: 'DateTimeField',
     17    }
    1218
    13 def get_table_description(cursor, table_name):
    14     "Returns a description of the table, with the DB-API cursor.description interface."
    15     cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % quote_name(table_name))
    16     return cursor.description
     19    def __init__(self, ops):
     20        self.ops = ops
     21       
     22    def get_table_list(self, cursor):
     23        "Returns a list of table names in the current database."
     24        cursor.execute("SELECT TABLE_NAME FROM USER_TABLES")
     25        return [row[0].upper() for row in cursor.fetchall()]
    1726
    18 def _name_to_index(cursor, table_name):
    19     """
    20     Returns a dictionary of {field_name: field_index} for the given table.
    21     Indexes are 0-based.
    22     """
    23     return dict([(d[0], i) for i, d in enumerate(get_table_description(cursor, table_name))])
     27    def get_table_description(self, cursor, table_name):
     28        "Returns a description of the table, with the DB-API cursor.description interface."
     29        cursor.execute("SELECT * FROM %s WHERE ROWNUM < 2" % self.ops.quote_name(table_name))
     30        return cursor.description
    2431
    25 def get_relations(cursor, table_name):
    26     """
    27     Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    28     representing all relationships to the given table. Indexes are 0-based.
    29     """
    30     cursor.execute("""
    31 SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
    32 FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
    33        user_tab_cols ta, user_tab_cols tb
    34 WHERE  user_constraints.table_name = %s AND
    35        ta.table_name = %s AND
    36        ta.column_name = ca.column_name AND
    37        ca.table_name = %s AND
    38        user_constraints.constraint_name = ca.constraint_name AND
    39        user_constraints.r_constraint_name = cb.constraint_name AND
    40        cb.table_name = tb.table_name AND
    41        cb.column_name = tb.column_name AND
    42        ca.position = cb.position""", [table_name, table_name, table_name])
     32    def _name_to_index(self, cursor, table_name):
     33        """
     34        Returns a dictionary of {field_name: field_index} for the given table.
     35        Indexes are 0-based.
     36        """
     37        return dict([(d[0], i) for i, d in enumerate(self.get_table_description(cursor, table_name))])
    4338
    44     relations = {}
    45     for row in cursor.fetchall():
    46         relations[row[0]] = (row[2], row[1])
    47     return relations
     39    def get_relations(self, cursor, table_name):
     40        """
     41        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     42        representing all relationships to the given table. Indexes are 0-based.
     43        """
     44        cursor.execute("""
     45    SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1
     46    FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb,
     47           user_tab_cols ta, user_tab_cols tb
     48    WHERE  user_constraints.table_name = %s AND
     49           ta.table_name = %s AND
     50           ta.column_name = ca.column_name AND
     51           ca.table_name = %s AND
     52           user_constraints.constraint_name = ca.constraint_name AND
     53           user_constraints.r_constraint_name = cb.constraint_name AND
     54           cb.table_name = tb.table_name AND
     55           cb.column_name = tb.column_name AND
     56           ca.position = cb.position""", [table_name, table_name, table_name])
    4857
    49 def get_indexes(cursor, table_name):
    50     """
    51     Returns a dictionary of fieldname -> infodict for the given table,
    52     where each infodict is in the format:
    53         {'primary_key': boolean representing whether it's the primary key,
    54          'unique': boolean representing whether it's a unique index}
    55     """
    56     # This query retrieves each index on the given table, including the
    57     # first associated field name
    58     # "We were in the nick of time; you were in great peril!"
    59     sql = """
    60 WITH primarycols AS (
    61  SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL
    62  FROM   user_cons_columns, user_constraints
    63  WHERE  user_cons_columns.constraint_name = user_constraints.constraint_name AND
    64         user_constraints.constraint_type = 'P' AND
    65         user_cons_columns.table_name = %s),
    66  uniquecols AS (
    67  SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL
    68  FROM   user_indexes, user_ind_columns
    69  WHERE  uniqueness = 'UNIQUE' AND
    70         user_indexes.index_name = user_ind_columns.index_name AND
    71         user_ind_columns.table_name = %s)
    72 SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL
    73 FROM   (SELECT column_name FROM primarycols UNION SELECT column_name FROM
    74 uniquecols) allcols,
    75       primarycols, uniquecols
    76 WHERE  allcols.column_name = primarycols.column_name (+) AND
    77       allcols.column_name = uniquecols.column_name (+)
    78     """
    79     cursor.execute(sql, [table_name, table_name])
    80     indexes = {}
    81     for row in cursor.fetchall():
    82         # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
    83         # a string of space-separated integers. This designates the field
    84         # indexes (1-based) of the fields that have indexes on the table.
    85         # Here, we skip any indexes across multiple fields.
    86         indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
    87     return indexes
     58        relations = {}
     59        for row in cursor.fetchall():
     60            relations[row[0]] = (row[2], row[1])
     61        return relations
    8862
    89 # Maps type objects to Django Field types.
    90 DATA_TYPES_REVERSE = {
    91     cx_Oracle.CLOB: 'TextField',
    92     cx_Oracle.DATETIME: 'DateTimeField',
    93     cx_Oracle.FIXED_CHAR: 'CharField',
    94     cx_Oracle.NCLOB: 'TextField',
    95     cx_Oracle.NUMBER: 'DecimalField',
    96     cx_Oracle.STRING: 'CharField',
    97     cx_Oracle.TIMESTAMP: 'DateTimeField',
    98 }
     63    def get_indexes(self, cursor, table_name):
     64        """
     65        Returns a dictionary of fieldname -> infodict for the given table,
     66        where each infodict is in the format:
     67            {'primary_key': boolean representing whether it's the primary key,
     68             'unique': boolean representing whether it's a unique index}
     69        """
     70        # This query retrieves each index on the given table, including the
     71        # first associated field name
     72        # "We were in the nick of time; you were in great peril!"
     73        sql = """
     74    WITH primarycols AS (
     75     SELECT user_cons_columns.table_name, user_cons_columns.column_name, 1 AS PRIMARYCOL
     76     FROM   user_cons_columns, user_constraints
     77     WHERE  user_cons_columns.constraint_name = user_constraints.constraint_name AND
     78            user_constraints.constraint_type = 'P' AND
     79            user_cons_columns.table_name = %s),
     80     uniquecols AS (
     81     SELECT user_ind_columns.table_name, user_ind_columns.column_name, 1 AS UNIQUECOL
     82     FROM   user_indexes, user_ind_columns
     83     WHERE  uniqueness = 'UNIQUE' AND
     84            user_indexes.index_name = user_ind_columns.index_name AND
     85            user_ind_columns.table_name = %s)
     86    SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL
     87    FROM   (SELECT column_name FROM primarycols UNION SELECT column_name FROM
     88    uniquecols) allcols,
     89          primarycols, uniquecols
     90    WHERE  allcols.column_name = primarycols.column_name (+) AND
     91          allcols.column_name = uniquecols.column_name (+)
     92        """
     93        cursor.execute(sql, [table_name, table_name])
     94        indexes = {}
     95        for row in cursor.fetchall():
     96            # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
     97            # a string of space-separated integers. This designates the field
     98            # indexes (1-based) of the fields that have indexes on the table.
     99            # Here, we skip any indexes across multiple fields.
     100            indexes[row[0]] = {'primary_key': row[1], 'unique': row[2]}
     101        return indexes
     102
  • django/db/backends/oracle/creation.py

     
    11import sys, time
    22from django.core import management
     3from django.db.backends import BaseDatabaseCreation
    34
    4 # This dictionary maps Field objects to their associated Oracle column
    5 # types, as strings. Column-type strings can contain format strings; they'll
    6 # be interpolated against the values of Field.__dict__ before being output.
    7 # If a column type is set to None, it won't be included in the output.
    8 #
    9 # Any format strings starting with "qn_" are quoted before being used in the
    10 # output (the "qn_" prefix is stripped before the lookup is performed.
    11 
    12 DATA_TYPES = {
    13     'AutoField':                    'NUMBER(11)',
    14     'BooleanField':                 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
    15     'CharField':                    'NVARCHAR2(%(max_length)s)',
    16     'CommaSeparatedIntegerField':   'VARCHAR2(%(max_length)s)',
    17     'DateField':                    'DATE',
    18     'DateTimeField':                'TIMESTAMP',
    19     'DecimalField':                 'NUMBER(%(max_digits)s, %(decimal_places)s)',
    20     'FileField':                    'NVARCHAR2(%(max_length)s)',
    21     'FilePathField':                'NVARCHAR2(%(max_length)s)',
    22     'FloatField':                   'DOUBLE PRECISION',
    23     'IntegerField':                 'NUMBER(11)',
    24     'IPAddressField':               'VARCHAR2(15)',
    25     'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
    26     'OneToOneField':                'NUMBER(11)',
    27     'PhoneNumberField':             'VARCHAR2(20)',
    28     'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
    29     'PositiveSmallIntegerField':    'NUMBER(11) CHECK (%(qn_column)s >= 0)',
    30     'SlugField':                    'NVARCHAR2(50)',
    31     'SmallIntegerField':            'NUMBER(11)',
    32     'TextField':                    'NCLOB',
    33     'TimeField':                    'TIMESTAMP',
    34     'URLField':                     'VARCHAR2(%(max_length)s)',
    35     'USStateField':                 'CHAR(2)',
    36 }
    37 
    385TEST_DATABASE_PREFIX = 'test_'
    396PASSWORD = 'Im_a_lumberjack'
    40 REMEMBER = {}
    417
    42 def create_test_db(settings, connection, verbosity=1, autoclobber=False):
    43     TEST_DATABASE_NAME = _test_database_name(settings)
    44     TEST_DATABASE_USER = _test_database_user(settings)
    45     TEST_DATABASE_PASSWD = _test_database_passwd(settings)
    46     TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
    47     TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
     8class DatabaseCreation(BaseDatabaseCreation):
     9    # This dictionary maps Field objects to their associated Oracle column
     10    # types, as strings. Column-type strings can contain format strings; they'll
     11    # be interpolated against the values of Field.__dict__ before being output.
     12    # If a column type is set to None, it won't be included in the output.
     13    #
     14    # Any format strings starting with "qn_" are quoted before being used in the
     15    # output (the "qn_" prefix is stripped before the lookup is performed.
    4816
    49     parameters = {
    50         'dbname': TEST_DATABASE_NAME,
    51         'user': TEST_DATABASE_USER,
    52         'password': TEST_DATABASE_PASSWD,
    53         'tblspace': TEST_DATABASE_TBLSPACE,
    54         'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
    55         }
     17    data_types = {
     18        'AutoField':                    'NUMBER(11)',
     19        'BooleanField':                 'NUMBER(1) CHECK (%(qn_column)s IN (0,1))',
     20        'CharField':                    'NVARCHAR2(%(max_length)s)',
     21        'CommaSeparatedIntegerField':   'VARCHAR2(%(max_length)s)',
     22        'DateField':                    'DATE',
     23        'DateTimeField':                'TIMESTAMP',
     24        'DecimalField':                 'NUMBER(%(max_digits)s, %(decimal_places)s)',
     25        'FileField':                    'NVARCHAR2(%(max_length)s)',
     26        'FilePathField':                'NVARCHAR2(%(max_length)s)',
     27        'FloatField':                   'DOUBLE PRECISION',
     28        'IntegerField':                 'NUMBER(11)',
     29        'IPAddressField':               'VARCHAR2(15)',
     30        'NullBooleanField':             'NUMBER(1) CHECK ((%(qn_column)s IN (0,1)) OR (%(qn_column)s IS NULL))',
     31        'OneToOneField':                'NUMBER(11)',
     32        'PhoneNumberField':             'VARCHAR2(20)',
     33        'PositiveIntegerField':         'NUMBER(11) CHECK (%(qn_column)s >= 0)',
     34        'PositiveSmallIntegerField':    'NUMBER(11) CHECK (%(qn_column)s >= 0)',
     35        'SlugField':                    'NVARCHAR2(50)',
     36        'SmallIntegerField':            'NUMBER(11)',
     37        'TextField':                    'NCLOB',
     38        'TimeField':                    'TIMESTAMP',
     39        'URLField':                     'VARCHAR2(%(max_length)s)',
     40        'USStateField':                 'CHAR(2)',
     41    }
     42   
     43    def create_test_db(settings, connection, verbosity=1, autoclobber=False):
     44        TEST_DATABASE_NAME = self._test_database_name(settings)
     45        TEST_DATABASE_USER = self._test_database_user(settings)
     46        TEST_DATABASE_PASSWD = self._test_database_passwd(settings)
     47        TEST_DATABASE_TBLSPACE = self._test_database_tblspace(settings)
     48        TEST_DATABASE_TBLSPACE_TMP = self._test_database_tblspace_tmp(settings)
    5649
    57     REMEMBER['user'] = settings.DATABASE_USER
    58     REMEMBER['passwd'] = settings.DATABASE_PASSWORD
     50        parameters = {
     51            'dbname': TEST_DATABASE_NAME,
     52            'user': TEST_DATABASE_USER,
     53            'password': TEST_DATABASE_PASSWD,
     54            'tblspace': TEST_DATABASE_TBLSPACE,
     55            'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
     56        }
    5957
    60     cursor = connection.cursor()
    61     if _test_database_create(settings):
    62         if verbosity >= 1:
    63             print 'Creating test database...'
    64         try:
    65             _create_test_db(cursor, parameters, verbosity)
    66         except Exception, e:
    67             sys.stderr.write("Got an error creating the test database: %s\n" % e)
    68             if not autoclobber:
    69                 confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
    70             if autoclobber or confirm == 'yes':
    71                 try:
    72                     if verbosity >= 1:
    73                         print "Destroying old test database..."
    74                     _destroy_test_db(cursor, parameters, verbosity)
    75                     if verbosity >= 1:
    76                         print "Creating test database..."
    77                     _create_test_db(cursor, parameters, verbosity)
    78                 except Exception, e:
    79                     sys.stderr.write("Got an error recreating the test database: %s\n" % e)
    80                     sys.exit(2)
    81             else:
    82                 print "Tests cancelled."
    83                 sys.exit(1)
     58        self.remember['user'] = settings.DATABASE_USER
     59        self.remember['passwd'] = settings.DATABASE_PASSWORD
    8460
    85     if _test_user_create(settings):
    86         if verbosity >= 1:
    87             print "Creating test user..."
    88         try:
    89             _create_test_user(cursor, parameters, verbosity)
    90         except Exception, e:
    91             sys.stderr.write("Got an error creating the test user: %s\n" % e)
    92             if not autoclobber:
    93                 confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER)
    94             if autoclobber or confirm == 'yes':
    95                 try:
    96                     if verbosity >= 1:
    97                         print "Destroying old test user..."
    98                     _destroy_test_user(cursor, parameters, verbosity)
    99                     if verbosity >= 1:
    100                         print "Creating test user..."
    101                     _create_test_user(cursor, parameters, verbosity)
    102                 except Exception, e:
    103                     sys.stderr.write("Got an error recreating the test user: %s\n" % e)
    104                     sys.exit(2)
    105             else:
    106                 print "Tests cancelled."
    107                 sys.exit(1)
     61        cursor = connection.cursor()
     62        if self._test_database_create(settings):
     63            if verbosity >= 1:
     64                print 'Creating test database...'
     65            try:
     66                self._create_test_db(cursor, parameters, verbosity)
     67            except Exception, e:
     68                sys.stderr.write("Got an error creating the test database: %s\n" % e)
     69                if not autoclobber:
     70                    confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME)
     71                if autoclobber or confirm == 'yes':
     72                    try:
     73                        if verbosity >= 1:
     74                            print "Destroying old test database..."
     75                        self._destroy_test_db(cursor, parameters, verbosity)
     76                        if verbosity >= 1:
     77                            print "Creating test database..."
     78                        self._create_test_db(cursor, parameters, verbosity)
     79                    except Exception, e:
     80                        sys.stderr.write("Got an error recreating the test database: %s\n" % e)
     81                        sys.exit(2)
     82                else:
     83                    print "Tests cancelled."
     84                    sys.exit(1)
    10885
    109     connection.close()
    110     settings.DATABASE_USER = TEST_DATABASE_USER
    111     settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD
     86        if self._test_user_create(settings):
     87            if verbosity >= 1:
     88                print "Creating test user..."
     89            try:
     90                self._create_test_user(cursor, parameters, verbosity)
     91            except Exception, e:
     92                sys.stderr.write("Got an error creating the test user: %s\n" % e)
     93                if not autoclobber:
     94                    confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER)
     95                if autoclobber or confirm == 'yes':
     96                    try:
     97                        if verbosity >= 1:
     98                            print "Destroying old test user..."
     99                        self._destroy_test_user(cursor, parameters, verbosity)
     100                        if verbosity >= 1:
     101                            print "Creating test user..."
     102                        self._create_test_user(cursor, parameters, verbosity)
     103                    except Exception, e:
     104                        sys.stderr.write("Got an error recreating the test user: %s\n" % e)
     105                        sys.exit(2)
     106                else:
     107                    print "Tests cancelled."
     108                    sys.exit(1)
    112109
    113     management.call_command('syncdb', verbosity=verbosity, interactive=False)
     110        connection.close()
     111        settings.DATABASE_USER = TEST_DATABASE_USER
     112        settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD
    114113
    115     # Get a cursor (even though we don't need one yet). This has
    116     # the side effect of initializing the test database.
    117     cursor = connection.cursor()
     114        management.call_command('syncdb', verbosity=verbosity, interactive=False)
    118115
    119 def destroy_test_db(settings, connection, old_database_name, verbosity=1):
    120     connection.close()
     116        # Get a cursor (even though we don't need one yet). This has
     117        # the side effect of initializing the test database.
     118        cursor = connection.cursor()
    121119
    122     TEST_DATABASE_NAME = _test_database_name(settings)
    123     TEST_DATABASE_USER = _test_database_user(settings)
    124     TEST_DATABASE_PASSWD = _test_database_passwd(settings)
    125     TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings)
    126     TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings)
     120    def destroy_test_db(settings, connection, old_database_name, verbosity=1):
     121        connection.close()
    127122
    128     settings.DATABASE_NAME = old_database_name
    129     settings.DATABASE_USER = REMEMBER['user']
    130     settings.DATABASE_PASSWORD = REMEMBER['passwd']
     123        TEST_DATABASE_NAME = self._test_database_name(settings)
     124        TEST_DATABASE_USER = self._test_database_user(settings)
     125        TEST_DATABASE_PASSWD = self._test_database_passwd(settings)
     126        TEST_DATABASE_TBLSPACE = self._test_database_tblspace(settings)
     127        TEST_DATABASE_TBLSPACE_TMP = self._test_database_tblspace_tmp(settings)
    131128
    132     parameters = {
    133         'dbname': TEST_DATABASE_NAME,
    134         'user': TEST_DATABASE_USER,
    135         'password': TEST_DATABASE_PASSWD,
    136         'tblspace': TEST_DATABASE_TBLSPACE,
    137         'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
    138         }
     129        settings.DATABASE_NAME = old_database_name
     130        settings.DATABASE_USER = self.remember['user']
     131        settings.DATABASE_PASSWORD = self.remember['passwd']
    139132
    140     REMEMBER['user'] = settings.DATABASE_USER
    141     REMEMBER['passwd'] = settings.DATABASE_PASSWORD
     133        parameters = {
     134            'dbname': TEST_DATABASE_NAME,
     135            'user': TEST_DATABASE_USER,
     136            'password': TEST_DATABASE_PASSWD,
     137            'tblspace': TEST_DATABASE_TBLSPACE,
     138            'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP,
     139        }
    142140
    143     cursor = connection.cursor()
    144     time.sleep(1) # To avoid "database is being accessed by other users" errors.
    145     if _test_user_create(settings):
    146         if verbosity >= 1:
    147             print 'Destroying test user...'
    148         _destroy_test_user(cursor, parameters, verbosity)
    149     if _test_database_create(settings):
    150         if verbosity >= 1:
    151             print 'Destroying test database...'
    152         _destroy_test_db(cursor, parameters, verbosity)
    153     connection.close()
     141        self.remember['user'] = settings.DATABASE_USER
     142        self.remember['passwd'] = settings.DATABASE_PASSWORD
    154143
    155 def _create_test_db(cursor, parameters, verbosity):
    156     if verbosity >= 2:
    157         print "_create_test_db(): dbname = %s" % parameters['dbname']
    158     statements = [
    159         """CREATE TABLESPACE %(tblspace)s
    160            DATAFILE '%(tblspace)s.dbf' SIZE 20M
    161            REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
    162         """,
    163         """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s
    164            TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M
    165            REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
    166         """,
    167     ]
    168     _execute_statements(cursor, statements, parameters, verbosity)
     144        cursor = connection.cursor()
     145        time.sleep(1) # To avoid "database is being accessed by other users" errors.
     146        if self._test_user_create(settings):
     147            if verbosity >= 1:
     148                print 'Destroying test user...'
     149            self._destroy_test_user(cursor, parameters, verbosity)
     150        if self._test_database_create(settings):
     151            if verbosity >= 1:
     152                print 'Destroying test database...'
     153            self._destroy_test_db(cursor, parameters, verbosity)
     154        connection.close()
    169155
    170 def _create_test_user(cursor, parameters, verbosity):
    171     if verbosity >= 2:
    172         print "_create_test_user(): username = %s" % parameters['user']
    173     statements = [
    174         """CREATE USER %(user)s
    175            IDENTIFIED BY %(password)s
    176            DEFAULT TABLESPACE %(tblspace)s
    177            TEMPORARY TABLESPACE %(tblspace_temp)s
    178         """,
    179         """GRANT CONNECT, RESOURCE TO %(user)s""",
    180     ]
    181     _execute_statements(cursor, statements, parameters, verbosity)
     156    def _create_test_db(cursor, parameters, verbosity):
     157        if verbosity >= 2:
     158            print "_create_test_db(): dbname = %s" % parameters['dbname']
     159        statements = [
     160            """CREATE TABLESPACE %(tblspace)s
     161               DATAFILE '%(tblspace)s.dbf' SIZE 20M
     162               REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
     163            """,
     164            """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s
     165               TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M
     166               REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M
     167            """,
     168        ]
     169        _execute_statements(cursor, statements, parameters, verbosity)
    182170
    183 def _destroy_test_db(cursor, parameters, verbosity):
    184     if verbosity >= 2:
    185         print "_destroy_test_db(): dbname=%s" % parameters['dbname']
    186     statements = [
    187         'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
    188         'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
     171    def _create_test_user(cursor, parameters, verbosity):
     172        if verbosity >= 2:
     173            print "_create_test_user(): username = %s" % parameters['user']
     174        statements = [
     175            """CREATE USER %(user)s
     176               IDENTIFIED BY %(password)s
     177               DEFAULT TABLESPACE %(tblspace)s
     178               TEMPORARY TABLESPACE %(tblspace_temp)s
     179            """,
     180            """GRANT CONNECT, RESOURCE TO %(user)s""",
    189181        ]
    190     _execute_statements(cursor, statements, parameters, verbosity)
     182        _execute_statements(cursor, statements, parameters, verbosity)
    191183
    192 def _destroy_test_user(cursor, parameters, verbosity):
    193     if verbosity >= 2:
    194         print "_destroy_test_user(): user=%s" % parameters['user']
    195         print "Be patient.  This can take some time..."
    196     statements = [
    197         'DROP USER %(user)s CASCADE',
    198     ]
    199     _execute_statements(cursor, statements, parameters, verbosity)
     184    def _destroy_test_db(cursor, parameters, verbosity):
     185        if verbosity >= 2:
     186            print "_destroy_test_db(): dbname=%s" % parameters['dbname']
     187        statements = [
     188            'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
     189            'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS',
     190            ]
     191        _execute_statements(cursor, statements, parameters, verbosity)
    200192
    201 def _execute_statements(cursor, statements, parameters, verbosity):
    202     for template in statements:
    203         stmt = template % parameters
     193    def _destroy_test_user(cursor, parameters, verbosity):
    204194        if verbosity >= 2:
    205             print stmt
     195            print "_destroy_test_user(): user=%s" % parameters['user']
     196            print "Be patient.  This can take some time..."
     197        statements = [
     198            'DROP USER %(user)s CASCADE',
     199        ]
     200        _execute_statements(cursor, statements, parameters, verbosity)
     201
     202    def _execute_statements(cursor, statements, parameters, verbosity):
     203        for template in statements:
     204            stmt = template % parameters
     205            if verbosity >= 2:
     206                print stmt
     207            try:
     208                cursor.execute(stmt)
     209            except Exception, err:
     210                sys.stderr.write("Failed (%s)\n" % (err))
     211                raise
     212
     213    def _test_database_name(settings):
     214        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
    206215        try:
    207             cursor.execute(stmt)
    208         except Exception, err:
    209             sys.stderr.write("Failed (%s)\n" % (err))
     216            if settings.TEST_DATABASE_NAME:
     217                name = settings.TEST_DATABASE_NAME
     218        except AttributeError:
     219            pass
     220        except:
    210221            raise
     222        return name
    211223
    212 def _test_database_name(settings):
    213     name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
    214     try:
    215         if settings.TEST_DATABASE_NAME:
    216             name = settings.TEST_DATABASE_NAME
    217     except AttributeError:
    218         pass
    219     except:
    220         raise
    221     return name
     224    def _test_database_create(settings):
     225        name = True
     226        try:
     227            if settings.TEST_DATABASE_CREATE:
     228                name = True
     229            else:
     230                name = False
     231        except AttributeError:
     232            pass
     233        except:
     234            raise
     235        return name
    222236
    223 def _test_database_create(settings):
    224     name = True
    225     try:
    226         if settings.TEST_DATABASE_CREATE:
    227             name = True
    228         else:
    229             name = False
    230     except AttributeError:
    231         pass
    232     except:
    233         raise
    234     return name
     237    def _test_user_create(settings):
     238        name = True
     239        try:
     240            if settings.TEST_USER_CREATE:
     241                name = True
     242            else:
     243                name = False
     244        except AttributeError:
     245            pass
     246        except:
     247            raise
     248        return name
    235249
    236 def _test_user_create(settings):
    237     name = True
    238     try:
    239         if settings.TEST_USER_CREATE:
    240             name = True
    241         else:
    242             name = False
    243     except AttributeError:
    244         pass
    245     except:
    246         raise
    247     return name
     250    def _test_database_user(settings):
     251        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
     252        try:
     253            if settings.TEST_DATABASE_USER:
     254                name = settings.TEST_DATABASE_USER
     255        except AttributeError:
     256            pass
     257        except:
     258            raise
     259        return name
    248260
    249 def _test_database_user(settings):
    250     name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
    251     try:
    252         if settings.TEST_DATABASE_USER:
    253             name = settings.TEST_DATABASE_USER
    254     except AttributeError:
    255         pass
    256     except:
    257         raise
    258     return name
     261    def _test_database_passwd(settings):
     262        name = PASSWORD
     263        try:
     264            if settings.TEST_DATABASE_PASSWD:
     265                name = settings.TEST_DATABASE_PASSWD
     266        except AttributeError:
     267            pass
     268        except:
     269            raise
     270        return name
    259271
    260 def _test_database_passwd(settings):
    261     name = PASSWORD
    262     try:
    263         if settings.TEST_DATABASE_PASSWD:
    264             name = settings.TEST_DATABASE_PASSWD
    265     except AttributeError:
    266         pass
    267     except:
    268         raise
    269     return name
     272    def _test_database_tblspace(settings):
     273        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
     274        try:
     275            if settings.TEST_DATABASE_TBLSPACE:
     276                name = settings.TEST_DATABASE_TBLSPACE
     277        except AttributeError:
     278            pass
     279        except:
     280            raise
     281        return name
    270282
    271 def _test_database_tblspace(settings):
    272     name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
    273     try:
    274         if settings.TEST_DATABASE_TBLSPACE:
    275             name = settings.TEST_DATABASE_TBLSPACE
    276     except AttributeError:
    277         pass
    278     except:
    279         raise
    280     return name
    281 
    282 def _test_database_tblspace_tmp(settings):
    283     name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
    284     try:
    285         if settings.TEST_DATABASE_TBLSPACE_TMP:
    286             name = settings.TEST_DATABASE_TBLSPACE_TMP
    287     except AttributeError:
    288         pass
    289     except:
    290         raise
    291     return name
     283    def _test_database_tblspace_tmp(settings):
     284        name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp'
     285        try:
     286            if settings.TEST_DATABASE_TBLSPACE_TMP:
     287                name = settings.TEST_DATABASE_TBLSPACE_TMP
     288        except AttributeError:
     289            pass
     290        except:
     291            raise
     292        return name
  • django/db/backends/__init__.py

     
    325325        """
    326326        return self.year_lookup_bounds(value)
    327327
     328class BaseDatabaseCreation(object):
     329    """
     330    This class encapsulates all backend-specific differences that pertain to
     331    database *creation*, such as the column types to use for particular Django
     332    Fields.
     333    """
     334    data_types = {}
     335
     336class BaseDatabaseIntrospection(object):
     337    """
     338    This class encapsulates all backend-specific introspection utilities
     339    """
     340    data_types_reverse = {}
     341
     342class BaseDatabaseClient(object):
     343    """
     344    This class encapsualtes all backend-specific methods for opening a
     345    client shell
     346    """
     347    def runshell():
     348            raise NotImplementedError()
     349       
     350 No newline at end of file
  • django/db/backends/postgresql_psycopg2/base.py

     
    66
    77from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
    88from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
     9from django.db.backends.postgresql.client import DatabaseClient
     10from django.db.backends.postgresql.creation import DatabaseCreation
     11from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
     12
    913from django.utils.safestring import SafeUnicode
    1014try:
    1115    import psycopg2 as Database
     
    3337class DatabaseWrapper(BaseDatabaseWrapper):
    3438    features = DatabaseFeatures()
    3539    ops = DatabaseOperations()
     40    client = DatabaseClient()
     41    creation = DatabaseCreation()
     42    introspection = DatabaseIntrospection(ops)
     43
    3644    operators = {
    3745        'exact': '= %s',
    3846        'iexact': 'ILIKE %s',
  • django/db/backends/postgresql_psycopg2/client.py

     
    1 from django.db.backends.postgresql.client import *
  • django/db/backends/postgresql_psycopg2/introspection.py

     
    1 from django.db.backends.postgresql_psycopg2.base import DatabaseOperations
     1from django.db.backends.postgresql.introspection import DatabaseIntrospection as PostgresDatabaseIntrospection
    22
    3 quote_name = DatabaseOperations().quote_name
     3class DatabaseIntrospection(PostgresDatabaseIntrospection):
    44
    5 def get_table_list(cursor):
    6     "Returns a list of table names in the current database."
    7     cursor.execute("""
    8         SELECT c.relname
    9         FROM pg_catalog.pg_class c
    10         LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    11         WHERE c.relkind IN ('r', 'v', '')
    12             AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
    13             AND pg_catalog.pg_table_is_visible(c.oid)""")
    14     return [row[0] for row in cursor.fetchall()]
    15 
    16 def get_table_description(cursor, table_name):
    17     "Returns a description of the table, with the DB-API cursor.description interface."
    18     cursor.execute("SELECT * FROM %s LIMIT 1" % quote_name(table_name))
    19     return cursor.description
    20 
    21 def get_relations(cursor, table_name):
    22     """
    23     Returns a dictionary of {field_index: (field_index_other_table, other_table)}
    24     representing all relationships to the given table. Indexes are 0-based.
    25     """
    26     cursor.execute("""
    27         SELECT con.conkey, con.confkey, c2.relname
    28         FROM pg_constraint con, pg_class c1, pg_class c2
    29         WHERE c1.oid = con.conrelid
    30             AND c2.oid = con.confrelid
    31             AND c1.relname = %s
    32             AND con.contype = 'f'""", [table_name])
    33     relations = {}
    34     for row in cursor.fetchall():
    35         # row[0] and row[1] are single-item lists, so grab the single item.
    36         relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
    37     return relations
    38 
    39 def get_indexes(cursor, table_name):
    40     """
    41     Returns a dictionary of fieldname -> infodict for the given table,
    42     where each infodict is in the format:
    43         {'primary_key': boolean representing whether it's the primary key,
    44          'unique': boolean representing whether it's a unique index}
    45     """
    46     # This query retrieves each index on the given table, including the
    47     # first associated field name
    48     cursor.execute("""
    49         SELECT attr.attname, idx.indkey, idx.indisunique, idx.indisprimary
    50         FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
    51             pg_catalog.pg_index idx, pg_catalog.pg_attribute attr
    52         WHERE c.oid = idx.indrelid
    53             AND idx.indexrelid = c2.oid
    54             AND attr.attrelid = c.oid
    55             AND attr.attnum = idx.indkey[0]
    56             AND c.relname = %s""", [table_name])
    57     indexes = {}
    58     for row in cursor.fetchall():
    59         # row[1] (idx.indkey) is stored in the DB as an array. It comes out as
    60         # a string of space-separated integers. This designates the field
    61         # indexes (1-based) of the fields that have indexes on the table.
    62         # Here, we skip any indexes across multiple fields.
    63         if ' ' in row[1]:
    64             continue
    65         indexes[row[0]] = {'primary_key': row[3], 'unique': row[2]}
    66     return indexes
    67 
    68 # Maps type codes to Django Field types.
    69 DATA_TYPES_REVERSE = {
    70     16: 'BooleanField',
    71     21: 'SmallIntegerField',
    72     23: 'IntegerField',
    73     25: 'TextField',
    74     701: 'FloatField',
    75     869: 'IPAddressField',
    76     1043: 'CharField',
    77     1082: 'DateField',
    78     1083: 'TimeField',
    79     1114: 'DateTimeField',
    80     1184: 'DateTimeField',
    81     1266: 'TimeField',
    82     1700: 'DecimalField',
    83 }
     5    def get_relations(self, cursor, table_name):
     6        """
     7        Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     8        representing all relationships to the given table. Indexes are 0-based.
     9        """
     10        cursor.execute("""
     11            SELECT con.conkey, con.confkey, c2.relname
     12            FROM pg_constraint con, pg_class c1, pg_class c2
     13            WHERE c1.oid = con.conrelid
     14                AND c2.oid = con.confrelid
     15                AND c1.relname = %s
     16                AND con.contype = 'f'""", [table_name])
     17        relations = {}
     18        for row in cursor.fetchall():
     19            # row[0] and row[1] are single-item lists, so grab the single item.
     20            relations[row[0][0] - 1] = (row[1][0] - 1, row[2])
     21        return relations
  • django/db/backends/postgresql_psycopg2/creation.py

     
    1 from django.db.backends.postgresql.creation import *
  • django/db/backends/dummy/base.py

     
    99
    1010from django.core.exceptions import ImproperlyConfigured
    1111from django.db.backends import BaseDatabaseFeatures, BaseDatabaseOperations
     12from django.db.backends import BaseDatabaseClient, BaseDatabaseCreation, BaseDatabaseIntrospection
    1213
    1314def complain(*args, **kwargs):
    1415    raise ImproperlyConfigured, "You haven't set the DATABASE_ENGINE setting yet."
     
    2526class DatabaseOperations(BaseDatabaseOperations):
    2627    quote_name = complain
    2728
     29class DatabaseClient(BaseDatabaseClient):
     30    runshell = complain
     31   
     32class DatabaseCreation(BaseDatabaseCreation):
     33    pass
     34   
     35class DatabaseIntrospection(BaseDatabaseIntrospection):
     36    get_table_list = complain
     37    get_table_description = complain
     38    get_relations = complain
     39    get_indexes = complain
     40   
    2841class DatabaseWrapper(object):
    2942    features = BaseDatabaseFeatures()
    3043    ops = DatabaseOperations()
     44    client = DatabaseClient()
     45    creation = DatabaseCreation()
     46    introspection = DatabaseIntrospection()
     47
    3148    operators = {}
    3249    cursor = complain
    3350    _commit = complain
  • django/db/backends/dummy/client.py

     
    1 from django.db.backends.dummy.base import complain
    2 
    3 runshell = complain
  • django/db/backends/dummy/introspection.py

     
    1 from django.db.backends.dummy.base import complain
    2 
    3 get_table_list = complain
    4 get_table_description = complain
    5 get_relations = complain
    6 get_indexes = complain
    7 
    8 DATA_TYPES_REVERSE = {}
  • django/db/backends/dummy/creation.py

     
    1 DATA_TYPES = {}
  • django/db/backends/creation.py

     
    1 class BaseCreation(object):
    2     """
    3     This class encapsulates all backend-specific differences that pertain to
    4     database *creation*, such as the column types to use for particular Django
    5     Fields.
    6     """
    7     pass
  • django/core/management/commands/inspectdb.py

     
    1313            raise CommandError("Database inspection isn't supported for the currently selected database backend.")
    1414
    1515    def handle_inspection(self):
    16         from django.db import connection, get_introspection_module
     16        from django.db import connection
    1717        import keyword
    1818
    19         introspection_module = get_introspection_module()
    20 
    2119        table2model = lambda table_name: table_name.title().replace('_', '')
    2220
    2321        cursor = connection.cursor()
     
    3230        yield ''
    3331        yield 'from django.db import models'
    3432        yield ''
    35         for table_name in introspection_module.get_table_list(cursor):
     33        for table_name in connection.introspection.get_table_list(cursor):
    3634            yield 'class %s(models.Model):' % table2model(table_name)
    3735            try:
    38                 relations = introspection_module.get_relations(cursor, table_name)
     36                relations = connection.introspection.get_relations(cursor, table_name)
    3937            except NotImplementedError:
    4038                relations = {}
    4139            try:
    42                 indexes = introspection_module.get_indexes(cursor, table_name)
     40                indexes = connection.introspection.get_indexes(cursor, table_name)
    4341            except NotImplementedError:
    4442                indexes = {}
    45             for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)):
     43            for i, row in enumerate(connection.introspection.get_table_description(cursor, table_name)):
    4644                att_name = row[0].lower()
    4745                comment_notes = [] # Holds Field notes, to be displayed in a Python comment.
    4846                extra_params = {}  # Holds Field parameters such as 'db_column'.
     
    6563                        extra_params['db_column'] = att_name
    6664                else:
    6765                    try:
    68                         field_type = introspection_module.DATA_TYPES_REVERSE[row[1]]
     66                        field_type = connection.introspection.data_types_reverse[row[1]]
    6967                    except KeyError:
    7068                        field_type = 'TextField'
    7169                        comment_notes.append('This field type is a guess.')
  • django/core/management/commands/dbshell.py

     
    66    requires_model_validation = False
    77
    88    def handle_noargs(self, **options):
    9         from django.db import runshell
    10         runshell()
     9        from django.db import connection
     10        connection.client.runshell()
  • django/core/management/sql.py

     
    99
    1010def table_names():
    1111    "Returns a list of all table names that exist in the database."
    12     from django.db import connection, get_introspection_module
     12    from django.db import connection
    1313    cursor = connection.cursor()
    14     return set(get_introspection_module().get_table_list(cursor))
     14    return set(connection.introspection.get_table_list(cursor))
    1515
    1616def django_table_names(only_existing=False):
    1717    """
Back to Top