162 lines
5.6 KiB
Python
162 lines
5.6 KiB
Python
|
import re
|
||
|
import sys
|
||
|
from .._compat import integer_types, long
|
||
|
from ..helpers.classes import Reference
|
||
|
from .base import SQLAdapter
|
||
|
from . import adapters, with_connection_or_raise
|
||
|
|
||
|
@adapters.register_for('oracle')
|
||
|
class Oracle(SQLAdapter):
|
||
|
dbengine = 'oracle'
|
||
|
drivers = ('cx_Oracle',)
|
||
|
|
||
|
cmd_fix = re.compile(
|
||
|
"[^']*('[^']*'[^']*)*\:(?P<clob>CLOB\('([^']+|'')*'\))")
|
||
|
|
||
|
def _initialize_(self, do_connect):
|
||
|
super(Oracle, self)._initialize_(do_connect)
|
||
|
self.ruri = self.uri.split('://', 1)[1]
|
||
|
if 'threaded' not in self.driver_args:
|
||
|
self.driver_args['threaded'] = True
|
||
|
|
||
|
def connector(self):
|
||
|
return self.driver.connect(self.ruri, **self.driver_args)
|
||
|
|
||
|
def after_connection(self):
|
||
|
self.execute(
|
||
|
"ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';")
|
||
|
self.execute(
|
||
|
"ALTER SESSION SET NLS_TIMESTAMP_FORMAT = " +
|
||
|
"'YYYY-MM-DD HH24:MI:SS';")
|
||
|
|
||
|
def test_connection(self):
|
||
|
self.execute('SELECT 1 FROM DUAL;')
|
||
|
|
||
|
@with_connection_or_raise
|
||
|
def execute(self, *args, **kwargs):
|
||
|
command = self.filter_sql_command(args[0])
|
||
|
i = 1
|
||
|
while True:
|
||
|
m = self.cmd_fix.match(command)
|
||
|
if not m:
|
||
|
break
|
||
|
command = command[:m.start('clob')] + str(i) + \
|
||
|
command[m.end('clob'):]
|
||
|
args.append(m.group('clob')[6:-2].replace("''", "'"))
|
||
|
i += 1
|
||
|
if command[-1:] == ';':
|
||
|
command = command[:-1]
|
||
|
handlers = self._build_handlers_for_execution()
|
||
|
for handler in handlers:
|
||
|
handler.before_execute(command)
|
||
|
rv = self.cursor.execute(command, *args[1:], **kwargs)
|
||
|
for handler in handlers:
|
||
|
handler.after_execute(command)
|
||
|
return rv
|
||
|
|
||
|
def lastrowid(self, table):
|
||
|
sequence_name = table._sequence_name
|
||
|
self.execute('SELECT %s.currval FROM dual;' % sequence_name)
|
||
|
return long(self.cursor.fetchone()[0])
|
||
|
|
||
|
def create_sequence_and_triggers(self, query, table, **args):
|
||
|
tablename = table._rname
|
||
|
id_name = table._id._rname
|
||
|
sequence_name = table._sequence_name
|
||
|
trigger_name = table._trigger_name
|
||
|
self.execute(query)
|
||
|
self.execute(
|
||
|
'CREATE SEQUENCE %s START WITH 1 INCREMENT BY 1 NOMAXVALUE MINVALUE -1;'
|
||
|
% sequence_name)
|
||
|
self.execute(_trigger_sql % dict(
|
||
|
trigger_name=trigger_name, tablename=tablename,
|
||
|
sequence_name=sequence_name,
|
||
|
id=id_name)
|
||
|
)
|
||
|
|
||
|
def _select_aux_execute(self, sql):
|
||
|
self.execute(sql)
|
||
|
return self.fetchall()
|
||
|
|
||
|
def fetchall(self):
|
||
|
from ..drivers import cx_Oracle
|
||
|
if any(x[1] == cx_Oracle.LOB or x[1] == cx_Oracle.CLOB
|
||
|
for x in self.cursor.description):
|
||
|
return [tuple(
|
||
|
[(c.read() if type(c) == cx_Oracle.LOB else c) for c in r]
|
||
|
) for r in self.cursor]
|
||
|
else:
|
||
|
return self.cursor.fetchall()
|
||
|
|
||
|
def sqlsafe_table(self, tablename, original_tablename=None):
|
||
|
if original_tablename is not None:
|
||
|
return (
|
||
|
self.dialect.quote_template + ' ' +
|
||
|
self.dialect.quote_template
|
||
|
) % (original_tablename, tablename)
|
||
|
return self.dialect.quote(tablename)
|
||
|
|
||
|
def _build_value_for_insert(self, field, value, r_values):
|
||
|
if field.type is 'text':
|
||
|
r_values[':' + field._rname] = self.expand(value, field.type)
|
||
|
return ':' + field._rname
|
||
|
return self.expand(value, field.type)
|
||
|
|
||
|
def _insert(self, table, fields):
|
||
|
if fields:
|
||
|
r_values = {}
|
||
|
return self.dialect.insert(
|
||
|
table._rname,
|
||
|
','.join(el[0]._rname for el in fields),
|
||
|
','.join(
|
||
|
self._build_value_for_insert(f, v, r_values)
|
||
|
for f, v in fields)
|
||
|
), r_values
|
||
|
return self.dialect.insert_empty(table._rname), None
|
||
|
|
||
|
def insert(self, table, fields):
|
||
|
query, values = self._insert(table, fields)
|
||
|
try:
|
||
|
if not values:
|
||
|
self.execute(query)
|
||
|
else:
|
||
|
self.execute(query, *values)
|
||
|
except:
|
||
|
e = sys.exc_info()[1]
|
||
|
if hasattr(table, '_on_insert_error'):
|
||
|
return table._on_insert_error(table, fields, e)
|
||
|
raise e
|
||
|
if hasattr(table, '_primarykey'):
|
||
|
pkdict = dict([
|
||
|
(k[0].name, k[1]) for k in fields
|
||
|
if k[0].name in table._primarykey])
|
||
|
if pkdict:
|
||
|
return pkdict
|
||
|
id = self.lastrowid(table)
|
||
|
if hasattr(table, '_primarykey') and len(table._primarykey) == 1:
|
||
|
id = {table._primarykey[0]: id}
|
||
|
if not isinstance(id, integer_types):
|
||
|
return id
|
||
|
rid = Reference(id)
|
||
|
(rid._table, rid._record) = (table, None)
|
||
|
return rid
|
||
|
|
||
|
_trigger_sql = """
|
||
|
CREATE OR REPLACE TRIGGER %(trigger_name)s BEFORE INSERT ON %(tablename)s FOR EACH ROW
|
||
|
DECLARE
|
||
|
curr_val NUMBER;
|
||
|
diff_val NUMBER;
|
||
|
PRAGMA autonomous_transaction;
|
||
|
BEGIN
|
||
|
IF :NEW.%(id)s IS NOT NULL THEN
|
||
|
EXECUTE IMMEDIATE 'SELECT %(sequence_name)s.nextval FROM dual' INTO curr_val;
|
||
|
diff_val := :NEW.%(id)s - curr_val - 1;
|
||
|
IF diff_val != 0 THEN
|
||
|
EXECUTE IMMEDIATE 'alter sequence %(sequence_name)s increment by '|| diff_val;
|
||
|
EXECUTE IMMEDIATE 'SELECT %(sequence_name)s.nextval FROM dual' INTO curr_val;
|
||
|
EXECUTE IMMEDIATE 'alter sequence %(sequence_name)s increment by 1';
|
||
|
END IF;
|
||
|
END IF;
|
||
|
SELECT %(sequence_name)s.nextval INTO :NEW.%(id)s FROM DUAL;
|
||
|
END;"""
|