Django

Code

OracleBranch: django-oracle-rev5392.diff

File django-oracle-rev5392.diff, 96.1 kB (added by bouldersprinters, 1 year ago)

Patch to add Oracle support to trunk as of trunk rev 5392

  • django/test/utils.py

    old new  
    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 
    44from django.core import management, mail 
    55from django.dispatch import dispatcher 
    66from django.test import signals 
     
    8888    return '' 
    8989 
    9090def create_test_db(verbosity=1, autoclobber=False): 
     91    # If the database backend wants to create the test DB itself, let it 
     92    creation_module = get_creation_module() 
     93    if hasattr(creation_module, "create_test_db"): 
     94        creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 
     95        return 
     96     
    9197    if verbosity >= 1: 
    9298        print "Creating test database..." 
    9399    # If we're using SQLite, it's more convenient to test against an 
     
    142148    cursor = connection.cursor() 
    143149 
    144150def destroy_test_db(old_database_name, verbosity=1): 
     151    # If the database wants to drop the test DB itself, let it 
     152    creation_module = get_creation_module() 
     153    if hasattr(creation_module, "destroy_test_db"): 
     154        creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 
     155        return 
     156     
    145157    # Unless we're using SQLite, remove the test database to clean up after 
    146158    # ourselves. Connect to the previous database (not the test database) 
    147159    # to do so, because it's not allowed to delete a database while being 
  • django/db/models/base.py

    old new  
    9696 
    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. 
    104104        args_len = len(args) 
     
    122122                # Maintain compatibility with existing calls. 
    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: 
    131131                if isinstance(field.rel, ManyToOneRel): 
     
    147147                            try: 
    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: 
    153153                    val = kwargs.pop(field.attname, field.get_default()) 
     
    210210        record_exists = True 
    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: 
    219220                    cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ 
    220221                        (backend.quote_name(self._meta.db_table), 
    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 
    226227        if not pk_set or not record_exists: 
  • django/db/models/options.py

    old new  
    1313 
    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): 
    1919    def __init__(self, meta): 
     
    2727        self.object_name, self.app_label = None, None 
    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 
    3233        self.pk = None 
     
    5960        del self.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) 
    6467            self.ordering = ('_order',) 
     
    7376        # If the db_table wasn't provided, use the app_label + module_name. 
    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): 
    7883        # Insert the given field in the order in which it was created, using 
     
    8893 
    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." 
    97102        to_search = many_to_many and (self.fields + self.many_to_many) or self.fields 
  • django/db/models/fields/__init__.py

    old new  
    7474        core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 
    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 
    8080        self.primary_key = primary_key 
    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 
    8589        self.serialize = serialize 
     
    9195        self.radio_admin = radio_admin 
    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. 
    96101        self.db_index = db_index 
     
    875880        Field.__init__(self, verbose_name, name, **kwargs) 
    876881 
    877882    def get_db_prep_lookup(self, lookup_type, value): 
     883        if settings.DATABASE_ENGINE == 'oracle': 
     884            # Oracle requires a date in order to parse. 
     885            def prep(value): 
     886                if isinstance(value, datetime.time): 
     887                    value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 
     888                return str(value) 
     889        else: 
     890            prep = str 
    878891        if lookup_type == 'range': 
    879             value = [str(v) for v in value] 
     892            value = [prep(v) for v in value] 
    880893        else: 
    881             value = str(value) 
     894            value = prep(value) 
    882895        return Field.get_db_prep_lookup(self, lookup_type, value) 
    883896 
    884897    def pre_save(self, model_instance, add): 
     
    896909            # doesn't support microseconds. 
    897910            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
    898911                value = value.replace(microsecond=0) 
     912            if settings.DATABASE_ENGINE == 'oracle': 
     913                # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 
     914                if isinstance(value, datetime.time): 
     915                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute, 
     916                                              value.second, value.microsecond) 
     917                elif isinstance(value, basestring): 
     918                    value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 
     919            else: 
    899920            value = str(value) 
    900921        return Field.get_db_prep_save(self, value) 
    901922 
  • django/db/models/fields/related.py

    old new  
    335335                    (target_col_name, self.join_table, source_col_name, 
    336336                    target_col_name, ",".join(['%s'] * len(new_ids))), 
    337337                    [self._pk_val] + list(new_ids)) 
    338                 if cursor.rowcount is not None and cursor.rowcount != 0: 
    339                     existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)]) 
    340                 else: 
    341                     existing_ids = set() 
     338                existing_ids = set([row[0] for row in cursor.fetchall()]) 
    342339 
    343340                # Add the ones that aren't there already 
    344341                for obj_id in (new_ids - existing_ids): 
  • django/db/models/query.py

    old new  
    44from django.dispatch import dispatcher 
    55from django.utils.datastructures import SortedDict 
    66from django.contrib.contenttypes import generic 
     7import datetime 
    78import operator 
    89import re 
    910 
     
    7778    else: 
    7879        return backend.quote_name(word) 
    7980 
    80 class QuerySet(object): 
     81class _QuerySet(object): 
    8182    "Represents a lazy database lookup for a set of objects" 
    8283    def __init__(self, model=None): 
    8384        self.model = model 
     
    181182 
    182183        cursor = connection.cursor() 
    183184        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     185 
    184186        fill_cache = self._select_related 
    185         index_end = len(self.model._meta.fields) 
     187        fields = self.model._meta.fields 
     188        index_end = len(fields) 
     189        has_resolve_columns = hasattr(self, 'resolve_columns') 
    186190        while 1: 
    187191            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    188192            if not rows: 
    189193                raise StopIteration 
    190194            for row in rows: 
     195                if has_resolve_columns: 
     196                    row = self.resolve_columns(row, fields) 
    191197                if fill_cache: 
    192198                    obj, index_end = get_cached_row(klass=self.model, row=row, 
    193199                                                    index_start=0, max_depth=self._max_related_depth) 
     
    551557 
    552558        return select, " ".join(sql), params 
    553559 
     560# Use the backend's QuerySet class if it defines one, otherwise use _QuerySet. 
     561if hasattr(backend, 'get_query_set_class'): 
     562    QuerySet = backend.get_query_set_class(_QuerySet) 
     563else: 
     564    QuerySet = _QuerySet 
     565 
    554566class ValuesQuerySet(QuerySet): 
    555567    def __init__(self, *args, **kwargs): 
    556568        super(ValuesQuerySet, self).__init__(*args, **kwargs) 
    557         # select_related isn't supported in values(). 
     569        # select_related and select aren't supported in values(). 
    558570        self._select_related = False 
     571        self._select = {} 
    559572 
    560573    def iterator(self): 
    561574        try: 
     
    565578 
    566579        # self._fields is a list of field names to fetch. 
    567580        if self._fields: 
    568             #columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
    569             if not self._select: 
    570                 columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
    571             else: 
    572                 columns = [] 
    573                 for f in self._fields: 
    574                     if f in [field.name for field in self.model._meta.fields]: 
    575                         columns.append( self.model._meta.get_field(f, many_to_many=False).column ) 
    576                     elif not self._select.has_key( f ): 
    577                         raise FieldDoesNotExist, '%s has no field named %r' % ( self.model._meta.object_name, f ) 
    578  
    579             field_names = self._fields 
     581            fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
    580582        else: # Default to all fields. 
    581             columns = [f.column for f in self.model._meta.fields] 
    582             field_names = [f.attname for f in self.model._meta.fields] 
     583            fields = self.model._meta.fields 
     584        columns = [f.column for f in fields] 
     585        field_names = [f.attname for f in fields] 
    583586 
    584587        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    585  
    586         # Add any additional SELECTs. 
    587         if self._select: 
    588             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 
    589  
    590588        cursor = connection.cursor() 
    591589        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     590 
     591        has_resolve_columns = hasattr(self, 'resolve_columns') 
    592592        while 1: 
    593593            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    594594            if not rows: 
    595595                raise StopIteration 
    596596            for row in rows: 
     597                if has_resolve_columns: 
     598                    row = self.resolve_columns(row, fields) 
    597599                yield dict(zip(field_names, row)) 
    598600 
    599601    def _clone(self, klass=None, **kwargs): 
     
    604606class DateQuerySet(QuerySet): 
    605607    def iterator(self): 
    606608        from django.db.backends.util import typecast_timestamp 
     609        from django.db.models.fields import DateTimeField 
    607610        self._order_by = () # Clear this because it'll mess things up otherwise. 
    608611        if self._field.null: 
    609612            self._where.append('%s.%s IS NOT NULL' % \ 
    610613                (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 
    611  
    612614        try: 
    613615            select, sql, params = self._get_sql_clause() 
    614616        except EmptyResultSet: 
    615617            raise StopIteration 
    616618 
    617         sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 
     619        table_name = backend.quote_name(self.model._meta.db_table) 
     620        field_name = backend.quote_name(self._field.column) 
     621 
     622        if backend.allows_group_by_ordinal: 
     623            group_by = '1' 
     624        else: 
     625            group_by = backend.get_date_trunc_sql(self._kind, 
     626                                                  '%s.%s' % (table_name, field_name)) 
     627 
     628        sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 
    618629            (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 
    619             backend.quote_name(self._field.column))), sql, self._order) 
     630            backend.quote_name(self._field.column))), sql, group_by, self._order) 
    620631        cursor = connection.cursor() 
    621632        cursor.execute(sql, params) 
    622         # We have to manually run typecast_timestamp(str()) on the results, because 
    623         # MySQL doesn't automatically cast the result of date functions as datetime 
    624         # objects -- MySQL returns the values as strings, instead. 
    625         return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] 
    626633 
     634        has_resolve_columns = hasattr(self, 'resolve_columns') 
     635        needs_datetime_string_cast = backend.needs_datetime_string_cast 
     636        dates = [] 
     637        # It would be better to use self._field here instead of DateTimeField(), 
     638        # but in Oracle that will result in a list of datetime.date instead of 
     639        # datetime.datetime. 
     640        fields = [DateTimeField()] 
     641        while 1: 
     642            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     643            if not rows: 
     644                return dates 
     645            for row in rows: 
     646                date = row[0] 
     647                if has_resolve_columns: 
     648                    date = self.resolve_columns([date], fields)[0] 
     649                elif needs_datetime_string_cast: 
     650                    date = typecast_timestamp(str(date)) 
     651                dates.append(date) 
     652 
    627653    def _clone(self, klass=None, **kwargs): 
    628654        c = super(DateQuerySet, self)._clone(klass, **kwargs) 
    629655        c._field = self._field 
     
    730756    if table_prefix.endswith('.'): 
    731757        table_prefix = backend.quote_name(table_prefix[:-1])+'.' 
    732758    field_name = backend.quote_name(field_name) 
     759    if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 
     760        cast_sql = backend.get_datetime_cast_sql() 
     761    else: 
     762        cast_sql = '%s' 
     763    if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 
     764        format = 'UPPER(%s%s) %s' 
     765    else: 
     766        format = '%s%s %s' 
    733767    try: 
    734         return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 
     768        return format % (table_prefix, field_name, 
     769                         backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 
    735770    except KeyError: 
    736771        pass 
    737772    if lookup_type == 'in': 
  • django/db/backends/ado_mssql/base.py

    old new  
    8989            self.connection.close() 
    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): 
    95102    if name.startswith('[') and name.endswith(']'): 
     
    117124    if lookup_type=='day': 
    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. 
    122132    sql = "LIMIT %s" % limit 
     
    139149def get_pk_default_value(): 
    140150    return "DEFAULT" 
    141151 
     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 
     163 
    142164def get_sql_flush(style, tables, sequences): 
    143165    """Return a list of SQL statements required to remove all data from 
    144166    all tables in the database (without actually removing the tables 
  • django/db/backends/postgresql/base.py

    old new  
    8787        global postgres_version 
    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) 
    9393        return cursor 
     
    105105            self.connection.close() 
    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): 
    111118    if name.startswith('"') and name.endswith('"'): 
     
    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) 
    140147 
     148def get_datetime_cast_sql(): 
     149    return None 
     150 
    141151def get_limit_offset_sql(limit, offset=None): 
    142152    sql = "LIMIT %s" % limit 
    143153    if offset and offset != 0: 
     
    149159 
    150160def get_deferrable_sql(): 
    151161    return " DEFERRABLE INITIALLY DEFERRED" 
    152      
     162 
    153163def get_fulltext_search_sql(field_name): 
    154164    raise NotImplementedError 
    155165 
     
    159169def get_pk_default_value(): 
    160170    return "DEFAULT" 
    161171 
     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 
     180 
    162181def get_sql_flush(style, tables, sequences): 
    163182    """Return a list of SQL statements required to remove all data from 
    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: 
    170189            # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to 
     
    175194                 style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) 
    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;' % \ 
    181200                    (style.SQL_KEYWORD('DELETE'), 
     
    238257                style.SQL_KEYWORD('FROM'), 
    239258                style.SQL_TABLE(f.m2m_db_table()))) 
    240259    return output 
    241          
     260 
    242261# Register these custom typecasts, because Django expects dates/times to be 
    243262# in Python's native (standard-library) datetime/time format, whereas psycopg 
    244263# use mx.DateTime by default. 
  • django/db/backends/sqlite3/base.py

    old new  
    107107    def convert_query(self, query, num_params): 
    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): 
    113120    if name.startswith('"') and name.endswith('"'): 
     
    139146    # sqlite doesn't support DATE_TRUNC, so we fake it as above. 
    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 
    144154    if offset and offset != 0: 
     
    160170def get_pk_default_value(): 
    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 
    170189    # Note: The DELETE FROM... SQL generated below works for SQLite databases 
     
    182201    "Returns a list of the SQL statements to reset sequences for the given models." 
    183202    # No sequence reset required 
    184203    return [] 
    185      
     204 
    186205def _sqlite_date_trunc(lookup_type, dt): 
    187206    try: 
    188207        dt = util.typecast_timestamp(dt) 
  • django/db/backends/util.py

    old new  
    11import datetime 
     2import md5 
    23from time import time 
    34 
    45try: 
     
    107108        return None 
    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 # 
    112123################################################################################## 
  • django/db/backends/mysql/base.py

    old new  
    134134            self.server_version = tuple([int(x) for x in m.groups()]) 
    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): 
    140147    if name.startswith("`") and name.endswith("`"): 
     
    167174        sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) 
    168175    return sql 
    169176 
     177def get_datetime_cast_sql(): 
     178    return None 
     179 
    170180def get_limit_offset_sql(limit, offset=None): 
    171181    sql = "LIMIT " 
    172182    if offset and offset != 0: 
     
    188198def get_pk_default_value(): 
    189199    return "DEFAULT" 
    190200 
     201def get_max_name_length(): 
     202    return 64; 
     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 
    198217    # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
     
    204223                 style.SQL_FIELD(quote_name(table)) 
    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 
    210229        sql.extend(["%s %s %s %s %s;" % \ 
  • django/db/backends/oracle/base.py

    old new  
    44Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 
    55""" 
    66 
     7from django.conf import settings 
    78from django.db.backends import util 
    89try: 
    910    import cx_Oracle as Database 
    1011except ImportError, e: 
    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 
    1316 
     17 
    1418DatabaseError = Database.Error 
    1519IntegrityError = Database.IntegrityError 
    1620 
     
    3135        return self.connection is not None 
    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: 
    3740                settings.DATABASE_HOST = 'localhost' 
     
    4144            else: 
    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): 
    5866        if self.connection is not None: 
    5967            self.connection.close() 
    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): 
    6580    """ 
     
    6782    This fixes it -- but note that if you want to use a literal "%s" in a query, 
    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 
    89124dictfetchmany = util.dictfetchmany 
    90125dictfetchall  = util.dictfetchall 
    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 
    104146 
     147def get_datetime_cast_sql(): 
     148    return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')" 
     149 
    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(): 
    111156    return "DBMS_RANDOM.RANDOM" 
     
    117162    raise NotImplementedError 
    118163 
    119164def get_drop_foreignkey_sql(): 
    120     return "DROP FOREIGN KEY
     165    return "DROP CONSTRAINT
    121166 
    122167def get_pk_default_value(): 
    123168    return "DEFAULT" 
    124169 
     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        /""" 
     217 
    125218def get_sql_flush(style, tables, sequences): 
    126219    """Return a list of SQL statements required to remove all data from 
    127220    all tables in the database (without actually removing the tables 
    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'), 
     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'), 
    135231             style.SQL_FIELD(quote_name(table)) 
    136232             )  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: 
     24