Ticket #5461: 5461-r8194.2.diff

File 5461-r8194.2.diff, 141.3 KB (added by ramiro, 7 years ago)

5461-r8194.diff but with two small modifications as described in http://groups.google.com/group/django-developers/msg/adfdaf65a6dbc8a7?hl=en

  • django/core/management/commands/dbshell.py

    diff -r fda9a525ebea django/core/management/commands/dbshell.py
    a b  
    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/commands/inspectdb.py

    diff -r fda9a525ebea django/core/management/commands/inspectdb.py
    a b  
    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
    18 
    19         introspection_module = get_introspection_module()
    2018
    2119        table2model = lambda table_name: table_name.title().replace('_', '')
    2220
     
    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/syncdb.py

    diff -r fda9a525ebea django/core/management/commands/syncdb.py
    a b  
    2121    def handle_noargs(self, **options):
    2222        from django.db import connection, transaction, models
    2323        from django.conf import settings
    24         from django.core.management.sql import table_names, installed_models, sql_model_create, sql_for_pending_references, many_to_many_sql_for_model, custom_sql_for_model, sql_indexes_for_model, emit_post_sync_signal
     24        from django.core.management.sql import table_names, installed_models, custom_sql_for_model, emit_post_sync_signal
    2525
    2626        verbosity = int(options.get('verbosity', 1))
    2727        interactive = options.get('interactive')
     
    7373                    print "Processing %s.%s model" % (app_name, model._meta.object_name)
    7474                if table_name_converter(model._meta.db_table) in tables:
    7575                    continue
    76                 sql, references = sql_model_create(model, self.style, seen_models)
     76                sql, references = connection.creation.sql_create_model(model, self.style, seen_models)
    7777                seen_models.add(model)
    7878                created_models.add(model)
    7979                for refto, refs in references.items():
    8080                    pending_references.setdefault(refto, []).extend(refs)
    8181                    if refto in seen_models:
    82                         sql.extend(sql_for_pending_references(refto, self.style, pending_references))
    83                 sql.extend(sql_for_pending_references(model, self.style, pending_references))
     82                        sql.extend(connection.creation.sql_for_pending_references(refto, self.style, pending_references))
     83                sql.extend(connection.creation.sql_for_pending_references(model, self.style, pending_references))
    8484                if verbosity >= 1:
    8585                    print "Creating table %s" % model._meta.db_table
    8686                for statement in sql:
     
    9494            model_list = models.get_models(app)
    9595            for model in model_list:
    9696                if model in created_models:
    97                     sql = many_to_many_sql_for_model(model, self.style)
     97                    sql = connection.creation.many_to_many_sql_for_model(model, self.style)
    9898                    if sql:
    9999                        if verbosity >= 2:
    100100                            print "Creating many-to-many tables for %s.%s model" % (app_name, model._meta.object_name)
     
    140140            app_name = app.__name__.split('.')[-2]
    141141            for model in models.get_models(app):
    142142                if model in created_models:
    143                     index_sql = sql_indexes_for_model(model, self.style)
     143                    index_sql = connection.creation.sql_indexes_for_model(model, self.style)
    144144                    if index_sql:
    145145                        if verbosity >= 1:
    146146                            print "Installing index for %s.%s model" % (app_name, model._meta.object_name)
  • django/core/management/commands/testserver.py

    diff -r fda9a525ebea django/core/management/commands/testserver.py
    a b  
    1818
    1919    def handle(self, *fixture_labels, **options):
    2020        from django.core.management import call_command
    21         from django.test.utils import create_test_db
     21        from django.db import connection
    2222
    2323        verbosity = int(options.get('verbosity', 1))
    2424        addrport = options.get('addrport')
    2525
    2626        # Create a test database.
    27         db_name = create_test_db(verbosity=verbosity)
     27        db_name = connection.creation.create_test_db(verbosity=verbosity)
    2828
    2929        # Import the fixture data into the test database.
    3030        call_command('loaddata', *fixture_labels, **{'verbosity': verbosity})
  • django/core/management/sql.py

    diff -r fda9a525ebea django/core/management/sql.py
    a b  
    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    """
     
    6565
    6666def sql_create(app, style):
    6767    "Returns a list of the CREATE TABLE SQL statements for the given app."
    68     from django.db import models
     68    from django.db import connection, models
    6969    from django.conf import settings
    7070
    7171    if settings.DATABASE_ENGINE == 'dummy':
     
    8585    pending_references = {}
    8686
    8787    for model in app_models:
    88         output, references = sql_model_create(model, style, known_models)
     88        output, references = connection.creation.sql_create_model(model, style, known_models)
    8989        final_output.extend(output)
    9090        for refto, refs in references.items():
    9191            pending_references.setdefault(refto, []).extend(refs)
    9292            if refto in known_models:
    93                 final_output.extend(sql_for_pending_references(refto, style, pending_references))
    94         final_output.extend(sql_for_pending_references(model, style, pending_references))
     93                final_output.extend(connection.creation.sql_for_pending_references(refto, style, pending_references))
     94        final_output.extend(connection.creation.sql_for_pending_references(model, style, pending_references))
    9595        # Keep track of the fact that we've created the table for this model.
    9696        known_models.add(model)
    9797
    9898    # Create the many-to-many join tables.
    9999    for model in app_models:
    100         final_output.extend(many_to_many_sql_for_model(model, style))
     100        final_output.extend(connection.creation.many_to_many_sql_for_model(model, style))
    101101
    102102    # Handle references to tables that are from other apps
    103103    # but don't exist physically.
     
    106106        alter_sql = []
    107107        for model in not_installed_models:
    108108            alter_sql.extend(['-- ' + sql for sql in
    109                 sql_for_pending_references(model, style, pending_references)])
     109                connection.creation.sql_for_pending_references(model, style, pending_references)])
    110110        if alter_sql:
    111111            final_output.append('-- The following references should be added but depend on non-existent tables:')
    112112            final_output.extend(alter_sql)
     
    115115
    116116def sql_delete(app, style):
    117117    "Returns a list of the DROP TABLE SQL statements for the given app."
    118     from django.db import connection, models, get_introspection_module
     118    from django.db import connection, models
    119119    from django.db.backends.util import truncate_name
    120120    from django.contrib.contenttypes import generic
    121     introspection = get_introspection_module()
    122121
    123122    # This should work even if a connection isn't available
    124123    try:
     
    128127
    129128    # Figure out which tables already exist
    130129    if cursor:
    131         table_names = introspection.get_table_list(cursor)
     130        table_names = connection.introspection.get_table_list(cursor)
    132131    else:
    133132        table_names = []
    134133    if connection.features.uses_case_insensitive_names:
     
    137136        table_name_converter = lambda x: x
    138137
    139138    output = []
    140     qn = connection.ops.quote_name
    141139
    142140    # Output DROP TABLE statements for standard application tables.
    143141    to_delete = set()
     
    155153            to_delete.add(model)
    156154
    157155    for model in app_models:
    158         if cursor and table_name_converter(model._meta.db_table) in table_names:
    159             # Drop the table now
    160             output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
    161                 style.SQL_TABLE(qn(model._meta.db_table))))
    162             if connection.features.supports_constraints and model in references_to_delete:
    163                 for rel_class, f in references_to_delete[model]:
    164                     table = rel_class._meta.db_table
    165                     col = f.column
    166                     r_table = model._meta.db_table
    167                     r_col = model._meta.get_field(f.rel.field_name).column
    168                     r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
    169                     output.append('%s %s %s %s;' % \
    170                         (style.SQL_KEYWORD('ALTER TABLE'),
    171                         style.SQL_TABLE(qn(table)),
    172                         style.SQL_KEYWORD(connection.ops.drop_foreignkey_sql()),
    173                         style.SQL_FIELD(truncate_name(r_name, connection.ops.max_name_length()))))
    174                 del references_to_delete[model]
    175             if model._meta.has_auto_field:
    176                 ds = connection.ops.drop_sequence_sql(model._meta.db_table)
    177                 if ds:
    178                     output.append(ds)
     156        if table_name_converter(model._meta.db_table) in table_names:
     157            output.extend(connection.creation.sql_destroy_model(model, references_to_delete, style))
    179158
    180159    # Output DROP TABLE statements for many-to-many tables.
    181160    for model in app_models:
    182161        opts = model._meta
    183162        for f in opts.local_many_to_many:
    184             if not f.creates_table:
    185                 continue
    186163            if cursor and table_name_converter(f.m2m_db_table()) in table_names:
    187                 output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
    188                     style.SQL_TABLE(qn(f.m2m_db_table()))))
    189                 ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
    190                 if ds:
    191                     output.append(ds)
     164                output.extend(connection.creation.sql_destroy_many_to_many(model, f, style))
    192165
    193166    app_label = app_models[0]._meta.app_label
    194167
     
    234207
    235208def sql_indexes(app, style):
    236209    "Returns a list of the CREATE INDEX SQL statements for all models in the given app."
    237     from django.db import models
     210    from django.db import connection, models
    238211    output = []
    239212    for model in models.get_models(app):
    240         output.extend(sql_indexes_for_model(model, style))
     213        output.extend(connection.creation.sql_indexes_for_model(model, style))
    241214    return output
    242215
    243216def sql_all(app, style):
    244217    "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module."
    245218    return sql_create(app, style) + sql_custom(app, style) + sql_indexes(app, style)
    246 
    247 def sql_model_create(model, style, known_models=set()):
    248     """
    249     Returns the SQL required to create a single model, as a tuple of:
    250         (list_of_sql, pending_references_dict)
    251     """
    252     from django.db import connection, models
    253 
    254     opts = model._meta
    255     final_output = []
    256     table_output = []
    257     pending_references = {}
    258     qn = connection.ops.quote_name
    259     inline_references = connection.features.inline_fk_references
    260     for f in opts.local_fields:
    261         col_type = f.db_type()
    262         tablespace = f.db_tablespace or opts.db_tablespace
    263         if col_type is None:
    264             # Skip ManyToManyFields, because they're not represented as
    265             # database columns in this table.
    266             continue
    267         # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
    268         field_output = [style.SQL_FIELD(qn(f.column)),
    269             style.SQL_COLTYPE(col_type)]
    270         field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
    271         if f.primary_key:
    272             field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
    273         elif f.unique:
    274             field_output.append(style.SQL_KEYWORD('UNIQUE'))
    275         if tablespace and connection.features.supports_tablespaces and f.unique:
    276             # We must specify the index tablespace inline, because we
    277             # won't be generating a CREATE INDEX statement for this field.
    278             field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))
    279         if f.rel:
    280             if inline_references and f.rel.to in known_models:
    281                 field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
    282                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
    283                     style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
    284                     connection.ops.deferrable_sql()
    285                 )
    286             else:
    287                 # We haven't yet created the table to which this field
    288                 # is related, so save it for later.
    289                 pr = pending_references.setdefault(f.rel.to, []).append((model, f))
    290         table_output.append(' '.join(field_output))
    291     if opts.order_with_respect_to:
    292         table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
    293             style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
    294             style.SQL_KEYWORD('NULL'))
    295     for field_constraints in opts.unique_together:
    296         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
    297             ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
    298 
    299     full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
    300     for i, line in enumerate(table_output): # Combine and add commas.
    301         full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    302     full_statement.append(')')
    303     if opts.db_tablespace and connection.features.supports_tablespaces:
    304         full_statement.append(connection.ops.tablespace_sql(opts.db_tablespace))
    305     full_statement.append(';')
    306     final_output.append('\n'.join(full_statement))
    307 
    308     if opts.has_auto_field:
    309         # Add any extra SQL needed to support auto-incrementing primary keys.
    310         auto_column = opts.auto_field.db_column or opts.auto_field.name
    311         autoinc_sql = connection.ops.autoinc_sql(opts.db_table, auto_column)
    312         if autoinc_sql:
    313             for stmt in autoinc_sql:
    314                 final_output.append(stmt)
    315 
    316     return final_output, pending_references
    317 
    318 def sql_for_pending_references(model, style, pending_references):
    319     """
    320     Returns any ALTER TABLE statements to add constraints after the fact.
    321     """
    322     from django.db import connection
    323     from django.db.backends.util import truncate_name
    324 
    325     qn = connection.ops.quote_name
    326     final_output = []
    327     if connection.features.supports_constraints:
    328         opts = model._meta
    329         if model in pending_references:
    330             for rel_class, f in pending_references[model]:
    331                 rel_opts = rel_class._meta
    332                 r_table = rel_opts.db_table
    333                 r_col = f.column
    334                 table = opts.db_table
    335                 col = opts.get_field(f.rel.field_name).column
    336                 # For MySQL, r_name must be unique in the first 64 characters.
    337                 # So we are careful with character usage here.
    338                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
    339                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
    340                     (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    341                     qn(r_col), qn(table), qn(col),
    342                     connection.ops.deferrable_sql()))
    343             del pending_references[model]
    344     return final_output
    345 
    346 def many_to_many_sql_for_model(model, style):
    347     from django.db import connection, models
    348     from django.contrib.contenttypes import generic
    349     from django.db.backends.util import truncate_name
    350 
    351     opts = model._meta
    352     final_output = []
    353     qn = connection.ops.quote_name
    354     inline_references = connection.features.inline_fk_references
    355     for f in opts.local_many_to_many:
    356         if f.creates_table:
    357             tablespace = f.db_tablespace or opts.db_tablespace
    358             if tablespace and connection.features.supports_tablespaces:
    359                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace, inline=True)
    360             else:
    361                 tablespace_sql = ''
    362             table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
    363                 style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
    364             table_output.append('    %s %s %s%s,' %
    365                 (style.SQL_FIELD(qn('id')),
    366                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
    367                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
    368                 tablespace_sql))
    369             if inline_references:
    370                 deferred = []
    371                 table_output.append('    %s %s %s %s (%s)%s,' %
    372                     (style.SQL_FIELD(qn(f.m2m_column_name())),
    373                     style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
    374                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
    375                     style.SQL_TABLE(qn(opts.db_table)),
    376                     style.SQL_FIELD(qn(opts.pk.column)),
    377                     connection.ops.deferrable_sql()))
    378                 table_output.append('    %s %s %s %s (%s)%s,' %
    379                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
    380                     style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
    381                     style.SQL_KEYWORD('NOT NULL REFERENCES'),
    382                     style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
    383                     style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
    384                     connection.ops.deferrable_sql()))
    385             else:
    386                 table_output.append('    %s %s %s,' %
    387                     (style.SQL_FIELD(qn(f.m2m_column_name())),
    388                     style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
    389                     style.SQL_KEYWORD('NOT NULL')))
    390                 table_output.append('    %s %s %s,' %
    391                     (style.SQL_FIELD(qn(f.m2m_reverse_name())),
    392                     style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
    393                     style.SQL_KEYWORD('NOT NULL')))
    394                 deferred = [
    395                     (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
    396                         opts.pk.column),
    397                     ( f.m2m_db_table(), f.m2m_reverse_name(),
    398                         f.rel.to._meta.db_table, f.rel.to._meta.pk.column)
    399                     ]
    400             table_output.append('    %s (%s, %s)%s' %
    401                 (style.SQL_KEYWORD('UNIQUE'),
    402                 style.SQL_FIELD(qn(f.m2m_column_name())),
    403                 style.SQL_FIELD(qn(f.m2m_reverse_name())),
    404                 tablespace_sql))
    405             table_output.append(')')
    406             if opts.db_tablespace and connection.features.supports_tablespaces:
    407                 # f.db_tablespace is only for indices, so ignore its value here.
    408                 table_output.append(connection.ops.tablespace_sql(opts.db_tablespace))
    409             table_output.append(';')
    410             final_output.append('\n'.join(table_output))
    411 
    412             for r_table, r_col, table, col in deferred:
    413                 r_name = '%s_refs_%s_%x' % (r_col, col,
    414                         abs(hash((r_table, table))))
    415                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
    416                 (qn(r_table),
    417                 truncate_name(r_name, connection.ops.max_name_length()),
    418                 qn(r_col), qn(table), qn(col),
    419                 connection.ops.deferrable_sql()))
    420 
    421             # Add any extra SQL needed to support auto-incrementing PKs
    422             autoinc_sql = connection.ops.autoinc_sql(f.m2m_db_table(), 'id')
    423             if autoinc_sql:
    424                 for stmt in autoinc_sql:
    425                     final_output.append(stmt)
    426 
    427     return final_output
    428219
    429220def custom_sql_for_model(model, style):
    430221    from django.db import models
     
    461252
    462253    return output
    463254
    464 def sql_indexes_for_model(model, style):
    465     "Returns the CREATE INDEX SQL statements for a single model"
    466     from django.db import connection
    467     output = []
    468 
    469     qn = connection.ops.quote_name
    470     for f in model._meta.local_fields:
    471         if f.db_index and not f.unique:
    472             tablespace = f.db_tablespace or model._meta.db_tablespace
    473             if tablespace and connection.features.supports_tablespaces:
    474                 tablespace_sql = ' ' + connection.ops.tablespace_sql(tablespace)
    475             else:
    476                 tablespace_sql = ''
    477             output.append(
    478                 style.SQL_KEYWORD('CREATE INDEX') + ' ' + \
    479                 style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \
    480                 style.SQL_KEYWORD('ON') + ' ' + \
    481                 style.SQL_TABLE(qn(model._meta.db_table)) + ' ' + \
    482                 "(%s)" % style.SQL_FIELD(qn(f.column)) + \
    483                 "%s;" % tablespace_sql
    484             )
    485     return output
    486255
    487256def emit_post_sync_signal(created_models, verbosity, interactive):
    488257    from django.db import models
  • django/core/management/validation.py

    diff -r fda9a525ebea django/core/management/validation.py
    a b  
    6161            if f.db_index not in (None, True, False):
    6262                e.add(opts, '"%s": "db_index" should be either None, True or False.' % f.name)
    6363
    64             # Check that max_length <= 255 if using older MySQL versions.
    65             if settings.DATABASE_ENGINE == 'mysql':
    66                 db_version = connection.get_server_version()
    67                 if db_version < (5, 0, 3) and isinstance(f, (models.CharField, models.CommaSeparatedIntegerField, models.SlugField)) and f.max_length > 255:
    68                     e.add(opts, '"%s": %s cannot have a "max_length" greater than 255 when you are using a version of MySQL prior to 5.0.3 (you are using %s).' % (f.name, f.__class__.__name__, '.'.join([str(n) for n in db_version[:3]])))
     64            # Perform any backend-specific field validation.
     65            connection.validation.validate_field(e, opts, f)
    6966
    7067            # Check to see if the related field will clash with any existing
    7168            # fields, m2m fields, m2m related objects or related objects
  • django/db/__init__.py

    diff -r fda9a525ebea django/db/__init__.py
    a b  
    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:
    2019    # If the import failed, we might be looking for a database backend
    2120    # distributed external to Django. So we'll try that next.
    2221    try:
    2322        _import_path = ''
    2423        backend = __import__('%s.base' % settings.DATABASE_ENGINE, {}, {}, [''])
    25         creation = __import__('%s.creation' % settings.DATABASE_ENGINE, {}, {}, [''])
    2624    except ImportError, e_user:
    2725        # The database backend wasn't found. Display a helpful error message
    2826        # listing all possible (built-in) database backends.
     
    3028        available_backends = [f for f in os.listdir(backend_dir) if not f.startswith('_') and not f.startswith('.') and not f.endswith('.py') and not f.endswith('.pyc')]
    3129        available_backends.sort()
    3230        if settings.DATABASE_ENGINE not in available_backends:
    33             raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s" % \
    34                 (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends)))
     31            raise ImproperlyConfigured, "%r isn't an available database backend. Available options are: %s\nError was: %s" % \
     32                (settings.DATABASE_ENGINE, ", ".join(map(repr, available_backends, e_user)))
    3533        else:
    3634            raise # If there's some other error, this must be an error in Django itself.
    37 
    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()
    5335
    5436# Convenient aliases for backend bits.
    5537connection = backend.DatabaseWrapper(**settings.DATABASE_OPTIONS)
  • django/db/backends/__init__.py

    diff -r fda9a525ebea django/db/backends/__init__.py
    a b  
    325325        """
    326326        return self.year_lookup_bounds(value)
    327327
     328class BaseDatabaseIntrospection(object):
     329    """
     330    This class encapsulates all backend-specific introspection utilities
     331    """
     332    data_types_reverse = {}
     333
     334class BaseDatabaseClient(object):
     335    """
     336    This class encapsualtes all backend-specific methods for opening a
     337    client shell
     338    """
     339    def runshell(self):
     340            raise NotImplementedError()
     341
     342class BaseDatabaseValidation(object):
     343    """
     344    This class encapsualtes all backend-specific model validation.
     345    """
     346    def validate_field(self, errors, opts, f):
     347        "By default, there is no backend-specific validation"
     348        pass
     349 No newline at end of file
  • django/db/backends/creation.py

    diff -r fda9a525ebea django/db/backends/creation.py
    a b  
    1 class BaseCreation(object):
     1import sys
     2import time
     3
     4from django.conf import settings
     5from django.core.management import call_command
     6
     7# The prefix to put on the default database name when creating
     8# the test database.
     9TEST_DATABASE_PREFIX = 'test_'
     10
     11class BaseDatabaseCreation(object):
    212    """
    313    This class encapsulates all backend-specific differences that pertain to
    414    database *creation*, such as the column types to use for particular Django
    515    Fields.
    616    """
    7     pass
     17    data_types = {}
     18   
     19    def __init__(self, ops, features):
     20        self.ops = ops
     21        self.features = features
     22       
     23    def sql_create_model(self, model, style, known_models=set()):
     24        """
     25        Returns the SQL required to create a single model, as a tuple of:
     26            (list_of_sql, pending_references_dict)
     27        """
     28        from django.db import models
     29
     30        opts = model._meta
     31        final_output = []
     32        table_output = []
     33        pending_references = {}
     34        qn = self.ops.quote_name
     35        inline_references = self.features.inline_fk_references
     36        for f in opts.local_fields:
     37            col_type = f.db_type()
     38            tablespace = f.db_tablespace or opts.db_tablespace
     39            if col_type is None:
     40                # Skip ManyToManyFields, because they're not represented as
     41                # database columns in this table.
     42                continue
     43            # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
     44            field_output = [style.SQL_FIELD(qn(f.column)),
     45                style.SQL_COLTYPE(col_type)]
     46            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or '')))
     47            if f.primary_key:
     48                field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
     49            elif f.unique:
     50                field_output.append(style.SQL_KEYWORD('UNIQUE'))
     51            if tablespace and self.features.supports_tablespaces and f.unique:
     52                # We must specify the index tablespace inline, because we
     53                # won't be generating a CREATE INDEX statement for this field.
     54                field_output.append(self.ops.tablespace_sql(tablespace, inline=True))
     55            if f.rel:
     56                if inline_references and f.rel.to in known_models:
     57                    field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
     58                        style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \
     59                        style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' +
     60                        self.ops.deferrable_sql()
     61                    )
     62                else:
     63                    # We haven't yet created the table to which this field
     64                    # is related, so save it for later.
     65                    pr = pending_references.setdefault(f.rel.to, []).append((model, f))
     66            table_output.append(' '.join(field_output))
     67        if opts.order_with_respect_to:
     68            table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
     69                style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
     70                style.SQL_KEYWORD('NULL'))
     71        for field_constraints in opts.unique_together:
     72            table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
     73                ", ".join([style.SQL_FIELD(qn(opts.get_field(f).column)) for f in field_constraints]))
     74
     75        full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     76        for i, line in enumerate(table_output): # Combine and add commas.
     77            full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     78        full_statement.append(')')
     79        if opts.db_tablespace and self.features.supports_tablespaces:
     80            full_statement.append(self.ops.tablespace_sql(opts.db_tablespace))
     81        full_statement.append(';')
     82        final_output.append('\n'.join(full_statement))
     83
     84        if opts.has_auto_field:
     85            # Add any extra SQL needed to support auto-incrementing primary keys.
     86            auto_column = opts.auto_field.db_column or opts.auto_field.name
     87            autoinc_sql = self.ops.autoinc_sql(opts.db_table, auto_column)
     88            if autoinc_sql:
     89                for stmt in autoinc_sql:
     90                    final_output.append(stmt)
     91
     92        return final_output, pending_references
     93
     94    def sql_for_pending_references(self, model, style, pending_references):
     95        """
     96        Returns any ALTER TABLE statements to add constraints after the fact.
     97        """
     98        from django.db.backends.util import truncate_name
     99
     100        qn = self.ops.quote_name
     101        final_output = []
     102        if self.features.supports_constraints:
     103            opts = model._meta
     104            if model in pending_references:
     105                for rel_class, f in pending_references[model]:
     106                    rel_opts = rel_class._meta
     107                    r_table = rel_opts.db_table
     108                    r_col = f.column
     109                    table = opts.db_table
     110                    col = opts.get_field(f.rel.field_name).column
     111                    # For MySQL, r_name must be unique in the first 64 characters.
     112                    # So we are careful with character usage here.
     113                    r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
     114                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
     115                        (qn(r_table), truncate_name(r_name, self.ops.max_name_length()),
     116                        qn(r_col), qn(table), qn(col),
     117                        self.ops.deferrable_sql()))
     118                del pending_references[model]
     119        return final_output
     120
     121    def many_to_many_sql_for_model(self, model, style):
     122        output = []
     123        for f in model._meta.local_many_to_many:
     124            output.extend(self.sql_for_many_to_many_field(model, f, style))
     125        return output
     126
     127    def sql_for_many_to_many_field(self, model, f, style):
     128        "Return the CREATE TABLE statements for a single m2m field"
     129        from django.db import models
     130        from django.db.backends.util import truncate_name
     131
     132        output = []
     133        if f.creates_table:
     134            opts = model._meta
     135            inline_references = self.features.inline_fk_references
     136            qn = self.ops.quote_name
     137            tablespace = f.db_tablespace or opts.db_tablespace
     138            if tablespace and self.features.supports_tablespaces:
     139                tablespace_sql = ' ' + self.ops.tablespace_sql(tablespace, inline=True)
     140            else:
     141                tablespace_sql = ''
     142            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     143                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
     144            table_output.append('    %s %s %s%s,' %
     145                (style.SQL_FIELD(qn('id')),
     146                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
     147                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'),
     148                tablespace_sql))
     149            if inline_references:
     150                deferred = []
     151                table_output.append('    %s %s %s %s (%s)%s,' %
     152                    (style.SQL_FIELD(qn(f.m2m_column_name())),
     153                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
     154                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
     155                    style.SQL_TABLE(qn(opts.db_table)),
     156                    style.SQL_FIELD(qn(opts.pk.column)),
     157                    self.ops.deferrable_sql()))
     158                table_output.append('    %s %s %s %s (%s)%s,' %
     159                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
     160                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
     161                    style.SQL_KEYWORD('NOT NULL REFERENCES'),
     162                    style.SQL_TABLE(qn(f.rel.to._meta.db_table)),
     163                    style.SQL_FIELD(qn(f.rel.to._meta.pk.column)),
     164                    self.ops.deferrable_sql()))
     165            else:
     166                table_output.append('    %s %s %s,' %
     167                    (style.SQL_FIELD(qn(f.m2m_column_name())),
     168                    style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
     169                    style.SQL_KEYWORD('NOT NULL')))
     170                table_output.append('    %s %s %s,' %
     171                    (style.SQL_FIELD(qn(f.m2m_reverse_name())),
     172                    style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
     173                    style.SQL_KEYWORD('NOT NULL')))
     174                deferred = [
     175                    (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
     176                        opts.pk.column),
     177                    ( f.m2m_db_table(), f.m2m_reverse_name(),
     178                        f.rel.to._meta.db_table, f.rel.to._meta.pk.column)
     179                    ]
     180            table_output.append('    %s (%s, %s)%s' %
     181                (style.SQL_KEYWORD('UNIQUE'),
     182                style.SQL_FIELD(qn(f.m2m_column_name())),
     183                style.SQL_FIELD(qn(f.m2m_reverse_name())),
     184                tablespace_sql))
     185            table_output.append(')')
     186            if opts.db_tablespace and self.features.supports_tablespaces:
     187                # f.db_tablespace is only for indices, so ignore its value here.
     188                table_output.append(self.ops.tablespace_sql(opts.db_tablespace))
     189            table_output.append(';')
     190            output.append('\n'.join(table_output))
     191
     192            for r_table, r_col, table, col in deferred:
     193                r_name = '%s_refs_%s_%x' % (r_col, col,
     194                        abs(hash((r_table, table))))
     195                output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
     196                (qn(r_table),
     197                truncate_name(r_name, self.ops.max_name_length()),
     198                qn(r_col), qn(table), qn(col),
     199                self.ops.deferrable_sql()))
     200
     201            # Add any extra SQL needed to support auto-incrementing PKs
     202            autoinc_sql = self.ops.autoinc_sql(f.m2m_db_table(), 'id')
     203            if autoinc_sql:
     204                for stmt in autoinc_sql:
     205                    output.append(stmt)
     206        return output       
     207
     208    def sql_indexes_for_model(self, model, style):
     209        "Returns the CREATE INDEX SQL statements for a single model"
     210        output = []
     211        for f in model._meta.local_fields:
     212            output.extend(self.sql_indexes_for_field(model, f, style))
     213        return output
     214       
     215    def sql_indexes_for_field(self, model, f, style):
     216        "Return the CREATE INDEX SQL statements for a single model field"
     217        if f.db_index and not f.unique:
     218            qn = self.ops.quote_name
     219            tablespace = f.db_tablespace or model._meta.db_tablespace
     220            if tablespace and self.features.supports_tablespaces:
     221                tablespace_sql = ' ' + self.ops.tablespace_sql(tablespace)
     222            else:
     223                tablespace_sql = ''
     224            output = [style.SQL_KEYWORD('CREATE INDEX') + ' ' +
     225                style.SQL_TABLE(qn('%s_%s' % (model._meta.db_table, f.column))) + ' ' +
     226                style.SQL_KEYWORD('ON') + ' ' +
     227                style.SQL_TABLE(qn(model._meta.db_table)) + ' ' +
     228                "(%s)" % style.SQL_FIELD(qn(f.column)) +
     229                "%s;" % tablespace_sql]
     230        else:
     231            output = []
     232        return output
     233
     234    def sql_destroy_model(self, model, references_to_delete, style):
     235        "Return the DROP TABLE and restraint dropping statements for a single model"
     236        # Drop the table now
     237        qn = self.ops.quote_name
     238        output = ['%s %s;' % (style.SQL_KEYWORD('DROP TABLE'),
     239                              style.SQL_TABLE(qn(model._meta.db_table)))]
     240        if self.features.supports_constraints and model in references_to_delete:
     241            for rel_class, f in references_to_delete[model]:
     242                table = rel_class._meta.db_table
     243                col = f.column
     244                r_table = model._meta.db_table
     245                r_col = model._meta.get_field(f.rel.field_name).column
     246                r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table))))
     247                output.append('%s %s %s %s;' % \
     248                    (style.SQL_KEYWORD('ALTER TABLE'),
     249                    style.SQL_TABLE(qn(table)),
     250                    style.SQL_KEYWORD(self.ops.drop_foreignkey_sql()),
     251                    style.SQL_FIELD(truncate_name(r_name, self.ops.max_name_length()))))
     252            del references_to_delete[model]
     253        if model._meta.has_auto_field:
     254            ds = self.ops.drop_sequence_sql(model._meta.db_table)
     255            if ds:
     256                output.append(ds)
     257        return output
     258   
     259    def sql_destroy_many_to_many(self, model, f, style):
     260        "Returns the DROP TABLE statements for a single m2m field"
     261        qn = self.ops.quote_name
     262        output = []
     263        if f.creates_table:
     264            output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'),
     265                style.SQL_TABLE(qn(f.m2m_db_table()))))
     266            ds = connection.ops.drop_sequence_sql("%s_%s" % (model._meta.db_table, f.column))
     267            if ds:
     268                output.append(ds)
     269        return output
     270       
     271    def create_test_db(self, verbosity=1, autoclobber=False):
     272        """
     273        Creates a test database, prompting the user for confirmation if the
     274        database already exists. Returns the name of the test database created.
     275        """
     276        from django.db import connection
     277        if verbosity >= 1:
     278            print "Creating test database..."
     279           
     280        test_database_name = self._create_test_db(connection, verbosity, autoclobber)
     281
     282        connection.close()
     283        settings.DATABASE_NAME = test_database_name
     284
     285        call_command('syncdb', verbosity=verbosity, interactive=False)
     286
     287        if settings.CACHE_BACKEND.startswith('db://'):
     288            cache_name = settings.CACHE_BACKEND[len('db://'):]
     289            call_command('createcachetable', cache_name)
     290
     291        # Get a cursor (even though we don't need one yet). This has
     292        # the side effect of initializing the test database.
     293        cursor = connection.cursor()
     294
     295        return test_database_name
     296
     297    def _create_test_db(self, connection, verbosity, autoclobber):
     298        suffix = self._creation_suffix()
     299       
     300        if settings.TEST_DATABASE_NAME:
     301            test_database_name = settings.TEST_DATABASE_NAME
     302        else:
     303            test_database_name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
     304
     305        qn = self.ops.quote_name
     306
     307        # Create the test database and connect to it. We need to autocommit
     308        # if the database supports it because PostgreSQL doesn't allow
     309        # CREATE/DROP DATABASE statements within transactions.
     310        cursor = connection.cursor()
     311        self._set_autocommit(connection)
     312        try:
     313            cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
     314        except Exception, e:
     315            sys.stderr.write("Got an error creating the test database: %s\n" % e)
     316            if not autoclobber:
     317                confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
     318            if autoclobber or confirm == 'yes':
     319                try:
     320                    if verbosity >= 1:
     321                        print "Destroying old test database..."
     322                    cursor.execute("DROP DATABASE %s" % qn(test_database_name))
     323                    if verbosity >= 1:
     324                        print "Creating test database..."
     325                    cursor.execute("CREATE DATABASE %s %s" % (qn(test_database_name), suffix))
     326                except Exception, e:
     327                    sys.stderr.write("Got an error recreating the test database: %s\n" % e)
     328                    sys.exit(2)
     329            else:
     330                print "Tests cancelled."
     331                sys.exit(1)
     332               
     333        return test_database_name
     334       
     335    def destroy_test_db(self, old_database_name, verbosity=1):
     336        """
     337        Destroy a test database, prompting the user for confirmation if the
     338        database already exists. Returns the name of the test database created.
     339        """
     340        from django.db import connection
     341        if verbosity >= 1:
     342            print "Destroying test database..."
     343        connection.close()
     344        test_database_name = settings.DATABASE_NAME
     345        settings.DATABASE_NAME = old_database_name
     346       
     347        self._destroy_test_db(connection, test_database_name, verbosity)
     348       
     349    def _destroy_test_db(self, connection, test_database_name, verbosity):
     350        # Remove the test database to clean up after
     351        # ourselves. Connect to the previous database (not the test database)
     352        # to do so, because it's not allowed to delete a database while being
     353        # connected to it.
     354        cursor = connection.cursor()
     355        self._set_autocommit(connection)
     356        time.sleep(1) # To avoid "database is being accessed by other users" errors.
     357        cursor.execute("DROP DATABASE %s" % self.ops.quote_name(test_database_name))
     358        connection.close()
     359
     360    def _set_autocommit(self, connection):
     361        "Make sure a connection is in autocommit mode."
     362        if hasattr(connection.connection, "autocommit"):
     363            if callable(connection.connection.autocommit):
     364                connection.connection.autocommit(True)
     365            else:
     366                connection.connection.autocommit = True
     367        elif hasattr(connection.connection, "set_isolation_level"):
     368            connection.connection.set_isolation_level(0)
     369
     370    def _creation_suffix(self):
     371        "SQL to append to the end of the test table creation statements"
     372        return ''
     373       
  • django/db/backends/dummy/base.py

    diff -r fda9a525ebea django/db/backends/dummy/base.py
    a b  
    88"""
    99
    1010from django.core.exceptions import ImproperlyConfigured
    11 from django.db.backends import BaseDatabaseFeatures, BaseDatabaseOperations
     11from django.db.backends import BaseDatabaseClient
     12from django.db.backends import BaseDatabaseFeatures
     13from django.db.backends import BaseDatabaseIntrospection
     14from django.db.backends import BaseDatabaseOperations
     15from django.db.backends import BaseDatabaseValidation
     16from django.db.backends.creation import BaseDatabaseCreation
    1217
    1318def complain(*args, **kwargs):
    1419    raise ImproperlyConfigured, "You haven't set the DATABASE_ENGINE setting yet."
     
    2530class DatabaseOperations(BaseDatabaseOperations):
    2631    quote_name = complain
    2732
     33class DatabaseClient(BaseDatabaseClient):
     34    runshell = complain
     35   
     36class DatabaseCreation(BaseDatabaseCreation):
     37    pass
     38   
     39class DatabaseIntrospection(BaseDatabaseIntrospection):
     40    get_table_list = complain
     41    get_table_description = complain
     42    get_relations = complain
     43    get_indexes = complain
     44
     45class DatabaseValidation(BaseDatabaseValidation):
     46    pass
     47   
    2848class DatabaseWrapper(object):
    2949    features = BaseDatabaseFeatures()
    3050    ops = DatabaseOperations()
     51    client = DatabaseClient()
     52    creation = DatabaseCreation(ops, features)
     53    introspection = DatabaseIntrospection()
     54    validation = DatabaseValidation()
     55   
    3156    operators = {}
    3257    cursor = complain
    3358    _commit = complain
  • django/db/backends/dummy/client.py

    diff -r fda9a525ebea django/db/backends/dummy/client.py
    a b  
    1 from django.db.backends.dummy.base import complain
    2 
    3 runshell = complain
  • django/db/backends/dummy/creation.py

    diff -r fda9a525ebea django/db/backends/dummy/creation.py
    a b  
    1 DATA_TYPES = {}
  • django/db/backends/dummy/introspection.py

    diff -r fda9a525ebea django/db/backends/dummy/introspection.py
    a b  
    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/mysql/base.py

    diff -r fda9a525ebea django/db/backends/mysql/base.py
    a b  
    44Requires MySQLdb: http://sourceforge.net/projects/mysql-python
    55"""
    66
    7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     7from django.db.backends import BaseDatabaseFeatures
     8from django.db.backends import BaseDatabaseOperations
     9from django.db.backends import BaseDatabaseWrapper
     10from django.db.backends import util
     11from django.db.backends.mysql.client import DatabaseClient
     12from django.db.backends.mysql.creation import DatabaseCreation
     13from django.db.backends.mysql.introspection import DatabaseIntrospection
     14from django.db.backends.mysql.validation import DatabaseValidation
     15
    816try:
    917    import MySQLdb as Database
    1018except ImportError, e:
     
    144152class DatabaseWrapper(BaseDatabaseWrapper):
    145153    features = DatabaseFeatures()
    146154    ops = DatabaseOperations()
     155    client = DatabaseClient()
     156    creation = DatabaseCreation(ops, features)
     157    introspection = DatabaseIntrospection(ops)
     158    validation = DatabaseValidation()
     159   
    147160    operators = {
    148161        'exact': '= BINARY %s',
    149162        'iexact': 'LIKE %s',
  • django/db/backends/mysql/client.py

    diff -r fda9a525ebea django/db/backends/mysql/client.py
    a b  
     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/creation.py

    diff -r fda9a525ebea django/db/backends/mysql/creation.py
    a b  
    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.conf import settings
     2from django.db.backends.creation import BaseDatabaseCreation
     3
     4class DatabaseCreation(BaseDatabaseCreation):
     5    # This dictionary maps Field objects to their associated MySQL column
     6    # types, as strings. Column-type strings can contain format strings; they'll
     7    # be interpolated against the values of Field.__dict__ before being output.
     8    # If a column type is set to None, it won't be included in the output.
     9    data_types = {
     10        'AutoField':         'integer AUTO_INCREMENT',
     11        'BooleanField':      'bool',
     12        'CharField':         'varchar(%(max_length)s)',
     13        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
     14        'DateField':         'date',
     15        'DateTimeField':     'datetime',
     16        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
     17        'FileField':         'varchar(%(max_length)s)',
     18        'FilePathField':     'varchar(%(max_length)s)',
     19        'FloatField':        'double precision',
     20        'IntegerField':      'integer',
     21        'IPAddressField':    'char(15)',
     22        'NullBooleanField':  'bool',
     23        'OneToOneField':     'integer',
     24        'PhoneNumberField':  'varchar(20)',
     25        'PositiveIntegerField': 'integer UNSIGNED',
     26        'PositiveSmallIntegerField': 'smallint UNSIGNED',
     27        'SlugField':         'varchar(%(max_length)s)',
     28        'SmallIntegerField': 'smallint',
     29        'TextField':         'longtext',
     30        'TimeField':         'time',
     31        'USStateField':      'varchar(2)',
     32    }
     33
     34    def _creation_suffix(self):
     35        suffix = []
     36        if settings.TEST_DATABASE_CHARSET:
     37            suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
     38        if settings.TEST_DATABASE_COLLATION:
     39            suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
     40        return ' '.join(suffix)
  • django/db/backends/mysql/introspection.py

    diff -r fda9a525ebea django/db/backends/mysql/introspection.py
    a b  
    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))
     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))])
     49
     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())
     80
     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)
     85
     86        return relations
     87
     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 = {}
    4897        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())
     98            indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
     99        return indexes
    56100
    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)
    61 
    62     return relations
    63 
    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
    76 
    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 }
  • django/db/backends/oracle/base.py

    diff -r fda9a525ebea django/db/backends/oracle/base.py
    a b  
    88import datetime
    99import time
    1010
    11 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     11from django.db.backends import BaseDatabaseFeatures
     12from django.db.backends import BaseDatabaseOperations
     13from django.db.backends import BaseDatabaseWrapper
     14from django.db.backends import BaseDatabaseValidation
     15from django.db.backends import util
    1216from django.db.backends.oracle import query
     17from django.db.backends.oracle.client import DatabaseClient
     18from django.db.backends.oracle.creation import DatabaseCreation
     19from django.db.backends.oracle.introspection import DatabaseIntrospection
    1320from django.utils.encoding import smart_str, force_unicode
    1421
    1522# Oracle takes client-side character set encoding from the environment.
     
    194201        return [first % value, second % value]
    195202
    196203
     204class DatabaseValidation(BaseDatabaseValidation):
     205    pass
    197206
    198207class DatabaseWrapper(BaseDatabaseWrapper):
    199208    features = DatabaseFeatures()
    200209    ops = DatabaseOperations()
     210    client = DatabaseClient()
     211    creation = DatabaseCreation(ops, features)
     212    introspection = DatabaseIntrospection(ops)
     213    validation = DatabaseValidation()
     214   
    201215    operators = {
    202216        'exact': '= %s',
    203217        'iexact': '= UPPER(%s)',
  • django/db/backends/oracle/client.py

    diff -r fda9a525ebea django/db/backends/oracle/client.py
    a b  
     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/creation.py

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

    diff -r fda9a525ebea django/db/backends/oracle/introspection.py
    a b  
    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/postgresql/base.py

    diff -r fda9a525ebea django/db/backends/postgresql/base.py
    a b  
    44Requires psycopg 1: http://initd.org/projects/psycopg1
    55"""
    66
     7from django.db.backends import BaseDatabaseFeatures
     8from django.db.backends import BaseDatabaseValidation
     9from django.db.backends import BaseDatabaseWrapper
     10from django.db.backends import util
     11from django.db.backends.postgresql.client import DatabaseClient
     12from django.db.backends.postgresql.creation import DatabaseCreation
     13from django.db.backends.postgresql.introspection import DatabaseIntrospection
     14from django.db.backends.postgresql.operations import DatabaseOperations
    715from django.utils.encoding import smart_str, smart_unicode
    8 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, util
    9 from django.db.backends.postgresql.operations import DatabaseOperations
     16
    1017try:
    1118    import psycopg as Database
    1219except ImportError, e:
     
    6269class DatabaseFeatures(BaseDatabaseFeatures):
    6370    pass # This backend uses all the defaults.
    6471
     72class DatabaseValidation(BaseDatabaseValidation):
     73    pass
     74
    6575class DatabaseWrapper(BaseDatabaseWrapper):
    6676    features = DatabaseFeatures()
    6777    ops = DatabaseOperations()
     78    client = DatabaseClient()
     79    creation = DatabaseCreation(ops, features)
     80    introspection = DatabaseIntrospection(ops)
     81    validation = DatabaseValidation()
     82
    6883    operators = {
    6984        'exact': '= %s',
    7085        'iexact': 'ILIKE %s',
  • django/db/backends/postgresql/client.py

    diff -r fda9a525ebea django/db/backends/postgresql/client.py
    a b  
     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/creation.py

    diff -r fda9a525ebea django/db/backends/postgresql/creation.py
    a b  
    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.conf import settings
     2from django.db.backends.creation import BaseDatabaseCreation
     3
     4class DatabaseCreation(BaseDatabaseCreation):
     5    # This dictionary maps Field objects to their associated PostgreSQL column
     6    # types, as strings. Column-type strings can contain format strings; they'll
     7    # be interpolated against the values of Field.__dict__ before being output.
     8    # If a column type is set to None, it won't be included in the output.
     9    data_types = {
     10        'AutoField':         'serial',
     11        'BooleanField':      'boolean',
     12        'CharField':         'varchar(%(max_length)s)',
     13        'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
     14        'DateField':         'date',
     15        'DateTimeField':     'timestamp with time zone',
     16        'DecimalField':      'numeric(%(max_digits)s, %(decimal_places)s)',
     17        'FileField':         'varchar(%(max_length)s)',
     18        'FilePathField':     'varchar(%(max_length)s)',
     19        'FloatField':        'double precision',
     20        'IntegerField':      'integer',
     21        'IPAddressField':    'inet',
     22        'NullBooleanField':  'boolean',
     23        'OneToOneField':     'integer',
     24        'PhoneNumberField':  'varchar(20)',
     25        'PositiveIntegerField': 'integer CHECK ("%(column)s" >= 0)',
     26        'PositiveSmallIntegerField': 'smallint CHECK ("%(column)s" >= 0)',
     27        'SlugField':         'varchar(%(max_length)s)',
     28        'SmallIntegerField': 'smallint',
     29        'TextField':         'text',
     30        'TimeField':         'time',
     31        'USStateField':      'varchar(2)',
     32    }
     33
     34    def _creation_suffix(self):
     35        assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
     36        if settings.TEST_DATABASE_CHARSET:
     37            return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
     38        return ''
  • django/db/backends/postgresql/introspection.py

    diff -r fda9a525ebea django/db/backends/postgresql/introspection.py
    a b  
    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_psycopg2/base.py

    diff -r fda9a525ebea django/db/backends/postgresql_psycopg2/base.py
    a b  
    44Requires psycopg 2: http://initd.org/projects/psycopg2
    55"""
    66
    7 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
     7from django.db.backends import BaseDatabaseFeatures
     8from django.db.backends import BaseDatabaseValidation
     9from django.db.backends import BaseDatabaseWrapper
    810from django.db.backends.postgresql.operations import DatabaseOperations as PostgresqlDatabaseOperations
     11from django.db.backends.postgresql.client import DatabaseClient
     12from django.db.backends.postgresql.creation import DatabaseCreation
     13from django.db.backends.postgresql_psycopg2.introspection import DatabaseIntrospection
     14
    915from django.utils.safestring import SafeUnicode
    1016try:
    1117    import psycopg2 as Database
     
    3036        # http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query
    3137        return cursor.query
    3238
     39class DatabaseValidation(BaseDatabaseValidation):
     40    pass
     41
    3342class DatabaseWrapper(BaseDatabaseWrapper):
    3443    features = DatabaseFeatures()
    3544    ops = DatabaseOperations()
     45    client = DatabaseClient()
     46    creation = DatabaseCreation(ops, features)
     47    introspection = DatabaseIntrospection(ops)
     48    validation = DatabaseValidation()
     49
    3650    operators = {
    3751        'exact': '= %s',
    3852        'iexact': 'ILIKE %s',
  • django/db/backends/postgresql_psycopg2/client.py

    diff -r fda9a525ebea django/db/backends/postgresql_psycopg2/client.py
    a b  
    1 from django.db.backends.postgresql.client import *
  • django/db/backends/postgresql_psycopg2/creation.py

    diff -r fda9a525ebea django/db/backends/postgresql_psycopg2/creation.py
    a b  
    1 from django.db.backends.postgresql.creation import *
  • django/db/backends/postgresql_psycopg2/introspection.py

    diff -r fda9a525ebea django/db/backends/postgresql_psycopg2/introspection.py
    a b  
    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/sqlite3/base.py

    diff -r fda9a525ebea django/db/backends/sqlite3/base.py
    a b  
    66Python 2.5 and later use the sqlite3 module in the standard library.
    77"""
    88
    9 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     9from django.db.backends import BaseDatabaseWrapper
     10from django.db.backends import BaseDatabaseFeatures
     11from django.db.backends import BaseDatabaseOperations
     12from django.db.backends import BaseDatabaseValidation
     13from django.db.backends import util
     14from django.db.backends.sqlite3.client import DatabaseClient
     15from django.db.backends.sqlite3.creation import DatabaseCreation
     16from django.db.backends.sqlite3.introspection import DatabaseIntrospection
     17
    1018try:
    1119    try:
    1220        from sqlite3 import dbapi2 as Database
     
    8997        second = '%s-12-31 23:59:59.999999'
    9098        return [first % value, second % value]
    9199
     100class DatabaseValidation(BaseDatabaseValidation):
     101    pass
    92102
    93103class DatabaseWrapper(BaseDatabaseWrapper):
    94104    features = DatabaseFeatures()
    95105    ops = DatabaseOperations()
    96 
     106    client = DatabaseClient()
     107    creation = DatabaseCreation(ops, features)
     108    introspection = DatabaseIntrospection(ops)
     109    validation = DatabaseValidation()
     110   
    97111    # SQLite requires LIKE statements to include an ESCAPE clause if the value
    98112    # being escaped has a percent or underscore in it.
    99113    # See http://www.sqlite.org/lang_expr.html for an explanation.
  • django/db/backends/sqlite3/client.py

    diff -r fda9a525ebea django/db/backends/sqlite3/client.py
    a b  
     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/creation.py

    diff -r fda9a525ebea django/db/backends/sqlite3/creation.py
    a b  
    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 }
     1import os
     2import sys
     3from django.conf import settings
     4from django.db.backends.creation import BaseDatabaseCreation
     5
     6class DatabaseCreation(BaseDatabaseCreation):
     7    # SQLite doesn't actually support most of these types, but it "does the right
     8    # thing" given more verbose field definitions, so leave them as is so that
     9    # schema inspection is more useful.
     10    data_types = {
     11        'AutoField':                    'integer',
     12        'BooleanField':                 'bool',
     13        'CharField':                    'varchar(%(max_length)s)',
     14        'CommaSeparatedIntegerField':   'varchar(%(max_length)s)',
     15        'DateField':                    'date',
     16        'DateTimeField':                'datetime',
     17        'DecimalField':                 'decimal',
     18        'FileField':                    'varchar(%(max_length)s)',
     19        'FilePathField':                'varchar(%(max_length)s)',
     20        'FloatField':                   'real',
     21        'IntegerField':                 'integer',
     22        'IPAddressField':               'char(15)',
     23        'NullBooleanField':             'bool',
     24        'OneToOneField':                'integer',
     25        'PhoneNumberField':             'varchar(20)',
     26        'PositiveIntegerField':         'integer unsigned',
     27        'PositiveSmallIntegerField':    'smallint unsigned',
     28        'SlugField':                    'varchar(%(max_length)s)',
     29        'SmallIntegerField':            'smallint',
     30        'TextField':                    'text',
     31        'TimeField':                    'time',
     32        'USStateField':                 'varchar(2)',
     33    }
     34
     35    def _create_test_db(self, connection, verbosity, autoclobber):
     36        if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
     37            test_database_name = settings.TEST_DATABASE_NAME
     38            # Erase the old test database
     39            if verbosity >= 1:
     40                print "Destroying old test database..."
     41            if os.access(test_database_name, os.F_OK):
     42                if not autoclobber:
     43                    confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % test_database_name)
     44                if autoclobber or confirm == 'yes':
     45                  try:
     46                      if verbosity >= 1:
     47                          print "Destroying old test database..."
     48                      os.remove(test_database_name)
     49                  except Exception, e:
     50                      sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
     51                      sys.exit(2)
     52                else:
     53                    print "Tests cancelled."
     54                    sys.exit(1)
     55            if verbosity >= 1:
     56                print "Creating test database..."
     57        else:
     58            test_database_name = ":memory:"
     59        return test_database_name
     60       
     61    def _destroy_test_db(self, connection, test_database_name, verbosity):
     62        if test_database_name and test_database_name != ":memory:":
     63            # Remove the SQLite database file
     64            os.remove(test_database_name)       
     65                   
     66 No newline at end of file
  • django/db/backends/sqlite3/introspection.py

    diff -r fda9a525ebea django/db/backends/sqlite3/introspection.py
    a b  
    1 from django.db.backends.sqlite3.base import DatabaseOperations
    2 
    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 }
     1from django.db.backends import BaseDatabaseIntrospection
    732
    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/models/fields/__init__.py

    diff -r fda9a525ebea django/db/models/fields/__init__.py
    a b  
    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
     
    145145        # as the TextField Django field type, which means XMLField's
    146146        # get_internal_type() returns 'TextField'.
    147147        #
    148         # But the limitation of the get_internal_type() / DATA_TYPES approach
     148        # But the limitation of the get_internal_type() / data_types approach
    149149        # is that it cannot handle database column types that aren't already
    150150        # mapped to one of the built-in Django field types. In this case, you
    151151        # can implement db_type() instead of get_internal_type() to specify
    152152        # exactly which wacky database column type you want to use.
    153153        data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
    154154        try:
    155             return get_creation_module().DATA_TYPES[self.get_internal_type()] % data
     155            return connection.creation.data_types[self.get_internal_type()] % data
    156156        except KeyError:
    157157            return None
    158158
  • django/test/simple.py

    diff -r fda9a525ebea django/test/simple.py
    a b  
    33from django.db.models import get_app, get_apps
    44from django.test import _doctest as doctest
    55from django.test.utils import setup_test_environment, teardown_test_environment
    6 from django.test.utils import create_test_db, destroy_test_db
    76from django.test.testcases import OutputChecker, DocTestRunner
    87
    98# The module name for tests outside models.py
     
    139138        suite.addTest(test)
    140139
    141140    old_name = settings.DATABASE_NAME
    142     create_test_db(verbosity, autoclobber=not interactive)
     141    from django.db import connection
     142    connection.creation.create_test_db(verbosity, autoclobber=not interactive)
    143143    result = unittest.TextTestRunner(verbosity=verbosity).run(suite)
    144     destroy_test_db(old_name, verbosity)
     144    connection.creation.destroy_test_db(old_name, verbosity)
    145145   
    146146    teardown_test_environment()
    147147   
  • django/test/utils.py

    diff -r fda9a525ebea django/test/utils.py
    a b  
    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
    5 from django.core.management import call_command
    65from django.dispatch import dispatcher
    76from django.test import signals
    87from django.template import Template
    98from django.utils.translation import deactivate
    10 
    11 # The prefix to put on the default database name when creating
    12 # the test database.
    13 TEST_DATABASE_PREFIX = 'test_'
    149
    1510def instrumented_test_render(self, context):
    1611    """
     
    7166
    7267    del mail.outbox
    7368
    74 def _set_autocommit(connection):
    75     "Make sure a connection is in autocommit mode."
    76     if hasattr(connection.connection, "autocommit"):
    77         if callable(connection.connection.autocommit):
    78             connection.connection.autocommit(True)
    79         else:
    80             connection.connection.autocommit = True
    81     elif hasattr(connection.connection, "set_isolation_level"):
    82         connection.connection.set_isolation_level(0)
    83 
    84 def get_mysql_create_suffix():
    85     suffix = []
    86     if settings.TEST_DATABASE_CHARSET:
    87         suffix.append('CHARACTER SET %s' % settings.TEST_DATABASE_CHARSET)
    88     if settings.TEST_DATABASE_COLLATION:
    89         suffix.append('COLLATE %s' % settings.TEST_DATABASE_COLLATION)
    90     return ' '.join(suffix)
    91 
    92 def get_postgresql_create_suffix():
    93     assert settings.TEST_DATABASE_COLLATION is None, "PostgreSQL does not support collation setting at database creation time."
    94     if settings.TEST_DATABASE_CHARSET:
    95         return "WITH ENCODING '%s'" % settings.TEST_DATABASE_CHARSET
    96     return ''
    97 
    98 def create_test_db(verbosity=1, autoclobber=False):
    99     """
    100     Creates a test database, prompting the user for confirmation if the
    101     database already exists. Returns the name of the test database created.
    102     """
    103     # 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)
    107         return
    108 
    109     if verbosity >= 1:
    110         print "Creating test database..."
    111     # If we're using SQLite, it's more convenient to test against an
    112     # in-memory database. Using the TEST_DATABASE_NAME setting you can still choose
    113     # to run on a physical database.
    114     if settings.DATABASE_ENGINE == "sqlite3":
    115         if settings.TEST_DATABASE_NAME and settings.TEST_DATABASE_NAME != ":memory:":
    116             TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
    117             # Erase the old test database
    118             if verbosity >= 1:
    119                 print "Destroying old test database..."
    120             if os.access(TEST_DATABASE_NAME, os.F_OK):
    121                 if not autoclobber:
    122                     confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
    123                 if autoclobber or confirm == 'yes':
    124                   try:
    125                       if verbosity >= 1:
    126                           print "Destroying old test database..."
    127                       os.remove(TEST_DATABASE_NAME)
    128                   except Exception, e:
    129                       sys.stderr.write("Got an error deleting the old test database: %s\n" % e)
    130                       sys.exit(2)
    131                 else:
    132                     print "Tests cancelled."
    133                     sys.exit(1)
    134             if verbosity >= 1:
    135                 print "Creating test database..."
    136         else:
    137             TEST_DATABASE_NAME = ":memory:"
    138     else:
    139         suffix = {
    140             'postgresql': get_postgresql_create_suffix,
    141             'postgresql_psycopg2': get_postgresql_create_suffix,
    142             'mysql': get_mysql_create_suffix,
    143         }.get(settings.DATABASE_ENGINE, lambda: '')()
    144         if settings.TEST_DATABASE_NAME:
    145             TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
    146         else:
    147             TEST_DATABASE_NAME = TEST_DATABASE_PREFIX + settings.DATABASE_NAME
    148 
    149         qn = connection.ops.quote_name
    150 
    151         # Create the test database and connect to it. We need to autocommit
    152         # if the database supports it because PostgreSQL doesn't allow
    153         # CREATE/DROP DATABASE statements within transactions.
    154         cursor = connection.cursor()
    155         _set_autocommit(connection)
    156         try:
    157             cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
    158         except Exception, e:
    159             sys.stderr.write("Got an error creating the test database: %s\n" % e)
    160             if not autoclobber:
    161                 confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
    162             if autoclobber or confirm == 'yes':
    163                 try:
    164                     if verbosity >= 1:
    165                         print "Destroying old test database..."
    166                     cursor.execute("DROP DATABASE %s" % qn(TEST_DATABASE_NAME))
    167                     if verbosity >= 1:
    168                         print "Creating test database..."
    169                     cursor.execute("CREATE DATABASE %s %s" % (qn(TEST_DATABASE_NAME), suffix))
    170                 except Exception, e:
    171                     sys.stderr.write("Got an error recreating the test database: %s\n" % e)
    172                     sys.exit(2)
    173             else:
    174                 print "Tests cancelled."
    175                 sys.exit(1)
    176 
    177     connection.close()
    178     settings.DATABASE_NAME = TEST_DATABASE_NAME
    179 
    180     call_command('syncdb', verbosity=verbosity, interactive=False)
    181 
    182     if settings.CACHE_BACKEND.startswith('db://'):
    183         cache_name = settings.CACHE_BACKEND[len('db://'):]
    184         call_command('createcachetable', cache_name)
    185 
    186     # Get a cursor (even though we don't need one yet). This has
    187     # the side effect of initializing the test database.
    188     cursor = connection.cursor()
    189 
    190     return TEST_DATABASE_NAME
    191 
    192 def destroy_test_db(old_database_name, verbosity=1):
    193     # 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)
    197         return
    198 
    199     if verbosity >= 1:
    200         print "Destroying test database..."
    201     connection.close()
    202     TEST_DATABASE_NAME = settings.DATABASE_NAME
    203     settings.DATABASE_NAME = old_database_name
    204     if settings.DATABASE_ENGINE == "sqlite3":
    205         if TEST_DATABASE_NAME and TEST_DATABASE_NAME != ":memory:":
    206             # Remove the SQLite database file
    207             os.remove(TEST_DATABASE_NAME)
    208     else:
    209         # Remove the test database to clean up after
    210         # ourselves. Connect to the previous database (not the test database)
    211         # to do so, because it's not allowed to delete a database while being
    212         # connected to it.
    213         cursor = connection.cursor()
    214         _set_autocommit(connection)
    215         time.sleep(1) # To avoid "database is being accessed by other users" errors.
    216         cursor.execute("DROP DATABASE %s" % connection.ops.quote_name(TEST_DATABASE_NAME))
    217         connection.close()
  • docs/testing.txt

    diff -r fda9a525ebea docs/testing.txt
    a b  
    10261026    black magic hooks into the template system and restoring normal e-mail
    10271027    services.
    10281028
     1029The creation module of the database backend (``connection.creation``) also
     1030provides some utilities that can be useful during testing.
     1031
    10291032``create_test_db(verbosity=1, autoclobber=False)``
    10301033    Creates a new test database and runs ``syncdb`` against it.
    10311034
Back to Top