Django

Code

OracleBranch: django-oracle-rev5036.diff

File django-oracle-rev5036.diff, 87.4 kB (added by ian.g.kelly@gmail.com, 3 years 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 dictionaries' key order is 
     279            # undefined, so we convert it to a list of tuples. 
     280            extra_select = self._select.items() 
     281 
     282            full_query = None 
     283 
     284            try: 
     285                try: 
     286                    select, sql, params, full_query = self._get_sql_clause(get_full_query=True) 
     287                except TypeError: 
     288                    select, sql, params = self._get_sql_clause() 
     289            except EmptyResultSet: 
     290                raise StopIteration 
     291            if not full_query: 
     292                full_query = "SELECT %s%s\n%s" % \ 
     293                             ((self._distinct and "DISTINCT " or ""), 
     294                              ', '.join(select), sql) 
     295 
     296            cursor = connection.cursor() 
     297            cursor.execute(full_query, params) 
     298 
     299            fill_cache = self._select_related 
     300            fields = self.model._meta.fields 
     301            index_end = len(fields) 
     302 
     303            # so here's the logic; 
     304            # 1. retrieve each row in turn 
     305            # 2. convert NCLOBs 
     306 
     307            while 1: 
     308                rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
     309                if not rows: 
     310                    raise StopIteration 
     311                for row in rows: 
     312                    row = self.resolve_columns(row, fields) 
     313                    if fill_cache: 
     314                        obj, index_end = get_cached_row(klass=self.model, row=row, 
     315                                                        index_start=0, max_depth=self._max_related_depth) 
     316                    else: 
     317                        obj = self.model(*row[:index_end]) 
     318                    for i, k in enumerate(extra_select): 
     319                        setattr(obj, k[0], row[index_end+i]) 
     320                    yield obj 
     321 
     322 
     323        def _get_sql_clause(self, get_full_query=False): 
     324            from django.db.models.query import fill_table_cache, \ 
     325                handle_legacy_orderlist, orderfield2column 
     326 
     327            opts = self.model._meta 
     328 
     329            # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 
     330            select = ["%s.%s" % (backend.quote_name(opts.db_table), backend.quote_name(f.column)) for f in opts.fields] 
     331            tables = [quote_only_if_word(t) for t in self._tables] 
     332            joins = SortedDict() 
     333            where = self._where[:] 
     334            params = self._params[:] 
     335 
     336            # Convert self._filters into SQL. 
     337            joins2, where2, params2 = self._filters.get_sql(opts) 
     338            joins.update(joins2) 
     339            where.extend(where2) 
     340            params.extend(params2) 
     341 
     342            # Add additional tables and WHERE clauses based on select_related. 
     343            if self._select_related: 
     344                fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 
     345 
     346            # Add any additional SELECTs. 
     347            if self._select: 
     348                select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), backend.quote_name(s[0])) for s in self._select.items()]) 
     349 
     350            # Start composing the body of the SQL statement. 
     351            sql = [" FROM", backend.quote_name(opts.db_table)] 
     352 
     353            # Compose the join dictionary into SQL describing the joins. 
     354            if joins: 
     355                sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition) 
     356                                for (alias, (table, join_type, condition)) in joins.items()])) 
     357 
     358            # Compose the tables clause into SQL. 
     359            if tables: 
     360                sql.append(", " + ", ".join(tables)) 
     361 
     362            # Compose the where clause into SQL. 
     363            if where: 
     364                sql.append(where and "WHERE " + " AND ".join(where)) 
     365 
     366            # ORDER BY clause 
     367            order_by = [] 
     368            if self._order_by is not None: 
     369                ordering_to_use = self._order_by 
     370            else: 
     371                ordering_to_use = opts.ordering 
     372            for f in handle_legacy_orderlist(ordering_to_use): 
     373                if f == '?': # Special case. 
     374                    order_by.append(backend.get_random_function_sql()) 
     375                else: 
     376                    if f.startswith('-'): 
     377                        col_name = f[1:] 
     378                        order = "DESC" 
     379                    else: 
     380                        col_name = f 
     381                        order = "ASC" 
     382                    if "." in col_name: 
     383                        table_prefix, col_name = col_name.split('.', 1) 
     384                        table_prefix = backend.quote_name(table_prefix) + '.' 
     385                    else: 
     386                        # Use the database table as a column prefix if it wasn't given, 
     387                        # and if the requested column isn't a custom SELECT. 
     388                        if "." not in col_name and col_name not in (self._select or ()): 
     389                            table_prefix = backend.quote_name(opts.db_table) + '.' 
     390                        else: 
     391                            table_prefix = '' 
     392                    order_by.append('%s%s %s' % (table_prefix, backend.quote_name(orderfield2column(col_name, opts)), order)) 
     393            if order_by: 
     394                sql.append("ORDER BY " + ", ".join(order_by)) 
     395 
     396            # Look for column name collisions in the select elements 
     397            # and fix them with an AS alias.  This allows us to do a 
     398            # SELECT * later in the paging query. 
     399            cols = [clause.split('.')[-1] for clause in select] 
     400            for index, col in enumerate(cols): 
     401                if cols.count(col) > 1: 
     402                    col = '%s%d' % (col.replace('"', ''), index) 
     403                    cols[index] = col 
     404                    select[index] = '%s AS %s' % (select[index], col) 
     405 
     406            # LIMIT and OFFSET clauses 
     407            # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query. 
     408            select_clause = ",".join(select) 
     409            distinct = (self._distinct and "DISTINCT " or "") 
     410 
     411            if order_by: 
     412                order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by)) 
     413            else: 
     414                #Oracle's row_number() function always requires an order-by clause. 
     415                #So we need to define a default order-by, since none was provided. 
     416                order_by_clause = " OVER (ORDER BY %s.%s)" % \ 
     417                    (backend.quote_name(opts.db_table), 
     418                    backend.quote_name(opts.fields[0].db_column or opts.fields[0].column)) 
     419            # limit_and_offset_clause 
     420            if self._limit is None: 
     421                assert self._offset is None, "'offset' is not allowed without 'limit'" 
     422 
     423            if self._offset is not None: 
     424                offset = int(self._offset) 
     425            else: 
     426                offset = 0 
     427            if self._limit is not None: 
     428                limit = int(self._limit) 
     429            else: 
     430                limit = None 
     431 
     432            limit_and_offset_clause = '' 
     433            if limit is not None: 
     434                limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset) 
     435            elif offset: 
     436                limit_and_offset_clause = "WHERE rn > %s" % (offset) 
     437 
     438            if len(limit_and_offset_clause) > 0: 
     439                fmt = \ 
     440"""SELECT * FROM 
     441  (SELECT %s%s, 
     442          ROW_NUMBER()%s AS rn 
     443   %s) 
     444%s""" 
     445                full_query = fmt % (distinct, select_clause, 
     446                                    order_by_clause, ' '.join(sql).strip(), 
     447                                    limit_and_offset_clause) 
     448            else: 
     449                full_query = None 
     450 
     451            if get_full_query: 
     452                return select, " ".join(sql), params, full_query 
     453            else: 
     454                return select, " ".join(sql), params 
     455 
     456        def resolve_columns(self, row, fields=()): 
     457            from django.db.models.fields import DateField, DateTimeField, TimeField 
     458            values = [] 
     459            for value, field in map(None, row, fields): 
     460                if isinstance(value, Database.LOB): 
     461                    value = value.read() 
     462                # Since Oracle won't distinguish between NULL and an empty 
     463                # string (''), we store empty strings as a space.  Here is 
     464                # where we undo that treachery. 
     465                if value == ' ': 
     466                    value = '' 
     467                # cx_Oracle always returns datetime.datetime objects for 
     468                # DATE and TIMESTAMP columns, but Django wants to see a 
     469                # python datetime.date, .time, or .datetime.  We use the type 
     470                # of the Field to determine which to cast to, but it's not 
     471                # always available. 
     472                # As a workaround, we cast to date if all the time-related 
     473                # values are 0, or to time if the date is 1/1/1900. 
     474                # This could be cleaned a bit by adding a method to the Field 
     475                # classes to normalize values from the database (the to_python 
     476                # method is used for validation and isn't what we want here). 
     477                elif isinstance(value, Database.Timestamp): 
     478                    # In Python 2.3, the cx_Oracle driver returns its own 
     479                    # Timestamp object that we must convert to a datetime class. 
     480                    if not isinstance(value, datetime.datetime): 
     481                        value = datetime.datetime(value.year, value.month, value.day, value.hour, 
     482                                                  value.minute, value.second, value.fsecond) 
     483                    if isinstance(field, DateTimeField): 
     484                        pass  # DateTimeField subclasses DateField so must be checked first. 
     485                    elif isinstance(field, DateField): 
     486                        value = value.date() 
     487                    elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1): 
     488                        value = value.time() 
     489                    elif value.hour == value.minute == value.second == value.microsecond == 0: 
     490                        value = value.date() 
     491                values.append(value) 
     492            return values 
     493 
     494    return OracleQuerySet 
     495 
     496 
    142497OPERATOR_MAPPING = { 
    143498    'exact': '= %s', 
    144     'iexact': 'LIKE %s', 
    145     'contains': 'LIKE %s'
    146     'icontains': 'LIKE %s'
     499    'iexact': '= UPPER(%s)', 
     500    'contains': "LIKE %s ESCAPE '\\'"
     501    'icontains': "LIKE UPPER(%s) ESCAPE '\\'"
    147502    'gt': '> %s', 
    148503    'gte': '>= %s', 
    149504    'lt': '< %s', 
    150505    'lte': '<= %s', 
    151     'startswith': 'LIKE %s'
    152     'endswith': 'LIKE %s'
    153     'istartswith': 'LIKE %s'
    154     'iendswith': 'LIKE %s'
     506    'startswith': "LIKE %s ESCAPE '\\'"
     507    'endswith': "LIKE %s ESCAPE '\\'"
     508    'istartswith': "LIKE UPPER(%s) ESCAPE '\\'"
     509    'iendswith': "LIKE UPPER(%s) ESCAPE '\\'"
    155510} 
  • django.oracle/django/db/backends/oracle/client.py

    old new  
    22import os 
    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.oracle/django/db/backends/oracle/introspection.py

    old new  
     1from django.db.backends.oracle.base import quote_name 
    12import re 
     3import cx_Oracle 
    24 
     5 
    36foreign_key_re = re.compile(r"\sCONSTRAINT `[^`]*` FOREIGN KEY \(`([^`]*)`\) REFERENCES `([^`]*)` \(`([^`]*)`\)") 
    47 
    58def get_table_list(cursor): 
    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): 
    1419    """ 
     
    2227    Returns a dictionary of {field_index: (field_index_other_table, other_table)} 
    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]) 
    2643 
     44    relations = {} 
     45    for row in cursor.fetchall(): 
     46        relations[row[0]] = (row[2], row[1]) 
     47    return relations 
     48 
    2749def get_indexes(cursor, table_name): 
    2850    """ 
    2951    Returns a dictionary of fieldname -> infodict for the given table, 
     
    3153        {'primary_key': boolean representing whether it's the primary key, 
    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: 'FloatField', 
     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: 'FloatField', 
     96    cx_Oracle.STRING: 'TextField', 
     97    cx_Oracle.TIMESTAMP: 'DateTimeField', 
    5098} 
  • django.oracle/django/db/backends/oracle/creation.py

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

    old new  
    5454        global postgres_version 
    5555        if not postgres_version: 
    5656            cursor.execute("SELECT version()") 
    57             postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')]         
     57            postgres_version = [int(val) for val in cursor.fetchone()[0].split()[1].split('.')] 
    5858        if settings.DEBUG: 
    5959            return util.CursorDebugWrapper(cursor, self) 
    6060        return cursor 
     
    7272            self.connection.close() 
    7373            self.connection = None 
    7474 
     75allows_group_by_ordinal = True 
     76allows_unique_and_pk = True 
     77autoindexes_primary_keys = True 
     78needs_datetime_string_cast = False 
     79needs_upper_for_iops = False 
    7580supports_constraints = True 
     81supports_tablespaces = False 
     82uses_case_insensitive_names = True 
    7683 
    7784def quote_name(name): 
    7885    if name.startswith('"') and name.endswith('"'): 
     
    97104    # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC 
    98105    return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name) 
    99106 
     107def get_datetime_cast_sql(): 
     108    return None 
     109 
    100110def get_limit_offset_sql(limit, offset=None): 
    101111    sql = "LIMIT %s" % limit 
    102112    if offset and offset != 0: 
     
    118128def get_pk_default_value(): 
    119129    return "DEFAULT" 
    120130 
     131def get_max_name_length(): 
     132    return None 
     133 
     134def get_start_transaction_sql(): 
     135    return "BEGIN;" 
     136 
     137def get_autoinc_sql(table): 
     138    return None 
     139 
    121140def get_sql_flush(style, tables, sequences): 
    122141    """Return a list of SQL statements required to remove all data from 
    123142    all tables in the database (without actually removing the tables 
     
    138157                     style.SQL_KEYWORD('FROM'), 
    139158                     style.SQL_FIELD(quote_name(table)) 
    140159                     ) for table in tables] 
    141                       
     160 
    142161        # 'ALTER SEQUENCE sequence_name RESTART WITH 1;'... style SQL statements 
    143162        # to reset sequence indices 
    144163        for sequence in sequences: 
     
    194213                style.SQL_KEYWORD('FROM'), 
    195214                style.SQL_TABLE(f.m2m_db_table()))) 
    196215    return output 
    197          
     216 
    198217OPERATOR_MAPPING = { 
    199218    'exact': '= %s', 
    200219    'iexact': 'ILIKE %s', 
  • django.oracle/django/db/backends/dummy/base.py

    old new  
    2727        pass # close() 
    2828 
    2929supports_constraints = False 
     30supports_tablespaces = False 
    3031quote_name = complain 
    3132dictfetchone = complain 
    3233dictfetchmany = complain 
  • django.oracle/django/core/management.py

    old new  
    5555 
    5656def _get_installed_models(table_list): 
    5757    "Gets a set of all models that are installed, given a list of existing tables" 
    58     from django.db import models 
     58    from django.db import backend, models 
    5959    all_models = [] 
    6060    for app in models.get_apps(): 
    6161        for model in models.get_models(app): 
    6262            all_models.append(model) 
    63     return set([m for m in all_models if m._meta.db_table in table_list]) 
     63    if backend.uses_case_insensitive_names: 
     64        converter = str.upper 
     65    else: 
     66        converter = lambda x: x 
     67    return set([m for m in all_models if converter(m._meta.db_table) in map(converter, table_list)]) 
    6468 
    6569def _get_table_list(): 
    6670    "Gets a list of all db tables that are physically installed." 
     
    104108def get_sql_create(app): 
    105109    "Returns a list of the CREATE TABLE SQL statements for the given app." 
    106110    from django.db import get_creation_module, models 
     111 
    107112    data_types = get_creation_module().DATA_TYPES 
    108113 
    109114    if not data_types: 
     
    173178            rel_field = f 
    174179            data_type = f.get_internal_type() 
    175180        col_type = data_types[data_type] 
     181        tablespace = f.db_tablespace or opts.db_tablespace 
    176182        if col_type is not None: 
    177183            # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
    178184            field_output = [style.SQL_FIELD(backend.quote_name(f.column)), 
    179185                style.SQL_COLTYPE(col_type % rel_field.__dict__)] 
    180186            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    181             if f.unique
     187            if f.unique and (not f.primary_key or backend.allows_unique_and_pk)
    182188                field_output.append(style.SQL_KEYWORD('UNIQUE')) 
    183189            if f.primary_key: 
    184190                field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) 
     191            if tablespace and backend.supports_tablespaces and (f.unique or f.primary_key) and backend.autoindexes_primary_keys: 
     192                # We must specify the index tablespace inline, because we 
     193                # won't be generating a CREATE INDEX statement for this field. 
     194                field_output.append(backend.get_tablespace_sql(tablespace, inline=True)) 
    185195            if f.rel: 
    186196                if f.rel.to in known_models: 
    187197                    field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ 
     
    205215    full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(backend.quote_name(opts.db_table)) + ' ('] 
    206216    for i, line in enumerate(table_output): # Combine and add commas. 
    207217        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or '')) 
    208     full_statement.append(');') 
     218    full_statement.append(')') 
     219    if opts.db_tablespace and backend.supports_tablespaces: 
     220        full_statement.append(backend.get_tablespace_sql(opts.db_tablespace)) 
     221    full_statement.append(';') 
    209222    final_output.append('\n'.join(full_statement)) 
    210223 
     224    if opts.has_auto_field and hasattr(backend, 'get_autoinc_sql'): 
     225        # Add any extra SQL needed to support auto-incrementing primary keys 
     226        autoinc_sql = backend.get_autoinc_sql(opts.db_table) 
     227        if autoinc_sql: 
     228            for stmt in autoinc_sql: 
     229                final_output.append(stmt) 
     230 
    211231    return final_output, pending_references 
    212232 
    213233def _get_sql_for_pending_references(model, pending_references): 
     
    215235    Get any ALTER TABLE statements to add constraints after the fact. 
    216236    """ 
    217237    from django.db import backend, get_creation_module 
     238    from django.db.backends.util import truncate_name 
    218239    data_types = get_creation_module().DATA_TYPES 
    219240 
    220241    final_output = [] 
     
    227248                r_col = f.column 
    228249                table = opts.db_table 
    229250                col = opts.get_field(f.rel.field_name).column 
    230                 # For MySQL, r_name must be unique in the first 64 characters. 
    231                 # So we are careful with character usage here. 
    232                 r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table)))) 
     251                r_name = '%s_refs_%s_%s_%s' % (r_col, col, r_table, table) 
    233252                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \ 
    234                     (backend.quote_name(r_table), r_name
     253                    (backend.quote_name(r_table), truncate_name(r_name, backend.get_max_name_length())
    235254                    backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col), 
    236255                    backend.get_deferrable_sql())) 
    237256            del pending_references[model] 
     
    247266    final_output = [] 
    248267    for f in opts.many_to_many: 
    249268        if not isinstance(f.rel, 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 = '' 
    250274            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \ 
    251275                style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] 
    252             table_output.append('    %s %s %s,' % \ 
     276            table_output.append('    %s %s %s%s,' % \ 
    253277                (style.SQL_FIELD(backend.quote_name('id')), 
    254278                style.SQL_COLTYPE(data_types['AutoField']), 
    255                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'))) 
     279                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 
     280                tablespace_sql)) 
    256281            table_output.append('    %s %s %s %s (%s)%s,' % \ 
    257282                (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    258283                style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__), 
     
    267292                style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), 
    268293                style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), 
    269294                backend.get_deferrable_sql())) 
    270             table_output.append('    %s (%s, %s)' % \ 
     295            table_output.append('    %s (%s, %s)%s' % \ 
    271296                (style.SQL_KEYWORD('UNIQUE'), 
    272297                style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    273                 style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())))) 
    274             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(';') 
    275305            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 
    276313    return final_output 
    277314 
    278315def get_sql_delete(app): 
    279316    "Returns a list of the DROP TABLE SQL statements for the given app." 
    280317    from django.db import backend, connection, models, get_introspection_module 
     318    from django.db.backends.util import truncate_name 
    281319    introspection = get_introspection_module() 
    282320 
    283321    # This should work even if a connection isn't available 
     
    291329        table_names = introspection.get_table_list(cursor) 
    292330    else: 
    293331        table_names = [] 
     332    if backend.uses_case_insensitive_names: 
     333        table_name_converter = str.upper 
     334    else: 
     335        table_name_converter = lambda x: x 
    294336 
    295337    output = [] 
    296338 
     
    300342    references_to_delete = {} 
    301343    app_models = models.get_models(app) 
    302344    for model in app_models: 
    303         if cursor and model._meta.db_table in table_names: 
     345        if cursor and table_name_converter(model._meta.db_table) in table_names: 
    304346            # The table exists, so it needs to be dropped 
    305347            opts = model._meta 
    306348            for f in opts.fields: 
     
    310352            to_delete.add(model) 
    311353 
    312354    for model in app_models: 
    313         if cursor and model._meta.db_table in table_names: 
     355        if cursor and table_name_converter(model._meta.db_table) in table_names: 
    314356            # Drop the table now 
    315357            output.append('%s %s;' % (style.SQL_KEYWORD('DROP TABLE'), 
    316358                style.SQL_TABLE(backend.quote_name(model._meta.db_table)))) 
     
    320362                    col = f.column 
    321363                    r_table = model._meta.db_table 
    322364                    r_col = model._meta.get_field(f.rel.field_name).column 
     365                    r_name = '%s_refs_%s_%s_%s' % (col, r_col, table, r_table) 
    323366                    output.append('%s %s %s %s;' % \ 
    324367                        (style.SQL_KEYWORD('ALTER TABLE'), 
    325368                        style.SQL_TABLE(backend.quote_name(table)), 
    326369                        style.SQL_KEYWORD(backend.get_drop_foreignkey_sql()), 
    327                         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())))) 
    328371                del references_to_delete[model] 
     372            if hasattr(backend, 'get_drop_sequence'): 
     373                output.append(backend.get_drop_sequence(model._meta.db_table)) 
    329374 
    330375    # Output DROP TABLE statements for many-to-many tables. 
    331376    for model in app_models: 
    332377        opts = model._meta 
    333378        for f in opts.many_to_many: 
    334             if cursor and f.m2m_db_table() in table_names: 
     379            if cursor and table_name_converter(f.m2m_db_table()) in table_names: 
    335380                output.append("%s %s;" % (style.SQL_KEYWORD('DROP TABLE'), 
    336381                    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))) 
    337384 
     385 
    338386    app_label = app_models[0]._meta.app_label 
    339387 
    340388    # Close database connection explicitly, in case this output is being piped 
     
    429477def get_sql_indexes_for_model(model): 
    430478    "Returns the CREATE INDEX SQL statements for a single model" 
    431479    from django.db import backend 
     480    from django.db.backends.util import truncate_name 
    432481    output = [] 
    433482 
    434483    for f in model._meta.fields: 
    435         if f.db_index
     484        if f.db_index and not ((f.primary_key or f.unique) and backend.autoindexes_primary_keys)
    436485            unique = f.unique and 'UNIQUE ' or '' 
     486            tablespace = f.db_tablespace or model._meta.db_tablespace 
     487            if tablespace and backend.supports_tablespaces: 
     488                tablespace_sql = ' ' + backend.get_tablespace_sql(tablespace) 
     489            else: 
     490                tablespace_sql = '' 
    437491            output.append( 
    438492                style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ 
    439493                style.SQL_TABLE(backend.quote_name('%s_%s' % (model._meta.db_table, f.column))) + ' ' + \ 
    440494                style.SQL_KEYWORD('ON') + ' ' + \ 
    441495                style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ 
    442                 "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) 
     496                "(%s)" % style.SQL_FIELD(backend.quote_name(f.column)) + \ 
     497                "%s;" % tablespace_sql 
    443498            ) 
    444499    return output 
    445500 
     
    463518 
    464519def syncdb(verbosity=1, interactive=True): 
    465520    "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    466     from django.db import connection, transaction, models, get_creation_module 
     521    from django.db import backend, connection, transaction, models, get_creation_module 
    467522    from django.conf import settings 
    468523 
    469524    disable_termcolors() 
     
    486541    # Get a list of all existing database tables, 
    487542    # so we know what needs to be added. 
    488543    table_list = _get_table_list() 
     544    if backend.uses_case_insensitive_names: 
     545        table_name_converter = str.upper 
     546    else: 
     547        table_name_converter = lambda x: x 
    489548 
    490549    # Get a list of already installed *models* so that references work right. 
    491550    seen_models = _get_installed_models(table_list) 
     
    500559            # Create the model's database table, if it doesn't already exist. 
    501560            if verbosity >= 2: 
    502561                print "Processing %s.%s model" % (app_name, model._meta.object_name) 
    503             if model._meta.db_table in table_list: 
     562            if table_name_converter(model._meta.db_table) in table_list: 
    504563                continue 
    505564            sql, references = _get_sql_model_create(model, seen_models) 
    506565            seen_models.add(model) 
     
    512571                print "Creating table %s" % model._meta.db_table 
    513572            for statement in sql: 
    514573                cursor.execute(statement) 
    515             table_list.append(model._meta.db_table
     574            table_list.append(table_name_converter(model._meta.db_table)
    516575 
    517576    # Create the m2m tables. This must be done after all tables have been created 
    518577    # to ensure that all referred tables will exist. 
     
    830889        except NotImplementedError: 
    831890            indexes = {} 
    832891        for i, row in enumerate(introspection_module.get_table_description(cursor, table_name)): 
    833             att_name = row[0] 
     892            att_name = row[0].lower() 
    834893            comment_notes = [] # Holds Field notes, to be displayed in a Python comment. 
    835894            extra_params = {}  # Holds Field parameters such as 'db_column'. 
    836895 
     
    13211380    # Keep a count of the installed objects and fixtures 
    13221381    count = [0,0] 
    13231382    models = set() 
    1324      
     1383 
    13251384    humanize = lambda dirname: dirname and "'%s'" % dirname or 'absolute path' 
    13261385 
    13271386    # Get a cursor (even though we don't need one yet). This has 
     
    16211680        if not mod_list: 
    16221681            parser.print_usage_and_exit() 
    16231682        if action not in NO_SQL_TRANSACTION: 
    1624             print style.SQL_KEYWORD("BEGIN;") 
     1683            from django.db import backend 
     1684            if backend.get_start_transaction_sql(): 
     1685                print style.SQL_KEYWORD(backend.get_start_transaction_sql()) 
    16251686        for mod in mod_list: 
    16261687            if action == 'reset': 
    16271688                output = action_mapping[action](mod, options.interactive) 
  • django.oracle/django/contrib/admin/models.py

    old new  
    99 
    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 
    1515class LogEntry(models.Model): 
  • django.oracle/docs/install.txt

    old new  
    6262 
    6363* If you're using SQLite, you'll need pysqlite_. Use version 2.0.3 or higher. 
    6464 
     65* If you're using Oracle, you'll need cx_Oracle_, version 4.3.1 or higher. 
     66 
    6567.. _PostgreSQL: http://www.postgresql.org/ 
    6668.. _MySQL: http://www.mysql.com/ 
    6769.. _Django's ticket system: http://code.djangoproject.com/report/1 
     
    7173.. _SQLite: http://www.sqlite.org/ 
    7274.. _pysqlite: http://initd.org/tracker/pysqlite 
    7375.. _MySQL backend: ../databases/ 
     76.. _cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/ 
    7477 
    7578Remove any old versions of Django 
    7679================================= 
  • django.oracle/docs/settings.txt

    old new  
    245245Default: ``''`` (Empty string) 
    246246 
    247247Which database backend to use. Either ``'postgresql_psycopg2'``,  
    248 ``'postgresql'``, ``'mysql'``,  ``'mysql_old'``, ``'sqlite3'`` or 
    249 ``'ado_mssql'``. 
     248``'postgresql'``, ``'mysql'``,  ``'mysql_old'``, ``'sqlite3'``, 
     249``'oracle'``, or ``'ado_mssql'``. 
    250250 
    251251DATABASE_HOST 
    252252------------- 
  • django.oracle/docs/faq.txt

    old new  
    301301 
    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/ 
    307307.. _Apache 2: http://httpd.apache.org/ 
     
    310310.. _PostgreSQL: http://www.postgresql.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? 
    315316----------------------------------------------------------------------------------------