Django

Code

OracleBranch: django-oracle-rev5036.diff

File django-oracle-rev5036.diff, 87.4 kB (added by ian.g.kelly@gmail.com, 1 year ago)

Diff of the Oracle branch at revision 5036

  • django.oracle/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 
    55from django.dispatch import dispatcher 
    66from django.test import signals 
     
    4444        connection.connection.set_isolation_level(0) 
    4545 
    4646def create_test_db(verbosity=1, autoclobber=False): 
     47    # If the database backend wants to create the test DB itself, let it 
     48    creation_module = get_creation_module() 
     49    if hasattr(creation_module, "create_test_db"): 
     50        creation_module.create_test_db(settings, connection, backend, verbosity, autoclobber) 
     51        return 
     52     
    4753    if verbosity >= 1: 
    4854        print "Creating test database..." 
    4955    # If we're using SQLite, it's more convenient to test against an 
     
    9298    cursor = connection.cursor() 
    9399 
    94100def destroy_test_db(old_database_name, verbosity=1): 
     101    # If the database wants to drop the test DB itself, let it 
     102    creation_module = get_creation_module() 
     103    if hasattr(creation_module, "destroy_test_db"): 
     104        creation_module.destroy_test_db(settings, connection, backend, old_database_name, verbosity) 
     105        return 
     106     
    95107    # Unless we're using SQLite, remove the test database to clean up after 
    96108    # ourselves. Connect to the previous database (not the test database) 
    97109    # to do so, because it's not allowed to delete a database while being 
  • django.oracle/django/db/models/base.py

    old new  
    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" % \ 
     213            cursor.execute("SELECT COUNT(*) FROM %s WHERE %s=%%s" % \ 
    214214                (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val]) 
    215215            # If it does already exist, do an UPDATE. 
    216             if cursor.fetchone()
     216            if cursor.fetchone()[0] > 0
    217217                db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks] 
    218218                if db_values: 
    219219                    cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % \ 
  • django.oracle/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.oracle/django/db/models/fields/__init__.py

    old new  
    7070        core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True, 
    7171        prepopulate_from=None, unique_for_date=None, unique_for_month=None, 
    7272        unique_for_year=None, validator_list=None, choices=None, radio_admin=None, 
    73         help_text='', db_column=None): 
     73        help_text='', db_column=None, db_tablespace=None): 
    7474        self.name = name 
    7575        self.verbose_name = verbose_name 
    7676        self.primary_key = primary_key 
     
    8787        self.radio_admin = radio_admin 
    8888        self.help_text = help_text 
    8989        self.db_column = db_column 
     90        self.db_tablespace = db_tablespace 
    9091 
    9192        # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 
    9293        self.db_index = db_index 
     
    161162 
    162163    def get_db_prep_save(self, value): 
    163164        "Returns field's value prepared for saving into a database." 
     165        # Oracle treats empty strings ('') the same as NULLs. 
     166        # To get around this wart, we need to change it to something else... 
     167        if settings.DATABASE_ENGINE == 'oracle' and value == '': 
     168            value = ' ' 
    164169        return value 
    165170 
    166171    def get_db_prep_lookup(self, lookup_type, value): 
     
    528533    def get_db_prep_save(self, value): 
    529534        # Casts dates into string format for entry into database. 
    530535        if value is not None: 
    531             # MySQL will throw a warning if microseconds are given, because it 
    532             # doesn't support microseconds. 
    533             if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
     536            # MySQL/Oracle will throw a warning if microseconds are given, because 
     537            # neither database supports microseconds. 
     538            if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'): 
    534539                value = value.replace(microsecond=0) 
    535             value = str(value) 
    536540        return Field.get_db_prep_save(self, value) 
    537541 
    538542    def get_db_prep_lookup(self, lookup_type, value): 
     543        # Oracle will throw an error if microseconds are given, because it 
     544        # doesn't support microseconds. 
     545        if settings.DATABASE_ENGINE == 'oracle' and hasattr(value, 'microsecond'): 
     546            value = value.replace(microsecond=0) 
    539547        if lookup_type == 'range': 
    540548            value = [str(v) for v in value] 
    541549        else: 
     
    808816        Field.__init__(self, verbose_name, name, **kwargs) 
    809817 
    810818    def get_db_prep_lookup(self, lookup_type, value): 
     819        if settings.DATABASE_ENGINE == 'oracle': 
     820            # Oracle requires a date in order to parse. 
     821            def prep(value): 
     822                if isinstance(value, datetime.time): 
     823                    value = datetime.datetime.combine(datetime.date(1900, 1, 1), value) 
     824                return str(value) 
     825        else: 
     826            prep = str 
    811827        if lookup_type == 'range': 
    812             value = [str(v) for v in value] 
     828            value = [prep(v) for v in value] 
    813829        else: 
    814             value = str(value) 
     830            value = prep(value) 
    815831        return Field.get_db_prep_lookup(self, lookup_type, value) 
    816832 
    817833    def pre_save(self, model_instance, add): 
     
    827843        if value is not None: 
    828844            # MySQL will throw a warning if microseconds are given, because it 
    829845            # doesn't support microseconds. 
    830             if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'): 
     846            if settings.DATABASE_ENGINE in ('mysql', 'oracle') and hasattr(value, 'microsecond'): 
    831847                value = value.replace(microsecond=0) 
    832             value = str(value) 
     848            if settings.DATABASE_ENGINE == 'oracle': 
     849                # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field. 
     850                if isinstance(value, datetime.time): 
     851                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute, value.second) 
     852                elif isinstance(value, basestring): 
     853                    value = datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6])) 
     854            else: 
     855                value = str(value) 
    833856        return Field.get_db_prep_save(self, value) 
    834857 
    835858    def get_manipulator_field_objs(self): 
  • django.oracle/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.oracle/django/db/models/query.py

    old new  
    44from django.db.models import signals 
    55from django.dispatch import dispatcher 
    66from django.utils.datastructures import SortedDict 
     7from django.conf import settings 
     8import datetime 
    79import operator 
    810import re 
    911 
     
    7779    else: 
    7880        return backend.quote_name(word) 
    7981 
    80 class QuerySet(object): 
     82class _QuerySet(object): 
    8183    "Represents a lazy database lookup for a set of objects" 
    8284    def __init__(self, model=None): 
    8385        self.model = model 
     
    181183 
    182184        cursor = connection.cursor() 
    183185        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     186 
    184187        fill_cache = self._select_related 
    185         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') 
    186191        while 1: 
    187192            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    188193            if not rows: 
    189194                raise StopIteration 
    190195            for row in rows: 
     196                if has_resolve_columns: 
     197                    row = self.resolve_columns(row, fields) 
    191198                if fill_cache: 
    192199                    obj, index_end = get_cached_row(klass=self.model, row=row, 
    193200                                                    index_start=0, max_depth=self._max_related_depth) 
     
    551558 
    552559        return select, " ".join(sql), params 
    553560 
     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 
    554567class ValuesQuerySet(QuerySet): 
    555568    def __init__(self, *args, **kwargs): 
    556569        super(ValuesQuerySet, self).__init__(*args, **kwargs) 
     
    566579 
    567580        # self._fields is a list of field names to fetch. 
    568581        if self._fields: 
    569             columns = [self.model._meta.get_field(f, many_to_many=False).column for f in self._fields] 
    570             field_names = self._fields 
     582            fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
    571583        else: # Default to all fields. 
    572             columns = [f.column for f in self.model._meta.fields] 
    573             field_names = [f.attname for f in self.model._meta.fields] 
     584            fields = self.model._meta.fields 
     585        columns = [f.column for f in fields] 
     586        field_names = [f.attname for f in fields] 
    574587 
    575588        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] 
    576589        cursor = connection.cursor() 
    577590        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
     591 
     592        has_resolve_columns = hasattr(self, 'resolve_columns') 
    578593        while 1: 
    579594            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    580595            if not rows: 
    581596                raise StopIteration 
    582597            for row in rows: 
     598                if has_resolve_columns: 
     599                    row = self.resolve_columns(row, fields) 
    583600                yield dict(zip(field_names, row)) 
    584601 
    585602    def _clone(self, klass=None, **kwargs): 
     
    590607class DateQuerySet(QuerySet): 
    591608    def iterator(self): 
    592609        from django.db.backends.util import typecast_timestamp 
     610        from django.db.models.fields import DateTimeField 
    593611        self._order_by = () # Clear this because it'll mess things up otherwise. 
    594612        if self._field.null: 
    595613            self._where.append('%s.%s IS NOT NULL' % \ 
    596614                (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) 
    597  
    598615        try: 
    599616            select, sql, params = self._get_sql_clause() 
    600617        except EmptyResultSet: 
    601618            raise StopIteration 
    602619 
    603         sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ 
     620        table_name = backend.quote_name(self.model._meta.db_table) 
     621        field_name = backend.quote_name(self._field.column) 
     622 
     623        if backend.allows_group_by_ordinal: 
     624            group_by = '1' 
     625        else: 
     626            group_by = backend.get_date_trunc_sql(self._kind, 
     627                                                  '%s.%s' % (table_name, field_name)) 
     628 
     629        sql = 'SELECT %s %s GROUP BY %s ORDER BY 1 %s' % \ 
    604630            (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), 
    605             backend.quote_name(self._field.column))), sql, self._order) 
     631            backend.quote_name(self._field.column))), sql, group_by, self._order) 
    606632        cursor = connection.cursor() 
    607633        cursor.execute(sql, params) 
    608         # We have to manually run typecast_timestamp(str()) on the results, because 
    609         # MySQL doesn't automatically cast the result of date functions as datetime 
    610         # objects -- MySQL returns the values as strings, instead. 
    611         return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] 
    612634 
     635        has_resolve_columns = hasattr(self, 'resolve_columns') 
     636        needs_datetime_string_cast = backend.needs_datetime_string_cast 
     637        dates = [] 
     638        # It would be better to use self._field here instead of DateTimeField(), 
     639        # but in Oracle that will result in a list of datetime.date instead of 
     640        # datetime.datetime. 
     641        fields = [DateTimeField()] 
     642        while 1: 
     643            rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     644            if not rows: 
     645                return dates 
     646            for row in rows: 
     647                date = row[0] 
     648                if has_resolve_columns: 
     649                    date = self.resolve_columns([date], fields)[0] 
     650                elif needs_datetime_string_cast: 
     651                    date = typecast_timestamp(str(date)) 
     652                dates.append(date) 
     653 
    613654    def _clone(self, klass=None, **kwargs): 
    614655        c = super(DateQuerySet, self)._clone(klass, **kwargs) 
    615656        c._field = self._field 
     
    716757    if table_prefix.endswith('.'): 
    717758        table_prefix = backend.quote_name(table_prefix[:-1])+'.' 
    718759    field_name = backend.quote_name(field_name) 
     760    if type(value) == datetime.datetime and backend.get_datetime_cast_sql(): 
     761        cast_sql = backend.get_datetime_cast_sql() 
     762    else: 
     763        cast_sql = '%s' 
     764    if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith') and backend.needs_upper_for_iops: 
     765        format = 'UPPER(%s%s) %s' 
     766    else: 
     767        format = '%s%s %s' 
    719768    try: 
    720         return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s')) 
     769        return format % (table_prefix, field_name, 
     770                         backend.OPERATOR_MAPPING[lookup_type] % cast_sql) 
    721771    except KeyError: 
    722772        pass 
    723773    if lookup_type == 'in': 
  • django.oracle/django/db/backends/ado_mssql/base.py

    old new  
    8888            self.connection.close() 
    8989            self.connection = None 
    9090 
     91allows_group_by_ordinal = True 
     92allows_unique_and_pk = True 
     93autoindexes_primary_keys = True 
     94needs_datetime_string_cast = True 
     95needs_upper_for_iops = False 
    9196supports_constraints = True 
     97supports_tablespaces = True 
     98uses_case_insensitive_names = False 
    9299 
    93100def quote_name(name): 
    94101    if name.startswith('[') and name.endswith(']'): 
     
    116123    if lookup_type=='day': 
    117124        return "Convert(datetime, Convert(varchar(12), %s))" % field_name 
    118125 
     126def get_datetime_cast_sql(): 
     127    return None 
     128 
    119129def get_limit_offset_sql(limit, offset=None): 
    120130    # TODO: This is a guess. Make sure this is correct. 
    121131    sql = "LIMIT %s" % limit 
     
    138148def get_pk_default_value(): 
    139149    return "DEFAULT" 
    140150 
     151def get_max_name_length(): 
     152    return None 
     153 
     154def get_start_transaction_sql(): 
     155    return "BEGIN;" 
     156 
     157def get_tablespace_sql(tablespace, inline=False): 
     158    return "ON %s" % quote_name(tablespace) 
     159 
     160def get_autoinc_sql(table): 
     161    return None 
     162 
    141163def get_sql_flush(style, tables, sequences): 
    142164    """Return a list of SQL statements required to remove all data from 
    143165    all tables in the database (without actually removing the tables 
  • django.oracle/django/db/backends/postgresql/base.py

    old new  
    8686        global postgres_version 
    8787        if not postgres_version: 
    8888            cursor.execute("SELECT version()") 
    89             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]         
     89            postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    9090        if settings.DEBUG: 
    9191            return util.CursorDebugWrapper(cursor, self) 
    9292        return cursor 
     
    104104            self.connection.close() 
    105105            self.connection = None 
    106106 
     107allows_group_by_ordinal = True 
     108allows_unique_and_pk = True 
     109autoindexes_primary_keys = True 
     110needs_datetime_string_cast = True 
     111needs_upper_for_iops = False 
    107112supports_constraints = True 
     113supports_tablespaces = False 
     114uses_case_insensitive_names = False 
    108115 
    109116def quote_name(name): 
    110117    if name.startswith('"') and name.endswith('"'): 
     
    137144    # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 
    138145    return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 
    139146 
     147def get_datetime_cast_sql(): 
     148    return None 
     149 
    140150def get_limit_offset_sql(limit, offset=None): 
    141151    sql = "LIMIT %s" % limit 
    142152    if offset and offset != 0: 
     
    148158 
    149159def get_deferrable_sql(): 
    150160    return " DEFERRABLE INITIALLY DEFERRED" 
    151      
     161 
    152162def get_fulltext_search_sql(field_name): 
    153163    raise NotImplementedError 
    154164 
     
    158168def get_pk_default_value(): 
    159169    return "DEFAULT" 
    160170 
     171def get_max_name_length(): 
     172    return None 
     173 
     174def get_start_transaction_sql(): 
     175    return "BEGIN;" 
     176 
     177def get_autoinc_sql(table): 
     178    return None 
     179 
    161180def get_sql_flush(style, tables, sequences): 
    162181    """Return a list of SQL statements required to remove all data from 
    163182    all tables in the database (without actually removing the tables 
    164183    themselves) and put the database in an empty 'initial' state 
    165      
    166     """     
     184 
     185    """ 
    167186    if tables: 
    168187        if postgres_version[0] >= 8 and postgres_version[1] >= 1: 
    169188            # Postgres 8.1+ can do 'TRUNCATE x, y, z...;'. In fact, it *has to* in order to be able to 
     
    174193                 style.SQL_FIELD(', '.join([quote_name(table) for table in tables])) 
    175194            )] 
    176195        else: 
    177             # Older versions of Postgres can't do TRUNCATE in a single call, so they must use  
     196            # Older versions of Postgres can't do TRUNCATE in a single call, so they must use 
    178197            # a simple delete. 
    179198            sql = ['%s %s %s;' % \ 
    180199                    (style.SQL_KEYWORD('DELETE'), 
     
    237256                style.SQL_KEYWORD('FROM'), 
    238257                style.SQL_TABLE(f.m2m_db_table()))) 
    239258    return output 
    240          
     259 
    241260# Register these custom typecasts, because Django expects dates/times to be 
    242261# in Python's native (standard-library) datetime/time format, whereas psycopg 
    243262# use mx.DateTime by default. 
  • django.oracle/django/db/backends/sqlite3/base.py

    old new  
    9999    def convert_query(self, query, num_params): 
    100100        return query % tuple("?" * num_params) 
    101101 
     102allows_group_by_ordinal = True 
     103allows_unique_and_pk = True 
     104autoindexes_primary_keys = True 
     105needs_datetime_string_cast = True 
     106needs_upper_for_iops = False 
    102107supports_constraints = False 
     108supports_tablespaces = False 
     109uses_case_insensitive_names = False 
    103110 
    104111def quote_name(name): 
    105112    if name.startswith('"') and name.endswith('"'): 
     
    131138    # sqlite doesn't support DATE_TRUNC, so we fake it as above. 
    132139    return 'django_date_trunc("%s", %s)' % (lookup_type.lower(), field_name) 
    133140 
     141def get_datetime_cast_sql(): 
     142    return None 
     143 
    134144def get_limit_offset_sql(limit, offset=None): 
    135145    sql = "LIMIT %s" % limit 
    136146    if offset and offset != 0: 
     
    152162def get_pk_default_value(): 
    153163    return "NULL" 
    154164 
     165def get_max_name_length(): 
     166    return None 
     167 
     168def get_start_transaction_sql(): 
     169    return "BEGIN;" 
     170 
     171def get_autoinc_sql(table): 
     172    return None 
     173 
    155174def get_sql_flush(style, tables, sequences): 
    156175    """Return a list of SQL statements required to remove all data from 
    157176    all tables in the database (without actually removing the tables 
    158177    themselves) and put the database in an empty 'initial' state 
    159      
     178 
    160179    """ 
    161180    # NB: The generated SQL below is specific to SQLite 
    162181    # Note: The DELETE FROM... SQL generated below works for SQLite databases 
     
    174193    "Returns a list of the SQL statements to reset sequences for the given models." 
    175194    # No sequence reset required 
    176195    return [] 
    177      
     196 
    178197def _sqlite_date_trunc(lookup_type, dt): 
    179198    try: 
    180199        dt = util.typecast_timestamp(dt) 
  • django.oracle/django/db/backends/util.py

    old new  
    11import datetime 
     2import md5 
    23from time import time 
    34 
    45class CursorDebugWrapper(object): 
     
    9293def rev_typecast_boolean(obj, d): 
    9394    return obj and '1' or '0' 
    9495 
     96def truncate_name(name, length=None): 
     97    """Shortens a string to a repeatable mangled version with the given length. 
     98    """ 
     99    if length is None or len(name) <= length: 
     100        return name 
     101 
     102    hash = md5.md5(name).hexdigest()[:4] 
     103 
     104    return '%s%s' % (name[:length-4], hash) 
     105 
    95106################################################################################## 
    96107# Helper functions for dictfetch* for databases that don't natively support them # 
    97108################################################################################## 
  • django.oracle/django/db/backends/mysql/base.py

    old new  
    1515# lexicographic ordering in this check because then (1, 2, 1, 'gamma') 
    1616# inadvertently passes the version test. 
    1717version = Database.version_info 
    18 if (version < (1,2,1) or (version[:3] == (1, 2, 1) and  
     18if (version < (1,2,1) or (version[:3] == (1, 2, 1) and 
    1919        (len(version) < 5 or version[3] != 'final' or version[4] < 2))): 
    2020    raise ImportError, "MySQLdb-1.2.1p2 or newer is required; you have %s" % Database.__version__ 
    2121 
     
    131131            self.server_version = tuple([int(x) for x in m.groups()]) 
    132132        return self.server_version 
    133133 
     134allows_group_by_ordinal = True 
     135allows_unique_and_pk = True 
     136autoindexes_primary_keys = False 
     137needs_datetime_string_cast = True     # MySQLdb requires a typecast for dates 
     138needs_upper_for_iops = False 
    134139supports_constraints = True 
     140supports_tablespaces = False 
     141uses_case_insensitive_names = False 
    135142 
    136143def quote_name(name): 
    137144    if name.startswith("`") and name.endswith("`"): 
     
    164171        sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str) 
    165172    return sql 
    166173 
     174def get_datetime_cast_sql(): 
     175    return None 
     176 
    167177def get_limit_offset_sql(limit, offset=None): 
    168178    sql = "LIMIT " 
    169179    if offset and offset != 0: 
     
    185195def get_pk_default_value(): 
    186196    return "DEFAULT" 
    187197 
     198def get_max_name_length(): 
     199    return 64; 
     200 
     201def get_start_transaction_sql(): 
     202    return "BEGIN;" 
     203 
     204def get_autoinc_sql(table): 
     205    return None 
     206 
    188207def get_sql_flush(style, tables, sequences): 
    189208    """Return a list of SQL statements required to remove all data from 
    190209    all tables in the database (without actually removing the tables 
    191210    themselves) and put the database in an empty 'initial' state 
    192      
     211 
    193212    """ 
    194213    # NB: The generated SQL below is specific to MySQL 
    195214    # 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
     
    201220                 style.SQL_FIELD(quote_name(table)) 
    202221                )  for table in tables] + \ 
    203222              ['SET FOREIGN_KEY_CHECKS = 1;'] 
    204                
     223 
    205224        # 'ALTER TABLE table AUTO_INCREMENT = 1;'... style SQL statements 
    206225        # to reset sequence indices 
    207226        sql.extend(["%s %s %s %s %s;" % \ 
  • django.oracle/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 
    1519 
    1620try: 
     
    3034        return self.connection is not None 
    3135 
    3236    def cursor(self): 
    33         from django.conf import settings 
    3437        if not self._valid_connection(): 
    3538            if len(settings.DATABASE_HOST.strip()) == 0: 
    3639                settings.DATABASE_HOST = 'localhost' 
     
    4043            else: 
    4144                conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME) 
    4245                self.connection = Database.connect(conn_string, **self.options) 
    43         return FormatStylePlaceholderCursor(self.connection) 
     46        cursor = FormatStylePlaceholderCursor(self.connection) 
     47        # default arraysize of 1 is highly sub-optimal 
     48        cursor.arraysize = 100 
     49        # set oracle date to ansi date format 
     50        cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'") 
     51        cursor.execute("ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'") 
     52        if settings.DEBUG: 
     53            return util.CursorDebugWrapper(cursor, self) 
     54        return cursor 
    4455 
    4556    def _commit(self): 
    4657        if self.connection is not None: 
    47             self.connection.commit() 
     58            return self.connection.commit() 
    4859 
    4960    def _rollback(self): 
    5061        if self.connection is not None: 
    51             try: 
    52                 self.connection.rollback() 
    53             except Database.NotSupportedError: 
    54                 pass 
     62            return self.connection.rollback() 
    5563 
    5664    def close(self): 
    5765        if self.connection is not None: 
    5866            self.connection.close() 
    5967            self.connection = None 
    6068 
     69allows_group_by_ordinal = False 
     70allows_unique_and_pk = False        # Suppress UNIQUE/PK for Oracle (ORA-02259) 
     71autoindexes_primary_keys = True 
     72needs_datetime_string_cast = False 
     73needs_upper_for_iops = True 
    6174supports_constraints = True 
     75supports_tablespaces = True 
     76uses_case_insensitive_names = True 
    6277 
    6378class FormatStylePlaceholderCursor(Database.Cursor): 
    6479    """ 
     
    6681    This fixes it -- but note that if you want to use a literal "%s" in a query, 
    6782    you'll need to use "%%s". 
    6883    """ 
     84    def _rewrite_args(self, query, params=None): 
     85        if params is None: 
     86            params = [] 
     87        else: 
     88            # cx_Oracle can't handle unicode parameters, so cast to str for now 
     89            for i, param in enumerate(params): 
     90                if type(param) == unicode: 
     91                    try: 
     92                        params[i] = param.encode('utf-8') 
     93                    except UnicodeError: 
     94                        params[i] = str(param) 
     95        args = [(':arg%d' % i) for i in range(len(params))] 
     96        query = query % tuple(args) 
     97        # cx_Oracle cannot execute a query with the closing ';' 
     98        if query.endswith(';'): 
     99            query = query[:-1] 
     100        return query, params 
     101 
    69102    def execute(self, query, params=None): 
    70         if params is None: params = [] 
    71         query = self.convert_arguments(query, len(params)) 
     103        query, params = self._rewrite_args(query, params) 
    72104        return Database.Cursor.execute(self, query, params) 
    73105 
    74106    def executemany(self, query, params=None): 
    75         if params is None: params = [] 
    76         query = self.convert_arguments(query, len(params[0])) 
     107        query, params = self._rewrite_args(query, params) 
    77108        return Database.Cursor.executemany(self, query, params) 
    78109 
    79     def convert_arguments(self, query, num_params): 
    80         # replace occurances of "%s" with ":arg" - Oracle requires colons for parameter placeholders. 
    81         args = [':arg' for i in range(num_params)] 
    82         return query % tuple(args) 
    83  
    84110def quote_name(name): 
    85     return name 
     111    # SQL92 requires delimited (quoted) names to be case-sensitive.  When 
     112    # not quoted, Oracle has case-insensitive behavior for identifiers, but 
     113    # always defaults to uppercase. 
     114    # We simplify things by making Oracle identifiers always uppercase. 
     115    if not name.startswith('"') and not name.endswith('"'): 
     116        name = '"%s"' % util.truncate_name(name.upper(), get_max_name_length()) 
     117    return name.upper() 
    86118 
    87119dictfetchone = util.dictfetchone 
    88120dictfetchmany = util.dictfetchmany 
    89121dictfetchall  = util.dictfetchall 
    90122 
    91123def get_last_insert_id(cursor, table_name, pk_name): 
    92     query = "SELECT %s_sq.currval from dual" % table_name 
    93     cursor.execute(query
     124    sq_name = util.truncate_name(table_name, get_max_name_length()-3) 
     125    cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name
    94126    return cursor.fetchone()[0] 
    95127 
    96128def get_date_extract_sql(lookup_type, table_name): 
    97129    # lookup_type is 'year', 'month', 'day' 
    98     # http://www.psoug.org/reference/date_func.html 
     130    # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163 
    99131    return "EXTRACT(%s FROM %s)" % (lookup_type, table_name) 
    100132 
    101133def get_date_trunc_sql(lookup_type, field_name): 
    102     return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name) 
     134    # lookup_type is 'year', 'month', 'day' 
     135    # Oracle uses TRUNC() for both dates and numbers. 
     136    # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151 
     137    if lookup_type == 'day': 
     138        sql = 'TRUNC(%s)' % (field_name,) 
     139    else: 
     140        sql = "TRUNC(%s, '%s')" % (field_name, lookup_type) 
     141    return sql 
    103142 
     143def get_datetime_cast_sql(): 
     144    return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS')" 
     145 
    104146def get_limit_offset_sql(limit, offset=None): 
    105147    # Limits and offset are too complicated to be handled here. 
    106     # Instead, they are handled in django/db/query.py. 
    107     pass 
     148    # Instead, they are handled in django/db/backends/oracle/query.py. 
     149    return "" 
    108150 
    109151def get_random_function_sql(): 
    110152    return "DBMS_RANDOM.RANDOM" 
     
    116158    raise NotImplementedError 
    117159 
    118160def get_drop_foreignkey_sql(): 
    119     return "DROP FOREIGN KEY
     161    return "DROP CONSTRAINT
    120162 
    121163def get_pk_default_value(): 
    122164    return "DEFAULT" 
    123165 
     166def get_max_name_length(): 
     167    return 30 
     168 
     169def get_start_transaction_sql(): 
     170    return None 
     171 
     172def get_tablespace_sql(tablespace, inline=False): 
     173    return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), quote_name(tablespace)) 
     174 
     175def get_autoinc_sql(table): 
     176    # To simulate auto-incrementing primary keys in Oracle, we have to 
     177    # create a sequence and a trigger. 
     178    sq_name = get_sequence_name(table) 
     179    tr_name = get_trigger_name(table) 
     180    sequence_sql = 'CREATE SEQUENCE %s;' % sq_name 
     181    trigger_sql = """CREATE OR REPLACE TRIGGER %s 
     182  BEFORE INSERT ON %s 
     183  FOR EACH ROW 
     184  WHEN (new.id IS NULL) 
     185    BEGIN 
     186      SELECT %s.nextval INTO :new.id FROM dual; 
     187    END;\n""" % (tr_name, quote_name(table), sq_name) 
     188    return sequence_sql, trigger_sql 
     189 
     190def get_drop_sequence(table): 
     191    return "DROP SEQUENCE %s;" % quote_name(get_sequence_name(table)) 
     192 
     193def _get_sequence_reset_sql(): 
     194    # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc. 
     195    return """ 
     196        DECLARE 
     197            startvalue integer; 
     198            cval integer; 
     199        BEGIN 
     200            LOCK TABLE %(table)s IN SHARE MODE; 
     201            SELECT NVL(MAX(id), 0) INTO startvalue FROM %(table)s; 
     202            SELECT %(sequence)s.nextval INTO cval FROM dual; 
     203            cval := startvalue - cval; 
     204            IF cval != 0 THEN 
     205                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval; 
     206                SELECT %(sequence)s.nextval INTO cval FROM dual; 
     207                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1'; 
     208            END IF; 
     209            COMMIT; 
     210        END;\n""" 
     211 
    124212def get_sql_flush(style, tables, sequences): 
    125213    """Return a list of SQL statements required to remove all data from 
    126214    all tables in the database (without actually removing the tables 
    127215    themselves) and put the database in an empty 'initial' state 
    128216    """ 
    129     # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 'TRUNCATE z;'... style SQL statements 
    130     # TODO - SQL not actually tested against Oracle yet! 
    131     # TODO - autoincrement indices reset required? See other get_sql_flush() implementations 
    132     sql = ['%s %s;' % \ 
    133             (style.SQL_KEYWORD('TRUNCATE'), 
    134              style.SQL_FIELD(quote_name(table)) 
    135              )  for table in tables] 
     217    # Return a list of 'TRUNCATE x;', 'TRUNCATE y;', 
     218    # 'TRUNCATE z;'... style SQL statements 
     219    if tables: 
     220        # Oracle does support TRUNCATE, but it seems to get us into 
     221        # FK referential trouble, whereas DELETE FROM table works. 
     222        sql = ['%s %s %s;' % \ 
     223                (style.SQL_KEYWORD('DELETE'), 
     224                 style.SQL_KEYWORD('FROM'), 
     225                 style.SQL_FIELD(quote_name(table)) 
     226                 ) for table in tables] 
     227        # Since we've just deleted all the rows, running our sequence 
     228        # ALTER code will reset the sequence to 0. 
     229        for sequence_info in sequences: 
     230            table_name = sequence_info['table'] 
     231            seq_name = get_sequence_name(table_name) 
     232            query = _get_sequence_reset_sql() % {'sequence':seq_name, 
     233                                                 'table':quote_name(table_name)} 
     234            sql.append(query) 
     235        return sql 
     236    else: 
     237        return [] 
    136238 
     239def get_sequence_name(table): 
     240    name_length = get_max_name_length() - 3 
     241    return '%s_SQ' % util.truncate_name(table, name_length).upper() 
     242 
    137243def get_sql_sequence_reset(style, model_list): 
    138244    "Returns a list of the SQL statements to reset sequences for the given models." 
    139     # No sequence reset required 
    140     return [] 
     245    from django.db import models 
     246    output = [] 
     247    query = _get_sequence_reset_sql() 
     248    for model in model_list: 
     249        for f in model._meta.fields: 
     250            if isinstance(f, models.AutoField): 
     251                sequence_name = get_sequence_name(model._meta.db_table) 
     252                output.append(query % {'sequence':sequence_name, 
     253                                       'table':model._meta.db_table}) 
     254                break # Only one AutoField is allowed per model, so don't bother continuing. 
     255        for f in model._meta.many_to_many: 
     256            sequence_name = get_sequence_name(f.m2m_db_table()) 
     257            output.append(query % {'sequence':sequence_name, 
     258                                   'table':f.m2m_db_table()}) 
     259    return output 
    141260 
     261def get_trigger_name(table): 
     262    name_length = get_max_name_length() - 3 
     263    return '%s_TR' % util.truncate_name(table, name_length).upper() 
     264 
     265def get_query_set_class(DefaultQuerySet): 
     266    "Create a custom QuerySet class for Oracle." 
     267 
     268    from django.db import backend, connection 
     269    from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE 
     270 
     271    class OracleQuerySet(DefaultQuerySet): 
     272 
     273        def iterator(self): 
     274            "Performs the SELECT database lookup of this QuerySet." 
     275 
     276            from django.db.models.query import get_cached_row 
     277 
     278            # self._select is a dictionary, and dicti