~ubuntu-branches/ubuntu/jaunty/python-django/jaunty

« back to all changes in this revision

Viewing changes to django/db/backends/oracle/base.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant, Eddy Mulyono
  • Date: 2008-09-16 12:18:47 UTC
  • mfrom: (1.1.5 upstream) (4.1.1 lenny)
  • Revision ID: james.westby@ubuntu.com-20080916121847-mg225rg5mnsdqzr0
Tags: 1.0-1ubuntu1
* Merge from Debian (LP: #264191), remaining changes:
  - Run test suite on build.

[Eddy Mulyono]
* Update patch to workaround network test case failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
5
5
"""
6
6
 
7
 
from django.db.backends import util
 
7
import os
 
8
import datetime
 
9
import time
 
10
 
 
11
# Oracle takes client-side character set encoding from the environment.
 
12
os.environ['NLS_LANG'] = '.UTF8'
8
13
try:
9
14
    import cx_Oracle as Database
10
15
except ImportError, e:
11
16
    from django.core.exceptions import ImproperlyConfigured
12
 
    raise ImproperlyConfigured, "Error loading cx_Oracle module: %s" % e
13
 
 
14
 
DatabaseError = Database.Error
15
 
 
16
 
try:
17
 
    # Only exists in Python 2.4+
18
 
    from threading import local
19
 
except ImportError:
20
 
    # Import copy of _thread_local.py from Python 2.4
21
 
    from django.utils._threading_local import local
22
 
 
23
 
class DatabaseWrapper(local):
24
 
    def __init__(self, **kwargs):
25
 
        self.connection = None
26
 
        self.queries = []
27
 
        self.options = kwargs
 
17
    raise ImproperlyConfigured("Error loading cx_Oracle module: %s" % e)
 
18
 
 
19
from django.db.backends import *
 
20
from django.db.backends.oracle import query
 
21
from django.db.backends.oracle.client import DatabaseClient
 
22
from django.db.backends.oracle.creation import DatabaseCreation
 
23
from django.db.backends.oracle.introspection import DatabaseIntrospection
 
24
from django.utils.encoding import smart_str, force_unicode
 
25
 
 
26
DatabaseError = Database.DatabaseError
 
27
IntegrityError = Database.IntegrityError
 
28
 
 
29
 
 
30
class DatabaseFeatures(BaseDatabaseFeatures):
 
31
    empty_fetchmany_value = ()
 
32
    needs_datetime_string_cast = False
 
33
    uses_custom_query_class = True
 
34
    interprets_empty_strings_as_nulls = True
 
35
 
 
36
 
 
37
class DatabaseOperations(BaseDatabaseOperations):
 
38
    def autoinc_sql(self, table, column):
 
39
        # To simulate auto-incrementing primary keys in Oracle, we have to
 
40
        # create a sequence and a trigger.
 
41
        sq_name = get_sequence_name(table)
 
42
        tr_name = get_trigger_name(table)
 
43
        tbl_name = self.quote_name(table)
 
44
        col_name = self.quote_name(column)
 
45
        sequence_sql = """
 
46
            DECLARE
 
47
                i INTEGER;
 
48
            BEGIN
 
49
                SELECT COUNT(*) INTO i FROM USER_CATALOG
 
50
                    WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
 
51
                IF i = 0 THEN
 
52
                    EXECUTE IMMEDIATE 'CREATE SEQUENCE %(sq_name)s';
 
53
                END IF;
 
54
            END;
 
55
            /""" % locals()
 
56
        trigger_sql = """
 
57
            CREATE OR REPLACE TRIGGER %(tr_name)s
 
58
            BEFORE INSERT ON %(tbl_name)s
 
59
            FOR EACH ROW
 
60
            WHEN (new.%(col_name)s IS NULL)
 
61
                BEGIN
 
62
                    SELECT %(sq_name)s.nextval
 
63
                    INTO :new.%(col_name)s FROM dual;
 
64
                END;
 
65
                /""" % locals()
 
66
        return sequence_sql, trigger_sql
 
67
 
 
68
    def date_extract_sql(self, lookup_type, field_name):
 
69
        # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions42a.htm#1017163
 
70
        return "EXTRACT(%s FROM %s)" % (lookup_type, field_name)
 
71
 
 
72
    def date_trunc_sql(self, lookup_type, field_name):
 
73
        # Oracle uses TRUNC() for both dates and numbers.
 
74
        # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
 
75
        if lookup_type == 'day':
 
76
            sql = 'TRUNC(%s)' % field_name
 
77
        else:
 
78
            sql = "TRUNC(%s, '%s')" % (field_name, lookup_type)
 
79
        return sql
 
80
 
 
81
    def datetime_cast_sql(self):
 
82
        return "TO_TIMESTAMP(%s, 'YYYY-MM-DD HH24:MI:SS.FF')"
 
83
 
 
84
    def deferrable_sql(self):
 
85
        return " DEFERRABLE INITIALLY DEFERRED"
 
86
 
 
87
    def drop_sequence_sql(self, table):
 
88
        return "DROP SEQUENCE %s;" % self.quote_name(get_sequence_name(table))
 
89
 
 
90
    def field_cast_sql(self, db_type):
 
91
        if db_type and db_type.endswith('LOB'):
 
92
            return "DBMS_LOB.SUBSTR(%s)"
 
93
        else:
 
94
            return "%s"
 
95
 
 
96
    def last_insert_id(self, cursor, table_name, pk_name):
 
97
        sq_name = util.truncate_name(table_name, self.max_name_length() - 3)
 
98
        cursor.execute('SELECT %s_sq.currval FROM dual' % sq_name)
 
99
        return cursor.fetchone()[0]
 
100
 
 
101
    def lookup_cast(self, lookup_type):
 
102
        if lookup_type in ('iexact', 'icontains', 'istartswith', 'iendswith'):
 
103
            return "UPPER(%s)"
 
104
        return "%s"
 
105
 
 
106
    def max_name_length(self):
 
107
        return 30
 
108
 
 
109
    def prep_for_iexact_query(self, x):
 
110
        return x
 
111
 
 
112
    def query_class(self, DefaultQueryClass):
 
113
        return query.query_class(DefaultQueryClass, Database)
 
114
 
 
115
    def quote_name(self, name):
 
116
        # SQL92 requires delimited (quoted) names to be case-sensitive.  When
 
117
        # not quoted, Oracle has case-insensitive behavior for identifiers, but
 
118
        # always defaults to uppercase.
 
119
        # We simplify things by making Oracle identifiers always uppercase.
 
120
        if not name.startswith('"') and not name.endswith('"'):
 
121
            name = '"%s"' % util.truncate_name(name.upper(), self.max_name_length())
 
122
        return name.upper()
 
123
 
 
124
    def random_function_sql(self):
 
125
        return "DBMS_RANDOM.RANDOM"
 
126
 
 
127
    def regex_lookup_9(self, lookup_type):
 
128
        raise NotImplementedError("Regexes are not supported in Oracle before version 10g.")
 
129
 
 
130
    def regex_lookup_10(self, lookup_type):
 
131
        if lookup_type == 'regex':
 
132
            match_option = "'c'"
 
133
        else:
 
134
            match_option = "'i'"
 
135
        return 'REGEXP_LIKE(%%s, %%s, %s)' % match_option
 
136
 
 
137
    def regex_lookup(self, lookup_type):
 
138
        # If regex_lookup is called before it's been initialized, then create
 
139
        # a cursor to initialize it and recur.
 
140
        from django.db import connection
 
141
        connection.cursor()
 
142
        return connection.ops.regex_lookup(lookup_type)
 
143
 
 
144
    def sql_flush(self, style, tables, sequences):
 
145
        # Return a list of 'TRUNCATE x;', 'TRUNCATE y;',
 
146
        # 'TRUNCATE z;'... style SQL statements
 
147
        if tables:
 
148
            # Oracle does support TRUNCATE, but it seems to get us into
 
149
            # FK referential trouble, whereas DELETE FROM table works.
 
150
            sql = ['%s %s %s;' % \
 
151
                    (style.SQL_KEYWORD('DELETE'),
 
152
                     style.SQL_KEYWORD('FROM'),
 
153
                     style.SQL_FIELD(self.quote_name(table))
 
154
                     ) for table in tables]
 
155
            # Since we've just deleted all the rows, running our sequence
 
156
            # ALTER code will reset the sequence to 0.
 
157
            for sequence_info in sequences:
 
158
                sequence_name = get_sequence_name(sequence_info['table'])
 
159
                table_name = self.quote_name(sequence_info['table'])
 
160
                column_name = self.quote_name(sequence_info['column'] or 'id')
 
161
                query = _get_sequence_reset_sql() % {'sequence': sequence_name,
 
162
                                                     'table': table_name,
 
163
                                                     'column': column_name}
 
164
                sql.append(query)
 
165
            return sql
 
166
        else:
 
167
            return []
 
168
 
 
169
    def sequence_reset_sql(self, style, model_list):
 
170
        from django.db import models
 
171
        output = []
 
172
        query = _get_sequence_reset_sql()
 
173
        for model in model_list:
 
174
            for f in model._meta.local_fields:
 
175
                if isinstance(f, models.AutoField):
 
176
                    table_name = self.quote_name(model._meta.db_table)
 
177
                    sequence_name = get_sequence_name(model._meta.db_table)
 
178
                    column_name = self.quote_name(f.column)
 
179
                    output.append(query % {'sequence': sequence_name,
 
180
                                           'table': table_name,
 
181
                                           'column': column_name})
 
182
                    break # Only one AutoField is allowed per model, so don't bother continuing.
 
183
            for f in model._meta.many_to_many:
 
184
                table_name = self.quote_name(f.m2m_db_table())
 
185
                sequence_name = get_sequence_name(f.m2m_db_table())
 
186
                column_name = self.quote_name('id')
 
187
                output.append(query % {'sequence': sequence_name,
 
188
                                       'table': table_name,
 
189
                                       'column': column_name})
 
190
        return output
 
191
 
 
192
    def start_transaction_sql(self):
 
193
        return ''
 
194
 
 
195
    def tablespace_sql(self, tablespace, inline=False):
 
196
        return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
 
197
 
 
198
    def value_to_db_time(self, value):
 
199
        if value is None:
 
200
            return None
 
201
        if isinstance(value, basestring):
 
202
            return datetime.datetime(*(time.strptime(value, '%H:%M:%S')[:6]))
 
203
        return datetime.datetime(1900, 1, 1, value.hour, value.minute,
 
204
                                 value.second, value.microsecond)
 
205
 
 
206
    def year_lookup_bounds_for_date_field(self, value):
 
207
        first = '%s-01-01'
 
208
        second = '%s-12-31'
 
209
        return [first % value, second % value]
 
210
 
 
211
 
 
212
class DatabaseWrapper(BaseDatabaseWrapper):
 
213
 
 
214
    operators = {
 
215
        'exact': '= %s',
 
216
        'iexact': '= UPPER(%s)',
 
217
        'contains': "LIKEC %s ESCAPE '\\'",
 
218
        'icontains': "LIKEC UPPER(%s) ESCAPE '\\'",
 
219
        'gt': '> %s',
 
220
        'gte': '>= %s',
 
221
        'lt': '< %s',
 
222
        'lte': '<= %s',
 
223
        'startswith': "LIKEC %s ESCAPE '\\'",
 
224
        'endswith': "LIKEC %s ESCAPE '\\'",
 
225
        'istartswith': "LIKEC UPPER(%s) ESCAPE '\\'",
 
226
        'iendswith': "LIKEC UPPER(%s) ESCAPE '\\'",
 
227
    }
 
228
    oracle_version = None
 
229
 
 
230
    def __init__(self, *args, **kwargs):
 
231
        super(DatabaseWrapper, self).__init__(*args, **kwargs)
 
232
 
 
233
        self.features = DatabaseFeatures()
 
234
        self.ops = DatabaseOperations()
 
235
        self.client = DatabaseClient()
 
236
        self.creation = DatabaseCreation(self)
 
237
        self.introspection = DatabaseIntrospection(self)
 
238
        self.validation = BaseDatabaseValidation()
28
239
 
29
240
    def _valid_connection(self):
30
241
        return self.connection is not None
31
242
 
32
 
    def cursor(self):
33
 
        from django.conf import settings
 
243
    def _cursor(self, settings):
 
244
        cursor = None
34
245
        if not self._valid_connection():
35
246
            if len(settings.DATABASE_HOST.strip()) == 0:
36
247
                settings.DATABASE_HOST = 'localhost'
40
251
            else:
41
252
                conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
42
253
                self.connection = Database.connect(conn_string, **self.options)
43
 
        return FormatStylePlaceholderCursor(self.connection)
44
 
 
45
 
    def _commit(self):
46
 
        if self.connection is not None:
47
 
            self.connection.commit()
48
 
 
49
 
    def _rollback(self):
50
 
        if self.connection is not None:
51
 
            try:
52
 
                self.connection.rollback()
53
 
            except Database.NotSupportedError:
54
 
                pass
55
 
 
56
 
    def close(self):
57
 
        if self.connection is not None:
58
 
            self.connection.close()
59
 
            self.connection = None
60
 
 
61
 
supports_constraints = True
 
254
            cursor = FormatStylePlaceholderCursor(self.connection)
 
255
            # Set oracle date to ansi date format.  This only needs to execute
 
256
            # once when we create a new connection.
 
257
            cursor.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD' "
 
258
                           "NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF'")
 
259
            try:
 
260
                self.oracle_version = int(self.connection.version.split('.')[0])
 
261
                # There's no way for the DatabaseOperations class to know the
 
262
                # currently active Oracle version, so we do some setups here.
 
263
                # TODO: Multi-db support will need a better solution (a way to
 
264
                # communicate the current version).
 
265
                if self.oracle_version <= 9:
 
266
                    self.ops.regex_lookup = self.ops.regex_lookup_9
 
267
                else:
 
268
                    self.ops.regex_lookup = self.ops.regex_lookup_10
 
269
            except ValueError:
 
270
                pass
 
271
            try:
 
272
                self.connection.stmtcachesize = 20
 
273
            except:
 
274
                # Django docs specify cx_Oracle version 4.3.1 or higher, but
 
275
                # stmtcachesize is available only in 4.3.2 and up.
 
276
                pass
 
277
        if not cursor:
 
278
            cursor = FormatStylePlaceholderCursor(self.connection)
 
279
        # Default arraysize of 1 is highly sub-optimal.
 
280
        cursor.arraysize = 100
 
281
        return cursor
 
282
 
 
283
 
 
284
class OracleParam(object):
 
285
    """
 
286
    Wrapper object for formatting parameters for Oracle. If the string
 
287
    representation of the value is large enough (greater than 4000 characters)
 
288
    the input size needs to be set as NCLOB. Alternatively, if the parameter has
 
289
    an `input_size` attribute, then the value of the `input_size` attribute will
 
290
    be used instead. Otherwise, no input size will be set for the parameter when
 
291
    executing the query.
 
292
    """
 
293
    def __init__(self, param, charset, strings_only=False):
 
294
        self.smart_str = smart_str(param, charset, strings_only)
 
295
        if hasattr(param, 'input_size'):
 
296
            # If parameter has `input_size` attribute, use that.
 
297
            self.input_size = param.input_size
 
298
        elif isinstance(param, basestring) and len(param) > 4000:
 
299
            # Mark any string parameter greater than 4000 characters as an NCLOB.
 
300
            self.input_size = Database.NCLOB
 
301
        else:
 
302
            self.input_size = None
 
303
 
62
304
 
63
305
class FormatStylePlaceholderCursor(Database.Cursor):
64
306
    """
65
 
    Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var" style.
66
 
    This fixes it -- but note that if you want to use a literal "%s" in a query,
67
 
    you'll need to use "%%s".
 
307
    Django uses "format" (e.g. '%s') style placeholders, but Oracle uses ":var"
 
308
    style. This fixes it -- but note that if you want to use a literal "%s" in
 
309
    a query, you'll need to use "%%s".
 
310
 
 
311
    We also do automatic conversion between Unicode on the Python side and
 
312
    UTF-8 -- for talking to Oracle -- in here.
68
313
    """
 
314
    charset = 'utf-8'
 
315
 
 
316
    def _format_params(self, params):
 
317
        if isinstance(params, dict):
 
318
            result = {}
 
319
            for key, value in params.items():
 
320
                result[smart_str(key, self.charset)] = OracleParam(param, self.charset)
 
321
            return result
 
322
        else:
 
323
            return tuple([OracleParam(p, self.charset, True) for p in params])
 
324
 
 
325
    def _guess_input_sizes(self, params_list):
 
326
        if isinstance(params_list[0], dict):
 
327
            sizes = {}
 
328
            iterators = [params.iteritems() for params in params_list]
 
329
        else:
 
330
            sizes = [None] * len(params_list[0])
 
331
            iterators = [enumerate(params) for params in params_list]
 
332
        for iterator in iterators:
 
333
            for key, value in iterator:
 
334
                if value.input_size: sizes[key] = value.input_size
 
335
        if isinstance(sizes, dict):
 
336
            self.setinputsizes(**sizes)
 
337
        else:
 
338
            self.setinputsizes(*sizes)
 
339
 
 
340
    def _param_generator(self, params):
 
341
        if isinstance(params, dict):
 
342
            return dict([(k, p.smart_str) for k, p in params.iteritems()])
 
343
        else:
 
344
            return [p.smart_str for p in params]
 
345
 
69
346
    def execute(self, query, params=None):
70
 
        if params is None: params = []
71
 
        query = self.convert_arguments(query, len(params))
72
 
        return Database.Cursor.execute(self, query, params)
 
347
        if params is None:
 
348
            params = []
 
349
        else:
 
350
            params = self._format_params(params)
 
351
        args = [(':arg%d' % i) for i in range(len(params))]
 
352
        # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it
 
353
        # it does want a trailing ';' but not a trailing '/'.  However, these
 
354
        # characters must be included in the original query in case the query
 
355
        # is being passed to SQL*Plus.
 
356
        if query.endswith(';') or query.endswith('/'):
 
357
            query = query[:-1]
 
358
        query = smart_str(query, self.charset) % tuple(args)
 
359
        self._guess_input_sizes([params])
 
360
        try:
 
361
            return Database.Cursor.execute(self, query, self._param_generator(params))
 
362
        except DatabaseError, e:
 
363
            # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
 
364
            if e.message.code == 1400 and type(e) != IntegrityError:
 
365
                e = IntegrityError(e.message)
 
366
            raise e
73
367
 
74
368
    def executemany(self, query, params=None):
75
 
        if params is None: params = []
76
 
        query = self.convert_arguments(query, len(params[0]))
77
 
        return Database.Cursor.executemany(self, query, params)
78
 
 
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
 
 
84
 
def quote_name(name):
85
 
    return name
86
 
 
87
 
dictfetchone = util.dictfetchone
88
 
dictfetchmany = util.dictfetchmany
89
 
dictfetchall  = util.dictfetchall
90
 
 
91
 
def get_last_insert_id(cursor, table_name, pk_name):
92
 
    query = "SELECT %s_sq.currval from dual" % table_name
93
 
    cursor.execute(query)
94
 
    return cursor.fetchone()[0]
95
 
 
96
 
def get_date_extract_sql(lookup_type, table_name):
97
 
    # lookup_type is 'year', 'month', 'day'
98
 
    # http://www.psoug.org/reference/date_func.html
99
 
    return "EXTRACT(%s FROM %s)" % (lookup_type, table_name)
100
 
 
101
 
def get_date_trunc_sql(lookup_type, field_name):
102
 
    return "EXTRACT(%s FROM TRUNC(%s))" % (lookup_type, field_name)
103
 
 
104
 
def get_limit_offset_sql(limit, offset=None):
105
 
    # Limits and offset are too complicated to be handled here.
106
 
    # Instead, they are handled in django/db/query.py.
107
 
    pass
108
 
 
109
 
def get_random_function_sql():
110
 
    return "DBMS_RANDOM.RANDOM"
111
 
 
112
 
def get_deferrable_sql():
113
 
    return " DEFERRABLE INITIALLY DEFERRED"
114
 
 
115
 
def get_fulltext_search_sql(field_name):
116
 
    raise NotImplementedError
117
 
 
118
 
def get_drop_foreignkey_sql():
119
 
    return "DROP FOREIGN KEY"
120
 
 
121
 
def get_pk_default_value():
122
 
    return "DEFAULT"
123
 
 
124
 
def get_sql_flush(style, tables, sequences):
125
 
    """Return a list of SQL statements required to remove all data from
126
 
    all tables in the database (without actually removing the tables
127
 
    themselves) and put the database in an empty 'initial' state
128
 
    """
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]
136
 
 
137
 
 
138
 
OPERATOR_MAPPING = {
139
 
    'exact': '= %s',
140
 
    'iexact': 'LIKE %s',
141
 
    'contains': 'LIKE %s',
142
 
    'icontains': 'LIKE %s',
143
 
    'gt': '> %s',
144
 
    'gte': '>= %s',
145
 
    'lt': '< %s',
146
 
    'lte': '<= %s',
147
 
    'startswith': 'LIKE %s',
148
 
    'endswith': 'LIKE %s',
149
 
    'istartswith': 'LIKE %s',
150
 
    'iendswith': 'LIKE %s',
151
 
}
 
369
        try:
 
370
          args = [(':arg%d' % i) for i in range(len(params[0]))]
 
371
        except (IndexError, TypeError):
 
372
          # No params given, nothing to do
 
373
          return None
 
374
        # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it
 
375
        # it does want a trailing ';' but not a trailing '/'.  However, these
 
376
        # characters must be included in the original query in case the query
 
377
        # is being passed to SQL*Plus.
 
378
        if query.endswith(';') or query.endswith('/'):
 
379
            query = query[:-1]
 
380
        query = smart_str(query, self.charset) % tuple(args)
 
381
        formatted = [self._format_params(i) for i in params]
 
382
        self._guess_input_sizes(formatted)
 
383
        try:
 
384
            return Database.Cursor.executemany(self, query, [self._param_generator(p) for p in formatted])
 
385
        except DatabaseError, e:
 
386
            # cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
 
387
            if e.message.code == 1400 and type(e) != IntegrityError:
 
388
                e = IntegrityError(e.message)
 
389
            raise e
 
390
 
 
391
    def fetchone(self):
 
392
        row = Database.Cursor.fetchone(self)
 
393
        if row is None:
 
394
            return row
 
395
        return tuple([to_unicode(e) for e in row])
 
396
 
 
397
    def fetchmany(self, size=None):
 
398
        if size is None:
 
399
            size = self.arraysize
 
400
        return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchmany(self, size)])
 
401
 
 
402
    def fetchall(self):
 
403
        return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchall(self)])
 
404
 
 
405
def to_unicode(s):
 
406
    """
 
407
    Convert strings to Unicode objects (and return all other data types
 
408
    unchanged).
 
409
    """
 
410
    if isinstance(s, basestring):
 
411
        return force_unicode(s)
 
412
    return s
 
413
 
 
414
def _get_sequence_reset_sql():
 
415
    # TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
 
416
    return """
 
417
        DECLARE
 
418
            startvalue integer;
 
419
            cval integer;
 
420
        BEGIN
 
421
            LOCK TABLE %(table)s IN SHARE MODE;
 
422
            SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
 
423
            SELECT %(sequence)s.nextval INTO cval FROM dual;
 
424
            cval := startvalue - cval;
 
425
            IF cval != 0 THEN
 
426
                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s MINVALUE 0 INCREMENT BY '||cval;
 
427
                SELECT %(sequence)s.nextval INTO cval FROM dual;
 
428
                EXECUTE IMMEDIATE 'ALTER SEQUENCE %(sequence)s INCREMENT BY 1';
 
429
            END IF;
 
430
            COMMIT;
 
431
        END;
 
432
        /"""
 
433
 
 
434
def get_sequence_name(table):
 
435
    name_length = DatabaseOperations().max_name_length() - 3
 
436
    return '%s_SQ' % util.truncate_name(table, name_length).upper()
 
437
 
 
438
def get_trigger_name(table):
 
439
    name_length = DatabaseOperations().max_name_length() - 3
 
440
    return '%s_TR' % util.truncate_name(table, name_length).upper()