Django

Code

Changeset 5519

Show
Ignore:
Timestamp:
06/23/07 09:16:00 (1 year ago)
Author:
mtredinnick
Message:

Merged boulder-oracle-sprint branch (r3965:5512) back into trunk. All
expected tests pass for all databases.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/admin/models.py

    r4265 r5519  
    1010class LogEntryManager(models.Manager): 
    1111    def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''): 
    12         e = self.model(None, None, user_id, content_type_id, object_id, object_repr[:200], action_flag, change_message) 
     12        e = self.model(None, None, user_id, content_type_id, str(object_id), object_repr[:200], action_flag, change_message) 
    1313        e.save() 
    1414 
  • django/trunk/django/core/management.py

    r5514 r5519  
    6060def _get_installed_models(table_list): 
    6161    "Gets a set of all models that are installed, given a list of existing tables" 
    62     from django.db import models 
     62    from django.db import backend, models 
    6363    all_models = [] 
    6464    for app in models.get_apps(): 
    6565        for model in models.get_models(app): 
    6666            all_models.append(model) 
    67     return set([m for m in all_models if m._meta.db_table in table_list]) 
     67    if backend.uses_case_insensitive_names: 
     68        converter = str.upper 
     69    else: 
     70        converter = lambda x: x 
     71    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 
    6872 
    6973def _get_table_list(): 
     
    101105    "Returns a list of the CREATE TABLE SQL statements for the given app." 
    102106    from django.db import get_creation_module, models 
     107 
    103108    data_types = get_creation_module().DATA_TYPES 
    104109 
     
    172177            data_type = f.get_internal_type() 
    173178        col_type = data_types[data_type] 
     179        tablespace = f.db_tablespace or opts.db_tablespace 
    174180        if col_type is not None: 
    175181            # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
     
    177183                style.SQL_COLTYPE(col_type % rel_field.__dict__)] 
    178184            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    179             if f.unique
     185            if f.unique and (not f.primary_key or backend.allows_unique_and_pk)
    180186                field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    181187            if f.primary_key: 
    182188                field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 
     189            if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys: 
     190                # We must specify the index tablespace inline, because we 
     191                # won't be generating a CREATE INDEX statement for this field. 
     192                field_output.append(backend.get_tablespace_sql(tablespace, inline=True)) 
    183193            if f.rel: 
    184194                if f.rel.to in known_models: 
     
    204214    for i, line in enumerate(table_output): # Combine and add commas. 
    205215        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or '')) 
    206     full_statement.append(');') 
     216    full_statement.append(')') 
     217    if opts.db_tablespace and backend.supports_tablespaces: 
     218        full_statement.append(backend.get_tablespace_sql(opts.db_tablespace)) 
     219    full_statement.append(';') 
    207220    final_output.append('\n'.join(full_statement)) 
     221 
     222    if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'): 
     223        # Add any extra SQL needed to support auto-incrementing primary keys 
     224        autoinc_sql = backend.get_autoinc_sql(opts.db_table) 
     225        if autoinc_sql: 
     226            for stmt in autoinc_sql: 
     227                final_output.append(stmt) 
    208228 
    209229    return final_output, pending_references 
     
    214234    """ 
    215235    from django.db import backend, get_creation_module 
     236    from django.db.backends.util import truncate_name 
    216237    data_types = get_creation_module().DATA_TYPES 
    217238 
     
    230251                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 
    231252                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 
    232                     (backend.quote_name(r_table), r_name
     253                    (backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length())
    233254                    backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), 
    234255                    backend.get_deferrable_sql())) 
     
    246267    for f in opts.many_to_many: 
    247268        if not isinstance(f.rel, generic.GenericRel): 
     269            tablespace = f.db_tablespace or opts.db_tablespace 
     270            if tablespace and backend.supports_tablespaces and backend.autoindexes_primary_keys: 
     271                tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace, inline=True) 
     272            else: 
     273                tablespace_sql = '' 
    248274            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 
    249275                style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] 
    250             table_output.append('    %s %s %s,' % \ 
     276            table_output.append('    %s %s %s%s,' % \ 
    251277                (style.SQL_FIELD(backend.quote_name('id')), 
    252278                style.SQL_COLTYPE(data_types['AutoField']), 
    253                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'))) 
     279                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 
     280                tablespace_sql)) 
    254281            table_output.append('    %s %s %s %s (%s)%s,' % \ 
    255282                (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
     
    266293                style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), 
    267294                backend.get_deferrable_sql())) 
    268             table_output.append('    %s (%s, %s)' % \ 
     295            table_output.append('    %s (%s, %s)%s' % \ 
    269296                (style.SQL_KEYWORD('UNIQUE'), 
    270297                style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    271                 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())))) 
    272             table_output.append(');') 
     298                style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 
     299                tablespace_sql)) 
     300            table_output.append(')') 
     301            if opts.db_tablespace and backend.supports_tablespaces: 
     302                # f.db_tablespace is only for indices, so ignore its value here. 
     303                table_output.append(backend.get_tablespace_sql(opts.db_tablespace)) 
     304            table_output.append(';') 
    273305            final_output.append('\n'.join(table_output)) 
     306 
     307            # Add any extra SQL needed to support auto-incrementing PKs 
     308            autoinc_sql = backend.get_autoinc_sql(f.m2m_db_table()) 
     309            if autoinc_sql: 
     310                for stmt in autoinc_sql: 
     311                    final_output.append(stmt) 
     312 
    274313    return final_output 
    275314 
     
    277316    "Returns a list of the DROP TABLE SQL statements for the given app." 
    278317    from django.db import backend, connection, models, get_introspection_module 
     318    from django.db.backends.util import truncate_name 
    279319    introspection = get_introspection_module() 
    280320 
     
    290330    else: 
    291331        table_names = [] 
     332    if backend.uses_case_insensitive_names: 
     333        table_name_converter = str.upper 
     334    else: 
     335        table_name_converter = lambda x: x 
    292336 
    293337    output = [] 
     
    299343    app_models = models.get_models(app) 
    300344    for model in app_models: 
    301         if cursor and model._meta.db_table in table_names: 
     345        if cursor and table_name_converter(model._meta.db_table) in table_names: 
    302346            # The table exists, so it needs to be dropped 
    303347            opts = model._meta 
     
    309353 
    310354    for model in app_models: 
    311         if cursor and model._meta.db_table in table_names: 
     355        if cursor and table_name_converter(model._meta.db_table) in table_names: 
    312356            # Drop the table now 
    313357            output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 
     
    319363                    r_table = model._meta.db_table 
    320364                    r_col = model._meta.get_field(f.rel.field_name).column 
     365                    r_name = '%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))) 
    321366                    output.append('%s %s %s %s;' % \ 
    322367                        (style.SQL_KEYWORD('ALTER TABLE'), 
    323368                        style.SQL_TABLE(backend.quote_name(table)), 
    324369                        style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 
    325                         style.SQL_FIELD(backend.quote_name('%s_refs_%s_%x' % (col, r_col, abs(hash((table, r_table)))))))) 
     370                        style.SQL_FIELD(truncate_name(r_name, backend.get_max_name_length())))) 
    326371                del references_to_delete[model] 
     372            if model._meta.has_auto_field and hasattr(backend, 'get_drop_sequence'): 
     373                output.append(backend.get_drop_sequence(model._meta.db_table)) 
    327374 
    328375    # Output DROP TABLE statements for many-to-many tables. 
     
    330377        opts = model._meta 
    331378        for f in opts.many_to_many: 
    332             if cursor and f.m2m_db_table() in table_names: 
     379            if cursor and table_name_converter(f.m2m_db_table()) in table_names: 
    333380                output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 
    334381                    style.SQL_TABLE(backend.quote_name(f.m2m_db_table())))) 
     382                if hasattr(backend, 'get_drop_sequence'): 
     383                    output.append(backend.get_drop_sequence("%s_%s" % (model._meta.db_table, f.column))) 
     384 
    335385 
    336386    app_label = app_models[0]._meta.app_label 
     
    431481 
    432482    for f in model._meta.fields: 
    433         if f.db_index
     483        if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys)
    434484            unique = f.unique and 'UNIQUE ' or '' 
     485            tablespace = f.db_tablespace or model._meta.db_tablespace 
     486            if tablespace and backend.supports_tablespaces: 
     487                tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace) 
     488            else: 
     489                tablespace_sql = '' 
    435490            output.append( 
    436491                style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 
     
    438493                style.SQL_KEYWORD('ON') + ' ' + \ 
    439494                style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 
    440                 "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) 
     495                "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ 
     496                "%s;" % tablespace_sql 
    441497            ) 
    442498    return output 
     
    462518def syncdb(verbosity=1, interactive=True): 
    463519    "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    464     from django.db import connection, transaction, models, get_creation_module 
     520    from django.db import backend, connection, transaction, models, get_creation_module 
    465521    from django.conf import settings 
    466522 
     
    485541    # so we know what needs to be added. 
    486542    table_list = _get_table_list() 
     543    if backend.uses_case_insensitive_names: 
     544        table_name_converter = str.upper 
     545    else: 
     546        table_name_converter = lambda x: x 
    487547 
    488548    # Get a list of already installed *models* so that references work right. 
     
    499559            if verbosity >= 2: 
    500560                print "Processing %s.%s model" % (app_name, model._meta.object_name) 
    501             if model._meta.db_table in table_list: 
     561            if table_name_converter(model._meta.db_table) in table_list: 
    502562                continue 
    503563            sql, references = _get_sql_model_create(model, seen_models) 
     
    511571            for statement in sql: 
    512572                cursor.execute(statement) 
    513             table_list.append(model._meta.db_table
     573            table_list.append(table_name_converter(model._meta.db_table)
    514574 
    515575    # Create the m2m tables. This must be done after all tables have been created 
     
    830890            indexes = {} 
    831891        for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 
    832             att_name = row[0] 
     892            att_name = row[0].lower() 
    833893            comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 
    834894            extra_params = {}  # Holds Field parameters such as 'db_column'. 
     
    13231383    count = [0,0] 
    13241384    models = set() 
    1325      
     1385 
    13261386    humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' 
    13271387 
     
    14011461                        print "No %s fixture '%s' in %s." % \ 
    14021462                            (format, fixture_name, humanize(fixture_dir)) 
    1403                              
     1463 
    14041464    if count[0] > 0: 
    14051465        sequence_sql = backend.get_sql_sequence_reset(style, models) 
     
    14091469            for line in sequence_sql: 
    14101470                cursor.execute(line) 
    1411              
     1471 
    14121472    transaction.commit() 
    14131473    transaction.leave_transaction_management() 
    1414      
     1474 
    14151475    if count[0] == 0: 
    14161476        if verbosity > 0: 
     
    16271687            parser.print_usage_and_exit() 
    16281688        if action not in NO_SQL_TRANSACTION: 
    1629             print style.SQL_KEYWORD("BEGIN;") 
     1689            from django.db import backend 
     1690            if backend.get_start_transaction_sql(): 
     1691                print style.SQL_KEYWORD(backend.get_start_transaction_sql()) 
    16301692        for mod in mod_list: 
    16311693            if action == 'reset': 
  • django/trunk/django/db/backends/ado_mssql/base.py

    r5076 r5519  
    9090            self.connection = None 
    9191 
     92allows_group_by_ordinal = True 
     93allows_unique_and_pk = True 
     94autoindexes_primary_keys = True 
     95needs_datetime_string_cast = True 
     96needs_upper_for_iops = False 
    9297supports_constraints = True 
     98supports_tablespaces = True 
     99uses_case_insensitive_names = False 
    93100 
    94101def quote_name(name): 
     
    118125        return "Convert(datetime, Convert(varchar(12), %s))" % field_name 
    119126 
     127def get_datetime_cast_sql(): 
     128    return None 
     129 
    120130def get_limit_offset_sql(limit, offset=None): 
    121131    # TODO: This is a guess. Make sure this is correct. 
     
    139149def get_pk_default_value(): 
    140150    return "DEFAULT" 
     151 
     152def get_max_name_length(): 
     153    return None 
     154 
     155def get_start_transaction_sql(): 
     156    return "BEGIN;" 
     157 
     158def get_tablespace_sql(tablespace, inline=False): 
     159    return "ON %s" % quote_name(tablespace) 
     160 
     161def get_autoinc_sql(table): 
     162    return None 
    141163 
    142164def get_sql_flush(style, tables, sequences): 
  • django/trunk/django/db/backends/dummy/base.py

    r5410 r5519  
    3434 
    3535supports_constraints = False 
     36supports_tablespaces = False 
    3637quote_name = complain 
    3738dictfetchone = complain 
  • django/trunk/django/db/backends/mysql/base.py

    r5302 r5519  
    135135        return self.server_version 
    136136 
     137allows_group_by_ordinal = True 
     138allows_unique_and_pk = True 
     139autoindexes_primary_keys = False 
     140needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates 
     141needs_upper_for_iops = False 
    137142supports_constraints = True 
     143supports_tablespaces = False 
     144uses_case_insensitive_names = False 
    138145 
    139146def quote_name(name): 
     
    168175    return sql 
    169176 
     177def get_datetime_cast_sql(): 
     178    return None 
     179 
    170180def get_limit_offset_sql(limit, offset=None): 
    171181    sql = "LIMIT " 
     
    189199    return "DEFAULT" 
    190200 
     201def get_max_name_length(): 
     202    return None; 
     203 
     204def get_start_transaction_sql(): 
     205    return "BEGIN;" 
     206 
     207def get_autoinc_sql(table): 
     208    return None 
     209 
    191210def get_sql_flush(style, tables, sequences): 
    192211    """Return a list of SQL statements required to remove all data from 
    193212    all tables in the database (without actually removing the tables 
    194213    themselves) and put the database in an empty 'initial' state 
    195      
     214 
    196215    """ 
    197216    # NB: The generated SQL below is specific to MySQL 
     
    205224                )  for table in tables] + \ 
    206225              ['SET FOREIGN_KEY_CHECKS = 1;'] 
    207                
     226 
    208227        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 
    209228        # to reset sequence indices 
  • django/trunk/django/db/backends/mysql_old/base.py

    r5313 r5519  
    136136        return self.server_version 
    137137 
     138allows_group_by_ordinal = True 
     139allows_unique_and_pk = True 
     140autoindexes_primary_keys = False 
     141needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates 
     142needs_upper_for_iops = False 
    138143supports_constraints = True 
     144supports_tablespaces = False 
     145uses_case_insensitive_names = False 
    139146 
    140147def quote_name(name): 
     
    169176    return sql 
    170177 
     178def get_datetime_cast_sql(): 
     179    return None 
     180 
    171181def get_limit_offset_sql(limit, offset=None): 
    172182    sql = "LIMIT " 
     
    190200    return "DEFAULT" 
    191201 
     202def get_max_name_length(): 
     203    return None; 
     204 
     205def get_start_transaction_sql(): 
     206    return "BEGIN;" 
     207 
     208def get_autoinc_sql(table): 
     209    return None 
     210 
    192211def get_sql_flush(style, tables, sequences): 
    193212    """Return a list of SQL statements required to remove all data from 
    194213    all tables in the database (without actually removing the tables 
    195214    themselves) and put the database in an empty 'initial' state 
    196      
     215 
    197216    """ 
    198217    # NB: The generated SQL below is specific to MySQL 
     
    206225                )  for table in tables] + \ 
    207226              ['SET FOREIGN_KEY_CHECKS = 1;'] 
    208                
     227 
    209228        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 
    210229        # to reset sequence indices 
  • django/trunk/django/db/backends/oracle/base.py

    r5076 r5519  
    55""" 
    66 
     7from django.conf import settings 
    78from django.db.backends import util 
    89try: 
     
    1112    from django.core.exceptions import ImproperlyConfigured 
    1213    raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e 
     14import datetime 
     15from django.utils.datastructures import SortedDict 
     16 
    1317 
    1418DatabaseError = Database.Error 
     
    3236 
    3337    def cursor(self): 
    34         from django.conf import settings 
    3538        if not self._valid_connection(): 
    3639            if len(settings.DATABASE_HOST.strip()) == 0: 
     
    4245                conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) 
    4346                self.connection = Database.connect(conn_string, **self.options) 
    44         return FormatStylePlaceholderCursor(self.connection) 
     47        cursor = FormatStylePlaceholderCursor(self.connection) 
     48        # default arraysize of 1 is highly sub-optimal 
     49        cursor.arraysize = 100 
     50        # set oracle date to ansi date format 
     51        cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") 
     52        cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") 
     53        if settings.DEBUG: 
     54            return util.CursorDebugWrapper(cursor, self) 
     55        return cursor 
    4556 
    4657    def _commit(self): 
    4758        if self.connection is not None: 
    48             self.connection.commit() 
     59            return self.connection.commit() 
    4960 
    5061    def _rollback(self): 
    5162        if self.connection is not None: 
    52             try: 
    53                 self.connection.rollback() 
    54             except Database.NotSupportedError: 
    55                 pass 
     63            return self.connection.rollback() 
    5664 
    5765    def close(self): 
     
    6068            self.connection = None 
    6169 
     70allows_group_by_ordinal = False 
     71allows_unique_and_pk = False        # Suppress UNIQUE/PK for Oracle (ORA-02259) 
     72autoindexes_primary_keys = True 
     73needs_datetime_string_cast = False 
     74needs_upper_for_iops = True 
    6275supports_constraints = True 
     76supports_tablespaces = True 
     77uses_case_insensitive_names = True 
    6378 
    6479class FormatStylePlaceholderCursor(Database.Cursor): 
     
    6883    you'll need to use "%%s". 
    6984    """ 
     85    def _rewrite_args(self, query, params=None): 
     86        if params is None: 
     87            params = [] 
     88        else: 
     89            # cx_Oracle can't handle unicode parameters, so cast to str for now 
     90            for i, param in enumerate(params): 
     91                if type(param) == unicode: 
     92                    try: 
     93                        params[i] = param.encode('utf-8') 
     94                    except UnicodeError: 
     95                        params[i] = str(param) 
     96        args = [(':arg%d' % i) for i in range(len(params))] 
     97        query = query % tuple(args) 
     98        # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it 
     99        # it does want a trailing ';' but not a trailing '/'.  However, these 
     100        # characters must be included in the original query in case the query 
     101        # is being passed to SQL*Plus. 
     102        if query.endswith(';') or query.endswith('/'): 
     103            query = query[:-1] 
     104        return query, params 
     105 
    70106    def execute(self, query, params=None): 
    71         if params is None: params = [] 
    72         query = self.convert_arguments(query, len(params)) 
     107        query, params = self._rewrite_args(query, params) 
    73108        return Database.Cursor.execute(self, query, params) 
    74109 
    75110    def executemany(self, query, params=None): 
    76         if params is None: params = [] 
    77         query = self.convert_arguments(query, len(params[0])) 
     111        query, params = self._rewrite_args(query, params) 
    78112        return Database.Cursor.executemany(self, query, params) 
    79113 
    80     def convert_arguments(self, query, num_params): 
    81         # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders. 
    82         args = [':arg' for i in range(num_params)] 
    83         return query % tuple(args) 
    84  
    85114def quote_name(name): 
    86     return name 
     115    # SQL92 requires delimited (quoted) names to be case-sensitive.  When 
     116    # not quoted, Oracle has case-insensitive behavior for identifiers, but 
     117    # always defaults to uppercase. 
     118    # We simplify things by making Oracle identifiers always uppercase. 
     119    if not name.startswith('"') and not name.endswith('"'): 
     120        name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length()) 
     121    return name.upper() 
    87122 
    88123dictfetchone = util.dictfetchone 
     
    91126 
    92127def get_last_insert_id(cursor, table_name, pk_name): 
    93     query = "SELECT %s_sq.currval from dual" % table_name 
    94     cursor.execute(query
     128    sq_name = util.truncate_name(table_name, get_max_name_length()-3) 
     129    cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name
    95130    return cursor.fetchone()[0] 
    96131 
    97132def get_date_extract_sql(lookup_type, table_name): 
    98133    # lookup_type is 'year', 'month', 'day' 
    99     # http://www.psoug.org/reference/date_func.html 
     134    # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 
    100135    return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) 
    101136 
    102137def get_date_trunc_sql(lookup_type, field_name): 
    103     return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name) 
     138    # lookup_type is 'year', 'month', 'day' 
     139    # Oracle uses TRUNC() for both dates and numbers. 
     140    # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 
     141    if lookup_type == 'day': 
     142        sql = 'TRUNC(%s)' % (field_name,) 
     143    else: 
     144        sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) 
     145    return sql 
     146 
     147def get_datetime_cast_sql(): 
     148    return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" 
    104149 
    105150def get_limit_offset_sql(limit, offset=None): 
    106151    # Limits and offset are too complicated to be handled here. 
    107     # Instead, they are handled in django/db/query.py. 
    108     pass 
     152    # Instead, they are handled in django/db/backends/oracle/query.py. 
     153    return "" 
    109154 
    110155def get_random_function_sql(): 
     
    118163 
    119164def get_drop_foreignkey_sql(): 
    120     return "DROP FOREIGN KEY
     165    return "DROP CONSTRAINT
    121166 
    122167def get_pk_default_value(): 
    123168    return "DEFAULT" 
     169 
     170def get_max_name_length(): 
     171    return 30 
     172 
     173def get_start_transaction_sql(): 
     174    return None 
     175 
     176def get_tablespace_sql(tablespace, inline=False): 
     177    return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) 
     178 
     179def get_autoinc_sql(table): 
     180    # To simulate auto-incrementing primary keys in Oracle, we have to 
     181    # create a sequence and a trigger. 
     182    sq_name = get_sequence_name(table) 
     183    tr_name = get_trigger_name(table) 
     184    sequence_sql = 'CREATE SEQUENCE %s;' % sq_name 
     185    trigger_sql = """CREATE OR REPLACE TRIGGER %s 
     186  BEFORE INSERT ON %s 
     187  FOR EACH ROW 
     188  WHEN (new.id IS NULL) 
     189    BEGIN 
     190      SELECT %s.nextval INTO :new.id FROM dual; 
     191    END; 
     192    /""" % (tr_name, quote_name(table), sq_name) 
     193    return sequence_sql, trigger_sql 
     194 
     195def get_drop_sequence(table): 
     196    return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) 
     197 
     198def _get_sequence_reset_sql(): 
     199    # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. 
     200    return """ 
     201        DECLARE 
     202            startvalue integer; 
     203            cval integer; 
     204        BEGIN 
     205            LOCK TABLE %(table)s IN SHARE MODE; 
     206            SELECT NVL(MAX(id), 0) INTO startvalue FROM %(table)s; 
     207            SELECT %(sequence)s.nextval INTO cval FROM dual; 
     208            cval := startvalue - cval; 
     209            IF cval != 0 THEN 
     210                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval; 
     211                SELECT %(sequence)s.nextval INTO cval FROM dual; 
     212                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1'; 
     213            END IF; 
     214            COMMIT; 
     215        END; 
     216        /""" 
    124217 
    125218def get_sql_flush(style, tables, sequences): 
     
    128221    themselves) and put the database in an empty 'initial' state 
    129222    """ 
    130     # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
    131     # TODO - SQL not actually tested against Oracle yet! 
    132     # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 
    133     sql = ['%s %s;' % \ 
    134             (style.SQL_KEYWORD('TRUNCATE'), 
    135              style.SQL_FIELD(quote_name(table)) 
    136              )  for table in tables] 
     223    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 
     224    # 'TRUNCATE z;'... style SQL statements 
     225    if tables: 
     226        # Oracle does support TRUNCATE, but it seems to get us into 
     227        # FK referential trouble, whereas DELETE FROM table works. 
     228        sql = ['%s %s %s;' % \ 
     229                (style.SQL_KEYWORD('DELETE'), 
     230                 style.SQL_KEYWORD('FROM'), 
     231                 style.SQL_FIELD(quote_name(table)) 
     232                 ) for table in tables] 
     233        # Since we've just deleted all the rows, running our sequence 
     234        # ALTER code will reset the sequence to 0. 
     235        for sequence_info in sequences: 
     236            table_name = sequence_info['table'] 
     237            seq_name = get_sequence_name(table_name) 
     238            query = _get_sequence_reset_sql() % {'sequence':seq_name, 
     239                                                 'table':quote_name(table_name)} 
     240            sql.append(query) 
     241        return sql 
     242    else: 
     243        return [] 
     244 
     245def get_sequence_name(table): 
     246    name_length = get_max_name_length() - 3 
     247    return '%s_SQ' % util.truncate_name(table, name_length).upper() 
    137248 
    138249def get_sql_sequence_reset(style, model_list): 
    139250    "Returns a list of the SQL statements to reset sequences for the given models." 
    140     # No sequence reset required 
    141     return [] 
     251    from django.db import models 
     252    output = [] 
     253    query = _get_sequence_reset_sql() 
     254    for model in model_list: 
     255        for f in model._meta.fields: 
     256            if isinstance(f, models.AutoField): 
     257                sequence_name = get_sequence_name(model._meta.db_table) 
     258                output.append(query % {'sequence':sequence_name, 
     259                                       'table':model._meta.db_table}) 
     260                break # Only one AutoField is allowed per model, so don't bother continuing. 
     261        for f in model._meta.many_to_many: 
     262            sequence_name = get_sequence_name(f.m2m_db_table()) 
     263            output.append(query % {'sequence':sequence_name, 
     264                                   'table':f.m2m_db_table()}) 
     265    return output 
     266 
     267def get_trigger_name(table): 
     268    name_length = get_max_name_length() - 3 
     269    return '%s_TR' % util.truncate_name(table, name_length).upper() 
     270 
     271def get_query_set_class(DefaultQuerySet): 
     272    "Create a custom QuerySet class for Oracle." 
     273 
     274    from django.db import backend, connection 
     275    from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE, quote_only_if_word 
     276 
     277    class OracleQuerySet(DefaultQuerySet): 
     278 
     279        def iterator(self): 
     280            "Performs the SELECT database lookup of this QuerySet." 
     281 
     282            from django.db.models.query import get_cached_row 
     283 
     284            # self._select is a dictionary, and dictionaries' key order is 
     285            # undefined, so we convert it to a list of tuples. 
     286            extra_select = self._select.items() 
     287 
     288            full_query = None 
     289 
     290            try: 
     291                try: 
     292                    select, sql, params, full_query = self._get_sql_clause(get_full_query=True) 
     293                except TypeError: 
     294                    select, sql, params = self._get_sql_clause() 
     295            except EmptyResultSet: 
     296                raise StopIteration 
     297            if not full_query: 
     298                full_query = "SELECT %s%s\n%s" % \ 
     299                             ((self._distinct and "DISTINCT " or ""), 
     300                              ', '.join(select), sql) 
     301 
     302            cursor = connection.cursor() 
     303            cursor.execute(full_query, params) 
     304 
     305            fill_cache = self._select_related 
     306            fields = self.model._meta.fields 
     307            index_end = len(fields) 
     308 
     309            # so here's the logic; 
     310            # 1. retrieve each row in turn 
     311            # 2. convert NCLOBs 
     312 
     313            while 1: 
     314                rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     315                if not rows: 
     316                    raise StopIteration 
     317                for row in rows: 
     318                    row = self.resolve_columns(row, fields) 
     319                    if fill_cache: 
     320                        obj, index_end = get_cached_row(klass=self.model, row=row, 
     321                                                        index_start=0, max_depth=self._max_related_depth) 
     322                    else: 
     323                        obj = self.model(*row[:index_end]) 
     324                    for i, k in enumerate(extra_select): 
     325                        setattr(obj, k[0], row[index_end+i]) 
     326                    yield obj 
     327 
     328 
     329        def _get_sql_clause(self, get_full_query=False): 
     330            from django.db.models.query import fill_table_cache, \ 
     331                handle_legacy_orderlist, orderfield2column 
     332 
     333            opts = self.model._meta 
     334 
     335            # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 
     336            select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] 
     337            tables = [quote_only_if_word(t) for t in self._tables] 
     338            joins = SortedDict() 
     339            where = self._where[:] 
     340            params = self._params[:] 
     341 
     342            # Convert self._filters into SQL. 
     343            joins2, where2, params2 = self._filters.get_sql(opts) 
     344            joins.update(joins2) 
     345            where.extend(where2) 
     346            params.extend(params2) 
     347 
     348            # Add additional tables and WHERE clauses based on select_related. 
     349            if self._select_related: 
     350                fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 
     351 
     352            # Add any additional SELECTs. 
     353            if self._select: 
     354                select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 
     355 
     356            # Start composing the body of the SQL statement. 
     357            sql = [" FROM", backend.quote_name(opts.db_table)] 
     358 
     359            # Compose the join dictionary into SQL describing the joins. 
     360            if joins: 
     361                sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 
     362                                for (alias, (table, join_type, condition)) in joins.items()])) 
     363 
     364            # Compose the tables clause into SQL. 
     365            if tables: 
     366                sql.append(", " + ", ".join(tables)) 
     367 
     368            # Compose the where clause into SQL. 
     369            if where: 
     370                sql.append(where and "WHERE " + " AND ".join(where)) 
     371 
     372            # ORDER BY clause 
     373            order_by = [] 
     374            if self._order_by is not None: 
     375                ordering_to_use = self._order_by 
     376            else: 
     377                ordering_to_use = opts.ordering 
     378            for f in handle_legacy_orderlist(ordering_to_use): 
     379                if f == '?': # Special case. 
     380                    order_by.append(backend.get_random_function_sql()) 
     381                else: 
     382                    if f.startswith('-'): 
     383                        col_name = f[1:] 
     384                        order = "DESC" 
     385                    else: