summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2017-03-13 12:27:51 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2017-03-13 14:34:43 -0400
commit957089f79e4b2369895ea989684f26b4f31cb07d (patch)
treeaa072c9175a9967c38af94794da9940024a7cd6e
parent7402f97d2b7a9e4a4fb3f8205480ba0b79c600f7 (diff)
downloadsqlalchemy-957089f79e4b2369895ea989684f26b4f31cb07d.tar.gz
Repair _execute_scalar for WITH_UNICODE mode
cx_Oracle 5.3 seems to code this flag ON now, so remove the warning and ensure WITH_UNICODE handling works. Additionally, the XE setup on jenkins is having more problems here, in particular low-connections mode is causing cx_Oracle to fail more frequently now. Turning off low-connections fixes those but then we get the TNS errors, so adding an emergency "retry" flag that is not yet a feature available to users. Real world applications are not dropping/creating thousands of tables the way our test suite is. Change-Id: Ie95b0e697276c404d3264c2e624e870463d966d6 Fixes: #3937
-rw-r--r--doc/build/changelog/changelog_10.rst15
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py39
-rw-r--r--lib/sqlalchemy/testing/provision.py6
-rw-r--r--test/dialect/test_oracle.py4
-rw-r--r--tox.ini2
5 files changed, 48 insertions, 18 deletions
diff --git a/doc/build/changelog/changelog_10.rst b/doc/build/changelog/changelog_10.rst
index c33b43a0a..b0047dd8e 100644
--- a/doc/build/changelog/changelog_10.rst
+++ b/doc/build/changelog/changelog_10.rst
@@ -15,6 +15,21 @@
.. include:: changelog_07.rst
:start-line: 5
+
+.. changelog::
+ :version: 1.0.18
+
+ .. change:: 3937
+ :tags: bug, oracle
+ :tickets: 3937
+ :versions: 1.1.7
+
+ A fix to cx_Oracle's WITH_UNICODE mode which was uncovered by the
+ fact that cx_Oracle 5.3 now seems to hardcode this flag on in
+ the build; an internal method that uses this mode wasn't using
+ the correct signature.
+
+
.. changelog::
:version: 1.0.17
:released: January 17, 2017
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index 5d1167684..9b3e3b8a1 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -298,6 +298,7 @@ import random
import collections
import decimal
import re
+import time
class _OracleNumeric(sqltypes.Numeric):
@@ -621,9 +622,9 @@ class OracleExecutionContext_cx_oracle_with_unicode(
OracleExecutionContext_cx_oracle.__init__(self, *arg, **kw)
self.statement = util.text_type(self.statement)
- def _execute_scalar(self, stmt):
+ def _execute_scalar(self, stmt, type_):
return super(OracleExecutionContext_cx_oracle_with_unicode, self).\
- _execute_scalar(util.text_type(stmt))
+ _execute_scalar(util.text_type(stmt), type_)
class ReturningResultProxy(_result.FullyBufferedResultProxy):
@@ -692,7 +693,8 @@ class OracleDialect_cx_oracle(OracleDialect):
allow_twophase=True,
coerce_to_decimal=True,
coerce_to_unicode=False,
- arraysize=50, **kwargs):
+ arraysize=50, _retry_on_12516=False,
+ **kwargs):
OracleDialect.__init__(self, **kwargs)
self.threaded = threaded
self.arraysize = arraysize
@@ -701,6 +703,7 @@ class OracleDialect_cx_oracle(OracleDialect):
hasattr(self.dbapi, 'TIMESTAMP')
self.auto_setinputsizes = auto_setinputsizes
self.auto_convert_lobs = auto_convert_lobs
+ self._retry_on_12516 = _retry_on_12516
if hasattr(self.dbapi, 'version'):
self.cx_oracle_ver = tuple([int(x) for x in
@@ -748,18 +751,8 @@ class OracleDialect_cx_oracle(OracleDialect):
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."
- )
+ # Python 2.x. However as of cx_oracle 5.3 it seems to be
+ # set to ON for default builds
self.execution_ctx_cls = \
OracleExecutionContext_cx_oracle_with_unicode
else:
@@ -785,6 +778,22 @@ class OracleDialect_cx_oracle(OracleDialect):
import cx_Oracle
return cx_Oracle
+ def connect(self, *cargs, **cparams):
+ if self._retry_on_12516:
+ # emergency flag for the SQLAlchemy test suite, which has
+ # decreased in stability since cx_oracle 5.3; generalized
+ # "retry on connect" functionality is part of an upcoming
+ # SQLAlchemy feature
+ try:
+ return self.dbapi.connect(*cargs, **cparams)
+ except self.dbapi.DatabaseError as err:
+ if "ORA-12516" in str(err):
+ time.sleep(2)
+ return self.dbapi.connect(*cargs, **cparams)
+ else:
+ return super(OracleDialect_cx_oracle, self).connect(
+ *cargs, **cparams)
+
def initialize(self, connection):
super(OracleDialect_cx_oracle, self).initialize(connection)
if self._is_oracle_8:
diff --git a/lib/sqlalchemy/testing/provision.py b/lib/sqlalchemy/testing/provision.py
index 23d504b84..7e4454465 100644
--- a/lib/sqlalchemy/testing/provision.py
+++ b/lib/sqlalchemy/testing/provision.py
@@ -126,6 +126,7 @@ def _mssql_update_db_opts(db_url, db_opts):
db_opts['legacy_schema_aliasing'] = False
+
@_follower_url_from_main.for_db("sqlite")
def _sqlite_follower_url_from_main(url, ident):
url = sa_url.make_url(url)
@@ -270,6 +271,11 @@ def _oracle_drop_db(cfg, eng, ident):
_ora_drop_ignore(conn, "%s_ts2" % ident)
+@_update_db_opts.for_db("oracle")
+def _oracle_update_db_opts(db_url, db_opts):
+ db_opts['_retry_on_12516'] = True
+
+
def reap_oracle_dbs(eng, idents_file):
log.info("Reaping Oracle dbs...")
with eng.connect() as conn:
diff --git a/test/dialect/test_oracle.py b/test/dialect/test_oracle.py
index bb3f56568..f6e1e14c7 100644
--- a/test/dialect/test_oracle.py
+++ b/test/dialect/test_oracle.py
@@ -1706,10 +1706,10 @@ class TypesTest(fixtures.TestBase):
@testing.provide_metadata
def test_reflect_nvarchar(self):
metadata = self.metadata
- Table('t', metadata, Column('data', sqltypes.NVARCHAR(255)))
+ Table('tnv', metadata, Column('data', sqltypes.NVARCHAR(255)))
metadata.create_all()
m2 = MetaData(testing.db)
- t2 = Table('t', m2, autoload=True)
+ t2 = Table('tnv', m2, autoload=True)
assert isinstance(t2.c.data.type, sqltypes.NVARCHAR)
if testing.against('oracle+cx_oracle'):
diff --git a/tox.ini b/tox.ini
index cdb454245..0ee4dbd9e 100644
--- a/tox.ini
+++ b/tox.ini
@@ -54,7 +54,7 @@ setenv=
sqlite: SQLITE=--db sqlite
postgresql: POSTGRESQL=--db postgresql
mysql: MYSQL=--db mysql --db pymysql
- oracle: ORACLE=--db oracle --low-connections --write-idents oracle_idents.txt
+ oracle: ORACLE=--db oracle --write-idents oracle_idents.txt
mssql: MSSQL=--db pyodbc --db pymssql
backendonly: BACKENDONLY=--backend-only