Reformatted PL/SQL flush left so "manage.py sqlall [app] | manage.py dbshell" works with Oracle. Also some PEP8 cleanup.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@9644 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
f1f1d366c8
commit
5257fd2225
|
@ -1,7 +1,7 @@
|
||||||
"""
|
"""
|
||||||
Oracle database backend for Django.
|
Oracle database backend for Django.
|
||||||
|
|
||||||
Requires cx_Oracle: http://www.python.net/crew/atuining/cx_Oracle/
|
Requires cx_Oracle: http://cx-oracle.sourceforge.net/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -35,6 +35,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
|
||||||
|
|
||||||
|
|
||||||
class DatabaseOperations(BaseDatabaseOperations):
|
class DatabaseOperations(BaseDatabaseOperations):
|
||||||
|
|
||||||
def autoinc_sql(self, table, column):
|
def autoinc_sql(self, table, column):
|
||||||
# To simulate auto-incrementing primary keys in Oracle, we have to
|
# To simulate auto-incrementing primary keys in Oracle, we have to
|
||||||
# create a sequence and a trigger.
|
# create a sequence and a trigger.
|
||||||
|
@ -43,26 +44,26 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
tbl_name = self.quote_name(table)
|
tbl_name = self.quote_name(table)
|
||||||
col_name = self.quote_name(column)
|
col_name = self.quote_name(column)
|
||||||
sequence_sql = """
|
sequence_sql = """
|
||||||
DECLARE
|
DECLARE
|
||||||
i INTEGER;
|
i INTEGER;
|
||||||
BEGIN
|
BEGIN
|
||||||
SELECT COUNT(*) INTO i FROM USER_CATALOG
|
SELECT COUNT(*) INTO i FROM USER_CATALOG
|
||||||
WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
|
WHERE TABLE_NAME = '%(sq_name)s' AND TABLE_TYPE = 'SEQUENCE';
|
||||||
IF i = 0 THEN
|
IF i = 0 THEN
|
||||||
EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
|
EXECUTE IMMEDIATE 'CREATE SEQUENCE "%(sq_name)s"';
|
||||||
END IF;
|
END IF;
|
||||||
END;
|
END;
|
||||||
/""" % locals()
|
/""" % locals()
|
||||||
trigger_sql = """
|
trigger_sql = """
|
||||||
CREATE OR REPLACE TRIGGER "%(tr_name)s"
|
CREATE OR REPLACE TRIGGER "%(tr_name)s"
|
||||||
BEFORE INSERT ON %(tbl_name)s
|
BEFORE INSERT ON %(tbl_name)s
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
WHEN (new.%(col_name)s IS NULL)
|
WHEN (new.%(col_name)s IS NULL)
|
||||||
BEGIN
|
BEGIN
|
||||||
SELECT "%(sq_name)s".nextval
|
SELECT "%(sq_name)s".nextval
|
||||||
INTO :new.%(col_name)s FROM dual;
|
INTO :new.%(col_name)s FROM dual;
|
||||||
END;
|
END;
|
||||||
/""" % locals()
|
/""" % locals()
|
||||||
return sequence_sql, trigger_sql
|
return sequence_sql, trigger_sql
|
||||||
|
|
||||||
def date_extract_sql(self, lookup_type, field_name):
|
def date_extract_sql(self, lookup_type, field_name):
|
||||||
|
@ -118,7 +119,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
# always defaults to uppercase.
|
# always defaults to uppercase.
|
||||||
# We simplify things by making Oracle identifiers always uppercase.
|
# We simplify things by making Oracle identifiers always uppercase.
|
||||||
if not name.startswith('"') and not name.endswith('"'):
|
if not name.startswith('"') and not name.endswith('"'):
|
||||||
name = '"%s"' % util.truncate_name(name.upper(), self.max_name_length())
|
name = '"%s"' % util.truncate_name(name.upper(),
|
||||||
|
self.max_name_length())
|
||||||
return name.upper()
|
return name.upper()
|
||||||
|
|
||||||
def random_function_sql(self):
|
def random_function_sql(self):
|
||||||
|
@ -150,8 +152,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
sql = ['%s %s %s;' % \
|
sql = ['%s %s %s;' % \
|
||||||
(style.SQL_KEYWORD('DELETE'),
|
(style.SQL_KEYWORD('DELETE'),
|
||||||
style.SQL_KEYWORD('FROM'),
|
style.SQL_KEYWORD('FROM'),
|
||||||
style.SQL_FIELD(self.quote_name(table))
|
style.SQL_FIELD(self.quote_name(table)))
|
||||||
) for table in tables]
|
for table in tables]
|
||||||
# Since we've just deleted all the rows, running our sequence
|
# Since we've just deleted all the rows, running our sequence
|
||||||
# ALTER code will reset the sequence to 0.
|
# ALTER code will reset the sequence to 0.
|
||||||
for sequence_info in sequences:
|
for sequence_info in sequences:
|
||||||
|
@ -179,7 +181,9 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
output.append(query % {'sequence': sequence_name,
|
output.append(query % {'sequence': sequence_name,
|
||||||
'table': table_name,
|
'table': table_name,
|
||||||
'column': column_name})
|
'column': column_name})
|
||||||
break # Only one AutoField is allowed per model, so don't bother continuing.
|
# Only one AutoField is allowed per model, so don't
|
||||||
|
# continue to loop
|
||||||
|
break
|
||||||
for f in model._meta.many_to_many:
|
for f in model._meta.many_to_many:
|
||||||
table_name = self.quote_name(f.m2m_db_table())
|
table_name = self.quote_name(f.m2m_db_table())
|
||||||
sequence_name = get_sequence_name(f.m2m_db_table())
|
sequence_name = get_sequence_name(f.m2m_db_table())
|
||||||
|
@ -193,7 +197,8 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def tablespace_sql(self, tablespace, inline=False):
|
def tablespace_sql(self, tablespace, inline=False):
|
||||||
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""), self.quote_name(tablespace))
|
return "%sTABLESPACE %s" % ((inline and "USING INDEX " or ""),
|
||||||
|
self.quote_name(tablespace))
|
||||||
|
|
||||||
def value_to_db_time(self, value):
|
def value_to_db_time(self, value):
|
||||||
if value is None:
|
if value is None:
|
||||||
|
@ -246,10 +251,16 @@ class DatabaseWrapper(BaseDatabaseWrapper):
|
||||||
if len(settings.DATABASE_HOST.strip()) == 0:
|
if len(settings.DATABASE_HOST.strip()) == 0:
|
||||||
settings.DATABASE_HOST = 'localhost'
|
settings.DATABASE_HOST = 'localhost'
|
||||||
if len(settings.DATABASE_PORT.strip()) != 0:
|
if len(settings.DATABASE_PORT.strip()) != 0:
|
||||||
dsn = Database.makedsn(settings.DATABASE_HOST, int(settings.DATABASE_PORT), settings.DATABASE_NAME)
|
dsn = Database.makedsn(settings.DATABASE_HOST,
|
||||||
self.connection = Database.connect(settings.DATABASE_USER, settings.DATABASE_PASSWORD, dsn, **self.options)
|
int(settings.DATABASE_PORT),
|
||||||
|
settings.DATABASE_NAME)
|
||||||
|
self.connection = Database.connect(settings.DATABASE_USER,
|
||||||
|
settings.DATABASE_PASSWORD,
|
||||||
|
dsn, **self.options)
|
||||||
else:
|
else:
|
||||||
conn_string = "%s/%s@%s" % (settings.DATABASE_USER, settings.DATABASE_PASSWORD, settings.DATABASE_NAME)
|
conn_string = "%s/%s@%s" % (settings.DATABASE_USER,
|
||||||
|
settings.DATABASE_PASSWORD,
|
||||||
|
settings.DATABASE_NAME)
|
||||||
self.connection = Database.connect(conn_string, **self.options)
|
self.connection = Database.connect(conn_string, **self.options)
|
||||||
cursor = FormatStylePlaceholderCursor(self.connection)
|
cursor = FormatStylePlaceholderCursor(self.connection)
|
||||||
# Set oracle date to ansi date format. This only needs to execute
|
# Set oracle date to ansi date format. This only needs to execute
|
||||||
|
@ -285,18 +296,19 @@ class OracleParam(object):
|
||||||
"""
|
"""
|
||||||
Wrapper object for formatting parameters for Oracle. If the string
|
Wrapper object for formatting parameters for Oracle. If the string
|
||||||
representation of the value is large enough (greater than 4000 characters)
|
representation of the value is large enough (greater than 4000 characters)
|
||||||
the input size needs to be set as NCLOB. Alternatively, if the parameter has
|
the input size needs to be set as NCLOB. Alternatively, if the parameter
|
||||||
an `input_size` attribute, then the value of the `input_size` attribute will
|
has an `input_size` attribute, then the value of the `input_size` attribute
|
||||||
be used instead. Otherwise, no input size will be set for the parameter when
|
will be used instead. Otherwise, no input size will be set for the
|
||||||
executing the query.
|
parameter when executing the query.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, param, charset, strings_only=False):
|
def __init__(self, param, charset, strings_only=False):
|
||||||
self.smart_str = smart_str(param, charset, strings_only)
|
self.smart_str = smart_str(param, charset, strings_only)
|
||||||
if hasattr(param, 'input_size'):
|
if hasattr(param, 'input_size'):
|
||||||
# If parameter has `input_size` attribute, use that.
|
# If parameter has `input_size` attribute, use that.
|
||||||
self.input_size = param.input_size
|
self.input_size = param.input_size
|
||||||
elif isinstance(param, basestring) and len(param) > 4000:
|
elif isinstance(param, basestring) and len(param) > 4000:
|
||||||
# Mark any string parameter greater than 4000 characters as an NCLOB.
|
# Mark any string param greater than 4000 characters as an NCLOB.
|
||||||
self.input_size = Database.NCLOB
|
self.input_size = Database.NCLOB
|
||||||
else:
|
else:
|
||||||
self.input_size = None
|
self.input_size = None
|
||||||
|
@ -320,7 +332,8 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
sizes = [None] * len(params_list[0])
|
sizes = [None] * len(params_list[0])
|
||||||
for params in params_list:
|
for params in params_list:
|
||||||
for i, value in enumerate(params):
|
for i, value in enumerate(params):
|
||||||
if value.input_size: sizes[i] = value.input_size
|
if value.input_size:
|
||||||
|
sizes[i] = value.input_size
|
||||||
self.setinputsizes(*sizes)
|
self.setinputsizes(*sizes)
|
||||||
|
|
||||||
def _param_generator(self, params):
|
def _param_generator(self, params):
|
||||||
|
@ -341,7 +354,8 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
query = smart_str(query, self.charset) % tuple(args)
|
query = smart_str(query, self.charset) % tuple(args)
|
||||||
self._guess_input_sizes([params])
|
self._guess_input_sizes([params])
|
||||||
try:
|
try:
|
||||||
return Database.Cursor.execute(self, query, self._param_generator(params))
|
return Database.Cursor.execute(self, query,
|
||||||
|
self._param_generator(params))
|
||||||
except DatabaseError, e:
|
except DatabaseError, e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
|
@ -350,10 +364,10 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
|
|
||||||
def executemany(self, query, params=None):
|
def executemany(self, query, params=None):
|
||||||
try:
|
try:
|
||||||
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
args = [(':arg%d' % i) for i in range(len(params[0]))]
|
||||||
except (IndexError, TypeError):
|
except (IndexError, TypeError):
|
||||||
# No params given, nothing to do
|
# No params given, nothing to do
|
||||||
return None
|
return None
|
||||||
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
# cx_Oracle wants no trailing ';' for SQL statements. For PL/SQL, it
|
||||||
# it does want a trailing ';' but not a trailing '/'. However, these
|
# it does want a trailing ';' but not a trailing '/'. However, these
|
||||||
# characters must be included in the original query in case the query
|
# characters must be included in the original query in case the query
|
||||||
|
@ -364,7 +378,8 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
formatted = [self._format_params(i) for i in params]
|
formatted = [self._format_params(i) for i in params]
|
||||||
self._guess_input_sizes(formatted)
|
self._guess_input_sizes(formatted)
|
||||||
try:
|
try:
|
||||||
return Database.Cursor.executemany(self, query, [self._param_generator(p) for p in formatted])
|
return Database.Cursor.executemany(self, query,
|
||||||
|
[self._param_generator(p) for p in formatted])
|
||||||
except DatabaseError, e:
|
except DatabaseError, e:
|
||||||
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
# cx_Oracle <= 4.4.0 wrongly raises a DatabaseError for ORA-01400.
|
||||||
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
if e.args[0].code == 1400 and not isinstance(e, IntegrityError):
|
||||||
|
@ -380,10 +395,13 @@ class FormatStylePlaceholderCursor(Database.Cursor):
|
||||||
def fetchmany(self, size=None):
|
def fetchmany(self, size=None):
|
||||||
if size is None:
|
if size is None:
|
||||||
size = self.arraysize
|
size = self.arraysize
|
||||||
return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchmany(self, size)])
|
return tuple([tuple([to_unicode(e) for e in r])
|
||||||
|
for r in Database.Cursor.fetchmany(self, size)])
|
||||||
|
|
||||||
def fetchall(self):
|
def fetchall(self):
|
||||||
return tuple([tuple([to_unicode(e) for e in r]) for r in Database.Cursor.fetchall(self)])
|
return tuple([tuple([to_unicode(e) for e in r])
|
||||||
|
for r in Database.Cursor.fetchall(self)])
|
||||||
|
|
||||||
|
|
||||||
def to_unicode(s):
|
def to_unicode(s):
|
||||||
"""
|
"""
|
||||||
|
@ -394,30 +412,33 @@ def to_unicode(s):
|
||||||
return force_unicode(s)
|
return force_unicode(s)
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
def _get_sequence_reset_sql():
|
def _get_sequence_reset_sql():
|
||||||
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
# TODO: colorize this SQL code with style.SQL_KEYWORD(), etc.
|
||||||
return """
|
return """
|
||||||
DECLARE
|
DECLARE
|
||||||
startvalue integer;
|
startvalue integer;
|
||||||
cval integer;
|
cval integer;
|
||||||
BEGIN
|
BEGIN
|
||||||
LOCK TABLE %(table)s IN SHARE MODE;
|
LOCK TABLE %(table)s IN SHARE MODE;
|
||||||
SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
|
SELECT NVL(MAX(%(column)s), 0) INTO startvalue FROM %(table)s;
|
||||||
SELECT "%(sequence)s".nextval INTO cval FROM dual;
|
SELECT "%(sequence)s".nextval INTO cval FROM dual;
|
||||||
cval := startvalue - cval;
|
cval := startvalue - cval;
|
||||||
IF cval != 0 THEN
|
IF cval != 0 THEN
|
||||||
EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" MINVALUE 0 INCREMENT BY '||cval;
|
EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" MINVALUE 0 INCREMENT BY '||cval;
|
||||||
SELECT "%(sequence)s".nextval INTO cval FROM dual;
|
SELECT "%(sequence)s".nextval INTO cval FROM dual;
|
||||||
EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" INCREMENT BY 1';
|
EXECUTE IMMEDIATE 'ALTER SEQUENCE "%(sequence)s" INCREMENT BY 1';
|
||||||
END IF;
|
END IF;
|
||||||
COMMIT;
|
COMMIT;
|
||||||
END;
|
END;
|
||||||
/"""
|
/"""
|
||||||
|
|
||||||
|
|
||||||
def get_sequence_name(table):
|
def get_sequence_name(table):
|
||||||
name_length = DatabaseOperations().max_name_length() - 3
|
name_length = DatabaseOperations().max_name_length() - 3
|
||||||
return '%s_SQ' % util.truncate_name(table, name_length).upper()
|
return '%s_SQ' % util.truncate_name(table, name_length).upper()
|
||||||
|
|
||||||
|
|
||||||
def get_trigger_name(table):
|
def get_trigger_name(table):
|
||||||
name_length = DatabaseOperations().max_name_length() - 3
|
name_length = DatabaseOperations().max_name_length() - 3
|
||||||
return '%s_TR' % util.truncate_name(table, name_length).upper()
|
return '%s_TR' % util.truncate_name(table, name_length).upper()
|
||||||
|
|
Loading…
Reference in New Issue