summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/dialects/oracle
diff options
context:
space:
mode:
authorBrian Jarrett <celttechie@gmail.com>2014-07-20 12:44:40 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2014-07-20 12:44:40 -0400
commitcca03097f47f22783d42d1853faac6cf84607c5a (patch)
tree4fe1a63d03a2d88d1cf37e1167759dfaf84f4ce7 /lib/sqlalchemy/dialects/oracle
parent827329a0cca5351094a1a86b6b2be2b9182f0ae2 (diff)
downloadsqlalchemy-cca03097f47f22783d42d1853faac6cf84607c5a.tar.gz
- apply pep8 formatting to sqlalchemy/sql, sqlalchemy/util, sqlalchemy/dialects,
sqlalchemy/orm, sqlalchemy/event, sqlalchemy/testing
Diffstat (limited to 'lib/sqlalchemy/dialects/oracle')
-rw-r--r--lib/sqlalchemy/dialects/oracle/__init__.py8
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py415
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py308
-rw-r--r--lib/sqlalchemy/dialects/oracle/zxjdbc.py47
4 files changed, 436 insertions, 342 deletions
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py
index 4e57e3cee..fd32f2235 100644
--- a/lib/sqlalchemy/dialects/oracle/__init__.py
+++ b/lib/sqlalchemy/dialects/oracle/__init__.py
@@ -17,8 +17,8 @@ from sqlalchemy.dialects.oracle.base import \
__all__ = (
-'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'NUMBER',
-'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW',
-'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL',
-'VARCHAR2', 'NVARCHAR2', 'ROWID'
+ 'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'NUMBER',
+ 'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW',
+ 'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL',
+ 'VARCHAR2', 'NVARCHAR2', 'ROWID'
)
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 781fc601f..40ba051f7 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -9,31 +9,37 @@
.. dialect:: oracle
:name: Oracle
- Oracle version 8 through current (11g at the time of this writing) are supported.
+ Oracle version 8 through current (11g at the time of this writing) are
+ supported.
Connect Arguments
-----------------
-The dialect supports several :func:`~sqlalchemy.create_engine()` arguments which
-affect the behavior of the dialect regardless of driver in use.
+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
- to ``True``. If ``False``, Oracle-8 compatible constructs are used for joins.
+* ``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
-----------------------
-SQLAlchemy Table objects which include integer primary keys are usually assumed to have
-"autoincrementing" behavior, meaning they can generate their own primary key values upon
-INSERT. Since Oracle has no "autoincrement" feature, SQLAlchemy relies upon sequences
-to produce these values. With the Oracle dialect, *a sequence must always be explicitly
-specified to enable autoincrement*. This is divergent with the majority of documentation
-examples which assume the usage of an autoincrement-capable database. To specify sequences,
-use the sqlalchemy.schema.Sequence object which is passed to a Column construct::
+SQLAlchemy Table objects which include integer primary keys are usually
+assumed to have "autoincrementing" behavior, meaning they can generate their
+own primary key values upon INSERT. Since Oracle has no "autoincrement"
+feature, SQLAlchemy relies upon sequences to produce these values. With the
+Oracle dialect, *a sequence must always be explicitly specified to enable
+autoincrement*. This is divergent with the majority of documentation
+examples which assume the usage of an autoincrement-capable database. To
+specify sequences, use the sqlalchemy.schema.Sequence object which is passed
+to a Column construct::
t = Table('mytable', metadata,
Column('id', Integer, Sequence('id_seq'), primary_key=True),
@@ -50,15 +56,16 @@ This step is also required when using table reflection, i.e. autoload=True::
Identifier Casing
-----------------
-In Oracle, the data dictionary represents all case insensitive identifier names
-using UPPERCASE text. SQLAlchemy on the other hand considers an all-lower case identifier
-name to be case insensitive. The Oracle dialect converts all case insensitive identifiers
-to and from those two formats during schema level communication, such as reflection of
-tables and indexes. Using an UPPERCASE name on the SQLAlchemy side indicates a
-case sensitive identifier, and SQLAlchemy will quote the name - this will cause mismatches
-against data dictionary data received from Oracle, so unless identifier names have been
-truly created as case sensitive (i.e. using quoted names), all lowercase names should be
-used on the SQLAlchemy side.
+In Oracle, the data dictionary represents all case insensitive identifier
+names using UPPERCASE text. SQLAlchemy on the other hand considers an
+all-lower case identifier name to be case insensitive. The Oracle dialect
+converts all case insensitive identifiers to and from those two formats during
+schema level communication, such as reflection of tables and indexes. Using
+an UPPERCASE name on the SQLAlchemy side indicates a case sensitive
+identifier, and SQLAlchemy will quote the name - this will cause mismatches
+against data dictionary data received from Oracle, so unless identifier names
+have been truly created as case sensitive (i.e. using quoted names), all
+lowercase names should be used on the SQLAlchemy side.
LIMIT/OFFSET Support
@@ -71,44 +78,49 @@ http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html .
There are two options which affect its behavior:
-* the "FIRST ROWS()" optimization keyword is not used by default. To enable the usage of this
- optimization directive, specify ``optimize_limits=True`` to :func:`.create_engine`.
-* the values passed for the limit/offset are sent as bound parameters. Some users have observed
- that Oracle produces a poor query plan when the values are sent as binds and not
- rendered literally. To render the limit/offset values literally within the SQL
- statement, specify ``use_binds_for_limits=False`` to :func:`.create_engine`.
-
-Some users have reported better performance when the entirely different approach of a
-window query is used, i.e. ROW_NUMBER() OVER (ORDER BY), to provide LIMIT/OFFSET (note
-that the majority of users don't observe this). To suit this case the
-method used for LIMIT/OFFSET can be replaced entirely. See the recipe at
+* the "FIRST ROWS()" optimization keyword is not used by default. To enable
+ the usage of this optimization directive, specify ``optimize_limits=True``
+ to :func:`.create_engine`.
+* the values passed for the limit/offset are sent as bound parameters. Some
+ users have observed that Oracle produces a poor query plan when the values
+ are sent as binds and not rendered literally. To render the limit/offset
+ values literally within the SQL statement, specify
+ ``use_binds_for_limits=False`` to :func:`.create_engine`.
+
+Some users have reported better performance when the entirely different
+approach of a window query is used, i.e. ROW_NUMBER() OVER (ORDER BY), to
+provide LIMIT/OFFSET (note that the majority of users don't observe this).
+To suit this case the method used for LIMIT/OFFSET can be replaced entirely.
+See the recipe at
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/WindowFunctionsByDefault
-which installs a select compiler that overrides the generation of limit/offset with
-a window function.
+which installs a select compiler that overrides the generation of limit/offset
+with a window function.
.. _oracle_returning:
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`).
+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`::
+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)
+ 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::
+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)
@@ -121,13 +133,15 @@ Implicit returning can also be disabled on a table-by-table basis as a table opt
.. seealso::
- :ref:`cx_oracle_returning` - additional cx_oracle-specific restrictions on implicit returning.
+ :ref:`cx_oracle_returning` - additional cx_oracle-specific restrictions on
+ implicit returning.
ON UPDATE CASCADE
-----------------
-Oracle doesn't have native ON UPDATE CASCADE functionality. A trigger based solution
-is available at http://asktom.oracle.com/tkyte/update_cascade/index.html .
+Oracle doesn't have native ON UPDATE CASCADE functionality. A trigger based
+solution is available at
+http://asktom.oracle.com/tkyte/update_cascade/index.html .
When using the SQLAlchemy ORM, the ORM has limited ability to manually issue
cascading updates - specify ForeignKey objects using the
@@ -137,29 +151,32 @@ and specify "passive_updates=False" on each relationship().
Oracle 8 Compatibility
----------------------
-When Oracle 8 is detected, the dialect internally configures itself to the following
-behaviors:
+When Oracle 8 is detected, the dialect internally configures itself to the
+following behaviors:
* the use_ansi flag is set to False. This has the effect of converting all
JOIN phrases into the WHERE clause, and in the case of LEFT OUTER JOIN
makes use of Oracle's (+) operator.
* the NVARCHAR2 and NCLOB datatypes are no longer generated as DDL when
- the :class:`~sqlalchemy.types.Unicode` is used - VARCHAR2 and CLOB are issued
- instead. This because these types don't seem to work correctly on Oracle 8
- even though they are available. The :class:`~sqlalchemy.types.NVARCHAR`
- and :class:`~sqlalchemy.dialects.oracle.NCLOB` types will always generate NVARCHAR2 and NCLOB.
+ the :class:`~sqlalchemy.types.Unicode` is used - VARCHAR2 and CLOB are
+ issued instead. This because these types don't seem to work correctly on
+ Oracle 8 even though they are available. The
+ :class:`~sqlalchemy.types.NVARCHAR` and
+ :class:`~sqlalchemy.dialects.oracle.NCLOB` types will always generate
+ NVARCHAR2 and NCLOB.
* the "native unicode" mode is disabled when using cx_oracle, i.e. SQLAlchemy
- encodes all Python unicode objects to "string" before passing in as bind parameters.
+ encodes all Python unicode objects to "string" before passing in as bind
+ parameters.
Synonym/DBLINK Reflection
-------------------------
-When using reflection with Table objects, the dialect can optionally search for tables
-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 :class:`.Table` construct::
+When using reflection with Table objects, the dialect can optionally search
+for tables 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 :class:`.Table` construct::
some_table = Table('some_table', autoload=True,
autoload_with=some_engine,
@@ -167,9 +184,10 @@ keyword argument to the :class:`.Table` construct::
When this flag is set, the given name (such as ``some_table`` above) will
be searched not just in the ``ALL_TABLES`` view, but also within the
-``ALL_SYNONYMS`` view to see if this name is actually a synonym to another name.
-If the synonym is located and refers to a DBLINK, the oracle dialect knows
-how to locate the table's information using DBLINK syntax (e.g. ``@dblink``).
+``ALL_SYNONYMS`` view to see if this name is actually a synonym to another
+name. If the synonym is located and refers to a DBLINK, the oracle dialect
+knows how to locate the table's information using DBLINK syntax(e.g.
+``@dblink``).
``oracle_resolve_synonyms`` is accepted wherever reflection arguments are
accepted, including methods such as :meth:`.MetaData.reflect` and
@@ -202,24 +220,25 @@ import re
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
+from sqlalchemy.sql import (operators as sql_operators,
+ functions as sql_functions)
from sqlalchemy import types as sqltypes, schema as sa_schema
from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, \
- BLOB, CLOB, TIMESTAMP, FLOAT
+ BLOB, CLOB, TIMESTAMP, FLOAT
RESERVED_WORDS = \
- set('SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN '\
- 'DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED '\
- 'ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE '\
- 'ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE '\
- 'BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES '\
- 'AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS '\
- 'NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER '\
- 'CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR '\
+ set('SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN '
+ 'DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED '
+ 'ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE '
+ 'ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE '
+ 'BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES '
+ 'AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS '
+ 'NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER '
+ 'CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR '
'DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT LEVEL'.split())
NO_ARG_FNS = set('UID CURRENT_DATE SYSDATE USER '
- 'CURRENT_TIME CURRENT_TIMESTAMP'.split())
+ 'CURRENT_TIME CURRENT_TIMESTAMP'.split())
class RAW(sqltypes._Binary):
@@ -244,7 +263,8 @@ class NUMBER(sqltypes.Numeric, sqltypes.Integer):
if asdecimal is None:
asdecimal = bool(scale and scale > 0)
- super(NUMBER, self).__init__(precision=precision, scale=scale, asdecimal=asdecimal)
+ super(NUMBER, self).__init__(
+ precision=precision, scale=scale, asdecimal=asdecimal)
def adapt(self, impltype):
ret = super(NUMBER, self).adapt(impltype)
@@ -267,7 +287,8 @@ class DOUBLE_PRECISION(sqltypes.Numeric):
if asdecimal is None:
asdecimal = False
- super(DOUBLE_PRECISION, self).__init__(precision=precision, scale=scale, asdecimal=asdecimal)
+ super(DOUBLE_PRECISION, self).__init__(
+ precision=precision, scale=scale, asdecimal=asdecimal)
class BFILE(sqltypes.LargeBinary):
@@ -277,6 +298,7 @@ class BFILE(sqltypes.LargeBinary):
class LONG(sqltypes.Text):
__visit_name__ = 'LONG'
+
class DATE(sqltypes.DateTime):
"""Provide the oracle DATE type.
@@ -289,7 +311,6 @@ class DATE(sqltypes.DateTime):
"""
__visit_name__ = 'DATE'
-
def _compare_type_affinity(self, other):
return other._type_affinity in (sqltypes.DateTime, sqltypes.Date)
@@ -298,18 +319,19 @@ class INTERVAL(sqltypes.TypeEngine):
__visit_name__ = 'INTERVAL'
def __init__(self,
- day_precision=None,
- second_precision=None):
+ day_precision=None,
+ second_precision=None):
"""Construct an INTERVAL.
Note that only DAY TO SECOND intervals are currently supported.
This is due to a lack of support for YEAR TO MONTH intervals
within available DBAPIs (cx_oracle and zxjdbc).
- :param day_precision: the day precision value. this is the number of digits
- to store for the day field. Defaults to "2"
- :param second_precision: the second precision value. this is the number of digits
- to store for the fractional seconds field. Defaults to "6".
+ :param day_precision: the day precision value. this is the number of
+ digits to store for the day field. Defaults to "2"
+ :param second_precision: the second precision value. this is the
+ number of digits to store for the fractional seconds field.
+ Defaults to "6".
"""
self.day_precision = day_precision
@@ -385,11 +407,11 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
def visit_INTERVAL(self, type_):
return "INTERVAL DAY%s TO SECOND%s" % (
type_.day_precision is not None and
- "(%d)" % type_.day_precision or
- "",
+ "(%d)" % type_.day_precision or
+ "",
type_.second_precision is not None and
- "(%d)" % type_.second_precision or
- "",
+ "(%d)" % type_.second_precision or
+ "",
)
def visit_LONG(self, type_):
@@ -483,7 +505,7 @@ class OracleCompiler(compiler.SQLCompiler):
compound_keywords = util.update_copy(
compiler.SQLCompiler.compound_keywords,
{
- expression.CompoundSelect.EXCEPT: 'MINUS'
+ expression.CompoundSelect.EXCEPT: 'MINUS'
}
)
@@ -504,7 +526,7 @@ class OracleCompiler(compiler.SQLCompiler):
def visit_match_op_binary(self, binary, operator, **kw):
return "CONTAINS (%s, %s)" % (self.process(binary.left),
- self.process(binary.right))
+ self.process(binary.right))
def visit_true(self, expr, **kw):
return '1'
@@ -542,8 +564,7 @@ class OracleCompiler(compiler.SQLCompiler):
else:
right = join.right
return self.process(join.left, **kwargs) + \
- ", " + self.process(right, **kwargs)
-
+ ", " + self.process(right, **kwargs)
def _get_nonansi_join_whereclause(self, froms):
clauses = []
@@ -556,8 +577,8 @@ class OracleCompiler(compiler.SQLCompiler):
binary.left = _OuterJoinColumn(binary.left)
elif join.right.is_derived_from(binary.right.table):
binary.right = _OuterJoinColumn(binary.right)
- clauses.append(visitors.cloned_traverse(join.onclause, {},
- {'binary': visit_binary}))
+ clauses.append(visitors.cloned_traverse(
+ join.onclause, {}, {'binary': visit_binary}))
else:
clauses.append(join.onclause)
@@ -580,46 +601,54 @@ class OracleCompiler(compiler.SQLCompiler):
return self.process(vc.column) + "(+)"
def visit_sequence(self, seq):
- return self.dialect.identifier_preparer.format_sequence(seq) + ".nextval"
+ return (self.dialect.identifier_preparer.format_sequence(seq) +
+ ".nextval")
def visit_alias(self, alias, asfrom=False, ashint=False, **kwargs):
- """Oracle doesn't like ``FROM table AS alias``. Is the AS standard SQL??"""
+ """Oracle doesn't like ``FROM table AS alias``. Is the AS standard
+ SQL??
+ """
if asfrom or ashint:
alias_name = isinstance(alias.name, expression._truncated_label) and \
- self._truncated_identifier("alias", alias.name) or alias.name
+ self._truncated_identifier("alias", alias.name) or alias.name
if ashint:
return alias_name
elif asfrom:
return self.process(alias.original, asfrom=asfrom, **kwargs) + \
- " " + self.preparer.format_alias(alias, alias_name)
+ " " + self.preparer.format_alias(alias, alias_name)
else:
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)):
+ 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))
+ 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)),
+ getattr(column, 'key', None)),
column.type
)
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."""
+ """Need to determine how to get ``LIMIT``/``OFFSET`` into a
+ ``UNION`` for Oracle.
+ """
pass
def visit_select(self, select, **kwargs):
@@ -630,7 +659,7 @@ class OracleCompiler(compiler.SQLCompiler):
if not getattr(select, '_oracle_visit', None):
if not self.dialect.use_ansi:
froms = self._display_froms_for_select(
- select, kwargs.get('asfrom', False))
+ select, kwargs.get('asfrom', False))
whereclause = self._get_nonansi_join_whereclause(froms)
if whereclause is not None:
select = select.where(whereclause)
@@ -639,15 +668,17 @@ class OracleCompiler(compiler.SQLCompiler):
limit_clause = select._limit_clause
offset_clause = select._offset_clause
if limit_clause is not None or offset_clause is not None:
- # See http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
+ # See http://www.oracle.com/technology/oramag/oracle/06-sep/\
+ # o56asktom.html
#
# Generalized form of an Oracle pagination query:
# select ... from (
- # select /*+ FIRST_ROWS(N) */ ...., rownum as ora_rn from (
- # select distinct ... where ... order by ...
+ # select /*+ FIRST_ROWS(N) */ ...., rownum as ora_rn from
+ # ( select distinct ... where ... order by ...
# ) where ROWNUM <= :limit+:offset
# ) where ora_rn > :offset
- # Outer select and "ROWNUM as ora_rn" can be dropped if limit=0
+ # Outer select and "ROWNUM as ora_rn" can be dropped if
+ # limit=0
# TODO: use annotations instead of clone + attr set ?
select = select._generate()
@@ -659,8 +690,8 @@ class OracleCompiler(compiler.SQLCompiler):
self.dialect.optimize_limits and \
select._simple_int_limit:
limitselect = limitselect.prefix_with(
- "/*+ FIRST_ROWS(%d) */" %
- select._limit)
+ "/*+ FIRST_ROWS(%d) */" %
+ select._limit)
limitselect._oracle_visit = True
limitselect._is_wrapper = True
@@ -680,7 +711,7 @@ class OracleCompiler(compiler.SQLCompiler):
if offset_clause is not None:
max_row = max_row + offset_clause
limitselect.append_whereclause(
- sql.literal_column("ROWNUM") <= max_row)
+ sql.literal_column("ROWNUM") <= max_row)
# If needed, add the ora_rn, and wrap again with offset.
if offset_clause is None:
@@ -688,20 +719,20 @@ class OracleCompiler(compiler.SQLCompiler):
select = limitselect
else:
limitselect = limitselect.column(
- sql.literal_column("ROWNUM").label("ora_rn"))
+ sql.literal_column("ROWNUM").label("ora_rn"))
limitselect._oracle_visit = True
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
if not self.dialect.use_binds_for_limits:
offset_clause = sql.literal_column(
- "%d" % select._offset)
+ "%d" % select._offset)
offsetselect.append_whereclause(
- sql.literal_column("ora_rn") > offset_clause)
+ sql.literal_column("ora_rn") > offset_clause)
offsetselect._for_update_arg = select._for_update_arg
select = offsetselect
@@ -720,9 +751,9 @@ class OracleCompiler(compiler.SQLCompiler):
if select._for_update_arg.of:
tmp += ' OF ' + ', '.join(
- self.process(elem) for elem in
- select._for_update_arg.of
- )
+ self.process(elem) for elem in
+ select._for_update_arg.of
+ )
if select._for_update_arg.nowait:
tmp += " NOWAIT"
@@ -738,18 +769,20 @@ class OracleDDLCompiler(compiler.DDLCompiler):
text += " ON DELETE %s" % constraint.ondelete
# oracle has no ON UPDATE CASCADE -
- # its only available via triggers http://asktom.oracle.com/tkyte/update_cascade/index.html
+ # its only available via triggers
+ # http://asktom.oracle.com/tkyte/update_cascade/index.html
if constraint.onupdate is not None:
util.warn(
"Oracle does not contain native UPDATE CASCADE "
- "functionality - onupdates will not be rendered for foreign keys. "
- "Consider using deferrable=True, initially='deferred' or triggers.")
+ "functionality - onupdates will not be rendered for foreign "
+ "keys. Consider using deferrable=True, initially='deferred' "
+ "or triggers.")
return text
def visit_create_index(self, create, **kw):
return super(OracleDDLCompiler, self).\
- visit_create_index(create, include_schema=True)
+ visit_create_index(create, include_schema=True)
class OracleIdentifierPreparer(compiler.IdentifierPreparer):
@@ -767,14 +800,16 @@ class OracleIdentifierPreparer(compiler.IdentifierPreparer):
def format_savepoint(self, savepoint):
name = re.sub(r'^_+', '', savepoint.ident)
- return super(OracleIdentifierPreparer, self).format_savepoint(savepoint, name)
+ return super(
+ OracleIdentifierPreparer, self).format_savepoint(savepoint, name)
class OracleExecutionContext(default.DefaultExecutionContext):
def fire_sequence(self, seq, type_):
- return self._execute_scalar("SELECT " +
- self.dialect.identifier_preparer.format_sequence(seq) +
- ".nextval FROM DUAL", type_)
+ return self._execute_scalar(
+ "SELECT " +
+ self.dialect.identifier_preparer.format_sequence(seq) +
+ ".nextval FROM DUAL", type_)
class OracleDialect(default.DefaultDialect):
@@ -811,10 +846,10 @@ class OracleDialect(default.DefaultDialect):
]
def __init__(self,
- use_ansi=True,
- optimize_limits=False,
- use_binds_for_limits=True,
- **kwargs):
+ use_ansi=True,
+ optimize_limits=False,
+ use_binds_for_limits=True,
+ **kwargs):
default.DefaultDialect.__init__(self, **kwargs)
self.use_ansi = use_ansi
self.optimize_limits = optimize_limits
@@ -823,9 +858,9 @@ class OracleDialect(default.DefaultDialect):
def initialize(self, connection):
super(OracleDialect, self).initialize(connection)
self.implicit_returning = self.__dict__.get(
- 'implicit_returning',
- self.server_version_info > (10, )
- )
+ 'implicit_returning',
+ self.server_version_info > (10, )
+ )
if self._is_oracle_8:
self.colspecs = self.colspecs.copy()
@@ -835,7 +870,7 @@ class OracleDialect(default.DefaultDialect):
@property
def _is_oracle_8(self):
return self.server_version_info and \
- self.server_version_info < (9, )
+ self.server_version_info < (9, )
@property
def _supports_char_length(self):
@@ -855,7 +890,8 @@ class OracleDialect(default.DefaultDialect):
cursor = connection.execute(
sql.text("SELECT table_name FROM all_tables "
"WHERE table_name = :name AND owner = :schema_name"),
- name=self.denormalize_name(table_name), schema_name=self.denormalize_name(schema))
+ name=self.denormalize_name(table_name),
+ schema_name=self.denormalize_name(schema))
return cursor.first() is not None
def has_sequence(self, connection, sequence_name, schema=None):
@@ -863,8 +899,10 @@ class OracleDialect(default.DefaultDialect):
schema = self.default_schema_name
cursor = connection.execute(
sql.text("SELECT sequence_name FROM all_sequences "
- "WHERE sequence_name = :name AND sequence_owner = :schema_name"),
- name=self.denormalize_name(sequence_name), schema_name=self.denormalize_name(schema))
+ "WHERE sequence_name = :name AND "
+ "sequence_owner = :schema_name"),
+ name=self.denormalize_name(sequence_name),
+ schema_name=self.denormalize_name(schema))
return cursor.first() is not None
def normalize_name(self, name):
@@ -873,8 +911,8 @@ class OracleDialect(default.DefaultDialect):
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()):
+ if name.upper() == name and not \
+ self.identifier_preparer._requires_quotes(name.lower()):
return name.lower()
else:
return name
@@ -882,7 +920,8 @@ class OracleDialect(default.DefaultDialect):
def denormalize_name(self, name):
if name is None:
return None
- elif name.lower() == name and not self.identifier_preparer._requires_quotes(name.lower()):
+ elif name.lower() == name and not \
+ self.identifier_preparer._requires_quotes(name.lower()):
name = name.upper()
if util.py2k:
if not self.supports_unicode_binds:
@@ -892,18 +931,21 @@ class OracleDialect(default.DefaultDialect):
return name
def _get_default_schema_name(self, connection):
- return self.normalize_name(connection.execute('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):
+ 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.
if desired_owner is None, attempts to locate a distinct owner.
- returns the actual name, owner, dblink name, and synonym name if found.
+ 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 "
+ "synonym_name FROM all_synonyms WHERE "
clauses = []
params = {}
if desired_synonym:
@@ -922,16 +964,20 @@ class OracleDialect(default.DefaultDialect):
if desired_owner:
row = result.first()
if row:
- return row['table_name'], row['table_owner'], row['db_link'], row['synonym_name']
+ return (row['table_name'], row['table_owner'],
+ row['db_link'], row['synonym_name'])
else:
return None, None, None, None
else:
rows = result.fetchall()
if len(rows) > 1:
- raise AssertionError("There are multiple tables visible to the schema, you must specify owner")
+ raise AssertionError(
+ "There are multiple tables visible to the schema, you "
+ "must specify owner")
elif len(rows) == 1:
row = rows[0]
- return row['table_name'], row['table_owner'], row['db_link'], row['synonym_name']
+ return (row['table_name'], row['table_owner'],
+ row['db_link'], row['synonym_name'])
else:
return None, None, None, None
@@ -941,10 +987,10 @@ 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:
@@ -957,8 +1003,8 @@ class OracleDialect(default.DefaultDialect):
# 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)
+ 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)
@@ -980,7 +1026,8 @@ class OracleDialect(default.DefaultDialect):
schema = self.default_schema_name
s = sql.text(
"SELECT table_name FROM all_tables "
- "WHERE nvl(tablespace_name, 'no tablespace') NOT IN ('SYSTEM', 'SYSAUX') "
+ "WHERE nvl(tablespace_name, 'no tablespace') NOT IN "
+ "('SYSTEM', 'SYSAUX') "
"AND OWNER = :owner "
"AND IOT_NAME IS NULL")
cursor = connection.execute(s, owner=schema)
@@ -1021,9 +1068,9 @@ class OracleDialect(default.DefaultDialect):
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"
+ "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 "
@@ -1034,7 +1081,8 @@ class OracleDialect(default.DefaultDialect):
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':
coltype = NUMBER(precision, scale)
@@ -1121,21 +1169,23 @@ class OracleDialect(default.DefaultDialect):
for rset in rp:
if rset.index_name != last_index_name:
remove_if_primary_key(index)
- index = dict(name=self.normalize_name(rset.index_name), column_names=[])
+ index = dict(name=self.normalize_name(rset.index_name),
+ column_names=[])
indexes.append(index)
index['unique'] = uniqueness.get(rset.uniqueness, False)
# filter out Oracle SYS_NC names. could also do an outer join
# to the all_tab_columns table and check for real col names there.
if not oracle_sys_col.match(rset.column_name):
- index['column_names'].append(self.normalize_name(rset.column_name))
+ index['column_names'].append(
+ self.normalize_name(rset.column_name))
last_index_name = rset.index_name
remove_if_primary_key(index)
return indexes
@reflection.cache
def _get_constraint_data(self, connection, table_name, schema=None,
- dblink='', **kw):
+ dblink='', **kw):
params = {'table_name': table_name}
@@ -1184,9 +1234,9 @@ class OracleDialect(default.DefaultDialect):
info_cache=info_cache)
pkeys = []
constraint_name = None
- constraint_data = self._get_constraint_data(connection, table_name,
- schema, dblink,
- info_cache=kw.get('info_cache'))
+ constraint_data = self._get_constraint_data(
+ connection, table_name, schema, dblink,
+ info_cache=kw.get('info_cache'))
for row in constraint_data:
(cons_name, cons_type, local_column, remote_table, remote_column, remote_owner) = \
@@ -1219,9 +1269,9 @@ class OracleDialect(default.DefaultDialect):
resolve_synonyms, dblink,
info_cache=info_cache)
- constraint_data = self._get_constraint_data(connection, table_name,
- schema, dblink,
- info_cache=kw.get('info_cache'))
+ constraint_data = self._get_constraint_data(
+ connection, table_name, schema, dblink,
+ info_cache=kw.get('info_cache'))
def fkey_rec():
return {
@@ -1236,7 +1286,7 @@ class OracleDialect(default.DefaultDialect):
for row in constraint_data:
(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]])
+ row[0:2] + tuple([self.normalize_name(x) for x in row[2:6]])
if cons_type == 'R':
if remote_table is None:
@@ -1249,23 +1299,28 @@ class OracleDialect(default.DefaultDialect):
rec = fkeys[cons_name]
rec['name'] = cons_name
- local_cols, remote_cols = rec['constrained_columns'], rec['referred_columns']
+ local_cols, remote_cols = rec[
+ 'constrained_columns'], rec['referred_columns']
if not rec['referred_table']:
if resolve_synonyms:
ref_remote_name, ref_remote_owner, ref_dblink, ref_synonym = \
- self._resolve_synonym(
- connection,
- desired_owner=self.denormalize_name(remote_owner),
- desired_table=self.denormalize_name(remote_table)
- )
+ self._resolve_synonym(
+ connection,
+ desired_owner=self.denormalize_name(
+ remote_owner),
+ desired_table=self.denormalize_name(
+ remote_table)
+ )
if ref_synonym:
remote_table = self.normalize_name(ref_synonym)
- remote_owner = self.normalize_name(ref_remote_owner)
+ remote_owner = self.normalize_name(
+ ref_remote_owner)
rec['referred_table'] = remote_table
- if requested_schema is not None or self.denormalize_name(remote_owner) != schema:
+ if requested_schema is not None or \
+ self.denormalize_name(remote_owner) != schema:
rec['referred_schema'] = remote_owner
local_cols.append(local_column)
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index bb3c837cc..4a1ceecb1 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -10,7 +10,8 @@
.. dialect:: oracle+cx_oracle
:name: cx-Oracle
:dbapi: cx_oracle
- :connectstring: oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
+ :connectstring: oracle+cx_oracle://user:pass@host:port/dbname\
+[?key=value&key=value...]
:url: http://cx-oracle.sourceforge.net/
Additional Connect Arguments
@@ -52,21 +53,21 @@ on the URL, or as keyword arguments to :func:`.create_engine()` are:
.. 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.
+* ``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.
+* ``threaded`` - enable multithreaded access to cx_oracle connections.
+ Defaults to ``True``. Note that this is the opposite default of the
+ cx_Oracle DBAPI itself.
.. _cx_oracle_unicode:
Unicode
-------
-The cx_Oracle DBAPI as of version 5 fully supports unicode, and has the ability
-to return string results as Python unicode objects natively.
+The cx_Oracle DBAPI as of version 5 fully supports unicode, and has the
+ability to return string results as Python unicode objects natively.
When used in Python 3, cx_Oracle returns all strings as Python unicode objects
(that is, plain ``str`` in Python 3). In Python 2, it will return as Python
@@ -74,37 +75,39 @@ unicode those column values that are of type ``NVARCHAR`` or ``NCLOB``. For
column values that are of type ``VARCHAR`` or other non-unicode string types,
it will return values as Python strings (e.g. bytestrings).
-The cx_Oracle SQLAlchemy dialect presents two different options for the use case of
-returning ``VARCHAR`` column values as Python unicode objects under Python 2:
+The cx_Oracle SQLAlchemy dialect presents two different options for the use
+case of returning ``VARCHAR`` column values as Python unicode objects under
+Python 2:
* the cx_Oracle DBAPI has the ability to coerce all string results to Python
unicode objects unconditionally using output type handlers. This has
the advantage that the unicode conversion is global to all statements
at the cx_Oracle driver level, meaning it works with raw textual SQL
statements that have no typing information associated. However, this system
- has been observed to incur signfiicant performance overhead, not only because
- it takes effect for all string values unconditionally, but also because cx_Oracle under
- Python 2 seems to use a pure-Python function call in order to do the
- decode operation, which under cPython can orders of magnitude slower
- than doing it using C functions alone.
-
-* SQLAlchemy has unicode-decoding services built in, and when using SQLAlchemy's
- C extensions, these functions do not use any Python function calls and
- are very fast. The disadvantage to this approach is that the unicode
- conversion only takes effect for statements where the :class:`.Unicode` type
- or :class:`.String` type with ``convert_unicode=True`` is explicitly
- associated with the result column. This is the case for any ORM or Core
- query or SQL expression as well as for a :func:`.text` construct that specifies
- output column types, so in the vast majority of cases this is not an issue.
- However, when sending a completely raw string to :meth:`.Connection.execute`,
- this typing information isn't present, unless the string is handled
- within a :func:`.text` construct that adds typing information.
+ has been observed to incur signfiicant performance overhead, not only
+ because it takes effect for all string values unconditionally, but also
+ because cx_Oracle under Python 2 seems to use a pure-Python function call in
+ order to do the decode operation, which under cPython can orders of
+ magnitude slower than doing it using C functions alone.
+
+* SQLAlchemy has unicode-decoding services built in, and when using
+ SQLAlchemy's C extensions, these functions do not use any Python function
+ calls and are very fast. The disadvantage to this approach is that the
+ unicode conversion only takes effect for statements where the
+ :class:`.Unicode` type or :class:`.String` type with
+ ``convert_unicode=True`` is explicitly associated with the result column.
+ This is the case for any ORM or Core query or SQL expression as well as for
+ a :func:`.text` construct that specifies output column types, so in the vast
+ majority of cases this is not an issue. However, when sending a completely
+ raw string to :meth:`.Connection.execute`, this typing information isn't
+ present, unless the string is handled within a :func:`.text` construct that
+ adds typing information.
As of version 0.9.2 of SQLAlchemy, the default approach is to use SQLAlchemy's
typing system. This keeps cx_Oracle's expensive Python 2 approach
-disabled unless the user explicitly wants it. Under Python 3, SQLAlchemy detects
-that cx_Oracle is returning unicode objects natively and cx_Oracle's system
-is used.
+disabled unless the user explicitly wants it. Under Python 3, SQLAlchemy
+detects that cx_Oracle is returning unicode objects natively and cx_Oracle's
+system is used.
To re-enable cx_Oracle's output type handler under Python 2, the
``coerce_to_unicode=True`` flag (new in 0.9.4) can be passed to
@@ -117,12 +120,13 @@ as Python unicode under Python 2 without using cx_Oracle's native handlers,
the :func:`.text` feature can be used::
from sqlalchemy import text, Unicode
- result = conn.execute(text("select username from user").columns(username=Unicode))
+ result = conn.execute(
+ text("select username from user").columns(username=Unicode))
-.. versionchanged:: 0.9.2 cx_Oracle's outputtypehandlers are no longer used for
- unicode results of non-unicode datatypes in Python 2, after they were identified as a major
- performance bottleneck. SQLAlchemy's own unicode facilities are used
- instead.
+.. versionchanged:: 0.9.2 cx_Oracle's outputtypehandlers are no longer used
+ for unicode results of non-unicode datatypes in Python 2, after they were
+ identified as a major performance bottleneck. SQLAlchemy's own unicode
+ facilities are used instead.
.. versionadded:: 0.9.4 Added the ``coerce_to_unicode`` flag, to re-enable
cx_Oracle's outputtypehandler and revert to pre-0.9.2 behavior.
@@ -132,38 +136,43 @@ the :func:`.text` feature can be used::
RETURNING Support
-----------------
-The cx_oracle DBAPI 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.
+The cx_oracle DBAPI 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.
-For this reason, stability may be enhanced by disabling RETURNING support completely;
-SQLAlchemy otherwise will use RETURNING to fetch newly sequence-generated
-primary keys. As illustrated in :ref:`oracle_returning`::
+For this reason, stability may be enhanced by disabling RETURNING support
+completely; SQLAlchemy otherwise will use RETURNING to fetch newly
+sequence-generated primary keys. As illustrated in :ref:`oracle_returning`::
- engine = create_engine("oracle://scott:tiger@dsn", implicit_returning=False)
+ engine = create_engine("oracle://scott:tiger@dsn",
+ implicit_returning=False)
.. seealso::
- http://docs.oracle.com/cd/B10501_01/appdev.920/a96584/oci05bnd.htm#420693 - OCI documentation for RETURNING
+ 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
+ http://sourceforge.net/mailarchive/message.php?msg_id=31338136
+ - cx_oracle developer commentary
.. _cx_oracle_lob:
LOB Objects
-----------
-cx_oracle returns oracle LOBs using the cx_oracle.LOB object. SQLAlchemy converts
-these to strings so that the interface of the Binary type is consistent with that of
-other backends, and so that the linkage to a live cursor is not needed in scenarios
-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.
+cx_oracle returns oracle LOBs using the cx_oracle.LOB object. SQLAlchemy
+converts these to strings so that the interface of the Binary type is
+consistent with that of other backends, and so that the linkage to a live
+cursor is not needed in scenarios 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
-----------------------------
@@ -314,7 +323,7 @@ class _OracleNumeric(sqltypes.Numeric):
if self.precision is None and self.scale is None:
return processors.to_float
elif not getattr(self, '_is_oracle_number', False) \
- and self.scale is not None:
+ and self.scale is not None:
return processors.to_float
else:
return None
@@ -322,7 +331,7 @@ class _OracleNumeric(sqltypes.Numeric):
# cx_oracle 4 behavior, will assume
# floats
return super(_OracleNumeric, self).\
- result_processor(dialect, coltype)
+ result_processor(dialect, coltype)
class _OracleDate(sqltypes.Date):
@@ -363,7 +372,8 @@ class _NativeUnicodeMixin(object):
return unicode(value)
return process
else:
- return super(_NativeUnicodeMixin, self).bind_processor(dialect)
+ return super(
+ _NativeUnicodeMixin, self).bind_processor(dialect)
# we apply a connection output handler that returns
# unicode in all cases, so the "native_unicode" flag
@@ -392,11 +402,13 @@ class _OracleLong(oracle.LONG):
def get_dbapi_type(self, dbapi):
return dbapi.LONG_STRING
+
class _OracleString(_NativeUnicodeMixin, sqltypes.String):
pass
-class _OracleUnicodeText(_LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
+class _OracleUnicodeText(
+ _LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
def get_dbapi_type(self, dbapi):
return dbapi.NCLOB
@@ -405,7 +417,8 @@ class _OracleUnicodeText(_LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
if lob_processor is None:
return None
- string_processor = sqltypes.UnicodeText.result_processor(self, dialect, coltype)
+ string_processor = sqltypes.UnicodeText.result_processor(
+ self, dialect, coltype)
if string_processor is None:
return lob_processor
@@ -450,7 +463,7 @@ class OracleCompiler_cx_oracle(OracleCompiler):
def bindparam_string(self, name, **kw):
quote = getattr(name, 'quote', None)
if quote is True or quote is not False and \
- self.preparer._bindparam_requires_quotes(name):
+ 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)
@@ -470,12 +483,12 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
# here. so convert names in quoted_bind_names
# to encoded as well.
quoted_bind_names = \
- dict(
- (fromname.encode(self.dialect.encoding),
- toname.encode(self.dialect.encoding))
- for fromname, toname in
- quoted_bind_names.items()
- )
+ dict(
+ (fromname.encode(self.dialect.encoding),
+ toname.encode(self.dialect.encoding))
+ for fromname, toname in
+ quoted_bind_names.items()
+ )
for param in self.parameters:
for fromname, toname in quoted_bind_names.items():
param[toname] = param[fromname]
@@ -485,29 +498,30 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
# cx_oracle really has issues when you setinputsizes
# on String, including that outparams/RETURNING
# breaks for varchars
- self.set_input_sizes(quoted_bind_names,
- exclude_types=self.dialect.exclude_setinputsizes
- )
+ self.set_input_sizes(
+ quoted_bind_names,
+ exclude_types=self.dialect.exclude_setinputsizes
+ )
# if a single execute, check for outparams
if len(self.compiled_parameters) == 1:
for bindparam in self.compiled.binds.values():
if bindparam.isoutparam:
dbtype = bindparam.type.dialect_impl(self.dialect).\
- get_dbapi_type(self.dialect.dbapi)
+ get_dbapi_type(self.dialect.dbapi)
if not hasattr(self, 'out_parameters'):
self.out_parameters = {}
if dbtype is None:
raise exc.InvalidRequestError(
- "Cannot create out parameter for parameter "
- "%r - its type %r is not supported by"
- " cx_oracle" %
- (bindparam.key, bindparam.type)
- )
+ "Cannot create out parameter for parameter "
+ "%r - its 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)] = \
- self.out_parameters[name]
+ self.out_parameters[name]
def create_cursor(self):
c = self._dbapi_connection.cursor()
@@ -519,9 +533,9 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
def get_result_proxy(self):
if hasattr(self, 'out_parameters') and self.compiled.returning:
returning_params = dict(
- (k, v.getvalue())
- for k, v in self.out_parameters.items()
- )
+ (k, v.getvalue())
+ for k, v in self.out_parameters.items()
+ )
return ReturningResultProxy(self, returning_params)
result = None
@@ -543,25 +557,29 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
if name in self.out_parameters:
type = bind.type
impl_type = type.dialect_impl(self.dialect)
- dbapi_type = impl_type.get_dbapi_type(self.dialect.dbapi)
+ dbapi_type = impl_type.get_dbapi_type(
+ self.dialect.dbapi)
result_processor = impl_type.\
- result_processor(self.dialect,
- dbapi_type)
+ result_processor(self.dialect,
+ dbapi_type)
if result_processor is not None:
out_parameters[name] = \
- result_processor(self.out_parameters[name].getvalue())
+ result_processor(
+ self.out_parameters[name].getvalue())
else:
- out_parameters[name] = self.out_parameters[name].getvalue()
+ out_parameters[name] = self.out_parameters[
+ name].getvalue()
else:
result.out_parameters = dict(
(k, v.getvalue())
- for k, v in self.out_parameters.items()
- )
+ for k, v in self.out_parameters.items()
+ )
return result
-class OracleExecutionContext_cx_oracle_with_unicode(OracleExecutionContext_cx_oracle):
+class OracleExecutionContext_cx_oracle_with_unicode(
+ OracleExecutionContext_cx_oracle):
"""Support WITH_UNICODE in Python 2.xx.
WITH_UNICODE allows cx_Oracle's Python 3 unicode handling
@@ -574,17 +592,19 @@ class OracleExecutionContext_cx_oracle_with_unicode(OracleExecutionContext_cx_or
passed as Python unicode objects.
"""
+
def __init__(self, *arg, **kw):
OracleExecutionContext_cx_oracle.__init__(self, *arg, **kw)
self.statement = util.text_type(self.statement)
def _execute_scalar(self, stmt):
return super(OracleExecutionContext_cx_oracle_with_unicode, self).\
- _execute_scalar(util.text_type(stmt))
+ _execute_scalar(util.text_type(stmt))
class ReturningResultProxy(_result.FullyBufferedResultProxy):
- """Result proxy which stuffs the _returning clause + outparams into the fetch."""
+ """Result proxy which stuffs the _returning clause + outparams
+ into the fetch."""
def __init__(self, context, returning_params):
self._returning_params = returning_params
@@ -598,8 +618,10 @@ class ReturningResultProxy(_result.FullyBufferedResultProxy):
]
def _buffer_rows(self):
- return collections.deque([tuple(self._returning_params["ret_%d" % i]
- for i, c in enumerate(self._returning_params))])
+ return collections.deque(
+ [tuple(self._returning_params["ret_%d" % i]
+ for i, c in enumerate(self._returning_params))]
+ )
class OracleDialect_cx_oracle(OracleDialect):
@@ -610,7 +632,8 @@ class OracleDialect_cx_oracle(OracleDialect):
colspecs = colspecs = {
sqltypes.Numeric: _OracleNumeric,
- sqltypes.Date: _OracleDate, # generic type, assume datetime.date is desired
+ # generic type, assume datetime.date is desired
+ sqltypes.Date: _OracleDate,
sqltypes.LargeBinary: _OracleBinary,
sqltypes.Boolean: oracle._OracleBoolean,
sqltypes.Interval: _OracleInterval,
@@ -637,50 +660,50 @@ class OracleDialect_cx_oracle(OracleDialect):
execute_sequence_format = list
def __init__(self,
- auto_setinputsizes=True,
- exclude_setinputsizes=("STRING", "UNICODE"),
- auto_convert_lobs=True,
- threaded=True,
- allow_twophase=True,
- coerce_to_decimal=True,
- coerce_to_unicode=False,
- arraysize=50, **kwargs):
+ auto_setinputsizes=True,
+ exclude_setinputsizes=("STRING", "UNICODE"),
+ auto_convert_lobs=True,
+ threaded=True,
+ allow_twophase=True,
+ coerce_to_decimal=True,
+ coerce_to_unicode=False,
+ arraysize=50, **kwargs):
OracleDialect.__init__(self, **kwargs)
self.threaded = threaded
self.arraysize = arraysize
self.allow_twophase = allow_twophase
self.supports_timestamp = self.dbapi is None or \
- hasattr(self.dbapi, 'TIMESTAMP')
+ 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.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])
+ getattr(self.dbapi, name, None) for name in names
+ ).difference([None])
self.exclude_setinputsizes = types(*(exclude_setinputsizes or ()))
self._cx_oracle_string_types = types("STRING", "UNICODE",
- "NCLOB", "CLOB")
+ "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)
self.coerce_to_unicode = (
- self.cx_oracle_ver >= (5, 0) and
- coerce_to_unicode
- )
+ self.cx_oracle_ver >= (5, 0) and
+ coerce_to_unicode
+ )
self.supports_native_decimal = (
- self.cx_oracle_ver >= (5, 0) and
- coerce_to_decimal
- )
+ self.cx_oracle_ver >= (5, 0) and
+ coerce_to_decimal
+ )
self._cx_oracle_native_nvarchar = self.cx_oracle_ver >= (5, 0)
@@ -688,7 +711,8 @@ class OracleDialect_cx_oracle(OracleDialect):
# this occurs in tests with mock DBAPIs
self._cx_oracle_string_types = set()
self._cx_oracle_with_unicode = False
- elif self.cx_oracle_ver >= (5,) and not hasattr(self.dbapi, 'UNICODE'):
+ elif self.cx_oracle_ver >= (5,) and not \
+ hasattr(self.dbapi, 'UNICODE'):
# cx_Oracle WITH_UNICODE mode. *only* python
# unicode objects accepted for anything
self.supports_unicode_statements = True
@@ -696,32 +720,32 @@ class OracleDialect_cx_oracle(OracleDialect):
self._cx_oracle_with_unicode = True
if util.py2k:
- # There's really no reason to run with WITH_UNICODE under Python 2.x.
- # Give the user a hint.
+ # 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,"
+ "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
+ OracleExecutionContext_cx_oracle_with_unicode
else:
self._cx_oracle_with_unicode = False
if self.cx_oracle_ver is None or \
- not self.auto_convert_lobs or \
- not hasattr(self.dbapi, 'CLOB'):
+ not self.auto_convert_lobs or \
+ not hasattr(self.dbapi, 'CLOB'):
self.dbapi_type_map = {}
else:
# only use this for LOB objects. using it for strings, dates
- # etc. leads to a little too much magic, reflection doesn't know if it should
- # expect encoded strings or unicodes, etc.
+ # etc. leads to a little too much magic, reflection doesn't know
+ # if it should expect encoded strings or unicodes, etc.
self.dbapi_type_map = {
self.dbapi.CLOB: oracle.CLOB(),
self.dbapi.NCLOB: oracle.NCLOB(),
@@ -764,8 +788,8 @@ class OracleDialect_cx_oracle(OracleDialect):
def output_type_handler(cursor, name, defaultType,
size, precision, scale):
return cursor.var(
- cx_Oracle.STRING,
- 255, arraysize=cursor.arraysize)
+ cx_Oracle.STRING,
+ 255, arraysize=cursor.arraysize)
cursor = conn.cursor()
cursor.outputtypehandler = output_type_handler
@@ -796,17 +820,17 @@ class OracleDialect_cx_oracle(OracleDialect):
cx_Oracle = self.dbapi
def output_type_handler(cursor, name, defaultType,
- size, precision, scale):
+ size, precision, scale):
# convert all NUMBER with precision + positive scale to Decimal
# this almost allows "native decimal" mode.
if self.supports_native_decimal and \
defaultType == cx_Oracle.NUMBER and \
precision and scale > 0:
return cursor.var(
- cx_Oracle.STRING,
- 255,
- outconverter=self._to_decimal,
- arraysize=cursor.arraysize)
+ cx_Oracle.STRING,
+ 255,
+ outconverter=self._to_decimal,
+ arraysize=cursor.arraysize)
# if NUMBER with zero precision and 0 or neg scale, this appears
# to indicate "ambiguous". Use a slower converter that will
# make a decision based on each value received - the type
@@ -816,10 +840,10 @@ class OracleDialect_cx_oracle(OracleDialect):
defaultType == cx_Oracle.NUMBER \
and not precision and scale <= 0:
return cursor.var(
- cx_Oracle.STRING,
- 255,
- outconverter=self._detect_decimal,
- arraysize=cursor.arraysize)
+ cx_Oracle.STRING,
+ 255,
+ outconverter=self._detect_decimal,
+ arraysize=cursor.arraysize)
# allow all strings to come back natively as Unicode
elif self.coerce_to_unicode and \
defaultType in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
@@ -856,7 +880,7 @@ class OracleDialect_cx_oracle(OracleDialect):
dsn=dsn,
threaded=self.threaded,
twophase=self.allow_twophase,
- )
+ )
if util.py2k:
if self._cx_oracle_with_unicode:
@@ -882,9 +906,9 @@ class OracleDialect_cx_oracle(OracleDialect):
def _get_server_version_info(self, connection):
return tuple(
- int(x)
- for x in connection.connection.version.split('.')
- )
+ int(x)
+ for x in connection.connection.version.split('.')
+ )
def is_disconnect(self, e, connection, cursor):
error, = e.args
@@ -924,11 +948,11 @@ class OracleDialect_cx_oracle(OracleDialect):
connection.info['cx_oracle_prepared'] = result
def do_rollback_twophase(self, connection, xid, is_prepared=True,
- recover=False):
+ recover=False):
self.do_rollback(connection.connection)
def do_commit_twophase(self, connection, xid, is_prepared=True,
- recover=False):
+ recover=False):
if not is_prepared:
self.do_commit(connection.connection)
else:
diff --git a/lib/sqlalchemy/dialects/oracle/zxjdbc.py b/lib/sqlalchemy/dialects/oracle/zxjdbc.py
index 19a668a3e..82c8e2f0f 100644
--- a/lib/sqlalchemy/dialects/oracle/zxjdbc.py
+++ b/lib/sqlalchemy/dialects/oracle/zxjdbc.py
@@ -10,7 +10,8 @@
: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.
+ :driverurl: http://www.oracle.com/technology/software/tech/java/\
+sqlj_jdbc/index.html.
"""
import decimal
@@ -18,7 +19,9 @@ 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.dialects.oracle.base import (OracleCompiler,
+ OracleDialect,
+ OracleExecutionContext)
from sqlalchemy.engine import result as _result
from sqlalchemy.sql import expression
import collections
@@ -40,7 +43,7 @@ class _ZxJDBCDate(sqltypes.Date):
class _ZxJDBCNumeric(sqltypes.Numeric):
def result_processor(self, dialect, coltype):
- #XXX: does the dialect return Decimal or not???
+ # XXX: does the dialect return Decimal or not???
# if it does (in all cases), we could use a None processor as well as
# the to_float generic processor
if self.asdecimal:
@@ -61,10 +64,12 @@ class _ZxJDBCNumeric(sqltypes.Numeric):
class OracleCompiler_zxjdbc(OracleCompiler):
def returning_clause(self, stmt, returning_cols):
- self.returning_cols = list(expression._select_iterables(returning_cols))
+ self.returning_cols = 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)
+ columns = [self.process(c, within_columns_clause=False,
+ result_map=self.result_map)
for c in self.returning_cols]
if not hasattr(self, 'returning_parameters'):
@@ -72,12 +77,15 @@ class OracleCompiler_zxjdbc(OracleCompiler):
binds = []
for i, col in enumerate(self.returning_cols):
- dbtype = col.type.dialect_impl(self.dialect).get_dbapi_type(self.dialect.dbapi)
+ dbtype = col.type.dialect_impl(
+ self.dialect).get_dbapi_type(self.dialect.dbapi)
self.returning_parameters.append((i + 1, dbtype))
- bindparam = sql.bindparam("ret_%d" % i, value=ReturningParam(dbtype))
+ bindparam = sql.bindparam(
+ "ret_%d" % i, value=ReturningParam(dbtype))
self.binds[bindparam.key] = bindparam
- binds.append(self.bindparam_string(self._truncate_bindparam(bindparam)))
+ binds.append(
+ self.bindparam_string(self._truncate_bindparam(bindparam)))
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
@@ -98,13 +106,17 @@ class OracleExecutionContext_zxjdbc(OracleExecutionContext):
rrs = self.statement.__statement__.getReturnResultSet()
next(rrs)
except SQLException as sqle:
- msg = '%s [SQLCode: %d]' % (sqle.getMessage(), sqle.getErrorCode())
+ msg = '%s [SQLCode: %d]' % (
+ sqle.getMessage(), sqle.getErrorCode())
if sqle.getSQLState() is not None:
msg += ' [SQLState: %s]' % sqle.getSQLState()
raise zxJDBC.Error(msg)
else:
- row = tuple(self.cursor.datahandler.getPyObject(rrs, index, dbtype)
- for index, dbtype in self.compiled.returning_parameters)
+ row = tuple(
+ self.cursor.datahandler.getPyObject(
+ rrs, index, dbtype)
+ for index, dbtype in
+ self.compiled.returning_parameters)
return ReturningResultProxy(self, row)
finally:
if rrs is not None:
@@ -165,8 +177,8 @@ class ReturningParam(object):
def __repr__(self):
kls = self.__class__
- return '<%s.%s object at 0x%x type=%s>' % (kls.__module__, kls.__name__, id(self),
- self.type)
+ return '<%s.%s object at 0x%x type=%s>' % (
+ kls.__module__, kls.__name__, id(self), self.type)
class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect):
@@ -207,13 +219,16 @@ class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect):
def initialize(self, connection):
super(OracleDialect_zxjdbc, self).initialize(connection)
- self.implicit_returning = connection.connection.driverversion >= '10.2'
+ self.implicit_returning = \
+ connection.connection.driverversion >= '10.2'
def _create_jdbc_url(self, url):
- return 'jdbc:oracle:thin:@%s:%s:%s' % (url.host, url.port or 1521, url.database)
+ return 'jdbc:oracle:thin:@%s:%s:%s' % (
+ url.host, url.port or 1521, url.database)
def _get_server_version_info(self, connection):
- version = re.search(r'Release ([\d\.]+)', connection.connection.dbversion).group(1)
+ version = re.search(
+ r'Release ([\d\.]+)', connection.connection.dbversion).group(1)
return tuple(int(x) for x in version.split('.'))
dialect = OracleDialect_zxjdbc