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
This commit is contained in:
Mike Bayer
2017-03-13 12:27:51 -04:00
parent 1da9d37521
commit fb33f9c54b
5 changed files with 48 additions and 18 deletions
+15
View File
@@ -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
+24 -15
View File
@@ -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:
+6
View File
@@ -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:
+2 -2
View File
@@ -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'):
+1 -1
View File
@@ -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