Packages update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# oracle/__init__.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# oracle/base.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 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
|
||||
@@ -22,6 +22,8 @@ affect the behavior of the dialect regardless of driver in use.
|
||||
|
||||
* *optimize_limits* - defaults to ``False``. see the section on LIMIT/OFFSET.
|
||||
|
||||
* *use_binds_for_limits* - defaults to ``True``. see the section on LIMIT/OFFSET.
|
||||
|
||||
Auto Increment Behavior
|
||||
-----------------------
|
||||
|
||||
@@ -74,13 +76,27 @@ requires NLS_LANG to be set.
|
||||
LIMIT/OFFSET Support
|
||||
--------------------
|
||||
|
||||
Oracle has no support for the LIMIT or OFFSET keywords. Whereas previous versions of SQLAlchemy
|
||||
used the "ROW NUMBER OVER..." construct to simulate LIMIT/OFFSET, SQLAlchemy 0.5 now uses
|
||||
a wrapped subquery approach in conjunction with ROWNUM. The exact methodology is taken from
|
||||
http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html . Note that the
|
||||
"FIRST ROWS()" optimization keyword mentioned is not used by default, as the user community felt
|
||||
this was stepping into the bounds of optimization that is better left on the DBA side, but this
|
||||
prefix can be added by enabling the optimize_limits=True flag on create_engine().
|
||||
Oracle has no support for the LIMIT or OFFSET keywords. SQLAlchemy uses
|
||||
a wrapped subquery approach in conjunction with ROWNUM. The exact methodology
|
||||
is taken from
|
||||
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
|
||||
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/WindowFunctionsByDefault
|
||||
which installs a select compiler that overrides the generation of limit/offset with
|
||||
a window function.
|
||||
|
||||
ON UPDATE CASCADE
|
||||
-----------------
|
||||
@@ -133,24 +149,30 @@ from sqlalchemy import types as sqltypes
|
||||
from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, \
|
||||
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 DECIMAL UNION PUBLIC '
|
||||
'AND START UID COMMENT'.split())
|
||||
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 '\
|
||||
'DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT'.split())
|
||||
|
||||
class RAW(sqltypes.LargeBinary):
|
||||
pass
|
||||
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'
|
||||
|
||||
VARCHAR2 = VARCHAR
|
||||
class VARCHAR2(VARCHAR):
|
||||
__visit_name__ = 'VARCHAR2'
|
||||
|
||||
NVARCHAR2 = NVARCHAR
|
||||
|
||||
class NUMBER(sqltypes.Numeric, sqltypes.Integer):
|
||||
@@ -216,10 +238,6 @@ class INTERVAL(sqltypes.TypeEngine):
|
||||
return INTERVAL(day_precision=interval.day_precision,
|
||||
second_precision=interval.second_precision)
|
||||
|
||||
def adapt(self, impltype):
|
||||
return impltype(day_precision=self.day_precision,
|
||||
second_precision=self.second_precision)
|
||||
|
||||
@property
|
||||
def _type_affinity(self):
|
||||
return sqltypes.Interval
|
||||
@@ -277,9 +295,9 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
|
||||
def visit_unicode(self, type_):
|
||||
if self.dialect._supports_nchar:
|
||||
return self.visit_NVARCHAR(type_)
|
||||
return self.visit_NVARCHAR2(type_)
|
||||
else:
|
||||
return self.visit_VARCHAR(type_)
|
||||
return self.visit_VARCHAR2(type_)
|
||||
|
||||
def visit_INTERVAL(self, type_):
|
||||
return "INTERVAL DAY%s TO SECOND%s" % (
|
||||
@@ -317,14 +335,27 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
else:
|
||||
return "%(name)s(%(precision)s, %(scale)s)" % {'name':name,'precision': precision, 'scale' : scale}
|
||||
|
||||
def visit_VARCHAR(self, type_):
|
||||
if self.dialect._supports_char_length:
|
||||
return "VARCHAR(%(length)s CHAR)" % {'length' : type_.length}
|
||||
else:
|
||||
return "VARCHAR(%(length)s)" % {'length' : type_.length}
|
||||
def visit_string(self, type_):
|
||||
return self.visit_VARCHAR2(type_)
|
||||
|
||||
def visit_NVARCHAR(self, type_):
|
||||
return "NVARCHAR2(%(length)s)" % {'length' : type_.length}
|
||||
def visit_VARCHAR2(self, type_):
|
||||
return self._visit_varchar(type_, '', '2')
|
||||
|
||||
def visit_NVARCHAR2(self, type_):
|
||||
return self._visit_varchar(type_, 'N', '2')
|
||||
visit_NVARCHAR = visit_NVARCHAR2
|
||||
|
||||
def visit_VARCHAR(self, type_):
|
||||
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}
|
||||
else:
|
||||
return "%(n)sVARCHAR%(two)s(%(length)s)" % {'length' : type_.length,
|
||||
'two':num, 'n':n}
|
||||
|
||||
def visit_text(self, type_):
|
||||
return self.visit_CLOB(type_)
|
||||
@@ -345,7 +376,10 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
return self.visit_SMALLINT(type_)
|
||||
|
||||
def visit_RAW(self, type_):
|
||||
return "RAW(%(length)s)" % {'length' : type_.length}
|
||||
if type_.length:
|
||||
return "RAW(%(length)s)" % {'length' : type_.length}
|
||||
else:
|
||||
return "RAW"
|
||||
|
||||
def visit_ROWID(self, type_):
|
||||
return "ROWID"
|
||||
@@ -364,9 +398,9 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OracleCompiler, self).__init__(*args, **kwargs)
|
||||
self.__wheres = {}
|
||||
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))
|
||||
@@ -386,13 +420,14 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
)
|
||||
|
||||
def function_argspec(self, fn, **kw):
|
||||
if len(fn.clauses) > 0:
|
||||
if len(fn.clauses) > 0 or fn.name.upper() not in NO_ARG_FNS:
|
||||
return compiler.SQLCompiler.function_argspec(self, fn, **kw)
|
||||
else:
|
||||
return ""
|
||||
|
||||
def default_from(self):
|
||||
"""Called when a ``SELECT`` statement has no froms, and no ``FROM`` clause is to be appended.
|
||||
"""Called when a ``SELECT`` statement has no froms,
|
||||
and no ``FROM`` clause is to be appended.
|
||||
|
||||
The Oracle compiler tacks a "FROM DUAL" to the statement.
|
||||
"""
|
||||
@@ -523,6 +558,8 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
max_row = select._limit
|
||||
if select._offset is not None:
|
||||
max_row += select._offset
|
||||
if not self.dialect.use_binds_for_limits:
|
||||
max_row = sql.literal_column("%d" % max_row)
|
||||
limitselect.append_whereclause(
|
||||
sql.literal_column("ROWNUM")<=max_row)
|
||||
|
||||
@@ -541,8 +578,11 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
offsetselect._oracle_visit = True
|
||||
offsetselect._is_wrapper = True
|
||||
|
||||
offset_value = select._offset
|
||||
if not self.dialect.use_binds_for_limits:
|
||||
offset_value = sql.literal_column("%d" % offset_value)
|
||||
offsetselect.append_whereclause(
|
||||
sql.literal_column("ora_rn")>select._offset)
|
||||
sql.literal_column("ora_rn")>offset_value)
|
||||
|
||||
offsetselect.for_update = select.for_update
|
||||
select = offsetselect
|
||||
@@ -597,10 +637,10 @@ class OracleIdentifierPreparer(compiler.IdentifierPreparer):
|
||||
|
||||
|
||||
class OracleExecutionContext(default.DefaultExecutionContext):
|
||||
def fire_sequence(self, seq):
|
||||
return int(self._execute_scalar("SELECT " +
|
||||
def fire_sequence(self, seq, type_):
|
||||
return self._execute_scalar("SELECT " +
|
||||
self.dialect.identifier_preparer.format_sequence(seq) +
|
||||
".nextval FROM DUAL"))
|
||||
".nextval FROM DUAL", type_)
|
||||
|
||||
class OracleDialect(default.DefaultDialect):
|
||||
name = 'oracle'
|
||||
@@ -634,10 +674,12 @@ class OracleDialect(default.DefaultDialect):
|
||||
def __init__(self,
|
||||
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
|
||||
self.use_binds_for_limits = use_binds_for_limits
|
||||
|
||||
def initialize(self, connection):
|
||||
super(OracleDialect, self).initialize(connection)
|
||||
@@ -861,6 +903,7 @@ class OracleDialect(default.DefaultDialect):
|
||||
'type': coltype,
|
||||
'nullable': nullable,
|
||||
'default': default,
|
||||
'autoincrement':default is None
|
||||
}
|
||||
if orig_colname.lower() == orig_colname:
|
||||
cdict['quote'] = True
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# oracle/cx_oracle.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 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
|
||||
@@ -50,6 +50,8 @@ Unicode
|
||||
cx_oracle 5 fully supports Python unicode objects. SQLAlchemy will pass
|
||||
all unicode strings directly to cx_oracle, and additionally uses an output
|
||||
handler so that all string based result values are returned as unicode as well.
|
||||
Generally, the ``NLS_LANG`` environment variable determines the nature
|
||||
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.
|
||||
@@ -127,7 +129,8 @@ from sqlalchemy.engine import base
|
||||
from sqlalchemy import types as sqltypes, util, exc, processors
|
||||
from datetime import datetime
|
||||
import random
|
||||
from decimal import Decimal
|
||||
import collections
|
||||
from sqlalchemy.util.compat import decimal
|
||||
import re
|
||||
|
||||
class _OracleNumeric(sqltypes.Numeric):
|
||||
@@ -154,10 +157,10 @@ class _OracleNumeric(sqltypes.Numeric):
|
||||
def to_decimal(value):
|
||||
if value is None:
|
||||
return None
|
||||
elif isinstance(value, Decimal):
|
||||
elif isinstance(value, decimal.Decimal):
|
||||
return value
|
||||
else:
|
||||
return Decimal(fstring % value)
|
||||
return decimal.Decimal(fstring % value)
|
||||
return to_decimal
|
||||
else:
|
||||
if self.precision is None and self.scale is None:
|
||||
@@ -293,10 +296,15 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
quoted_bind_names = \
|
||||
getattr(self.compiled, '_quoted_bind_names', None)
|
||||
if quoted_bind_names:
|
||||
if not self.dialect.supports_unicode_binds:
|
||||
if not self.dialect.supports_unicode_statements:
|
||||
# if DBAPI doesn't accept unicode statements,
|
||||
# keys in self.parameters would have been encoded
|
||||
# here. so convert names in quoted_bind_names
|
||||
# to encoded as well.
|
||||
quoted_bind_names = \
|
||||
dict(
|
||||
(fromname, toname.encode(self.dialect.encoding))
|
||||
(fromname.encode(self.dialect.encoding),
|
||||
toname.encode(self.dialect.encoding))
|
||||
for fromname, toname in
|
||||
quoted_bind_names.items()
|
||||
)
|
||||
@@ -333,7 +341,7 @@ class OracleExecutionContext_cx_oracle(OracleExecutionContext):
|
||||
self.out_parameters[name]
|
||||
|
||||
def create_cursor(self):
|
||||
c = self._connection.connection.cursor()
|
||||
c = self._dbapi_connection.cursor()
|
||||
if self.dialect.arraysize:
|
||||
c.arraysize = self.dialect.arraysize
|
||||
|
||||
@@ -423,8 +431,8 @@ class ReturningResultProxy(base.FullyBufferedResultProxy):
|
||||
return ret
|
||||
|
||||
def _buffer_rows(self):
|
||||
return [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):
|
||||
execution_ctx_cls = OracleExecutionContext_cx_oracle
|
||||
@@ -575,15 +583,15 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
self._detect_decimal = \
|
||||
lambda value: _detect_decimal(value.replace(char, '.'))
|
||||
self._to_decimal = \
|
||||
lambda value: Decimal(value.replace(char, '.'))
|
||||
lambda value: decimal.Decimal(value.replace(char, '.'))
|
||||
|
||||
def _detect_decimal(self, value):
|
||||
if "." in value:
|
||||
return Decimal(value)
|
||||
return decimal.Decimal(value)
|
||||
else:
|
||||
return int(value)
|
||||
|
||||
_to_decimal = Decimal
|
||||
_to_decimal = decimal.Decimal
|
||||
|
||||
def on_connect(self):
|
||||
if self.cx_oracle_ver < (5,):
|
||||
@@ -679,11 +687,20 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
for x in connection.connection.version.split('.')
|
||||
)
|
||||
|
||||
def is_disconnect(self, e):
|
||||
def is_disconnect(self, e, connection, cursor):
|
||||
error, = e.args
|
||||
if isinstance(e, self.dbapi.InterfaceError):
|
||||
return "not connected" in str(e)
|
||||
elif hasattr(error, 'code'):
|
||||
# ORA-00028: your session has been killed
|
||||
# ORA-03114: not connected to ORACLE
|
||||
# ORA-03113: end-of-file on communication channel
|
||||
# ORA-03135: connection lost contact
|
||||
# ORA-01033: ORACLE initialization or shutdown in progress
|
||||
# TODO: Others ?
|
||||
return error.code in (28, 3114, 3113, 3135, 1033)
|
||||
else:
|
||||
return "ORA-03114" in str(e) or "ORA-03113" in str(e)
|
||||
return False
|
||||
|
||||
def create_xid(self):
|
||||
"""create a two-phase transaction ID.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# oracle/zxjdbc.py
|
||||
# Copyright (C) 2005-2011 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2012 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
|
||||
@@ -21,6 +21,7 @@ from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
|
||||
from sqlalchemy.dialects.oracle.base import OracleCompiler, OracleDialect, OracleExecutionContext
|
||||
from sqlalchemy.engine import base, default
|
||||
from sqlalchemy.sql import expression
|
||||
import collections
|
||||
|
||||
SQLException = zxJDBC = None
|
||||
|
||||
@@ -115,7 +116,7 @@ class OracleExecutionContext_zxjdbc(OracleExecutionContext):
|
||||
return base.ResultProxy(self)
|
||||
|
||||
def create_cursor(self):
|
||||
cursor = self._connection.connection.cursor()
|
||||
cursor = self._dbapi_connection.cursor()
|
||||
cursor.datahandler = self.dialect.DataHandler(cursor.datahandler)
|
||||
return cursor
|
||||
|
||||
@@ -138,7 +139,7 @@ class ReturningResultProxy(base.FullyBufferedResultProxy):
|
||||
return ret
|
||||
|
||||
def _buffer_rows(self):
|
||||
return [self._returning_row]
|
||||
return collections.deque([self._returning_row])
|
||||
|
||||
|
||||
class ReturningParam(object):
|
||||
|
||||
Reference in New Issue
Block a user