]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Repair _execute_scalar for WITH_UNICODE mode
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 13 Mar 2017 16:27:51 +0000 (12:27 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 13 Mar 2017 18:16:42 +0000 (14:16 -0400)
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
doc/build/changelog/changelog_10.rst
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/testing/provision.py
test/dialect/test_oracle.py
tox.ini

index c33b43a0aaa43c8807eb03c2532d81d06ef0ad22..b0047dd8e79e04906cd7e8bf2f3c45dc679a55c7 100644 (file)
     .. 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
index 5d1167684a01e3c12db47fba188e62684b401700..9b3e3b8a12241185e441c1744d0db5e75943dab8 100644 (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:
index 23d504b848649ae09d681747423f82871275f97e..7e445446563643e2a786d2fd7d217c89864ea2a9 100644 (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:
index bb3f565687c0db42862fdb61fba267327a011eed..f6e1e14c78690e3c3a805bd78b3a829804ca0191 100644 (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'):
diff --git a/tox.ini b/tox.ini
index 56aedbb38408b6d2e9e5fb76693ef6cbfb98947a..2f743914fe3ccb08eb025614d70742516095f374 100644 (file)
--- 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