Django

Code

OracleBranch: django-oracle-rev5046.diff

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

Updated after we fixed #4093, also ignore whitespace diffs this time.

  • django/db/backends/mysql/base.py

    old new  
    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 
  • 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/db/backends/postgresql/base.py

    old new  
    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: 
     
    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 
  • 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 
  • 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/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'), 
     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'), 
    134225             style.SQL_FIELD(quote_name(table)) 
    135226             )  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, \ 
     458                TimeField, BooleanField, NullBooleanField 
     459            values = [] 
     460            for value, field in map(None, row, fields): 
     461                if isinstance(value, Database.LOB): 
     462                    value = value.read() 
     463                # Since Oracle won't distinguish between NULL and an empty 
     464                # string (''), we store empty strings as a space.  Here is 
     465                # where we undo that treachery. 
     466                if value == ' ': 
     467                    value = '' 
     468                # Convert 1 or 0 to True or False 
     469                elif value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)): 
     470                    value = bool(value) 
     471                # cx_Oracle always returns datetime.datetime objects for 
     472                # DATE and TIMESTAMP columns, but Django wants to see a 
     473                # python datetime.date, .time, or .datetime.  We use the type 
     474                # of the Field to determine which to cast to, but it's not 
     475                # always available. 
     476                # As a workaround, we cast to date if all the time-related 
     477                # values are 0, or to time if the date is 1/1/1900. 
     478                # This could be cleaned a bit by adding a method to the Field 
     479                # classes to normalize values from the database (the to_python 
     480                # method is used for validation and isn't what we want here). 
     481                elif isinstance(value, Database.Timestamp): 
     482                    # In Python 2.3, the cx_Oracle driver returns its own 
     483                    # Timestamp object that we must convert to a datetime class. 
     484                    if not isinstance(value, datetime.datetime): 
     485                        value = datetime.datetime(value.year, value.month, value.day, value.hour, 
     486                                                  value.minute, value.second, value.fsecond) 
     487                    if isinstance(field, DateTimeField): 
     488                        pass  # DateTimeField subclasses DateField so must be checked first. 
     489                    elif isinstance(field, DateField): 
     490                        value = value.date() 
     491                    elif isinstance(field, TimeField) or (value.year == 1900 and value.month == value.day == 1): 
     492                        value = value.time() 
     493                    elif value.hour == value.minute == value.second == value.microsecond == 0: 
     494                        value = value.date() 
     495                values.append(value) 
     496            return values 
     497 
     498    return OracleQuerySet 
     499 
     500 
    142501OPERATOR_MAPPING = { 
    143502    'exact': '= %s', 
    144     'iexact': 'LIKE %s', 
    145     'contains': 'LIKE %s'
    146     'icontains': 'LIKE %s'
     503    'iexact': '= UPPER(%s)', 
     504    'contains': "LIKE %s ESCAPE '\\'"
     505    'icontains': "LIKE UPPER(%s) ESCAPE '\\'"
    147506    'gt': '> %s', 
    148507    'gte': '>= %s', 
    149508    'lt': '< %s', 
    150509    'lte': '<= %s', 
    151     'startswith': 'LIKE %s'
    152     'endswith': 'LIKE %s'
    153     'istartswith': 'LIKE %s'
    154     'iendswith': 'LIKE %s'
     510    'startswith': "LIKE %s ESCAPE '\\'"
     511    'endswith': "LIKE %s ESCAPE '\\'"
     512    'istartswith': "LIKE UPPER(%s) ESCAPE '\\'"
     513    'iendswith': "LIKE UPPER(%s) ESCAPE '\\'"
    155514} 
  • 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/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/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)', 
     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)', 
    1421    '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)', 
     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  &nbs