Remove Elixir library
Update SQLAlchemy
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# oracle/__init__.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -12,7 +12,7 @@ from sqlalchemy.dialects.oracle.base import \
|
||||
VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, NUMBER,\
|
||||
BLOB, BFILE, CLOB, NCLOB, TIMESTAMP, RAW,\
|
||||
FLOAT, DOUBLE_PRECISION, LONG, dialect, INTERVAL,\
|
||||
VARCHAR2, NVARCHAR2, ROWID
|
||||
VARCHAR2, NVARCHAR2, ROWID, dialect
|
||||
|
||||
|
||||
__all__ = (
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
# oracle/base.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Support for the Oracle database.
|
||||
"""
|
||||
.. dialect:: oracle
|
||||
:name: Oracle
|
||||
|
||||
Oracle version 8 through current (11g at the time of this writing) are supported.
|
||||
|
||||
For information on connecting via specific drivers, see the documentation
|
||||
for that driver.
|
||||
Oracle version 8 through current (11g at the time of this writing) are supported.
|
||||
|
||||
Connect Arguments
|
||||
-----------------
|
||||
@@ -17,12 +16,12 @@ Connect Arguments
|
||||
The dialect supports several :func:`~sqlalchemy.create_engine()` arguments which
|
||||
affect the behavior of the dialect regardless of driver in use.
|
||||
|
||||
* *use_ansi* - Use ANSI JOIN constructs (see the section on Oracle 8). Defaults
|
||||
* ``use_ansi`` - Use ANSI JOIN constructs (see the section on Oracle 8). Defaults
|
||||
to ``True``. If ``False``, Oracle-8 compatible constructs are used for joins.
|
||||
|
||||
* *optimize_limits* - defaults to ``False``. see the section on LIMIT/OFFSET.
|
||||
* ``optimize_limits`` - defaults to ``False``. see the section on LIMIT/OFFSET.
|
||||
|
||||
* *use_binds_for_limits* - defaults to ``True``. see the section on LIMIT/OFFSET.
|
||||
* ``use_binds_for_limits`` - defaults to ``True``. see the section on LIMIT/OFFSET.
|
||||
|
||||
Auto Increment Behavior
|
||||
-----------------------
|
||||
@@ -100,6 +99,41 @@ http://www.sqlalchemy.org/trac/wiki/UsageRecipes/WindowFunctionsByDefault
|
||||
which installs a select compiler that overrides the generation of limit/offset with
|
||||
a window function.
|
||||
|
||||
RETURNING Support
|
||||
-----------------
|
||||
|
||||
The Oracle database supports a limited form of RETURNING, in order to retrieve result
|
||||
sets of matched rows from INSERT, UPDATE and DELETE statements. Oracle's
|
||||
RETURNING..INTO syntax only supports one row being returned, as it relies upon
|
||||
OUT parameters in order to function. In addition, supported DBAPIs have further
|
||||
limitations (see :ref:`cx_oracle_returning`).
|
||||
|
||||
SQLAlchemy's "implicit returning" feature, which employs RETURNING within an INSERT
|
||||
and sometimes an UPDATE statement in order to fetch newly generated primary key values
|
||||
and other SQL defaults and expressions, is normally enabled on the Oracle
|
||||
backend. By default, "implicit returning" typically only fetches the value of a
|
||||
single ``nextval(some_seq)`` expression embedded into an INSERT in order to increment
|
||||
a sequence within an INSERT statement and get the value back at the same time.
|
||||
To disable this feature across the board, specify ``implicit_returning=False`` to
|
||||
:func:`.create_engine`::
|
||||
|
||||
engine = create_engine("oracle://scott:tiger@dsn", implicit_returning=False)
|
||||
|
||||
Implicit returning can also be disabled on a table-by-table basis as a table option::
|
||||
|
||||
# Core Table
|
||||
my_table = Table("my_table", metadata, ..., implicit_returning=False)
|
||||
|
||||
|
||||
# declarative
|
||||
class MyClass(Base):
|
||||
__tablename__ = 'my_table'
|
||||
__table_args__ = {"implicit_returning": False}
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`cx_oracle_returning` - additional cx_oracle-specific restrictions on implicit returning.
|
||||
|
||||
ON UPDATE CASCADE
|
||||
-----------------
|
||||
|
||||
@@ -134,16 +168,16 @@ Synonym/DBLINK Reflection
|
||||
-------------------------
|
||||
|
||||
When using reflection with Table objects, the dialect can optionally search for tables
|
||||
indicated by synonyms that reference DBLINK-ed tables by passing the flag
|
||||
oracle_resolve_synonyms=True as a keyword argument to the Table construct. If DBLINK
|
||||
is not in use this flag should be left off.
|
||||
indicated by synonyms, either in local or remote schemas or accessed over DBLINK,
|
||||
by passing the flag oracle_resolve_synonyms=True as a
|
||||
keyword argument to the Table construct. If synonyms are not in use
|
||||
this flag should be left off.
|
||||
|
||||
"""
|
||||
|
||||
import random, re
|
||||
import re
|
||||
|
||||
from sqlalchemy import schema as sa_schema
|
||||
from sqlalchemy import util, sql, log
|
||||
from sqlalchemy import util, sql
|
||||
from sqlalchemy.engine import default, base, reflection
|
||||
from sqlalchemy.sql import compiler, visitors, expression
|
||||
from sqlalchemy.sql import operators as sql_operators, functions as sql_functions
|
||||
@@ -165,18 +199,22 @@ RESERVED_WORDS = \
|
||||
NO_ARG_FNS = set('UID CURRENT_DATE SYSDATE USER '
|
||||
'CURRENT_TIME CURRENT_TIMESTAMP'.split())
|
||||
|
||||
|
||||
class RAW(sqltypes._Binary):
|
||||
__visit_name__ = 'RAW'
|
||||
OracleRaw = RAW
|
||||
|
||||
|
||||
class NCLOB(sqltypes.Text):
|
||||
__visit_name__ = 'NCLOB'
|
||||
|
||||
|
||||
class VARCHAR2(VARCHAR):
|
||||
__visit_name__ = 'VARCHAR2'
|
||||
|
||||
NVARCHAR2 = NVARCHAR
|
||||
|
||||
|
||||
class NUMBER(sqltypes.Numeric, sqltypes.Integer):
|
||||
__visit_name__ = 'NUMBER'
|
||||
|
||||
@@ -202,18 +240,22 @@ class NUMBER(sqltypes.Numeric, sqltypes.Integer):
|
||||
|
||||
class DOUBLE_PRECISION(sqltypes.Numeric):
|
||||
__visit_name__ = 'DOUBLE_PRECISION'
|
||||
|
||||
def __init__(self, precision=None, scale=None, asdecimal=None):
|
||||
if asdecimal is None:
|
||||
asdecimal = False
|
||||
|
||||
super(DOUBLE_PRECISION, self).__init__(precision=precision, scale=scale, asdecimal=asdecimal)
|
||||
|
||||
|
||||
class BFILE(sqltypes.LargeBinary):
|
||||
__visit_name__ = 'BFILE'
|
||||
|
||||
|
||||
class LONG(sqltypes.Text):
|
||||
__visit_name__ = 'LONG'
|
||||
|
||||
|
||||
class INTERVAL(sqltypes.TypeEngine):
|
||||
__visit_name__ = 'INTERVAL'
|
||||
|
||||
@@ -244,6 +286,7 @@ class INTERVAL(sqltypes.TypeEngine):
|
||||
def _type_affinity(self):
|
||||
return sqltypes.Interval
|
||||
|
||||
|
||||
class ROWID(sqltypes.TypeEngine):
|
||||
"""Oracle ROWID type.
|
||||
|
||||
@@ -253,33 +296,32 @@ class ROWID(sqltypes.TypeEngine):
|
||||
__visit_name__ = 'ROWID'
|
||||
|
||||
|
||||
|
||||
class _OracleBoolean(sqltypes.Boolean):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.NUMBER
|
||||
|
||||
colspecs = {
|
||||
sqltypes.Boolean : _OracleBoolean,
|
||||
sqltypes.Interval : INTERVAL,
|
||||
sqltypes.Boolean: _OracleBoolean,
|
||||
sqltypes.Interval: INTERVAL,
|
||||
}
|
||||
|
||||
ischema_names = {
|
||||
'VARCHAR2' : VARCHAR,
|
||||
'NVARCHAR2' : NVARCHAR,
|
||||
'CHAR' : CHAR,
|
||||
'DATE' : DATE,
|
||||
'NUMBER' : NUMBER,
|
||||
'BLOB' : BLOB,
|
||||
'BFILE' : BFILE,
|
||||
'CLOB' : CLOB,
|
||||
'NCLOB' : NCLOB,
|
||||
'TIMESTAMP' : TIMESTAMP,
|
||||
'TIMESTAMP WITH TIME ZONE' : TIMESTAMP,
|
||||
'INTERVAL DAY TO SECOND' : INTERVAL,
|
||||
'RAW' : RAW,
|
||||
'FLOAT' : FLOAT,
|
||||
'DOUBLE PRECISION' : DOUBLE_PRECISION,
|
||||
'LONG' : LONG,
|
||||
'VARCHAR2': VARCHAR,
|
||||
'NVARCHAR2': NVARCHAR,
|
||||
'CHAR': CHAR,
|
||||
'DATE': DATE,
|
||||
'NUMBER': NUMBER,
|
||||
'BLOB': BLOB,
|
||||
'BFILE': BFILE,
|
||||
'CLOB': CLOB,
|
||||
'NCLOB': NCLOB,
|
||||
'TIMESTAMP': TIMESTAMP,
|
||||
'TIMESTAMP WITH TIME ZONE': TIMESTAMP,
|
||||
'INTERVAL DAY TO SECOND': INTERVAL,
|
||||
'RAW': RAW,
|
||||
'FLOAT': FLOAT,
|
||||
'DOUBLE PRECISION': DOUBLE_PRECISION,
|
||||
'LONG': LONG,
|
||||
}
|
||||
|
||||
|
||||
@@ -336,9 +378,11 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
if precision is None:
|
||||
return name
|
||||
elif scale is None:
|
||||
return "%(name)s(%(precision)s)" % {'name':name,'precision': precision}
|
||||
n = "%(name)s(%(precision)s)"
|
||||
return n % {'name': name, 'precision': precision}
|
||||
else:
|
||||
return "%(name)s(%(precision)s, %(scale)s)" % {'name':name,'precision': precision, 'scale' : scale}
|
||||
n = "%(name)s(%(precision)s, %(scale)s)"
|
||||
return n % {'name': name, 'precision': precision, 'scale': scale}
|
||||
|
||||
def visit_string(self, type_):
|
||||
return self.visit_VARCHAR2(type_)
|
||||
@@ -354,13 +398,14 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
return self._visit_varchar(type_, '', '')
|
||||
|
||||
def _visit_varchar(self, type_, n, num):
|
||||
if not n and self.dialect._supports_char_length:
|
||||
return "VARCHAR%(two)s(%(length)s CHAR)" % {
|
||||
'length' : type_.length,
|
||||
'two':num}
|
||||
if not type_.length:
|
||||
return "%(n)sVARCHAR%(two)s" % {'two': num, 'n': n}
|
||||
elif not n and self.dialect._supports_char_length:
|
||||
varchar = "VARCHAR%(two)s(%(length)s CHAR)"
|
||||
return varchar % {'length': type_.length, 'two': num}
|
||||
else:
|
||||
return "%(n)sVARCHAR%(two)s(%(length)s)" % {'length' : type_.length,
|
||||
'two':num, 'n':n}
|
||||
varchar = "%(n)sVARCHAR%(two)s(%(length)s)"
|
||||
return varchar % {'length': type_.length, 'two': num, 'n': n}
|
||||
|
||||
def visit_text(self, type_):
|
||||
return self.visit_CLOB(type_)
|
||||
@@ -382,13 +427,14 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
|
||||
def visit_RAW(self, type_):
|
||||
if type_.length:
|
||||
return "RAW(%(length)s)" % {'length' : type_.length}
|
||||
return "RAW(%(length)s)" % {'length': type_.length}
|
||||
else:
|
||||
return "RAW"
|
||||
|
||||
def visit_ROWID(self, type_):
|
||||
return "ROWID"
|
||||
|
||||
|
||||
class OracleCompiler(compiler.SQLCompiler):
|
||||
"""Oracle compiler modifies the lexical structure of Select
|
||||
statements to work under non-ANSI configured Oracle databases, if
|
||||
@@ -398,7 +444,7 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
compound_keywords = util.update_copy(
|
||||
compiler.SQLCompiler.compound_keywords,
|
||||
{
|
||||
expression.CompoundSelect.EXCEPT : 'MINUS'
|
||||
expression.CompoundSelect.EXCEPT: 'MINUS'
|
||||
}
|
||||
)
|
||||
|
||||
@@ -407,8 +453,9 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
self._quoted_bind_names = {}
|
||||
super(OracleCompiler, self).__init__(*args, **kwargs)
|
||||
|
||||
def visit_mod(self, binary, **kw):
|
||||
return "mod(%s, %s)" % (self.process(binary.left), self.process(binary.right))
|
||||
def visit_mod_binary(self, binary, operator, **kw):
|
||||
return "mod(%s, %s)" % (self.process(binary.left, **kw),
|
||||
self.process(binary.right, **kw))
|
||||
|
||||
def visit_now_func(self, fn, **kw):
|
||||
return "CURRENT_TIMESTAMP"
|
||||
@@ -416,8 +463,15 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
def visit_char_length_func(self, fn, **kw):
|
||||
return "LENGTH" + self.function_argspec(fn, **kw)
|
||||
|
||||
def visit_match_op(self, binary, **kw):
|
||||
return "CONTAINS (%s, %s)" % (self.process(binary.left), self.process(binary.right))
|
||||
def visit_match_op_binary(self, binary, operator, **kw):
|
||||
return "CONTAINS (%s, %s)" % (self.process(binary.left),
|
||||
self.process(binary.right))
|
||||
|
||||
def visit_true(self, expr, **kw):
|
||||
return '1'
|
||||
|
||||
def visit_false(self, expr, **kw):
|
||||
return '0'
|
||||
|
||||
def get_select_hint_text(self, byfroms):
|
||||
return " ".join(
|
||||
@@ -444,8 +498,13 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
return compiler.SQLCompiler.visit_join(self, join, **kwargs)
|
||||
else:
|
||||
kwargs['asfrom'] = True
|
||||
if isinstance(join.right, expression.FromGrouping):
|
||||
right = join.right.element
|
||||
else:
|
||||
right = join.right
|
||||
return self.process(join.left, **kwargs) + \
|
||||
", " + self.process(join.right, **kwargs)
|
||||
", " + self.process(right, **kwargs)
|
||||
|
||||
|
||||
def _get_nonansi_join_whereclause(self, froms):
|
||||
clauses = []
|
||||
@@ -454,18 +513,20 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
if join.isouter:
|
||||
def visit_binary(binary):
|
||||
if binary.operator == sql_operators.eq:
|
||||
if binary.left.table is join.right:
|
||||
if join.right.is_derived_from(binary.left.table):
|
||||
binary.left = _OuterJoinColumn(binary.left)
|
||||
elif binary.right.table is join.right:
|
||||
elif join.right.is_derived_from(binary.right.table):
|
||||
binary.right = _OuterJoinColumn(binary.right)
|
||||
clauses.append(visitors.cloned_traverse(join.onclause, {},
|
||||
{'binary':visit_binary}))
|
||||
{'binary': visit_binary}))
|
||||
else:
|
||||
clauses.append(join.onclause)
|
||||
|
||||
for j in join.left, join.right:
|
||||
if isinstance(j, expression.Join):
|
||||
visit_join(j)
|
||||
elif isinstance(j, expression.FromGrouping):
|
||||
visit_join(j.element)
|
||||
|
||||
for f in froms:
|
||||
if isinstance(f, expression.Join):
|
||||
@@ -498,20 +559,25 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
return self.process(alias.original, **kwargs)
|
||||
|
||||
def returning_clause(self, stmt, returning_cols):
|
||||
columns = []
|
||||
binds = []
|
||||
for i, column in enumerate(expression._select_iterables(returning_cols)):
|
||||
if column.type._has_column_expression:
|
||||
col_expr = column.type.column_expression(column)
|
||||
else:
|
||||
col_expr = column
|
||||
outparam = sql.outparam("ret_%d" % i, type_=column.type)
|
||||
self.binds[outparam.key] = outparam
|
||||
binds.append(self.bindparam_string(self._truncate_bindparam(outparam)))
|
||||
columns.append(self.process(col_expr, within_columns_clause=False))
|
||||
self.result_map[outparam.key] = (
|
||||
outparam.key,
|
||||
(column, getattr(column, 'name', None),
|
||||
getattr(column, 'key', None)),
|
||||
column.type
|
||||
)
|
||||
|
||||
def create_out_param(col, i):
|
||||
bindparam = sql.outparam("ret_%d" % i, type_=col.type)
|
||||
self.binds[bindparam.key] = bindparam
|
||||
return self.bindparam_string(self._truncate_bindparam(bindparam))
|
||||
|
||||
columnlist = list(expression._select_iterables(returning_cols))
|
||||
|
||||
# within_columns_clause =False so that labels (foo AS bar) don't render
|
||||
columns = [self.process(c, within_columns_clause=False, result_map=self.result_map) for c in columnlist]
|
||||
|
||||
binds = [create_out_param(c, i) for i, c in enumerate(columnlist)]
|
||||
|
||||
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
|
||||
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
|
||||
|
||||
def _TODO_visit_compound_select(self, select):
|
||||
"""Need to determine how to get ``LIMIT``/``OFFSET`` into a ``UNION`` for Oracle."""
|
||||
@@ -524,12 +590,8 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
|
||||
if not getattr(select, '_oracle_visit', None):
|
||||
if not self.dialect.use_ansi:
|
||||
if self.stack and 'from' in self.stack[-1]:
|
||||
existingfroms = self.stack[-1]['from']
|
||||
else:
|
||||
existingfroms = None
|
||||
|
||||
froms = select._get_display_froms(existingfroms)
|
||||
froms = self._display_froms_for_select(
|
||||
select, kwargs.get('asfrom', False))
|
||||
whereclause = self._get_nonansi_join_whereclause(froms)
|
||||
if whereclause is not None:
|
||||
select = select.where(whereclause)
|
||||
@@ -570,7 +632,7 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
|
||||
# If needed, add the ora_rn, and wrap again with offset.
|
||||
if select._offset is None:
|
||||
limitselect.for_update = select.for_update
|
||||
limitselect._for_update_arg = select._for_update_arg
|
||||
select = limitselect
|
||||
else:
|
||||
limitselect = limitselect.column(
|
||||
@@ -579,7 +641,7 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
limitselect._is_wrapper = True
|
||||
|
||||
offsetselect = sql.select(
|
||||
[c for c in limitselect.c if c.key!='ora_rn'])
|
||||
[c for c in limitselect.c if c.key != 'ora_rn'])
|
||||
offsetselect._oracle_visit = True
|
||||
offsetselect._is_wrapper = True
|
||||
|
||||
@@ -587,9 +649,9 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
if not self.dialect.use_binds_for_limits:
|
||||
offset_value = sql.literal_column("%d" % offset_value)
|
||||
offsetselect.append_whereclause(
|
||||
sql.literal_column("ora_rn")>offset_value)
|
||||
sql.literal_column("ora_rn") > offset_value)
|
||||
|
||||
offsetselect.for_update = select.for_update
|
||||
offsetselect._for_update_arg = select._for_update_arg
|
||||
select = offsetselect
|
||||
|
||||
kwargs['iswrapper'] = getattr(select, '_is_wrapper', False)
|
||||
@@ -601,10 +663,20 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
def for_update_clause(self, select):
|
||||
if self.is_subquery():
|
||||
return ""
|
||||
elif select.for_update == "nowait":
|
||||
return " FOR UPDATE NOWAIT"
|
||||
else:
|
||||
return super(OracleCompiler, self).for_update_clause(select)
|
||||
|
||||
tmp = ' FOR UPDATE'
|
||||
|
||||
if select._for_update_arg.of:
|
||||
tmp += ' OF ' + ', '.join(
|
||||
self.process(elem) for elem in
|
||||
select._for_update_arg.of
|
||||
)
|
||||
|
||||
if select._for_update_arg.nowait:
|
||||
tmp += " NOWAIT"
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
class OracleDDLCompiler(compiler.DDLCompiler):
|
||||
|
||||
@@ -623,17 +695,22 @@ class OracleDDLCompiler(compiler.DDLCompiler):
|
||||
|
||||
return text
|
||||
|
||||
def visit_create_index(self, create, **kw):
|
||||
return super(OracleDDLCompiler, self).\
|
||||
visit_create_index(create, include_schema=True)
|
||||
|
||||
|
||||
class OracleIdentifierPreparer(compiler.IdentifierPreparer):
|
||||
|
||||
reserved_words = set([x.lower() for x in RESERVED_WORDS])
|
||||
illegal_initial_characters = set(xrange(0, 10)).union(["_", "$"])
|
||||
illegal_initial_characters = set(range(0, 10)).union(["_", "$"])
|
||||
|
||||
def _bindparam_requires_quotes(self, value):
|
||||
"""Return True if the given identifier requires quoting."""
|
||||
lc_value = value.lower()
|
||||
return (lc_value in self.reserved_words
|
||||
or value[0] in self.illegal_initial_characters
|
||||
or not self.legal_characters.match(unicode(value))
|
||||
or not self.legal_characters.match(util.text_type(value))
|
||||
)
|
||||
|
||||
def format_savepoint(self, savepoint):
|
||||
@@ -647,6 +724,7 @@ class OracleExecutionContext(default.DefaultExecutionContext):
|
||||
self.dialect.identifier_preparer.format_sequence(seq) +
|
||||
".nextval FROM DUAL", type_)
|
||||
|
||||
|
||||
class OracleDialect(default.DefaultDialect):
|
||||
name = 'oracle'
|
||||
supports_alter = True
|
||||
@@ -736,10 +814,9 @@ class OracleDialect(default.DefaultDialect):
|
||||
def normalize_name(self, name):
|
||||
if name is None:
|
||||
return None
|
||||
# Py2K
|
||||
if isinstance(name, str):
|
||||
name = name.decode(self.encoding)
|
||||
# end Py2K
|
||||
if util.py2k:
|
||||
if isinstance(name, str):
|
||||
name = name.decode(self.encoding)
|
||||
if name.upper() == name and \
|
||||
not self.identifier_preparer._requires_quotes(name.lower()):
|
||||
return name.lower()
|
||||
@@ -751,16 +828,15 @@ class OracleDialect(default.DefaultDialect):
|
||||
return None
|
||||
elif name.lower() == name and not self.identifier_preparer._requires_quotes(name.lower()):
|
||||
name = name.upper()
|
||||
# Py2K
|
||||
if not self.supports_unicode_binds:
|
||||
name = name.encode(self.encoding)
|
||||
else:
|
||||
name = unicode(name)
|
||||
# end Py2K
|
||||
if util.py2k:
|
||||
if not self.supports_unicode_binds:
|
||||
name = name.encode(self.encoding)
|
||||
else:
|
||||
name = unicode(name)
|
||||
return name
|
||||
|
||||
def _get_default_schema_name(self, connection):
|
||||
return self.normalize_name(connection.execute(u'SELECT USER FROM DUAL').scalar())
|
||||
return self.normalize_name(connection.execute('SELECT USER FROM DUAL').scalar())
|
||||
|
||||
def _resolve_synonym(self, connection, desired_owner=None, desired_synonym=None, desired_table=None):
|
||||
"""search for a local synonym matching the given desired owner/name.
|
||||
@@ -770,14 +846,15 @@ class OracleDialect(default.DefaultDialect):
|
||||
returns the actual name, owner, dblink name, and synonym name if found.
|
||||
"""
|
||||
|
||||
q = "SELECT owner, table_owner, table_name, db_link, synonym_name FROM all_synonyms WHERE "
|
||||
q = "SELECT owner, table_owner, table_name, db_link, "\
|
||||
"synonym_name FROM all_synonyms WHERE "
|
||||
clauses = []
|
||||
params = {}
|
||||
if desired_synonym:
|
||||
clauses.append("synonym_name = :synonym_name")
|
||||
params['synonym_name'] = desired_synonym
|
||||
if desired_owner:
|
||||
clauses.append("table_owner = :desired_owner")
|
||||
clauses.append("owner = :desired_owner")
|
||||
params['desired_owner'] = desired_owner
|
||||
if desired_table:
|
||||
clauses.append("table_name = :tname")
|
||||
@@ -808,19 +885,29 @@ class OracleDialect(default.DefaultDialect):
|
||||
|
||||
if resolve_synonyms:
|
||||
actual_name, owner, dblink, synonym = self._resolve_synonym(
|
||||
connection,
|
||||
desired_owner=self.denormalize_name(schema),
|
||||
desired_synonym=self.denormalize_name(table_name)
|
||||
)
|
||||
connection,
|
||||
desired_owner=self.denormalize_name(schema),
|
||||
desired_synonym=self.denormalize_name(table_name)
|
||||
)
|
||||
else:
|
||||
actual_name, owner, dblink, synonym = None, None, None, None
|
||||
if not actual_name:
|
||||
actual_name = self.denormalize_name(table_name)
|
||||
if not dblink:
|
||||
dblink = ''
|
||||
if not owner:
|
||||
|
||||
if dblink:
|
||||
# using user_db_links here since all_db_links appears
|
||||
# to have more restricted permissions.
|
||||
# http://docs.oracle.com/cd/B28359_01/server.111/b28310/ds_admin005.htm
|
||||
# will need to hear from more users if we are doing
|
||||
# the right thing here. See [ticket:2619]
|
||||
owner = connection.scalar(
|
||||
sql.text("SELECT username FROM user_db_links "
|
||||
"WHERE db_link=:link"), link=dblink)
|
||||
dblink = "@" + dblink
|
||||
elif not owner:
|
||||
owner = self.denormalize_name(schema or self.default_schema_name)
|
||||
return (actual_name, owner, dblink, synonym)
|
||||
|
||||
return (actual_name, owner, dblink or '', synonym)
|
||||
|
||||
@reflection.cache
|
||||
def get_schema_names(self, connection, **kw):
|
||||
@@ -843,7 +930,6 @@ class OracleDialect(default.DefaultDialect):
|
||||
cursor = connection.execute(s, owner=schema)
|
||||
return [self.normalize_name(row[0]) for row in cursor]
|
||||
|
||||
|
||||
@reflection.cache
|
||||
def get_view_names(self, connection, schema=None, **kw):
|
||||
schema = self.denormalize_name(schema or self.default_schema_name)
|
||||
@@ -877,18 +963,24 @@ class OracleDialect(default.DefaultDialect):
|
||||
else:
|
||||
char_length_col = 'data_length'
|
||||
|
||||
c = connection.execute(sql.text(
|
||||
"SELECT column_name, data_type, %(char_length_col)s, data_precision, data_scale, "
|
||||
"nullable, data_default FROM ALL_TAB_COLUMNS%(dblink)s "
|
||||
"WHERE table_name = :table_name AND owner = :owner "
|
||||
"ORDER BY column_id" % {'dblink': dblink, 'char_length_col':char_length_col}),
|
||||
table_name=table_name, owner=schema)
|
||||
params = {"table_name": table_name}
|
||||
text = "SELECT column_name, data_type, %(char_length_col)s, "\
|
||||
"data_precision, data_scale, "\
|
||||
"nullable, data_default FROM ALL_TAB_COLUMNS%(dblink)s "\
|
||||
"WHERE table_name = :table_name"
|
||||
if schema is not None:
|
||||
params['owner'] = schema
|
||||
text += " AND owner = :owner "
|
||||
text += " ORDER BY column_id"
|
||||
text = text % {'dblink': dblink, 'char_length_col': char_length_col}
|
||||
|
||||
c = connection.execute(sql.text(text), **params)
|
||||
|
||||
for row in c:
|
||||
(colname, orig_colname, coltype, length, precision, scale, nullable, default) = \
|
||||
(self.normalize_name(row[0]), row[0], row[1], row[2], row[3], row[4], row[5]=='Y', row[6])
|
||||
(self.normalize_name(row[0]), row[0], row[1], row[2], row[3], row[4], row[5] == 'Y', row[6])
|
||||
|
||||
if coltype == 'NUMBER' :
|
||||
if coltype == 'NUMBER':
|
||||
coltype = NUMBER(precision, scale)
|
||||
elif coltype in ('VARCHAR2', 'NVARCHAR2', 'CHAR'):
|
||||
coltype = self.ischema_names.get(coltype)(length)
|
||||
@@ -908,7 +1000,7 @@ class OracleDialect(default.DefaultDialect):
|
||||
'type': coltype,
|
||||
'nullable': nullable,
|
||||
'default': default,
|
||||
'autoincrement':default is None
|
||||
'autoincrement': default is None
|
||||
}
|
||||
if orig_colname.lower() == orig_colname:
|
||||
cdict['quote'] = True
|
||||
@@ -920,33 +1012,40 @@ class OracleDialect(default.DefaultDialect):
|
||||
def get_indexes(self, connection, table_name, schema=None,
|
||||
resolve_synonyms=False, dblink='', **kw):
|
||||
|
||||
|
||||
info_cache = kw.get('info_cache')
|
||||
(table_name, schema, dblink, synonym) = \
|
||||
self._prepare_reflection_args(connection, table_name, schema,
|
||||
resolve_synonyms, dblink,
|
||||
info_cache=info_cache)
|
||||
indexes = []
|
||||
q = sql.text("""
|
||||
SELECT a.index_name, a.column_name, b.uniqueness
|
||||
FROM ALL_IND_COLUMNS%(dblink)s a,
|
||||
ALL_INDEXES%(dblink)s b
|
||||
WHERE
|
||||
a.index_name = b.index_name
|
||||
AND a.table_owner = b.table_owner
|
||||
AND a.table_name = b.table_name
|
||||
|
||||
AND a.table_name = :table_name
|
||||
AND a.table_owner = :schema
|
||||
ORDER BY a.index_name, a.column_position""" % {'dblink': dblink})
|
||||
rp = connection.execute(q, table_name=self.denormalize_name(table_name),
|
||||
schema=self.denormalize_name(schema))
|
||||
params = {'table_name': table_name}
|
||||
text = \
|
||||
"SELECT a.index_name, a.column_name, b.uniqueness "\
|
||||
"\nFROM ALL_IND_COLUMNS%(dblink)s a, "\
|
||||
"\nALL_INDEXES%(dblink)s b "\
|
||||
"\nWHERE "\
|
||||
"\na.index_name = b.index_name "\
|
||||
"\nAND a.table_owner = b.table_owner "\
|
||||
"\nAND a.table_name = b.table_name "\
|
||||
"\nAND a.table_name = :table_name "
|
||||
|
||||
if schema is not None:
|
||||
params['schema'] = schema
|
||||
text += "AND a.table_owner = :schema "
|
||||
|
||||
text += "ORDER BY a.index_name, a.column_position"
|
||||
|
||||
text = text % {'dblink': dblink}
|
||||
|
||||
q = sql.text(text)
|
||||
rp = connection.execute(q, **params)
|
||||
indexes = []
|
||||
last_index_name = None
|
||||
pkeys = self.get_primary_keys(connection, table_name, schema,
|
||||
resolve_synonyms=resolve_synonyms,
|
||||
dblink=dblink,
|
||||
info_cache=kw.get('info_cache'))
|
||||
pk_constraint = self.get_pk_constraint(
|
||||
connection, table_name, schema, resolve_synonyms=resolve_synonyms,
|
||||
dblink=dblink, info_cache=kw.get('info_cache'))
|
||||
pkeys = pk_constraint['constrained_columns']
|
||||
uniqueness = dict(NONUNIQUE=False, UNIQUE=True)
|
||||
|
||||
oracle_sys_col = re.compile(r'SYS_NC\d+\$', re.IGNORECASE)
|
||||
@@ -982,46 +1081,43 @@ class OracleDialect(default.DefaultDialect):
|
||||
def _get_constraint_data(self, connection, table_name, schema=None,
|
||||
dblink='', **kw):
|
||||
|
||||
rp = connection.execute(
|
||||
sql.text("""SELECT
|
||||
ac.constraint_name,
|
||||
ac.constraint_type,
|
||||
loc.column_name AS local_column,
|
||||
rem.table_name AS remote_table,
|
||||
rem.column_name AS remote_column,
|
||||
rem.owner AS remote_owner,
|
||||
loc.position as loc_pos,
|
||||
rem.position as rem_pos
|
||||
FROM all_constraints%(dblink)s ac,
|
||||
all_cons_columns%(dblink)s loc,
|
||||
all_cons_columns%(dblink)s rem
|
||||
WHERE ac.table_name = :table_name
|
||||
AND ac.constraint_type IN ('R','P')
|
||||
AND ac.owner = :owner
|
||||
AND ac.owner = loc.owner
|
||||
AND ac.constraint_name = loc.constraint_name
|
||||
AND ac.r_owner = rem.owner(+)
|
||||
AND ac.r_constraint_name = rem.constraint_name(+)
|
||||
AND (rem.position IS NULL or loc.position=rem.position)
|
||||
ORDER BY ac.constraint_name, loc.position""" % {'dblink': dblink}),
|
||||
table_name=table_name, owner=schema)
|
||||
params = {'table_name': table_name}
|
||||
|
||||
text = \
|
||||
"SELECT"\
|
||||
"\nac.constraint_name,"\
|
||||
"\nac.constraint_type,"\
|
||||
"\nloc.column_name AS local_column,"\
|
||||
"\nrem.table_name AS remote_table,"\
|
||||
"\nrem.column_name AS remote_column,"\
|
||||
"\nrem.owner AS remote_owner,"\
|
||||
"\nloc.position as loc_pos,"\
|
||||
"\nrem.position as rem_pos"\
|
||||
"\nFROM all_constraints%(dblink)s ac,"\
|
||||
"\nall_cons_columns%(dblink)s loc,"\
|
||||
"\nall_cons_columns%(dblink)s rem"\
|
||||
"\nWHERE ac.table_name = :table_name"\
|
||||
"\nAND ac.constraint_type IN ('R','P')"
|
||||
|
||||
if schema is not None:
|
||||
params['owner'] = schema
|
||||
text += "\nAND ac.owner = :owner"
|
||||
|
||||
text += \
|
||||
"\nAND ac.owner = loc.owner"\
|
||||
"\nAND ac.constraint_name = loc.constraint_name"\
|
||||
"\nAND ac.r_owner = rem.owner(+)"\
|
||||
"\nAND ac.r_constraint_name = rem.constraint_name(+)"\
|
||||
"\nAND (rem.position IS NULL or loc.position=rem.position)"\
|
||||
"\nORDER BY ac.constraint_name, loc.position"
|
||||
|
||||
text = text % {'dblink': dblink}
|
||||
rp = connection.execute(sql.text(text), **params)
|
||||
constraint_data = rp.fetchall()
|
||||
return constraint_data
|
||||
|
||||
def get_primary_keys(self, connection, table_name, schema=None, **kw):
|
||||
"""
|
||||
|
||||
kw arguments can be:
|
||||
|
||||
oracle_resolve_synonyms
|
||||
|
||||
dblink
|
||||
|
||||
"""
|
||||
return self._get_primary_keys(connection, table_name, schema, **kw)[0]
|
||||
|
||||
@reflection.cache
|
||||
def _get_primary_keys(self, connection, table_name, schema=None, **kw):
|
||||
def get_pk_constraint(self, connection, table_name, schema=None, **kw):
|
||||
resolve_synonyms = kw.get('oracle_resolve_synonyms', False)
|
||||
dblink = kw.get('dblink', '')
|
||||
info_cache = kw.get('info_cache')
|
||||
@@ -1037,22 +1133,13 @@ class OracleDialect(default.DefaultDialect):
|
||||
info_cache=kw.get('info_cache'))
|
||||
|
||||
for row in constraint_data:
|
||||
#print "ROW:" , row
|
||||
(cons_name, cons_type, local_column, remote_table, remote_column, remote_owner) = \
|
||||
row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]])
|
||||
if cons_type == 'P':
|
||||
if constraint_name is None:
|
||||
constraint_name = self.normalize_name(cons_name)
|
||||
pkeys.append(local_column)
|
||||
return pkeys, constraint_name
|
||||
|
||||
def get_pk_constraint(self, connection, table_name, schema=None, **kw):
|
||||
cols, name = self._get_primary_keys(connection, table_name, schema=schema, **kw)
|
||||
|
||||
return {
|
||||
'constrained_columns':cols,
|
||||
'name':name
|
||||
}
|
||||
return {'constrained_columns': pkeys, 'name': constraint_name}
|
||||
|
||||
@reflection.cache
|
||||
def get_foreign_keys(self, connection, table_name, schema=None, **kw):
|
||||
@@ -1066,7 +1153,7 @@ class OracleDialect(default.DefaultDialect):
|
||||
|
||||
"""
|
||||
|
||||
requested_schema = schema # to check later on
|
||||
requested_schema = schema # to check later on
|
||||
resolve_synonyms = kw.get('oracle_resolve_synonyms', False)
|
||||
dblink = kw.get('dblink', '')
|
||||
info_cache = kw.get('info_cache')
|
||||
@@ -1082,11 +1169,11 @@ class OracleDialect(default.DefaultDialect):
|
||||
|
||||
def fkey_rec():
|
||||
return {
|
||||
'name' : None,
|
||||
'constrained_columns' : [],
|
||||
'referred_schema' : None,
|
||||
'referred_table' : None,
|
||||
'referred_columns' : []
|
||||
'name': None,
|
||||
'constrained_columns': [],
|
||||
'referred_schema': None,
|
||||
'referred_table': None,
|
||||
'referred_columns': []
|
||||
}
|
||||
|
||||
fkeys = util.defaultdict(fkey_rec)
|
||||
@@ -1101,7 +1188,7 @@ class OracleDialect(default.DefaultDialect):
|
||||
util.warn(
|
||||
("Got 'None' querying 'table_name' from "
|
||||
"all_cons_columns%(dblink)s - does the user have "
|
||||
"proper rights to the table?") % {'dblink':dblink})
|
||||
"proper rights to the table?") % {'dblink': dblink})
|
||||
continue
|
||||
|
||||
rec = fkeys[cons_name]
|
||||
@@ -1128,7 +1215,7 @@ class OracleDialect(default.DefaultDialect):
|
||||
local_cols.append(local_column)
|
||||
remote_cols.append(remote_column)
|
||||
|
||||
return fkeys.values()
|
||||
return list(fkeys.values())
|
||||
|
||||
@reflection.cache
|
||||
def get_view_definition(self, connection, view_name, schema=None,
|
||||
@@ -1138,25 +1225,25 @@ class OracleDialect(default.DefaultDialect):
|
||||
self._prepare_reflection_args(connection, view_name, schema,
|
||||
resolve_synonyms, dblink,
|
||||
info_cache=info_cache)
|
||||
s = sql.text("""
|
||||
SELECT text FROM all_views
|
||||
WHERE owner = :schema
|
||||
AND view_name = :view_name
|
||||
""")
|
||||
rp = connection.execute(s,
|
||||
view_name=view_name, schema=schema).scalar()
|
||||
|
||||
params = {'view_name': view_name}
|
||||
text = "SELECT text FROM all_views WHERE view_name=:view_name"
|
||||
|
||||
if schema is not None:
|
||||
text += " AND owner = :schema"
|
||||
params['schema'] = schema
|
||||
|
||||
rp = connection.execute(sql.text(text), **params).scalar()
|
||||
if rp:
|
||||
return rp.decode(self.encoding)
|
||||
if util.py2k:
|
||||
rp = rp.decode(self.encoding)
|
||||
return rp
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
|
||||
class _OuterJoinColumn(sql.ClauseElement):
|
||||
__visit_name__ = 'outer_join_column'
|
||||
|
||||
def __init__(self, column):
|
||||
self.column = column
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,48 +1,57 @@
|
||||
# oracle/cx_oracle.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Support for the Oracle database via the cx_oracle driver.
|
||||
"""
|
||||
|
||||
Driver
|
||||
------
|
||||
.. dialect:: oracle+cx_oracle
|
||||
:name: cx-Oracle
|
||||
:dbapi: cx_oracle
|
||||
:connectstring: oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
|
||||
:url: http://cx-oracle.sourceforge.net/
|
||||
|
||||
The Oracle dialect uses the cx_oracle driver, available at
|
||||
http://cx-oracle.sourceforge.net/ . The dialect has several behaviors
|
||||
which are specifically tailored towards compatibility with this module.
|
||||
Version 5.0 or greater is **strongly** recommended, as SQLAlchemy makes
|
||||
extensive use of the cx_oracle output converters for numeric and
|
||||
string conversions.
|
||||
Additional Connect Arguments
|
||||
----------------------------
|
||||
|
||||
Connecting
|
||||
----------
|
||||
When connecting with ``dbname`` present, the host, port, and dbname tokens are
|
||||
converted to a TNS name using
|
||||
the cx_oracle ``makedsn()`` function. Otherwise, the host token is taken
|
||||
directly as a TNS name.
|
||||
|
||||
Connecting with create_engine() uses the standard URL approach of
|
||||
``oracle://user:pass@host:port/dbname[?key=value&key=value...]``. If dbname is present, the
|
||||
host, port, and dbname tokens are converted to a TNS name using the cx_oracle
|
||||
:func:`makedsn()` function. Otherwise, the host token is taken directly as a TNS name.
|
||||
Additional arguments which may be specified either as query string arguments
|
||||
on the URL, or as keyword arguments to :func:`.create_engine()` are:
|
||||
|
||||
Additional arguments which may be specified either as query string arguments on the
|
||||
URL, or as keyword arguments to :func:`~sqlalchemy.create_engine()` are:
|
||||
* allow_twophase - enable two-phase transactions. Defaults to ``True``.
|
||||
|
||||
* *allow_twophase* - enable two-phase transactions. Defaults to ``True``.
|
||||
|
||||
* *arraysize* - set the cx_oracle.arraysize value on cursors, in SQLAlchemy
|
||||
* arraysize - set the cx_oracle.arraysize value on cursors, in SQLAlchemy
|
||||
it defaults to 50. See the section on "LOB Objects" below.
|
||||
|
||||
* *auto_convert_lobs* - defaults to True, see the section on LOB objects.
|
||||
* auto_convert_lobs - defaults to True, see the section on LOB objects.
|
||||
|
||||
* *auto_setinputsizes* - the cx_oracle.setinputsizes() call is issued for all bind parameters.
|
||||
This is required for LOB datatypes but can be disabled to reduce overhead. Defaults
|
||||
to ``True``.
|
||||
* auto_setinputsizes - the cx_oracle.setinputsizes() call is issued for
|
||||
all bind parameters. This is required for LOB datatypes but can be
|
||||
disabled to reduce overhead. Defaults to ``True``. Specific types
|
||||
can be excluded from this process using the ``exclude_setinputsizes``
|
||||
parameter.
|
||||
|
||||
* *mode* - This is given the string value of SYSDBA or SYSOPER, or alternatively an
|
||||
integer value. This value is only available as a URL query string argument.
|
||||
* exclude_setinputsizes - a tuple or list of string DBAPI type names to
|
||||
be excluded from the "auto setinputsizes" feature. The type names here
|
||||
must match DBAPI types that are found in the "cx_Oracle" module namespace,
|
||||
such as cx_Oracle.UNICODE, cx_Oracle.NCLOB, etc. Defaults to
|
||||
``(STRING, UNICODE)``.
|
||||
|
||||
* *threaded* - enable multithreaded access to cx_oracle connections. Defaults
|
||||
to ``True``. Note that this is the opposite default of cx_oracle itself.
|
||||
.. versionadded:: 0.8 specific DBAPI types can be excluded from the
|
||||
auto_setinputsizes feature via the exclude_setinputsizes attribute.
|
||||
|
||||
* mode - This is given the string value of SYSDBA or SYSOPER, or alternatively
|
||||
an integer value. This value is only available as a URL query string
|
||||
argument.
|
||||
|
||||
* threaded - enable multithreaded access to cx_oracle connections. Defaults
|
||||
to ``True``. Note that this is the opposite default of the cx_Oracle DBAPI
|
||||
itself.
|
||||
|
||||
Unicode
|
||||
-------
|
||||
@@ -56,6 +65,27 @@ of the encoding to be used.
|
||||
Note that this behavior is disabled when Oracle 8 is detected, as it has been
|
||||
observed that issues remain when passing Python unicodes to cx_oracle with Oracle 8.
|
||||
|
||||
.. _cx_oracle_returning:
|
||||
|
||||
RETURNING Support
|
||||
-----------------
|
||||
|
||||
cx_oracle supports a limited subset of Oracle's already limited RETURNING support.
|
||||
Typically, results can only be guaranteed for at most one column being returned;
|
||||
this is the typical case when SQLAlchemy uses RETURNING to get just the value of a
|
||||
primary-key-associated sequence value. Additional column expressions will
|
||||
cause problems in a non-determinative way, due to cx_oracle's lack of support for
|
||||
the OCI_DATA_AT_EXEC API which is required for more complex RETURNING scenarios.
|
||||
|
||||
.. seealso::
|
||||
|
||||
http://docs.oracle.com/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#420693 - OCI documentation for RETURNING
|
||||
|
||||
http://sourceforge.net/mailarchive/message.php?msg_id=31338136 - cx_oracle developer commentary
|
||||
|
||||
|
||||
|
||||
|
||||
LOB Objects
|
||||
-----------
|
||||
|
||||
@@ -66,7 +96,7 @@ like result.fetchmany() and result.fetchall(). This means that by default, LOB
|
||||
objects are fully fetched unconditionally by SQLAlchemy, and the linkage to a live
|
||||
cursor is broken.
|
||||
|
||||
To disable this processing, pass ``auto_convert_lobs=False`` to :func:`create_engine()`.
|
||||
To disable this processing, pass ``auto_convert_lobs=False`` to :func:`.create_engine()`.
|
||||
|
||||
Two Phase Transaction Support
|
||||
-----------------------------
|
||||
@@ -99,7 +129,7 @@ the application can make one of several choices:
|
||||
|
||||
* For ad-hoc two-phase operations without disabling pooling, the DBAPI
|
||||
connection in use can be evicted from the connection pool using the
|
||||
:class:`.Connection.detach` method.
|
||||
:meth:`.Connection.detach` method.
|
||||
|
||||
.. versionchanged:: 0.8.0b2,0.7.10
|
||||
Support for cx_oracle prepared transactions has been implemented
|
||||
@@ -173,16 +203,18 @@ a period "." as the decimal character.
|
||||
|
||||
"""
|
||||
|
||||
from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, \
|
||||
OracleExecutionContext
|
||||
from sqlalchemy.dialects.oracle import base as oracle
|
||||
from sqlalchemy.engine import base
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .base import OracleCompiler, OracleDialect, OracleExecutionContext
|
||||
from . import base as oracle
|
||||
from ...engine import result as _result
|
||||
from sqlalchemy import types as sqltypes, util, exc, processors
|
||||
import random
|
||||
import collections
|
||||
from sqlalchemy.util.compat import decimal
|
||||
import decimal
|
||||
import re
|
||||
|
||||
|
||||
class _OracleNumeric(sqltypes.Numeric):
|
||||
def bind_processor(self, dialect):
|
||||
# cx_oracle accepts Decimal objects and floats
|
||||
@@ -200,10 +232,8 @@ class _OracleNumeric(sqltypes.Numeric):
|
||||
|
||||
if dialect.supports_native_decimal:
|
||||
if self.asdecimal:
|
||||
if self.scale is None:
|
||||
fstring = "%.10f"
|
||||
else:
|
||||
fstring = "%%.%df" % self.scale
|
||||
fstring = "%%.%df" % self._effective_decimal_return_scale
|
||||
|
||||
def to_decimal(value):
|
||||
if value is None:
|
||||
return None
|
||||
@@ -211,6 +241,7 @@ class _OracleNumeric(sqltypes.Numeric):
|
||||
return value
|
||||
else:
|
||||
return decimal.Decimal(fstring % value)
|
||||
|
||||
return to_decimal
|
||||
else:
|
||||
if self.precision is None and self.scale is None:
|
||||
@@ -226,6 +257,7 @@ class _OracleNumeric(sqltypes.Numeric):
|
||||
return super(_OracleNumeric, self).\
|
||||
result_processor(dialect, coltype)
|
||||
|
||||
|
||||
class _OracleDate(sqltypes.Date):
|
||||
def bind_processor(self, dialect):
|
||||
return None
|
||||
@@ -238,6 +270,7 @@ class _OracleDate(sqltypes.Date):
|
||||
return value
|
||||
return process
|
||||
|
||||
|
||||
class _LOBMixin(object):
|
||||
def result_processor(self, dialect, coltype):
|
||||
if not dialect.auto_convert_lobs:
|
||||
@@ -251,38 +284,40 @@ class _LOBMixin(object):
|
||||
return value
|
||||
return process
|
||||
|
||||
|
||||
class _NativeUnicodeMixin(object):
|
||||
# Py3K
|
||||
#pass
|
||||
# Py2K
|
||||
def bind_processor(self, dialect):
|
||||
if dialect._cx_oracle_with_unicode:
|
||||
def process(value):
|
||||
if value is None:
|
||||
return value
|
||||
else:
|
||||
return unicode(value)
|
||||
return process
|
||||
else:
|
||||
return super(_NativeUnicodeMixin, self).bind_processor(dialect)
|
||||
# end Py2K
|
||||
if util.py2k:
|
||||
def bind_processor(self, dialect):
|
||||
if dialect._cx_oracle_with_unicode:
|
||||
def process(value):
|
||||
if value is None:
|
||||
return value
|
||||
else:
|
||||
return unicode(value)
|
||||
return process
|
||||
else:
|
||||
return super(_NativeUnicodeMixin, self).bind_processor(dialect)
|
||||
|
||||
# we apply a connection output handler that returns
|
||||
# unicode in all cases, so the "native_unicode" flag
|
||||
# will be set for the default String.result_processor.
|
||||
|
||||
|
||||
class _OracleChar(_NativeUnicodeMixin, sqltypes.CHAR):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.FIXED_CHAR
|
||||
|
||||
|
||||
class _OracleNVarChar(_NativeUnicodeMixin, sqltypes.NVARCHAR):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return getattr(dbapi, 'UNICODE', dbapi.STRING)
|
||||
|
||||
|
||||
class _OracleText(_LOBMixin, sqltypes.Text):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.CLOB
|
||||
|
||||
|
||||
class _OracleLong(oracle.LONG):
|
||||
# a raw LONG is a text type, but does *not*
|
||||
# get the LobMixin with cx_oracle.
|
||||
@@ -293,6 +328,7 @@ class _OracleLong(oracle.LONG):
|
||||
class _OracleString(_NativeUnicodeMixin, sqltypes.String):
|
||||
pass
|
||||
|
||||
|
||||
class _OracleUnicodeText(_LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.NCLOB
|
||||
@@ -311,6 +347,7 @@ class _OracleUnicodeText(_LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
|
||||
return string_processor(lob_processor(value))
|
||||
return process
|
||||
|
||||
|
||||
class _OracleInteger(sqltypes.Integer):
|
||||
def result_processor(self, dialect, coltype):
|
||||
def to_int(val):
|
||||
@@ -319,6 +356,7 @@ class _OracleInteger(sqltypes.Integer):
|
||||
return val
|
||||
return to_int
|
||||
|
||||
|
||||
class _OracleBinary(_LOBMixin, sqltypes.LargeBinary):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.BLOB
|
||||
@@ -326,20 +364,26 @@ class _OracleBinary(_LOBMixin, sqltypes.LargeBinary):
|
||||
def bind_processor(self, dialect):
|
||||
return None
|
||||
|
||||
|
||||
class _OracleInterval(oracle.INTERVAL):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.INTERVAL
|
||||
|
||||
|
||||
class _OracleRaw(oracle.RAW):
|
||||
pass
|
||||
|
||||
|
||||
class _OracleRowid(oracle.ROWID):
|
||||
def get_dbapi_type(self, dbapi):
|
||||
return dbapi.ROWID
|
||||
|
||||
|
||||
class OracleCompiler_cx_oracle(OracleCompiler):
|
||||
def bindparam_string(self, name, **kw):
|
||||
if self.preparer._bindparam_requires_quotes(name):
|
||||
quote = getattr(name, 'quote', None)
|
||||
if quote is True or quote is not False and \
|
||||
self.preparer._bindparam_requires_quotes(name):
|
||||
quoted_name = '"%s"' % name
|
||||
self._quoted_bind_names[name] = quoted_name
|
||||
return OracleCompiler.bindparam_string(self, quoted_name, **kw)
|
||||
@@ -375,7 +419,7 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
# on String, including that outparams/RETURNING
|
||||
# breaks for varchars
|
||||
self.set_input_sizes(quoted_bind_names,
|
||||
exclude_types=self.dialect._cx_oracle_exclude_setinputsizes
|
||||
exclude_types=self.dialect.exclude_setinputsizes
|
||||
)
|
||||
|
||||
# if a single execute, check for outparams
|
||||
@@ -387,11 +431,12 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
if not hasattr(self, 'out_parameters'):
|
||||
self.out_parameters = {}
|
||||
if dbtype is None:
|
||||
raise exc.InvalidRequestError("Cannot create out parameter for parameter "
|
||||
"%r - it's type %r is not supported by"
|
||||
" cx_oracle" %
|
||||
(name, bindparam.type)
|
||||
)
|
||||
raise exc.InvalidRequestError(
|
||||
"Cannot create out parameter for parameter "
|
||||
"%r - it's type %r is not supported by"
|
||||
" cx_oracle" %
|
||||
(bindparam.key, bindparam.type)
|
||||
)
|
||||
name = self.compiled.bind_names[bindparam]
|
||||
self.out_parameters[name] = self.cursor.var(dbtype)
|
||||
self.parameters[0][quoted_bind_names.get(name, name)] = \
|
||||
@@ -417,10 +462,10 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
for column in self.cursor.description:
|
||||
type_code = column[1]
|
||||
if type_code in self.dialect._cx_oracle_binary_types:
|
||||
result = base.BufferedColumnResultProxy(self)
|
||||
result = _result.BufferedColumnResultProxy(self)
|
||||
|
||||
if result is None:
|
||||
result = base.ResultProxy(self)
|
||||
result = _result.ResultProxy(self)
|
||||
|
||||
if hasattr(self, 'out_parameters'):
|
||||
if self.compiled_parameters is not None and \
|
||||
@@ -448,6 +493,7 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class OracleExecutionContext_cx_oracle_with_unicode(OracleExecutionContext_cx_oracle):
|
||||
"""Support WITH_UNICODE in Python 2.xx.
|
||||
|
||||
@@ -463,13 +509,14 @@ class OracleExecutionContext_cx_oracle_with_unicode(OracleExecutionContext_cx_or
|
||||
"""
|
||||
def __init__(self, *arg, **kw):
|
||||
OracleExecutionContext_cx_oracle.__init__(self, *arg, **kw)
|
||||
self.statement = unicode(self.statement)
|
||||
self.statement = util.text_type(self.statement)
|
||||
|
||||
def _execute_scalar(self, stmt):
|
||||
return super(OracleExecutionContext_cx_oracle_with_unicode, self).\
|
||||
_execute_scalar(unicode(stmt))
|
||||
_execute_scalar(util.text_type(stmt))
|
||||
|
||||
class ReturningResultProxy(base.FullyBufferedResultProxy):
|
||||
|
||||
class ReturningResultProxy(_result.FullyBufferedResultProxy):
|
||||
"""Result proxy which stuffs the _returning clause + outparams into the fetch."""
|
||||
|
||||
def __init__(self, context, returning_params):
|
||||
@@ -478,19 +525,16 @@ class ReturningResultProxy(base.FullyBufferedResultProxy):
|
||||
|
||||
def _cursor_description(self):
|
||||
returning = self.context.compiled.returning
|
||||
|
||||
ret = []
|
||||
for c in returning:
|
||||
if hasattr(c, 'name'):
|
||||
ret.append((c.name, c.type))
|
||||
else:
|
||||
ret.append((c.anon_label, c.type))
|
||||
return ret
|
||||
return [
|
||||
("ret_%d" % i, None)
|
||||
for i, col in enumerate(returning)
|
||||
]
|
||||
|
||||
def _buffer_rows(self):
|
||||
return collections.deque([tuple(self._returning_params["ret_%d" % i]
|
||||
for i, c in enumerate(self._returning_params))])
|
||||
|
||||
|
||||
class OracleDialect_cx_oracle(OracleDialect):
|
||||
execution_ctx_cls = OracleExecutionContext_cx_oracle
|
||||
statement_compiler = OracleCompiler_cx_oracle
|
||||
@@ -499,34 +543,36 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
|
||||
colspecs = colspecs = {
|
||||
sqltypes.Numeric: _OracleNumeric,
|
||||
sqltypes.Date : _OracleDate, # generic type, assume datetime.date is desired
|
||||
sqltypes.Date: _OracleDate, # generic type, assume datetime.date is desired
|
||||
oracle.DATE: oracle.DATE, # non generic type - passthru
|
||||
sqltypes.LargeBinary : _OracleBinary,
|
||||
sqltypes.Boolean : oracle._OracleBoolean,
|
||||
sqltypes.Interval : _OracleInterval,
|
||||
oracle.INTERVAL : _OracleInterval,
|
||||
sqltypes.Text : _OracleText,
|
||||
sqltypes.String : _OracleString,
|
||||
sqltypes.UnicodeText : _OracleUnicodeText,
|
||||
sqltypes.CHAR : _OracleChar,
|
||||
sqltypes.LargeBinary: _OracleBinary,
|
||||
sqltypes.Boolean: oracle._OracleBoolean,
|
||||
sqltypes.Interval: _OracleInterval,
|
||||
oracle.INTERVAL: _OracleInterval,
|
||||
sqltypes.Text: _OracleText,
|
||||
sqltypes.String: _OracleString,
|
||||
sqltypes.UnicodeText: _OracleUnicodeText,
|
||||
sqltypes.CHAR: _OracleChar,
|
||||
|
||||
# a raw LONG is a text type, but does *not*
|
||||
# get the LobMixin with cx_oracle.
|
||||
oracle.LONG: _OracleLong,
|
||||
|
||||
sqltypes.Integer : _OracleInteger, # this is only needed for OUT parameters.
|
||||
# it would be nice if we could not use it otherwise.
|
||||
# this is only needed for OUT parameters.
|
||||
# it would be nice if we could not use it otherwise.
|
||||
sqltypes.Integer: _OracleInteger,
|
||||
|
||||
oracle.RAW: _OracleRaw,
|
||||
sqltypes.Unicode: _OracleNVarChar,
|
||||
sqltypes.NVARCHAR : _OracleNVarChar,
|
||||
sqltypes.NVARCHAR: _OracleNVarChar,
|
||||
oracle.ROWID: _OracleRowid,
|
||||
}
|
||||
|
||||
|
||||
execute_sequence_format = list
|
||||
|
||||
def __init__(self,
|
||||
auto_setinputsizes=True,
|
||||
exclude_setinputsizes=("STRING", "UNICODE"),
|
||||
auto_convert_lobs=True,
|
||||
threaded=True,
|
||||
allow_twophase=True,
|
||||
@@ -536,22 +582,25 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
self.threaded = threaded
|
||||
self.arraysize = arraysize
|
||||
self.allow_twophase = allow_twophase
|
||||
self.supports_timestamp = self.dbapi is None or hasattr(self.dbapi, 'TIMESTAMP' )
|
||||
self.supports_timestamp = self.dbapi is None or \
|
||||
hasattr(self.dbapi, 'TIMESTAMP')
|
||||
self.auto_setinputsizes = auto_setinputsizes
|
||||
self.auto_convert_lobs = auto_convert_lobs
|
||||
|
||||
if hasattr(self.dbapi, 'version'):
|
||||
self.cx_oracle_ver = tuple([int(x) for x in self.dbapi.version.split('.')])
|
||||
self.cx_oracle_ver = tuple([int(x) for x in
|
||||
self.dbapi.version.split('.')])
|
||||
else:
|
||||
self.cx_oracle_ver = (0, 0, 0)
|
||||
|
||||
def types(*names):
|
||||
return set([
|
||||
getattr(self.dbapi, name, None) for name in names
|
||||
]).difference([None])
|
||||
return set(
|
||||
getattr(self.dbapi, name, None) for name in names
|
||||
).difference([None])
|
||||
|
||||
self._cx_oracle_exclude_setinputsizes = types("STRING", "UNICODE")
|
||||
self._cx_oracle_string_types = types("STRING", "UNICODE", "NCLOB", "CLOB")
|
||||
self.exclude_setinputsizes = types(*(exclude_setinputsizes or ()))
|
||||
self._cx_oracle_string_types = types("STRING", "UNICODE",
|
||||
"NCLOB", "CLOB")
|
||||
self._cx_oracle_unicode_types = types("UNICODE", "NCLOB")
|
||||
self._cx_oracle_binary_types = types("BFILE", "CLOB", "NCLOB", "BLOB")
|
||||
self.supports_unicode_binds = self.cx_oracle_ver >= (5, 0)
|
||||
@@ -573,19 +622,23 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
self.supports_unicode_statements = True
|
||||
self.supports_unicode_binds = True
|
||||
self._cx_oracle_with_unicode = True
|
||||
# Py2K
|
||||
# There's really no reason to run with WITH_UNICODE under Python 2.x.
|
||||
# Give the user a hint.
|
||||
util.warn("cx_Oracle is compiled under Python 2.xx using the "
|
||||
"WITH_UNICODE flag. Consider recompiling cx_Oracle without "
|
||||
"this flag, which is in no way necessary for full support of Unicode. "
|
||||
"Otherwise, all string-holding bind parameters must "
|
||||
"be explicitly typed using SQLAlchemy's String type or one of its subtypes,"
|
||||
"or otherwise be passed as Python unicode. Plain Python strings "
|
||||
"passed as bind parameters will be silently corrupted by cx_Oracle."
|
||||
)
|
||||
self.execution_ctx_cls = OracleExecutionContext_cx_oracle_with_unicode
|
||||
# end Py2K
|
||||
|
||||
if util.py2k:
|
||||
# There's really no reason to run with WITH_UNICODE under Python 2.x.
|
||||
# Give the user a hint.
|
||||
util.warn(
|
||||
"cx_Oracle is compiled under Python 2.xx using the "
|
||||
"WITH_UNICODE flag. Consider recompiling cx_Oracle "
|
||||
"without this flag, which is in no way necessary for full "
|
||||
"support of Unicode. Otherwise, all string-holding bind "
|
||||
"parameters must be explicitly typed using SQLAlchemy's "
|
||||
"String type or one of its subtypes,"
|
||||
"or otherwise be passed as Python unicode. "
|
||||
"Plain Python strings passed as bind parameters will be "
|
||||
"silently corrupted by cx_Oracle."
|
||||
)
|
||||
self.execution_ctx_cls = \
|
||||
OracleExecutionContext_cx_oracle_with_unicode
|
||||
else:
|
||||
self._cx_oracle_with_unicode = False
|
||||
|
||||
@@ -603,9 +656,10 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
self.dbapi.BLOB: oracle.BLOB(),
|
||||
self.dbapi.BINARY: oracle.RAW(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def dbapi(cls):
|
||||
cx_Oracle = __import__('cx_Oracle')
|
||||
import cx_Oracle
|
||||
return cx_Oracle
|
||||
|
||||
def initialize(self, connection):
|
||||
@@ -668,6 +722,7 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
return
|
||||
|
||||
cx_Oracle = self.dbapi
|
||||
|
||||
def output_type_handler(cursor, name, defaultType,
|
||||
size, precision, scale):
|
||||
# convert all NUMBER with precision + positive scale to Decimal
|
||||
@@ -695,7 +750,7 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
arraysize=cursor.arraysize)
|
||||
# allow all strings to come back natively as Unicode
|
||||
elif defaultType in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
|
||||
return cursor.var(unicode, size, cursor.arraysize)
|
||||
return cursor.var(util.text_type, size, cursor.arraysize)
|
||||
|
||||
def on_connect(conn):
|
||||
conn.outputtypehandler = output_type_handler
|
||||
@@ -730,20 +785,19 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
twophase=self.allow_twophase,
|
||||
)
|
||||
|
||||
# Py2K
|
||||
if self._cx_oracle_with_unicode:
|
||||
for k, v in opts.items():
|
||||
if isinstance(v, str):
|
||||
opts[k] = unicode(v)
|
||||
else:
|
||||
for k, v in opts.items():
|
||||
if isinstance(v, unicode):
|
||||
opts[k] = str(v)
|
||||
# end Py2K
|
||||
if util.py2k:
|
||||
if self._cx_oracle_with_unicode:
|
||||
for k, v in opts.items():
|
||||
if isinstance(v, str):
|
||||
opts[k] = unicode(v)
|
||||
else:
|
||||
for k, v in opts.items():
|
||||
if isinstance(v, unicode):
|
||||
opts[k] = str(v)
|
||||
|
||||
if 'mode' in url.query:
|
||||
opts['mode'] = url.query['mode']
|
||||
if isinstance(opts['mode'], basestring):
|
||||
if isinstance(opts['mode'], util.string_types):
|
||||
mode = opts['mode'].upper()
|
||||
if mode == 'SYSDBA':
|
||||
opts['mode'] = self.dbapi.SYSDBA
|
||||
@@ -769,8 +823,9 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
# ORA-03113: end-of-file on communication channel
|
||||
# ORA-03135: connection lost contact
|
||||
# ORA-01033: ORACLE initialization or shutdown in progress
|
||||
# ORA-02396: exceeded maximum idle time, please connect again
|
||||
# TODO: Others ?
|
||||
return error.code in (28, 3114, 3113, 3135, 1033)
|
||||
return error.code in (28, 3114, 3113, 3135, 1033, 2396)
|
||||
else:
|
||||
return False
|
||||
|
||||
@@ -783,6 +838,11 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
id = random.randint(0, 2 ** 128)
|
||||
return (0x1234, "%032x" % id, "%032x" % 9)
|
||||
|
||||
def do_executemany(self, cursor, statement, parameters, context=None):
|
||||
if isinstance(parameters, tuple):
|
||||
parameters = list(parameters)
|
||||
cursor.executemany(statement, parameters)
|
||||
|
||||
def do_begin_twophase(self, connection, xid):
|
||||
connection.connection.begin(*xid)
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
# oracle/zxjdbc.py
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
"""Support for the Oracle database via the zxjdbc JDBC connector.
|
||||
|
||||
JDBC Driver
|
||||
-----------
|
||||
|
||||
The official Oracle JDBC driver is at
|
||||
http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html.
|
||||
"""
|
||||
.. dialect:: oracle+zxjdbc
|
||||
:name: zxJDBC for Jython
|
||||
:dbapi: zxjdbc
|
||||
:connectstring: oracle+zxjdbc://user:pass@host/dbname
|
||||
:driverurl: http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html.
|
||||
|
||||
"""
|
||||
import decimal
|
||||
@@ -19,12 +18,13 @@ import re
|
||||
from sqlalchemy import sql, types as sqltypes, util
|
||||
from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
|
||||
from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, OracleExecutionContext
|
||||
from sqlalchemy.engine import base, default
|
||||
from sqlalchemy.engine import result as _result
|
||||
from sqlalchemy.sql import expression
|
||||
import collections
|
||||
|
||||
SQLException = zxJDBC = None
|
||||
|
||||
|
||||
class _ZxJDBCDate(sqltypes.Date):
|
||||
|
||||
def result_processor(self, dialect, coltype):
|
||||
@@ -78,7 +78,7 @@ class OracleCompiler_zxjdbc(OracleCompiler):
|
||||
self.binds[bindparam.key] = bindparam
|
||||
binds.append(self.bindparam_string(self._truncate_bindparam(bindparam)))
|
||||
|
||||
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
|
||||
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
|
||||
|
||||
|
||||
class OracleExecutionContext_zxjdbc(OracleExecutionContext):
|
||||
@@ -95,8 +95,8 @@ class OracleExecutionContext_zxjdbc(OracleExecutionContext):
|
||||
try:
|
||||
try:
|
||||
rrs = self.statement.__statement__.getReturnResultSet()
|
||||
rrs.next()
|
||||
except SQLException, sqle:
|
||||
next(rrs)
|
||||
except SQLException as sqle:
|
||||
msg = '%s [SQLCode: %d]' % (sqle.getMessage(), sqle.getErrorCode())
|
||||
if sqle.getSQLState() is not None:
|
||||
msg += ' [SQLState: %s]' % sqle.getSQLState()
|
||||
@@ -113,7 +113,7 @@ class OracleExecutionContext_zxjdbc(OracleExecutionContext):
|
||||
pass
|
||||
self.statement.close()
|
||||
|
||||
return base.ResultProxy(self)
|
||||
return _result.ResultProxy(self)
|
||||
|
||||
def create_cursor(self):
|
||||
cursor = self._dbapi_connection.cursor()
|
||||
@@ -121,7 +121,7 @@ class OracleExecutionContext_zxjdbc(OracleExecutionContext):
|
||||
return cursor
|
||||
|
||||
|
||||
class ReturningResultProxy(base.FullyBufferedResultProxy):
|
||||
class ReturningResultProxy(_result.FullyBufferedResultProxy):
|
||||
|
||||
"""ResultProxy backed by the RETURNING ResultSet results."""
|
||||
|
||||
@@ -178,7 +178,7 @@ class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect):
|
||||
colspecs = util.update_copy(
|
||||
OracleDialect.colspecs,
|
||||
{
|
||||
sqltypes.Date : _ZxJDBCDate,
|
||||
sqltypes.Date: _ZxJDBCDate,
|
||||
sqltypes.Numeric: _ZxJDBCNumeric
|
||||
}
|
||||
)
|
||||
@@ -189,17 +189,19 @@ class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect):
|
||||
from java.sql import SQLException
|
||||
from com.ziclix.python.sql import zxJDBC
|
||||
from com.ziclix.python.sql.handler import OracleDataHandler
|
||||
class OracleReturningDataHandler(OracleDataHandler):
|
||||
|
||||
class OracleReturningDataHandler(OracleDataHandler):
|
||||
"""zxJDBC DataHandler that specially handles ReturningParam."""
|
||||
|
||||
def setJDBCObject(self, statement, index, object, dbtype=None):
|
||||
if type(object) is ReturningParam:
|
||||
statement.registerReturnParameter(index, object.type)
|
||||
elif dbtype is None:
|
||||
OracleDataHandler.setJDBCObject(self, statement, index, object)
|
||||
OracleDataHandler.setJDBCObject(
|
||||
self, statement, index, object)
|
||||
else:
|
||||
OracleDataHandler.setJDBCObject(self, statement, index, object, dbtype)
|
||||
OracleDataHandler.setJDBCObject(
|
||||
self, statement, index, object, dbtype)
|
||||
self.DataHandler = OracleReturningDataHandler
|
||||
|
||||
def initialize(self, connection):
|
||||
|
||||
Reference in New Issue
Block a user