Ticket #1990: django_oracle_release-0.95.patch

File django_oracle_release-0.95.patch, 19.4 KB (added by tzellman at gmail dot com, 18 years ago)

I created a patch for the stable 0.95 release (in SVN at /tags/releases/0.95/). This patch incorporates the changes made by pk11 and Winston Lee (basically, the previous 2 patches). You can apply this to a freshly checked out 0.95 release and you'll get all the latest updates that everyone has been contributing. As always, thanks to everyone for continuing the effort to supply Oracle bindings for Django!

  • db/models/base.py

     
    171171        record_exists = True
    172172        if pk_set:
    173173            # Determine whether a record with the primary key already exists.
    174             cursor.execute("SELECT 1 FROM %s WHERE %s=%%s LIMIT 1" % \
    175                 (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column)), [pk_val])
     174            lim = settings.DATABASE_ENGINE != 'oracle' and ' LIMIT 1' or ''
     175            cursor.execute("SELECT 1 FROM %s WHERE %s=%%s %s" % \
     176                (backend.quote_name(self._meta.db_table), backend.quote_name(self._meta.pk.column), lim), [pk_val])
    176177            # If it does already exist, do an UPDATE.
    177178            if cursor.fetchone():
    178179                db_values = [f.get_db_prep_save(f.pre_save(self, False)) for f in non_pks]
  • db/models/fields/__init__.py

     
    158158
    159159    def get_db_prep_save(self, value):
    160160        "Returns field's value prepared for saving into a database."
     161        # Oracle treats empty strings ('') the same as NULLs. 
     162        # To get around this wart, we need to change it to something else... 
     163        if settings.DATABASE_ENGINE == 'oracle' and  value == '':
     164            value = ' '
    161165        return value
    162166
    163167    def get_db_prep_lookup(self, lookup_type, value):
     
    446450    def get_db_prep_save(self, value):
    447451        # Casts dates into string format for entry into database.
    448452        if value is not None:
    449             value = value.strftime('%Y-%m-%d')
     453            if settings.DATABASE_ENGINE != 'oracle':
     454                #Oracle does not need a string conversion
     455                value = value.strftime('%Y-%m-%d')
    450456        return Field.get_db_prep_save(self, value)
    451457
    452458    def get_manipulator_field_objs(self):
     
    476482    def get_db_prep_save(self, value):
    477483        # Casts dates into string format for entry into database.
    478484        if value is not None:
    479             # MySQL will throw a warning if microseconds are given, because it
     485            # MySQL/Oracle will throw a warning if microseconds are given, because it
    480486            # doesn't support microseconds.
    481             if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
     487            if (settings.DATABASE_ENGINE == 'mysql' or settings.DATABASE_ENGINE=='oracle') and hasattr(value, 'microsecond'):
    482488                value = value.replace(microsecond=0)
    483489            value = str(value)
    484490        return Field.get_db_prep_save(self, value)
    485491
    486492    def get_db_prep_lookup(self, lookup_type, value):
     493        # Oracle will throw an error if microseconds are given, because it
     494        # doesn't support microseconds.
     495        if (settings.DATABASE_ENGINE=='oracle') and hasattr(value, 'microsecond'):
     496            value = value.replace(microsecond=0)
    487497        if lookup_type == 'range':
    488498            value = [str(v) for v in value]
    489499        else:
     
    511521    def flatten_data(self,follow, obj = None):
    512522        val = self._get_val_from_obj(obj)
    513523        date_field, time_field = self.get_manipulator_field_names('')
    514         return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
    515                 time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
     524        #cx_Oracle does not support strftime
     525        if (settings.DATABASE_ENGINE=='oracle'):
     526            return {date_field: (val is not None or ''),
     527                    time_field: (val is not None or '')}
     528        else:
     529            return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
     530                    time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
    516531
    517532class EmailField(CharField):
    518533    def __init__(self, *args, **kwargs):
     
    744759            # doesn't support microseconds.
    745760            if settings.DATABASE_ENGINE == 'mysql':
    746761                value = value.replace(microsecond=0)
    747             value = str(value)
     762                value = str(value)
     763            elif settings.DATABASE_ENGINE == 'oracle':
     764                value = value.replace(microsecond=0)
     765            else:
     766                value = str(value)
    748767        return Field.get_db_prep_save(self, value)
    749768
    750769    def get_manipulator_field_objs(self):
  • db/models/query.py

     
    33from django.db.models import signals
    44from django.dispatch import dispatcher
    55from django.utils.datastructures import SortedDict
     6from django.conf import settings
    67import operator
    78import re
    89
     
    168169        extra_select = self._select.items()
    169170
    170171        cursor = connection.cursor()
    171         select, sql, params = self._get_sql_clause()
    172         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
     172        select, sql, params, full_query = self._get_sql_clause()
     173
     174        if not full_query:
     175            cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
     176        else:
     177            cursor.execute(full_query, params)
    173178        fill_cache = self._select_related
    174179        index_end = len(self.model._meta.fields)
    175180        while 1:
     
    192197        counter._offset = None
    193198        counter._limit = None
    194199        counter._select_related = False
    195         select, sql, params = counter._get_sql_clause()
     200        select, sql, params, full_query = counter._get_sql_clause()
    196201        cursor = connection.cursor()
    197202        if self._distinct:
    198203            id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
     
    501506            sql.append("ORDER BY " + ", ".join(order_by))
    502507
    503508        # LIMIT and OFFSET clauses
    504         if self._limit is not None:
    505             sql.append("%s " % backend.get_limit_offset_sql(self._limit, self._offset))
     509        if (settings.DATABASE_ENGINE != 'oracle'):             
     510            if self._limit is not None:
     511                sql.append("%s " % backend.get_limit_offset_sql(self._limit, self._offset))
     512            else:
     513                assert self._offset is None, "'offset' is not allowed without 'limit'"
     514
     515            return select, " ".join(sql), params
    506516        else:
    507             assert self._offset is None, "'offset' is not allowed without 'limit'"
     517            # To support limits and offsets, Oracle requires some funky rewriting of an otherwise normal looking query.
     518            select_clause = ",".join(select)
     519            distinct = (self._distinct and "DISTINCT " or "")
    508520
    509         return select, " ".join(sql), params
     521            if order_by: 
     522                order_by_clause = " OVER (ORDER BY %s )" % (", ".join(order_by))
     523            else:
     524                #Oracle's row_number() function always requires an order-by clause.
     525                #So we need to define a default order-by, since none was provided.
     526                order_by_clause = " OVER (ORDER BY %s.%s)" % \
     527                    (backend.quote_name(opts.db_table), 
     528                    backend.quote_name(opts.fields[0].db_column or opts.fields[0].column))
     529            # limit_and_offset_clause
     530            offset = self._offset and int(self._offset) or 0
     531            limit = self._limit and int(self._limit) or None
     532            limit_and_offset_clause = ''
     533            if limit:
     534                limit_and_offset_clause = "WHERE rn > %s AND rn <= %s" % (offset, limit+offset)
     535            elif offset:
     536                limit_and_offset_clause = "WHERE rn > %s" % (offset)
    510537
     538            if len(limit_and_offset_clause) > 0:
     539                full_query = """SELECT * FROM 
     540                    (SELECT %s   
     541                    %s,
     542                    ROW_NUMBER() %s AS rn
     543                    %s
     544                    )
     545                    %s
     546                    """ % (distinct, select_clause, order_by_clause, " ".join(sql), limit_and_offset_clause)
     547            else:
     548                full_query = None
     549             
     550            return select, " ".join(sql), params, full_query
    511551class ValuesQuerySet(QuerySet):
    512552    def iterator(self):
    513553        # select_related and select aren't supported in values().
     
    523563            field_names = [f.attname for f in self.model._meta.fields]
    524564
    525565        cursor = connection.cursor()
    526         select, sql, params = self._get_sql_clause()
     566        select, sql, params, full_query = self._get_sql_clause()
    527567        select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
    528568        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
    529569        while 1:
     
    636676    if table_prefix.endswith('.'):
    637677        table_prefix = backend.quote_name(table_prefix[:-1])+'.'
    638678    field_name = backend.quote_name(field_name)
     679    #put some oracle exceptions here
     680    if lookup_type == "icontains" and settings.DATABASE_ENGINE == 'oracle':
     681        return 'lower(%s%s) %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s'))
    639682    try:
    640683        return '%s%s %s' % (table_prefix, field_name, (backend.OPERATOR_MAPPING[lookup_type] % '%s'))
    641684    except KeyError:
     
    667710    Helper function that recursively populates the select, tables and where (in
    668711    place) for select_related queries.
    669712    """
     713    from django.db.models.fields import AutoField
    670714    qn = backend.quote_name
    671715    for f in opts.fields:
    672716        if f.rel and not f.null:
     
    680724            cache_tables_seen.append(db_table)
    681725            where.append('%s.%s = %s.%s' % \
    682726                (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column)))
    683             select.extend(['%s.%s' % (qn(db_table), qn(f2.column)) for f2 in f.rel.to._meta.fields])
     727            if settings.DATABASE_ENGINE == 'oracle':
     728                select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields if not isinstance(f2, AutoField)])
     729            else:
     730                select.extend(['%s.%s' % (backend.quote_name(db_table), backend.quote_name(f2.column)) for f2 in f.rel.to._meta.fields])
    684731            fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen)
    685732
    686733def parse_lookup(kwarg_items, opts):
  • db/backends/oracle/base.py

     
    3939            else:
    4040                conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
    4141                self.connection = Database.connect(conn_string)
     42        # set oracle date to ansi date format
     43        cursor =  self.connection.cursor()
     44        cursor.execute("alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'")
     45        cursor.close()                                       
    4246        return FormatStylePlaceholderCursor(self.connection)
    4347
    4448    def _commit(self):
     
    6771    def execute(self, query, params=None):
    6872        if params is None: params = []
    6973        query = self.convert_arguments(query, len(params))
    70         return Database.Cursor.execute(self, query, params)
     74        # cx can not execute the query with the closing ';'
     75        if query.endswith(';') :
     76            query = query[0:len(query)-1]                       
     77        print query
     78        print params   
     79        return Database.Cursor.execute(self, query, params)
    7180
    7281    def executemany(self, query, params=None):
    7382        if params is None: params = []
    7483        query = self.convert_arguments(query, len(params[0]))
     84        # cx can not execute the query with the closing ';'
     85        if query.endswith(';') :
     86            query = query[0:len(query)-1]                                                   
    7587        return Database.Cursor.executemany(self, query, params)
    7688
    7789    def convert_arguments(self, query, num_params):
  • core/management.py

     
    66import os, re, shutil, sys, textwrap
    77from optparse import OptionParser
    88from django.utils import termcolors
     9from django.conf import settings
    910
    1011# For Python 2.3
    1112if not hasattr(__builtins__, 'set'):
     
    8384
    8485def get_sql_create(app):
    8586    "Returns a list of the CREATE TABLE SQL statements for the given app."
    86     from django.db import get_creation_module, models
     87    from django.db import models,get_creation_module, backend
    8788    data_types = get_creation_module().DATA_TYPES
    8889
    8990    if not data_types:
     
    157158                field_output.append(style.SQL_KEYWORD('UNIQUE'))
    158159            if f.primary_key:
    159160                field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
     161            if (settings.DATABASE_ENGINE == 'oracle') and f.unique and f.primary_key:
     162                # Suppress UNIQUE/PRIMARY KEY for Oracle (ORA-02259)
     163                field_output.remove(style.SQL_KEYWORD('UNIQUE'))
    160164            if f.rel:
    161165                if f.rel.to in known_models:
    162166                    field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \
     
    181185        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
    182186    full_statement.append(');')
    183187    final_output.append('\n'.join(full_statement))
     188   
     189    # To simulate auto-incrementing primary keys in Oracle -- creating primary tables
     190    if (settings.DATABASE_ENGINE == 'oracle') & (opts.has_auto_field):
     191        sequence_statement = 'CREATE SEQUENCE %s_sq;' % opts.db_table
     192        final_output.append(sequence_statement)
     193        trigger_statement = '' + \
     194            'CREATE OR REPLACE trigger %s_tr\n'    % opts.db_table + \
     195            '  before insert on %s\n'           % backend.quote_name(opts.db_table) + \
     196            '    for each row\n'  + \
     197            '      when (new.id is NULL)\n' + \
     198            '        begin\n' + \
     199            '         select %s_sq.NEXTVAL into :new.id from DUAL;\n' % opts.db_table + \
     200            '      end;\n'                                                                       
     201        final_output.append(trigger_statement)
    184202
     203
    185204    return final_output, pending_references
    186205
    187206def _get_sql_for_pending_references(model, pending_references):
     
    208227                    r_name += '_%s' % reference_names[r_name]
    209228                else:
    210229                    reference_names[r_name] = 0
    211                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
    212                     (backend.quote_name(r_table), r_name,
    213                     backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
     230                # if constraint name size is over 29 char and db is oracle, chop it
     231                if settings.DATABASE_ENGINE == 'oracle' and len(r_name) > 29:
     232                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
     233                                        (backend.quote_name(r_table), r_name[0:29],
     234                                         backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
     235                else:
     236                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
     237                                        (backend.quote_name(r_table), r_name,
     238                                         backend.quote_name(r_col), backend.quote_name(table), backend.quote_name(col)))
    214239            del pending_references[model]
    215240    return final_output
    216241
     
    248273                style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name()))))
    249274            table_output.append(');')
    250275            final_output.append('\n'.join(table_output))
     276        # To simulate auto-incrementing primary keys in Oracle -- creating m2m tables
     277        if (settings.DATABASE_ENGINE == 'oracle'):
     278            m_table = f.m2m_db_table()
     279            sequence_statement = 'CREATE SEQUENCE %s_sq;' % m_table
     280            final_output.append(sequence_statement)
     281            trigger_statement = '' + \
     282            'CREATE OR REPLACE trigger %s_tr\n'    % m_table + \
     283            '  before insert on %s\n'           % backend.quote_name(m_table) + \
     284            '    for each row\n'  + \
     285            '      when (new.id is NULL)\n' + \
     286            '        begin\n' + \
     287            '         select %s_sq.NEXTVAL into :new.id from DUAL;\n' % m_table + \
     288            '      end;\n'
     289            final_output.append(trigger_statement)
    251290    return final_output
    252291
    253292def get_sql_delete(app):
     
    471510            sql.extend(_get_sql_for_pending_references(model, pending_references))
    472511            print "Creating table %s" % model._meta.db_table
    473512            for statement in sql:
    474                 cursor.execute(statement)
     513                # go on if one table could not be created
     514                try:
     515                    cursor.execute(statement)
     516                except Exception, e:
     517                    print statement
     518                    print e
    475519            table_list.append(model._meta.db_table)
    476520
    477521        for model in model_list:
     
    12811325        if not mod_list:
    12821326            parser.print_usage_and_exit()
    12831327        if action not in NO_SQL_TRANSACTION:
    1284             print style.SQL_KEYWORD("BEGIN;")
     1328            if settings.DATABASE_ENGINE != 'oracle':
     1329                print style.SQL_KEYWORD("BEGIN;")
    12851330        for mod in mod_list:
    12861331            output = action_mapping[action](mod)
    12871332            if output:
    12881333                print '\n'.join(output)
    12891334        if action not in NO_SQL_TRANSACTION:
    1290             print style.SQL_KEYWORD("COMMIT;")
     1335            if settings.DATABASE_ENGINE != 'oracle':
     1336                print style.SQL_KEYWORD("COMMIT;")
    12911337
    12921338def execute_manager(settings_mod, argv=None):
    12931339    # Add this project to sys.path so that it's importable in the conventional
  • contrib/sessions/models.py

     
    5151    session_key = models.CharField(_('session key'), maxlength=40, primary_key=True)
    5252    session_data = models.TextField(_('session data'))
    5353    expire_date = models.DateTimeField(_('expire date'))
     54
    5455    objects = SessionManager()
    5556    class Meta:
    5657        db_table = 'django_session'
  • contrib/admin/models.py

     
    1616    action_time = models.DateTimeField(_('action time'), auto_now=True)
    1717    user = models.ForeignKey(User)
    1818    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    19     object_id = models.TextField(_('object id'), blank=True, null=True)
     19    #changed for Oracle support
     20    object_id = models.CharField(_('object id'), maxlength=200, blank=True, null=True)
    2021    object_repr = models.CharField(_('object repr'), maxlength=200)
    2122    action_flag = models.PositiveSmallIntegerField(_('action flag'))
    2223    change_message = models.TextField(_('change message'), blank=True)
Back to Top