Django

Code

Changeset 5519

Show
Ignore:
Timestamp:
06/23/07 09:16:00 (2 years 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: 
     386                        col_name = f 
     387                        order = "ASC" 
     388                    if "." in col_name: 
     389                        table_prefix, col_name = col_name.split('.', 1) 
     390                        table_prefix = backend.quote_name(table_prefix) + '.' 
     391                    else: 
     392                        # Use the database table as a column prefix if it wasn't given, 
     393                        # and if the requested column isn't a custom SELECT. 
     394                        if "." not in col_name and col_name not in (self._select or ()): 
     395                            table_prefix = backend.quote_name(opts.db_table) + '.' 
     396                        else: 
     397                            table_prefix = '' 
     398                    order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order)) 
     399            if order_by: 
     400                sql.append("ORDER BY " + ", ".join(order_by)) 
     401 
     402            # Look for column name collisions in the select elements 
     403            # and fix them with an AS alias.  This allows us to do a 
     404            # SELECT * later in the paging query. 
     405            cols = [clause.split('.')[-1] for clause in select] 
     406            for index, col in enumerate(cols): 
     407                if cols.count(col) > 1: 
     408                    col = '%s%d' % (col.replace('"', ''), index) 
     409                    cols[index] = col 
     410                    select[index] = '%s AS %s' % (select[index], col) 
     411 
     412            # LIMIT and OFFSET clauses 
     413            # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. 
     414            select_clause = ",".join(select) 
     415            distinct = (self._distinct and "DISTINCT " or "") 
     416 
     417            if order_by: 
     418                order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) 
     419            else: 
     420                #Oracle's row_number() function always requires an order-by clause. 
     421                #So we need to define a default order-by, since none was provided. 
     422                order_by_clause = " OVER (ORDER BY %s.%s)" % \ 
     423                    (backend.quote_name(opts.db_table), 
     424                    backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) 
     425            # limit_and_offset_clause 
     426            if self._limit is None: 
     427                assert self._offset is None, "'offset' is not allowed without 'limit'" 
     428 
     429            if self._offset is not None: 
     430                offset = int(self._offset) 
     431            else: 
     432                offset = 0 
     433            if self._limit is not None: 
     434                limit = int(self._limit) 
     435            else: 
     436                limit = None 
     437 
     438            limit_and_offset_clause = '' 
     439            if limit is not None: 
     440                limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) 
     441            elif offset: 
     442                limit_and_offset_clause = "WHERE rn > %s" % (offset) 
     443 
     444            if len(limit_and_offset_clause) > 0: 
     445                fmt = \ 
     446"""SELECT * FROM 
     447  (SELECT %s%s, 
     448          ROW_NUMBER()%s AS rn 
     449   %s) 
     450%s""" 
     451                full_query = fmt % (distinct, select_clause, 
     452                                    order_by_clause, ' '.join(sql).strip(), 
     453                                    limit_and_offset_clause) 
     454            else: 
     455                full_query = None 
     456 
     457            if get_full_query: 
     458                return select, " ".join(sql), params, full_query 
     459            else: 
     460                return select, " ".join(sql), params 
     461 
     462        def resolve_columns(self, row, fields=()): 
     463            from django.db.models.fields import DateField, DateTimeField, \ 
     464                TimeField, BooleanField, NullBooleanField, DecimalField, Field 
     465            values = [] 
     466            for value, field in map(None, row, fields): 
     467                if isinstance(value, Database.LOB): 
     468                    value = value.read() 
     469                # Oracle stores empty strings as null. We need to undo this in 
     470                # order to adhere to the Django convention of using the empty 
     471                # string instead of null, but only if the field accepts the 
     472                # empty string. 
     473                if value is None and isinstance(field, Field) and field.empty_strings_allowed: 
     474                    value = '' 
     475                # Convert 1 or 0 to True or False 
     476                elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)): 
     477                    value = bool(value) 
     478                # Convert floats to decimals 
     479                elif value is not None and isinstance(field, DecimalField): 
     480                    value = util.typecast_decimal(field.format_number(value)) 
     481                # cx_Oracle always returns datetime.datetime objects for 
     482                # DATE and TIMESTAMP columns, but Django wants to see a 
     483                # python datetime.date, .time, or .datetime.  We use the type 
     484                # of the Field to determine which to cast to, but it's not 
     485                # always available. 
     486                # As a workaround, we cast to date if all the time-related 
     487                # values are 0, or to time if the date is 1/1/1900. 
     488                # This could be cleaned a bit by adding a method to the Field 
     489                # classes to normalize values from the database (the to_python 
     490                # method is used for validation and isn't what we want here). 
     491                elif isinstance(value, Database.Timestamp): 
     492                    # In Python 2.3, the cx_Oracle driver returns its own 
     493                    # Timestamp object that we must convert to a datetime class. 
     494                    if not isinstance(value, datetime.datetime): 
     495                        value = datetime.datetime(value.year, value.month, value.day, value.hour, 
     496                                                  value.minute, value.second, value.fsecond) 
     497                    if isinstance(field, DateTimeField): 
     498                        pass  # DateTimeField subclasses DateField so must be checked first. 
     499                    elif isinstance(field, DateField): 
     500                        value = value.date() 
     501                    elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1): 
     502                        value = value.time() 
     503                    elif value.hour == value.minute == value.second == value.microsecond == 0: 
     504                        value = value.date() 
     505                values.append(value) 
     506            return values 
     507 
     508    return OracleQuerySet 
     509 
    142510 
    143511OPERATOR_MAPPING = { 
    144512    'exact': '= %s', 
    145     'iexact': 'LIKE %s', 
    146     'contains': 'LIKE %s'
    147     'icontains': 'LIKE %s'
     513    'iexact': '= UPPER(%s)', 
     514    'contains': "LIKE %s ESCAPE '\\'"
     515    'icontains': "LIKE UPPER(%s) ESCAPE '\\'"
    148516    'gt': '> %s', 
    149517    'gte': '>= %s', 
    150518    'lt': '< %s', 
    151519    'lte': '<= %s', 
    152     'startswith': 'LIKE %s'
    153     'endswith': 'LIKE %s'
    154     'istartswith': 'LIKE %s'
    155     'iendswith': 'LIKE %s'
     520    'startswith': "LIKE %s ESCAPE '\\'"
     521    'endswith': "LIKE %s ESCAPE '\\'"
     522    'istartswith': "LIKE UPPER(%s) ESCAPE '\\'"
     523    'iendswith': "LIKE UPPER(%s) ESCAPE '\\'"
    156524} 
  • django/trunk/django/db/backends/oracle/client.py

    r4265 r5519  
    33 
    44def runshell(): 
    5     args = '' 
    6     args += settings.DATABASE_USER 
     5    dsn = settings.DATABASE_USER 
    76    if settings.DATABASE_PASSWORD: 
    8         args += "/%s" % settings.DATABASE_PASSWORD 
    9     args += "@%s" % settings.DATABASE_NAME 
    10     os.execvp('sqlplus', args) 
     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) 
  • django/trunk/django/db/backends/oracle/creation.py

    r5302 r5519  
     1import sys, time 
     2from 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. 
    18DATA_TYPES = { 
    2     'AutoField':         'number(38)', 
    3     'BooleanField':      'number(1)', 
    4     'CharField':         'varchar2(%(maxlength)s)', 
    5     'CommaSeparatedIntegerField': 'varchar2(%(maxlength)s)', 
    6     'DateField':         'date', 
    7     'DateTimeField':     'date', 
    8     'DecimalField':      'number(%(max_digits)s, %(decimal_places)s)', 
    9     'FileField':         'varchar2(100)', 
    10     'FilePathField':     'varchar2(100)', 
    11     'FloatField':        'double precision', 
    12     'ImageField':        'varchar2(100)', 
    13     'IntegerField':      'integer', 
    14     'IPAddressField':    'char(15)', 
    15     'ManyToManyField':   None, 
    16     'NullBooleanField':  'integer', 
    17     'OneToOneField':     'integer', 
    18     'PhoneNumberField':  'varchar(20)', 
    19     'PositiveIntegerField': 'integer', 
    20     'PositiveSmallIntegerField': 'smallint', 
    21     'SlugField':         'varchar(50)', 
    22     'SmallIntegerField': 'smallint', 
    23     'TextField':         'long', 
    24     'TimeField':         'timestamp', 
    25     'USStateField':      'varchar(2)', 
     9    'AutoField':                    'NUMBER(11)', 
     10    'BooleanField':                 'NUMBER(1) CHECK (%(column)s IN (0,1))', 
     11    'CharField':                    'VARCHAR2(%(maxlength)s)', 
     12    'CommaSeparatedIntegerField':   'VARCHAR2(%(maxlength)s)', 
     13    'DateField':                    'DATE', 
     14    'DateTimeField':                'TIMESTAMP', 
     15    'DecimalField':                 'NUMBER(%(max_digits)s, %(decimal_places)s)', 
     16    'FileField':                    'VARCHAR2(100)', 
     17    'FilePathField':                'VARCHAR2(100)', 
     18    'FloatField':                   'DOUBLE PRECISION', 
     19    'ImageField':                   'VARCHAR2(100)', 
     20    'IntegerField':                 'NUMBER(11)', 
     21    'IPAddressField':               'VARCHAR2(15)', 
     22    'ManyToManyField':              None, 
     23    'NullBooleanField':             'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', 
     24    'OneToOneField':                'NUMBER(11)', 
     25    'PhoneNumberField':             'VARCHAR2(20)', 
     26    'PositiveIntegerField':         'NUMBER(11) CHECK (%(column)s >= 0)', 
     27    'PositiveSmallIntegerField':    'NUMBER(11) CHECK (%(column)s >= 0)', 
     28    'SlugField':                    'VARCHAR2(50)', 
     29    'SmallIntegerField':            'NUMBER(11)', 
     30    'TextField':                    'NCLOB', 
     31    'TimeField':                    'TIMESTAMP', 
     32    'URLField':                     'VARCHAR2(200)', 
     33    'USStateField':                 'CHAR(2)', 
    2634} 
     35 
     36TEST_DATABASE_PREFIX = 'test_' 
     37PASSWORD = 'Im_a_lumberjack' 
     38REMEMBER = {} 
     39 
     40 
     41def create_test_db(settings, connection, backend, verbosity=1, autoclobber=False): 
     42 
     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) 
     48 
     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        } 
     56 
     57    REMEMBER['user'] = settings.DATABASE_USER 
     58    REMEMBER['passwd'] = settings.DATABASE_PASSWORD 
     59 
     60    cursor = connection.cursor() 
     61    if _test_database_create(settings): 
     62        if verbosity >= 1: 
     63            print 'Creating test database...' 
     64        try: 
     65            _create_test_db(cursor, parameters, verbosity) 
     66        except Exception, e: 
     67            sys.stderr.write("Got an error creating the test database: %s\n" % e) 
     68            if not autoclobber: 
     69                confirm = raw_input("It appears the test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME) 
     70            if autoclobber or confirm == 'yes': 
     71                try: 
     72                    if verbosity >= 1: 
     73                        print "Destroying old test database..." 
     74                    _destroy_test_db(cursor, parameters, verbosity) 
     75                    if verbosity >= 1: 
     76                        print "Creating test database..." 
     77                    _create_test_db(cursor, parameters, verbosity) 
     78                except Exception, e: 
     79                    sys.stderr.write("Got an error recreating the test database: %s\n" % e) 
     80                    sys.exit(2) 
     81            else: 
     82                print "Tests cancelled." 
     83                sys.exit(1) 
     84 
     85    if _test_user_create(settings): 
     86        if verbosity >= 1: 
     87            print "Creating test user..." 
     88        try: 
     89            _create_test_user(cursor, parameters, verbosity) 
     90        except Exception, e: 
     91            sys.stderr.write("Got an error creating the test user: %s\n" % e) 
     92            if not autoclobber: 
     93                confirm = raw_input("It appears the test user, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_USER) 
     94            if autoclobber or confirm == 'yes': 
     95                try: 
     96                    if verbosity >= 1: 
     97                        print "Destroying old test user..." 
     98                    _destroy_test_user(cursor, parameters, verbosity) 
     99                    if verbosity >= 1: 
     100                        print "Creating test user..." 
     101                    _create_test_user(cursor, parameters, verbosity) 
     102                except Exception, e: 
     103                    sys.stderr.write("Got an error recreating the test user: %s\n" % e) 
     104                    sys.exit(2) 
     105            else: 
     106                print "Tests cancelled." 
     107                sys.exit(1) 
     108 
     109    connection.close() 
     110    settings.DATABASE_USER = TEST_DATABASE_USER 
     111    settings.DATABASE_PASSWORD = TEST_DATABASE_PASSWD 
     112 
     113    management.syncdb(verbosity, interactive=False) 
     114 
     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() 
     118 
     119 
     120def destroy_test_db(settings, connection, backend, old_database_name, verbosity=1): 
     121    connection.close() 
     122 
     123    TEST_DATABASE_NAME = _test_database_name(settings) 
     124    TEST_DATABASE_USER = _test_database_user(settings) 
     125    TEST_DATABASE_PASSWD = _test_database_passwd(settings) 
     126    TEST_DATABASE_TBLSPACE = _test_database_tblspace(settings) 
     127    TEST_DATABASE_TBLSPACE_TMP = _test_database_tblspace_tmp(settings) 
     128 
     129    settings.DATABASE_NAME = old_database_name 
     130    settings.DATABASE_USER = REMEMBER['user'] 
     131    settings.DATABASE_PASSWORD = REMEMBER['passwd'] 
     132 
     133    parameters = { 
     134        'dbname': TEST_DATABASE_NAME, 
     135        'user': TEST_DATABASE_USER, 
     136        'password': TEST_DATABASE_PASSWD, 
     137        'tblspace': TEST_DATABASE_TBLSPACE, 
     138        'tblspace_temp': TEST_DATABASE_TBLSPACE_TMP, 
     139        } 
     140 
     141    REMEMBER['user'] = settings.DATABASE_USER 
     142    REMEMBER['passwd'] = settings.DATABASE_PASSWORD 
     143 
     144    cursor = connection.cursor() 
     145    time.sleep(1) # To avoid "database is being accessed by other users" errors. 
     146    if _test_user_create(settings): 
     147        if verbosity >= 1: 
     148            print 'Destroying test user...' 
     149        _destroy_test_user(cursor, parameters, verbosity) 
     150    if _test_database_create(settings): 
     151        if verbosity >= 1: 
     152            print 'Destroying test database...' 
     153        _destroy_test_db(cursor, parameters, verbosity) 
     154    connection.close() 
     155 
     156 
     157def _create_test_db(cursor, parameters, verbosity): 
     158    if verbosity >= 2: 
     159        print "_create_test_db(): dbname = %s" % parameters['dbname'] 
     160    statements = [ 
     161        """CREATE TABLESPACE %(tblspace)s 
     162           DATAFILE '%(tblspace)s.dbf' SIZE 20M 
     163           REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M 
     164        """, 
     165        """CREATE TEMPORARY TABLESPACE %(tblspace_temp)s 
     166           TEMPFILE '%(tblspace_temp)s.dbf' SIZE 20M 
     167           REUSE AUTOEXTEND ON NEXT 10M MAXSIZE 100M 
     168        """, 
     169    ] 
     170    _execute_statements(cursor, statements, parameters, verbosity) 
     171 
     172 
     173def _create_test_user(cursor, parameters, verbosity): 
     174    if verbosity >= 2: 
     175        print "_create_test_user(): username = %s" % parameters['user'] 
     176    statements = [ 
     177        """CREATE USER %(user)s 
     178           IDENTIFIED BY %(password)s 
     179           DEFAULT TABLESPACE %(tblspace)s 
     180           TEMPORARY TABLESPACE %(tblspace_temp)s 
     181        """, 
     182        """GRANT CONNECT, RESOURCE TO %(user)s""", 
     183    ] 
     184    _execute_statements(cursor, statements, parameters, verbosity) 
     185 
     186 
     187def _destroy_test_db(cursor, parameters, verbosity): 
     188    if verbosity >= 2: 
     189        print "_destroy_test_db(): dbname=%s" % parameters['dbname'] 
     190    statements = [ 
     191        'DROP TABLESPACE %(tblspace)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', 
     192        'DROP TABLESPACE %(tblspace_temp)s INCLUDING CONTENTS AND DATAFILES CASCADE CONSTRAINTS', 
     193        ] 
     194    _execute_statements(cursor, statements, parameters, verbosity) 
     195 
     196 
     197def _destroy_test_user(cursor, parameters, verbosity): 
     198    if verbosity >= 2: 
     199        print "_destroy_test_user(): user=%s" % parameters['user'] 
     200        print "Be patient.  This can take some time..." 
     201    statements = [ 
     202        'DROP USER %(user)s CASCADE', 
     203    ] 
     204    _execute_statements(cursor, statements, parameters, verbosity) 
     205 
     206 
     207def _execute_statements(cursor, statements, parameters, verbosity): 
     208    for template in statements: 
     209        stmt = template % parameters 
     210        if verbosity >= 2: 
     211            print stmt 
     212        try: 
     213            cursor.execute(stmt) 
     214        except Exception, err: 
     215            sys.stderr.write("Failed (%s)\n" % (err)) 
     216            raise 
     217 
     218 
     219def _test_database_name(settings): 
     220    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 
     221    try: 
     222        if settings.TEST_DATABASE_NAME: 
     223            name = settings.TEST_DATABASE_NAME 
     224    except AttributeError: 
     225        pass 
     226    except: 
     227        raise 
     228    return name 
     229 
     230 
     231def _test_database_create(settings): 
     232    name = True 
     233    try: 
     234        if settings.TEST_DATABASE_CREATE: 
     235            name = True 
     236        else: 
     237            name = False 
     238    except AttributeError: 
     239        pass 
     240    except: 
     241        raise 
     242    return name 
     243 
     244 
     245def _test_user_create(settings): 
     246    name = True 
     247    try: 
     248        if settings.TEST_USER_CREATE: 
     249            name = True 
     250        else: 
     251            name = False 
     252    except AttributeError: 
     253        pass 
     254    except: 
     255        raise 
     256    return name 
     257 
     258 
     259def _test_database_user(settings): 
     260    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 
     261    try: 
     262        if settings.TEST_DATABASE_USER: 
     263            name = settings.TEST_DATABASE_USER 
     264    except AttributeError: 
     265        pass 
     266    except: 
     267        raise 
     268    return name 
     269 
     270 
     271def _test_database_passwd(settings): 
     272    name = PASSWORD 
     273    try: 
     274        if settings.TEST_DATABASE_PASSWD: 
     275            name = settings.TEST_DATABASE_PASSWD 
     276    except AttributeError: 
     277        pass 
     278    except: 
     279        raise 
     280    return name 
     281 
     282 
     283def _test_database_tblspace(settings): 
     284    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME 
     285    try: 
     286        if settings.TEST_DATABASE_TBLSPACE: 
     287            name = settings.TEST_DATABASE_TBLSPACE 
     288    except AttributeError: 
     289        pass 
     290    except: 
     291        raise 
     292    return name 
     293 
     294 
     295def _test_database_tblspace_tmp(settings): 
     296    name = TEST_DATABASE_PREFIX + settings.DATABASE_NAME + '_temp' 
     297    try: 
     298        if settings.TEST_DATABASE_TBLSPACE_TMP: 
     299            name = settings.TEST_DATABASE_TBLSPACE_TMP 
     300    except AttributeError: 
     301        pass 
     302    except: 
     303        raise 
     304    return name 
  • django/trunk/django/db/backends/oracle/introspection.py

    r5302 r5519  
     1from django.db.backends.oracle.base import quote_name 
    12import re 
     3import cx_Oracle 
     4 
    25 
    36foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") 
     
    69    "Returns a list of table names in the current database." 
    710    cursor.execute("SELECT TABLE_NAME FROM USER_TABLES") 
    8     return [row[0] for row in cursor.fetchall()] 
     11    return [row[0].upper() for row in cursor.fetchall()] 
    912 
    1013def get_table_description(cursor, table_name): 
    11     return 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 
    1217 
    1318def _name_to_index(cursor, table_name): 
     
    2328    representing all relationships to the given table. Indexes are 0-based. 
    2429    """ 
    25     raise NotImplementedError 
     30    cursor.execute(""" 
     31SELECT ta.column_id - 1, tb.table_name, tb.column_id - 1 
     32FROM   user_constraints, USER_CONS_COLUMNS ca, USER_CONS_COLUMNS cb, 
     33       user_tab_cols ta, user_tab_cols tb 
     34WHERE  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]) 
     43 
     44    relations = {} 
     45    for row in cursor.fetchall(): 
     46        relations[row[0]] = (row[2], row[1]) 
     47    return relations 
    2648 
    2749def get_indexes(cursor, table_name): 
     
    3254         'unique': boolean representing whether it's a unique index} 
    3355    """ 
    34     raise NotImplementedError 
     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 = """ 
     60WITH 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) 
     72SELECT allcols.column_name, primarycols.primarycol, uniquecols.UNIQUECOL 
     73FROM   (SELECT column_name FROM primarycols UNION SELECT column_name FROM 
     74uniquecols) allcols, 
     75      primarycols, uniquecols 
     76WHERE  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 
    3588 
    36 # Maps type codes to Django Field types. 
     89# Maps type objects to Django Field types. 
    3790DATA_TYPES_REVERSE = { 
    38     16: 'BooleanField', 
    39     21: 'SmallIntegerField', 
    40     23: 'IntegerField', 
    41     25: 'TextField', 
    42     869: 'IPAddressField', 
    43     1043: 'CharField', 
    44     1082: 'DateField', 
    45     1083: 'TimeField', 
    46     1114: 'DateTimeField', 
    47     1184: 'DateTimeField', 
    48     1266: 'TimeField', 
    49     1700: 'DecimalField', 
     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', 
    5098} 
  • django/trunk/django/db/backends/postgresql/base.py

    r5455 r5519  
    8888        if not postgres_version: 
    8989            cursor.execute("SELECT version()") 
    90             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]         
     90            postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    9191        if settings.DEBUG: 
    9292            return util.CursorDebugWrapper(cursor, self) 
     
    106106            self.connection = None 
    107107 
     108allows_group_by_ordinal = True 
     109allows_unique_and_pk = True 
     110autoindexes_primary_keys = True 
     111needs_datetime_string_cast = True 
     112needs_upper_for_iops = False 
    108113supports_constraints = True 
     114supports_tablespaces = False 
     115uses_case_insensitive_names = False 
    109116 
    110117def quote_name(name): 
     
    138145    # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 
    139146    return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 
     147 
     148def get_datetime_cast_sql(): 
     149    return None 
    140150 
    141151def get_limit_offset_sql(limit, offset=None): 
     
    150160def get_deferrable_sql(): 
    151161    return " DEFERRABLE INITIALLY DEFERRED" 
    152      
     162 
    153163def get_fulltext_search_sql(field_name): 
    154164    raise NotImplementedError 
     
    159169def get_pk_default_value(): 
    160170    return "DEFAULT" 
     171 
     172def get_max_name_length(): 
     173    return None 
     174 
     175def get_start_transaction_sql(): 
     176    return "BEGIN;" 
     177 
     178def get_autoinc_sql(table): 
     179    return None 
    161180 
    162181def get_sql_flush(style, tables, sequences): 
     
    164183    all tables in the database (without actually removing the tables 
    165184    themselves) and put the database in an empty 'initial' state 
    166      
    167     """     
     185 
     186    """ 
    168187    if tables: 
    169188        if postgres_version[0] >= 8 and postgres_version[1] >= 1: 
     
    176195            )] 
    177196        else: 
    178             # Older versions of Postgres can't do TRUNCATE in a single call, so they must use  
     197            # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 
    179198            # a simple delete. 
    180199            sql = ['%s %s %s;' % \ 
     
    244263                style.SQL_TABLE(f.m2m_db_table()))) 
    245264    return output 
    246          
     265 
    247266# Register these custom typecasts, because Django expects dates/times to be 
    248267# in Python's native (standard-library) datetime/time format, whereas psycopg 
  • django/trunk/django/db/backends/postgresql_psycopg2/base.py

    r5455 r5519  
    5656        if not postgres_version: 
    5757            cursor.execute("SELECT version()") 
    58             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]         
     58            postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    5959        if settings.DEBUG: 
    6060            return util.CursorDebugWrapper(cursor, self) 
     
    7474            self.connection = None 
    7575 
     76allows_group_by_ordinal = True 
     77allows_unique_and_pk = True 
     78autoindexes_primary_keys = True 
     79needs_datetime_string_cast = False 
     80needs_upper_for_iops = False 
    7681supports_constraints = True 
     82supports_tablespaces = False 
     83uses_case_insensitive_names = True 
    7784 
    7885def quote_name(name): 
     
    98105    # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 
    99106    return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 
     107 
     108def get_datetime_cast_sql(): 
     109    return None 
    100110 
    101111def get_limit_offset_sql(limit, offset=None): 
     
    119129def get_pk_default_value(): 
    120130    return "DEFAULT" 
     131 
     132def get_max_name_length(): 
     133    return None 
     134 
     135def get_start_transaction_sql(): 
     136    return "BEGIN;" 
     137 
     138def get_autoinc_sql(table): 
     139    return None 
    121140 
    122141def get_sql_flush(style, tables, sequences): 
     
    140159                     style.SQL_FIELD(quote_name(table)) 
    141160                     ) for table in tables] 
    142                       
     161 
    143162        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements 
    144163        # to reset sequence indices 
     
    201220                style.SQL_TABLE(f.m2m_db_table()))) 
    202221    return output 
    203          
     222 
    204223OPERATOR_MAPPING = { 
    205224    'exact': '= %s', 
  • django/trunk/django/db/backends/sqlite3/base.py

    r5302 r5519  
    108108        return query % tuple("?" * num_params) 
    109109 
     110allows_group_by_ordinal = True 
     111allows_unique_and_pk = True 
     112autoindexes_primary_keys = True 
     113needs_datetime_string_cast = True 
     114needs_upper_for_iops = False 
    110115supports_constraints = False 
     116supports_tablespaces = False 
     117uses_case_insensitive_names = False 
    111118 
    112119def quote_name(name): 
     
    140147    return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 
    141148 
     149def get_datetime_cast_sql(): 
     150    return None 
     151 
    142152def get_limit_offset_sql(limit, offset=None): 
    143153    sql = "LIMIT %s" % limit 
     
    161171    return "NULL" 
    162172 
     173def get_max_name_length(): 
     174    return None 
     175 
     176def get_start_transaction_sql(): 
     177    return "BEGIN;" 
     178 
     179def get_autoinc_sql(table): 
     180    return None 
     181 
    163182def get_sql_flush(style, tables, sequences): 
    164183    """Return a list of SQL statements required to remove all data from 
    165184    all tables in the database (without actually removing the tables 
    166185    themselves) and put the database in an empty 'initial' state 
    167      
     186 
    168187    """ 
    169188    # NB: The generated SQL below is specific to SQLite 
     
    183202    # No sequence reset required 
    184203    return [] 
    185      
     204 
    186205def _sqlite_date_trunc(lookup_type, dt): 
    187206    try: 
  • django/trunk/django/db/backends/util.py

    r5450 r5519  
    11import datetime 
     2import md5 
    23from time import time 
    34 
     
    108109    return str(d) 
    109110 
     111def truncate_name(name, length=None): 
     112    """Shortens a string to a repeatable mangled version with the given length. 
     113    """ 
     114    if length is None or len(name) <= length: 
     115        return name 
     116 
     117    hash = md5.md5(name).hexdigest()[:4] 
     118 
     119    return '%s%s' % (name[:length-4], hash) 
     120 
    110121################################################################################## 
    111122# Helper functions for dictfetch* for databases that don't natively support them # 
  • django/trunk/django/db/models/base.py

    r5163 r5519  
    9797    def __init__(self, *args, **kwargs): 
    9898        dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
    99          
     99 
    100100        # There is a rather weird disparity here; if kwargs, it's set, then args 
    101         # overrides it. It should be one or the other; don't duplicate the work  
     101        # overrides it. It should be one or the other; don't duplicate the work 
    102102        # The reason for the kwargs check is that standard iterator passes in by 
    103103        # args, and nstantiation for iteration is 33% faster. 
     
    123123                if isinstance(field.rel, ManyToOneRel): 
    124124                    kwargs.pop(field.attname, None) 
    125          
     125 
    126126        # Now we're left with the unprocessed fields that *must* come from 
    127127        # keywords, or default. 
    128          
     128 
    129129        for field in fields_iter: 
    130130            if kwargs: 
     
    148148                                val = getattr(rel_obj, field.rel.get_related_field().attname) 
    149149                            except AttributeError: 
    150                                 raise TypeError("Invalid value: %r should be a %s instance, not a %s" %  
     150                                raise TypeError("Invalid value: %r should be a %s instance, not a %s" % 
    151151                                    (field.name, field.rel.to, type(rel_obj))) 
    152152                else: 
     
    211211        if pk_set: 
    212212            # Determine whether a record with the primary key already exists. 
    213             cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \ 
    214                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) 
     213            cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \ 
     214                (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), 
     215                self._meta.pk.get_db_prep_lookup('exact', pk_val)) 
    215216            # If it does already exist, do an UPDATE. 
    216             if cursor.fetchone()
     217            if cursor.fetchone()[0] > 0
    217218                db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] 
    218219                if db_values: 
     
    221222                        ','.join(['%s=%%s' % backend.quote_name(f.column) for f in non_pks]), 
    222223                        backend.quote_name(self._meta.pk.column)), 
    223                         db_values + [pk_val]
     224                        db_values + self._meta.pk.get_db_prep_lookup('exact', pk_val)
    224225            else: 
    225226                record_exists = False 
  • django/trunk/django/db/models/fields/__init__.py

    r5302 r5519  
    7575        prepopulate_from=None, unique_for_date=None, unique_for_month=None, 
    7676        unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 
    77         help_text='', db_column=None): 
     77        help_text='', db_column=None, db_tablespace=None): 
    7878        self.name = name 
    7979        self.verbose_name = verbose_name 
     
    8181        self.maxlength, self.unique = maxlength, unique 
    8282        self.blank, self.null = blank, null 
     83        # Oracle treats the empty string ('') as null, so coerce the null 
     84        # option whenever '' is a possible value. 
     85        if self.empty_strings_allowed and settings.DATABASE_ENGINE == 'oracle': 
     86            self.null = True 
    8387        self.core, self.rel, self.default = core, rel, default 
    8488        self.editable = editable 
     
    9296        self.help_text = help_text 
    9397        self.db_column = db_column 
     98        self.db_tablespace = db_tablespace 
    9499 
    95100        # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 
     
    202207                return self.default() 
    203208            return self.default 
    204         if not self.empty_strings_allowed or self.null
     209        if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle')
    205210            return None 
    206211        return "" 
     
    807812 
    808813class NullBooleanField(Field): 
     814    empty_strings_allowed = False 
    809815    def __init__(self, *args, **kwargs): 
    810816        kwargs['null'] = True 
     
    876882 
    877883    def get_db_prep_lookup(self, lookup_type, value): 
     884        if settings.DATABASE_ENGINE == 'oracle': 
     885            # Oracle requires a date in order to parse. 
     886            def prep(value): 
     887                if isinstance(value, datetime.time): 
     888                    value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 
     889                return str(value) 
     890        else: 
     891            prep = str 
    878892        if lookup_type == 'range': 
    879             value = [str(v) for v in value] 
    880         else: 
    881             value = str(value) 
     893            value = [prep(v) for v in value] 
     894        else: 
     895            value = prep(value) 
    882896        return Field.get_db_prep_lookup(self, lookup_type, value) 
    883897 
     
    897911            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
    898912                value = value.replace(microsecond=0) 
    899             value = str(value) 
     913            if settings.DATABASE_ENGINE == 'oracle': 
     914                # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 
     915                if isinstance(value, datetime.time): 
     916                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 
     917                                              value.second, value.microsecond) 
     918                elif isinstance(value, basestring): 
     919                    value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 
     920            else: 
     921                value = str(value) 
    900922        return Field.get_db_prep_save(self, value) 
    901923 
  • django/trunk/django/db/models/fields/related.py

    r5514 r5519  
    337337                    target_col_name, ",".join(['%s'] * len(new_ids))), 
    338338                    [self._pk_val] + list(new_ids)) 
    339                 if cursor.rowcount is not None and cursor.rowcount != 0: 
    340                     existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 
    341                 else: 
    342                     existing_ids = set() 
     339                existing_ids = set([row[0] for row in cursor.fetchall()]) 
    343340 
    344341                # Add the ones that aren't there already 
  • django/trunk/django/db/models/options.py

    r5091 r5519  
    1414DEFAULT_NAMES = ('verbose_name', 'db_table', 'ordering', 
    1515                 'unique_together', 'permissions', 'get_latest_by', 
    16                  'order_with_respect_to', 'app_label'
     16                 'order_with_respect_to', 'app_label', 'db_tablespace'
    1717 
    1818class Options(object): 
     
    2828        self.get_latest_by = None 
    2929        self.order_with_respect_to = None 
     30        self.db_tablespace = None 
    3031        self.admin = None 
    3132        self.meta = meta 
     
    6061 
    6162    def _prepare(self, model): 
     63        from django.db import backend 
     64        from django.db.backends.util import truncate_name 
    6265        if self.order_with_respect_to: 
    6366            self.order_with_respect_to = self.get_field(self.order_with_respect_to) 
     
    7477        if not self.db_table: 
    7578            self.db_table = "%s_%s" % (self.app_label, self.module_name) 
     79            self.db_table = truncate_name(self.db_table, 
     80                                          backend.get_max_name_length()) 
    7681 
    7782    def add_field(self, field): 
     
    8994    def __repr__(self): 
    9095        return '<Options for %s>' % self.object_name 
    91          
     96 
    9297    def __str__(self): 
    9398        return "%s.%s" % (self.app_label, self.module_name) 
    94          
     99 
    95100    def get_field(self, name, many_to_many=True): 
    96101        "Returns the requested field by name. Raises FieldDoesNotExist on error." 
  • django/trunk/django/db/models/query.py

    r5514 r5519  
    55from django.utils.datastructures import SortedDict 
    66from django.contrib.contenttypes import generic 
     7import datetime 
    78import operator 
    89import re 
     
    7980        return backend.quote_name(word) 
    8081 
    81 class QuerySet(object): 
     82class _QuerySet(object): 
    8283    "Represents a lazy database lookup for a set of objects" 
    8384    def __init__(self, model=None): 
     
    183184        cursor = connection.cursor() 
    184185        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     186 
    185187        fill_cache = self._select_related 
    186         index_end = len(self.model._meta.fields) 
     188        fields = self.model._meta.fields 
     189        index_end = len(fields) 
     190        has_resolve_columns = hasattr(self, 'resolve_columns') 
    187191        while 1: 
    188192            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     
    190194                raise StopIteration 
    191195            for row in rows: 
     196                if has_resolve_columns: 
     197                    row = self.resolve_columns(row, fields) 
    192198                if fill_cache: 
    193199                    obj, index_end = get_cached_row(klass=self.model, row=row, 
     
    553559        return select, " ".join(sql), params 
    554560 
     561# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. 
     562if hasattr(backend, 'get_query_set_class'): 
     563    QuerySet = backend.get_query_set_class(_QuerySet) 
     564else: 
     565    QuerySet = _QuerySet 
     566 
    555567class ValuesQuerySet(QuerySet): 
    556568    def __init__(self, *args, **kwargs): 
     
    567579        # self._fields is a list of field names to fetch. 
    568580        if self._fields: 
    569             #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
    570581            if not self._select: 
    571                 columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
     582                fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
    572583            else: 
    573                 columns = [] 
     584                fields = [] 
    574585                for f in self._fields: 
    575586                    if f in [field.name for field in self.model._meta.fields]: 
    576                         columns.append( self.model._meta.get_field(f, many_to_many=False).column
     587                        fields.append(self.model._meta.get_field(f, many_to_many=False)
    577588                    elif not self._select.has_key( f ): 
    578589                        raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) 
     
    580591            field_names = self._fields 
    581592        else: # Default to all fields. 
    582             columns = [f.column for f in self.model._meta.fields] 
    583             field_names = [f.attname for f in self.model._meta.fields] 
    584  
     593            fields = self.model._meta.fields 
     594            field_names = [f.attname for f in fields] 
     595 
     596        columns = [f.column for f in fields] 
    585597        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    586  
    587598        # Add any additional SELECTs. 
    588599        if self._select: 
     
    591602        cursor = connection.cursor() 
    592603        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     604 
     605        has_resolve_columns = hasattr(self, 'resolve_columns') 
    593606        while 1: 
    594607            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     
    596609                raise StopIteration 
    597610            for row in rows: 
     611                if has_resolve_columns: 
     612                    row = self.resolve_columns(row, fields) 
    598613                yield dict(zip(field_names, row)) 
    599614 
     
    606621    def iterator(self): 
    607622        from django.db.backends.util import typecast_timestamp 
     623        from django.db.models.fields import DateTimeField 
    608624        self._order_by = () # Clear this because it'll mess things up otherwise. 
    609625        if self._field.null: 
    610626            self._where.append('%s.%s IS NOT NULL' % \ 
    611627                (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 
    612  
    613628        try: 
    614629            select, sql, params = self._get_sql_clause() 
     
    616631            raise StopIteration 
    617632 
    618         sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 
     633        table_name = backend.quote_name(self.model._meta.db_table) 
     634        field_name = backend.quote_name(self._field.column) 
     635 
     636        if backend.allows_group_by_ordinal: 
     637            group_by = '1' 
     638        else: 
     639            group_by = backend.get_date_trunc_sql(self._kind, 
     640                                                  '%s.%s' % (table_name, field_name)) 
     641 
     642        sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 
    619643            (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 
    620             backend.quote_name(self._field.column))), sql, self._order) 
     644            backend.quote_name(self._field.column))), sql, group_by, self._order) 
    621645        cursor = connection.cursor() 
    622646        cursor.execute(sql, params) 
    623         # We have to manually run typecast_timestamp(str()) on the results, because 
    624         # MySQL doesn't automatically cast the result of date functions as datetime 
    625         # objects -- MySQL returns the values as strings, instead. 
    626         return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] 
     647 
     648        has_resolve_columns = hasattr(self, 'resolve_columns') 
     649        needs_datetime_string_cast = backend.needs_datetime_string_cast 
     650        dates = [] 
     651        # It would be better to use self._field here instead of DateTimeField(), 
     652        # but in Oracle that will result in a list of datetime.date instead of 
     653        # datetime.datetime. 
     654        fields = [DateTimeField()] 
     655        while 1: 
     656            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     657            if not rows: 
     658                return dates 
     659            for row in rows: 
     660                date = row[0] 
     661                if has_resolve_columns: 
     662                    date = self.resolve_columns([date], fields)[0] 
     663                elif needs_datetime_string_cast: 
     664                    date = typecast_timestamp(str(date)) 
     665                dates.append(date) 
    627666 
    628667    def _clone(self, klass=None, **kwargs): 
     
    732771        table_prefix = backend.quote_name(table_prefix[:-1])+'.' 
    733772    field_name = backend.quote_name(field_name) 
     773    if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 
     774        cast_sql = backend.get_datetime_cast_sql() 
     775    else: 
     776        cast_sql = '%s' 
     777    if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 
     778        format = 'UPPER(%s%s) %s' 
     779    else: 
     780        format = '%s%s %s' 
    734781    try: 
    735         return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 
     782        return format % (table_prefix, field_name, 
     783                         backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 
    736784    except KeyError: 
    737785        pass 
  • django/trunk/django/newforms/widgets.py

    r5514 r5519  
    99 
    1010from itertools import chain 
    11  
    1211from django.utils.datastructures import MultiValueDict 
    1312from django.utils.html import escape 
    1413from django.utils.translation import gettext 
    1514from django.utils.encoding import StrAndUnicode, smart_unicode 
    16  
    1715from util import flatatt 
    1816 
  • django/trunk/django/test/utils.py

    r5511 r5519  
    11import sys, time 
    22from django.conf import settings 
    3 from django.db import connection, transaction, backend 
     3from django.db import connection, backend, get_creation_module 
     4from django.core import management, mail 
    45from django.core import management, mail 
    56from django.dispatch import dispatcher 
     
    8990 
    9091def create_test_db(verbosity=1, autoclobber=False): 
     92    # If the database backend wants to create the test DB itself, let it 
     93    creation_module = get_creation_module() 
     94    if hasattr(creation_module, "create_test_db"): 
     95        creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 
     96        return 
     97     
    9198    if verbosity >= 1: 
    9299        print "Creating test database..." 
     
    143150 
    144151def destroy_test_db(old_database_name, verbosity=1): 
     152    # If the database wants to drop the test DB itself, let it 
     153    creation_module = get_creation_module() 
     154    if hasattr(creation_module, "destroy_test_db"): 
     155        creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 
     156        return 
     157     
    145158    # Unless we're using SQLite, remove the test database to clean up after 
    146159    # ourselves. Connect to the previous database (not the test database) 
  • django/trunk/docs/faq.txt

    r5064 r5519  
    302302If you want to use Django with a database, which is probably the case, you'll 
    303303also need a database engine. PostgreSQL_ is recommended, because we're 
    304 PostgreSQL fans, and MySQL_ and `SQLite 3`_ are also supported. 
     304PostgreSQL fans, and MySQL_, `SQLite 3`_, and Oracle_ are also supported. 
    305305 
    306306.. _Python: http://www.python.org/ 
     
    311311.. _MySQL: http://www.mysql.com/ 
    312312.. _`SQLite 3`: http://www.sqlite.org/ 
     313.. _Oracle: http://www.oracle.com/ 
    313314 
    314315Do I lose anything by using Python 2.3 versus newer Python versions, such as Python 2.5? 
  • django/trunk/docs/install.txt

    r5503 r5519  
    6565* If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. 
    6666 
     67* If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher. 
     68 
    6769.. _PostgreSQL: http://www.postgresql.org/ 
    6870.. _MySQL: http://www.mysql.com/ 
     
    7476.. _pysqlite: http://initd.org/tracker/pysqlite 
    7577.. _MySQL backend: ../databases/ 
     78.. _cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 
    7679 
    7780Remove any old versions of Django 
  • django/trunk/docs/model-api.txt

    r5505 r5519  
    493493string, not ``NULL``. 
    494494 
     495.. note:: 
     496    Due to database limitations, when using the Oracle backend the 
     497    ``null=True`` option will be coerced for string-based fields that can 
     498    blank, and the value ``NULL`` will be stored to denote the empty string. 
     499 
    495500``blank`` 
    496501~~~~~~~~~ 
     
    586591If ``True``, ``django-admin.py sqlindexes`` will output a ``CREATE INDEX`` 
    587592statement for this field. 
     593 
     594``db_tablespace`` 
     595~~~~~~~~~~~~~~~~~ 
     596 
     597If this field is indexed, the name of the database tablespace to use for the 
     598index. The default is the ``db_tablespace`` of the model, if any. If the 
     599backend doesn't support tablespaces, this option is ignored. 
    588600 
    589601``default`` 
     
    9961008that aren't allowed in Python variable names -- notably, the hyphen -- 
    9971009that's OK. Django quotes column and table names behind the scenes. 
     1010 
     1011``db_tablespace`` 
     1012----------------- 
     1013 
     1014The name of the database tablespace to use for the model. If the backend 
     1015doesn't support tablespaces, this option is ignored. 
    9981016 
    9991017``get_latest_by`` 
  • django/trunk/docs/settings.txt

    r5413 r5519  
    245245Default: ``''`` (Empty string) 
    246246 
    247 Which database backend to use. Either ``'postgresql_psycopg2'``,  
    248 ``'postgresql'``, ``'mysql'``,  ``'mysql_old'``, ``'sqlite3'`` or 
    249 ``'ado_mssql'``. 
     247The database backend to use. Either ``'postgresql_psycopg2'``, 
     248``'postgresql'``, ``'mysql'``,  ``'mysql_old'``, ``'sqlite3'``, 
     249``'oracle'``, or ``'ado_mssql'``. 
    250250 
    251251DATABASE_HOST 
  • django/trunk/tests/regressiontests/datatypes/models.py

    r5416 r5519  
    55 
    66from django.db import models 
     7from django.conf import settings 
    78 
    89class Donut(models.Model): 
     
    3233>>> d.has_sprinkles 
    3334>>> d.has_sprinkles = True 
    34 >>> d.has_sprinkles 
     35>>> d.has_sprinkles == True 
    3536True 
    3637>>> d.save() 
     
    3839>>> d2 
    3940<Donut: Apple Fritter> 
    40 >>> d2.is_frosted 
    41 Fals
    42 >>> d2.has_sprinkles 
     41>>> d2.is_frosted == False 
     42Tru
     43>>> d2.has_sprinkles == True 
    4344True 
    4445 
  • django/trunk/tests/regressiontests/serializers_regress/tests.py

    r5453 r5519  
    1616from django.db import transaction 
    1717from django.core import management 
     18from django.conf import settings 
    1819 
    1920from models import * 
     
    117118    (data_obj, 40, EmailData, "hovercraft@example.com"), 
    118119    (data_obj, 41, EmailData, None), 
     120    (data_obj, 42, EmailData, ""), 
    119121    (data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'), 
    120122    (data_obj, 51, FileData, None), 
     123    (data_obj, 52, FileData, ""), 
    121124    (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"), 
    122125    (data_obj, 61, FilePathData, None), 
     126    (data_obj, 62, FilePathData, ""), 
    123127    (data_obj, 70, DecimalData, decimal.Decimal('12.345')), 
    124128    (data_obj, 71, DecimalData, decimal.Decimal('-12.345')), 
     
    147151    (data_obj, 140, SlugData, "this-is-a-slug"), 
    148152    (data_obj, 141, SlugData, None), 
     153    (data_obj, 142, SlugData, ""), 
    149154    (data_obj, 150, SmallData, 12), 
    150155    (data_obj, 151, SmallData, -12), 
     
    161166    (data_obj, 180, USStateData, "MA"), 
    162167    (data_obj, 181, USStateData, None), 
     168    (data_obj, 182, USStateData, ""), 
    163169    (data_obj, 190, XMLData, "<foo></foo>"), 
    164170    (data_obj, 191, XMLData, None), 
     171    (data_obj, 192, XMLData, ""), 
    165172 
    166173    (generic_obj, 200, GenericData, ['Generic Object 1', 'tag1', 'tag2']), 
     
    242249] 
    243250 
     251# Because Oracle treats the empty string as NULL, Oracle is expected to fail 
     252# when field.empty_strings_allowed is True and the value is None; skip these 
     253# tests. 
     254if settings.DATABASE_ENGINE == 'oracle': 
     255    test_data = [data for data in test_data 
     256                 if not (data[0] == data_obj and 
     257                         data[2]._meta.get_field('data').empty_strings_allowed and 
     258                         data[3] is None)] 
     259 
    244260# Dynamically create serializer tests to ensure that all 
    245261# registered serializers are automatically tested.