]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add use_nchar_for_unicode flag; don't use nchar types for generic unicode
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 13 Jun 2018 21:48:51 +0000 (17:48 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Sep 2018 01:34:32 +0000 (21:34 -0400)
The Oracle dialect will no longer use the NCHAR/NCLOB datatypes to
represent generic unicode strings or clob fields in conjunction with
:class:`.Unicode` and :class:`.UnicodeText` unless the flag
``use_nchar_for_unicode=True`` is passed to :func:`.create_engine`.
Additionally, string types under Oracle now coerce to unicode under Python
2 in all cases, however unlike in previous iterations, we use SQLAlchemy's
native unicode handlers which are very performant (when C extensions are
enabled; when they are not, we use cx_Oracle's handlers).

Change-Id: I3939012e9396520875bc52e69bf81f27393836ee
Fixes: #4242
doc/build/changelog/migration_13.rst
doc/build/changelog/unreleased_13/4242.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/engine/default.py
test/dialect/oracle/test_dialect.py
test/dialect/oracle/test_types.py
test/profiles.txt

index 423b69579efe8ae0125afc7f3e7604a114d0788d..f742eb6672d9e33ca6c7fff60b5303eba064863b 100644 (file)
@@ -413,6 +413,45 @@ JSON is being used for its familiarity.
 Dialect Improvements and Changes - Oracle
 =============================================
 
+.. _change_4242:
+
+National char datatypes de-emphasized for generic unicode, re-enabled with option
+---------------------------------------------------------------------------------
+
+The :class:`.Unicode` and :class:`.UnicodeText` datatypes by default now
+correspond to the ``VARCHAR2`` and ``CLOB`` datatypes on Oracle, rather than
+``NVARCHAR2`` and ``NCLOB`` (otherwise known as "national" character set
+types).  This will be seen in behaviors such  as that of how they render in
+``CREATE TABLE`` statements, as well as that no type object will be passed to
+``setinputsizes()`` when bound parameters using :class:`.Unicode` or
+:class:`.UnicodeText` are used; cx_Oracle handles the string value natively.
+This change is based on advice from cx_Oracle's maintainer that the "national"
+datatypes in Oracle are largely obsolete and are not performant.   They also
+interfere in some situations such as when applied to the format specifier for
+functions like ``trunc()``.
+
+The one case where ``NVARCHAR2`` and related types may be needed is for a
+database that is not using a Unicode-compliant character set.  In this case,
+the flag ``use_nchar_for_unicode`` can be passed to :func:`.create_engine` to
+re-enable the old behavior.
+
+As always, using the :class:`.oracle.NVARCHAR2` and :class:`.oracle.NCLOB`
+datatypes explicitly will continue to make use of ``NVARCHAR2`` and ``NCLOB``,
+including within DDL as well as when handling bound parameters with cx_Oracle's
+``setinputsizes()``.
+
+On the read side, automatic Unicode conversion under Python 2 has been added to
+CHAR/VARCHAR/CLOB result rows, to match the behavior of cx_Oracle under Python
+3.  In order to mitigate the performance hit that the cx_Oracle dialect  had
+previously with this behavior under Python 2, SQLAlchemy's very performant
+(when C extensions are built) native Unicode handlers are used under Python 2.
+The automatic unicode coercion can be disabled by setting the
+``coerce_to_unicode`` flag to False. This flag now defaults to True and applies
+to all string data returned in a result set that isn't explicitly under
+:class:`.Unicode` or Oracle's NVARCHAR2/NCHAR/NCLOB datatypes.
+
+:ticket:`4242`
+
 Dialect Improvements and Changes - SQL Server
 =============================================
 
diff --git a/doc/build/changelog/unreleased_13/4242.rst b/doc/build/changelog/unreleased_13/4242.rst
new file mode 100644 (file)
index 0000000..b138513
--- /dev/null
@@ -0,0 +1,18 @@
+.. change::
+    :tags: bug, oracle
+    :tickets: 4242
+
+    The Oracle dialect will no longer use the NCHAR/NCLOB datatypes
+    represent generic unicode strings or clob fields in conjunction with
+    :class:`.Unicode` and :class:`.UnicodeText` unless the flag
+    ``use_nchar_for_unicode=True`` is passed to :func:`.create_engine` -
+    this includes CREATE TABLE behavior as well as ``setinputsizes()`` for
+    bound parameters.   On the read side, automatic Unicode conversion under
+    Python 2 has been added to CHAR/VARCHAR/CLOB result rows, to match the
+    behavior of cx_Oracle under Python 3.  In order to mitigate the performance
+    hit under Python 2, SQLAlchemy's very performant (when C extensions
+    are built) native Unicode handlers are used under Python 2.
+
+    .. seealso::
+
+        :ref:`change_4242`
index 76ae1ced6c065afc36cd1935abddd24fbbd6dacf..b5aea4386b0845a346b8c3f6397a6dfb6b79bc96 100644 (file)
@@ -533,7 +533,7 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
         return self.visit_FLOAT(type_, **kw)
 
     def visit_unicode(self, type_, **kw):
-        if self.dialect._supports_nchar:
+        if self.dialect._use_nchar_for_unicode:
             return self.visit_NVARCHAR2(type_, **kw)
         else:
             return self.visit_VARCHAR2(type_, **kw)
@@ -620,7 +620,7 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
         return self.visit_CLOB(type_, **kw)
 
     def visit_unicode_text(self, type_, **kw):
-        if self.dialect._supports_nchar:
+        if self.dialect._use_nchar_for_unicode:
             return self.visit_NCLOB(type_, **kw)
         else:
             return self.visit_CLOB(type_, **kw)
@@ -1056,6 +1056,8 @@ class OracleDialect(default.DefaultDialect):
 
     reflection_options = ('oracle_resolve_synonyms', )
 
+    _use_nchar_for_unicode = False
+
     construct_arguments = [
         (sa_schema.Table, {
             "resolve_synonyms": False,
@@ -1072,9 +1074,11 @@ class OracleDialect(default.DefaultDialect):
                  use_ansi=True,
                  optimize_limits=False,
                  use_binds_for_limits=True,
+                 use_nchar_for_unicode=False,
                  exclude_tablespaces=('SYSTEM', 'SYSAUX', ),
                  **kwargs):
         default.DefaultDialect.__init__(self, **kwargs)
+        self._use_nchar_for_unicode = use_nchar_for_unicode
         self.use_ansi = use_ansi
         self.optimize_limits = optimize_limits
         self.use_binds_for_limits = use_binds_for_limits
@@ -1111,14 +1115,20 @@ class OracleDialect(default.DefaultDialect):
     def _supports_char_length(self):
         return not self._is_oracle_8
 
-    @property
-    def _supports_nchar(self):
-        return not self._is_oracle_8
-
     def do_release_savepoint(self, connection, name):
         # Oracle does not support RELEASE SAVEPOINT
         pass
 
+    def _check_unicode_returns(self, connection):
+        additional_tests = [
+            expression.cast(
+                expression.literal_column("'test nvarchar2 returns'"),
+                sqltypes.NVARCHAR(60)
+            ),
+        ]
+        return super(OracleDialect, self)._check_unicode_returns(
+            connection, additional_tests)
+
     def has_table(self, connection, table_name, schema=None):
         if not schema:
             schema = self.default_schema_name
index eb75f750acd2903cfefaa047937f1179e78a07da..620b5384baf7038bf39c5f68302d1bbb27ec03a7 100644 (file)
@@ -56,76 +56,46 @@ on the URL, or as keyword arguments to :func:`.create_engine()` are:
 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.
+
+Explicit Unicode support is available by using the :class:`.Unicode` datatype
+with SQLAlchemy Core expression language, as well as the :class:`.UnicodeText`
+datatype.  These types correspond to the  VARCHAR2 and CLOB Oracle datatypes by
+default.   When using these datatypes with Unicode data, it is expected that
+the Oracle database is configured with a Unicode-aware character set, as well
+as that the ``NLS_LANG`` environment variable is set appropriately, so that
+the VARCHAR2 and CLOB datatypes can accommodate the data.
+
+In the case that the Oracle database is not configured with a Unicode character
+set, the two options are to use the :class:`.oracle.NCHAR` and
+:class:`.oracle.NCLOB` datatypes explicitly, or to pass the flag
+``use_nchar_for_unicode=True`` to :func:`.create_engine`, which will cause the
+SQLAlchemy dialect to use NCHAR/NCLOB for the :class:`.Unicode` /
+:class:`.UnicodeText` datatypes instead of VARCHAR/CLOB.
+
+.. versionchanged:: 1.3  The :class:`.Unicode` and :class:`.UnicodeText`
+   datatypes now correspond to the ``VARCHAR2`` and ``CLOB`` Oracle datatypes
+   unless the ``use_nchar_for_unicode=True`` is passed to the dialect
+   when :func:`.create_engine` is called.
+
+When result sets are fetched that include strings, under Python 3 the cx_Oracle
+DBAPI returns all strings as Python Unicode objects, since Python 3 only has a
+Unicode string type.  This occurs for data fetched from datatypes such as
+VARCHAR2, CHAR, CLOB, NCHAR, NCLOB, etc.  In order to provide cross-
+compatibility under Python 2, the SQLAlchemy cx_Oracle dialect will add
+Unicode-conversion to string data under Python 2 as well.  Historically, this
+made use of converters that were supplied by cx_Oracle but were found to be
+non-performant; SQLAlchemy's own converters are used for the string to Unicode
+conversion under Python 2.  To disable the Python 2 Unicode conversion for
+VARCHAR2, CHAR, and CLOB, the flag ``coerce_to_unicode=False`` can be passed to
+:func:`.create_engine`.
+
+.. versionchanged:: 1.3 Unicode conversion is applied to all string values
+   by default under python 2.  The ``coerce_to_unicode`` now defaults to True
+   and can be set to False to disable the Unicode coersion of strings that are
+   delivered as VARCHAR2/CHAR/CLOB data.
 
-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
-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 several different options for the use
-case of receiving ``VARCHAR`` column values as Python unicode objects under
-Python 2:
-
-* When using Core expression objects as well as the ORM, SQLAlchemy's
-  unicode-decoding services are available, which are established by
-  using either the :class:`.Unicode` datatype or by using the
-  :class:`.String` datatype with :paramref:`.String.convert_unicode` set
-  to True.
-
-* When using raw SQL strings, typing behavior can be added for unicode
-  conversion using the :func:`.text` construct::
-
-    from sqlalchemy import text, Unicode
-    result = conn.execute(
-        text("select username from user").columns(username=Unicode))
-
-* Otherwise, when using raw SQL strings sent directly to an ``.execute()``
-  method without any Core typing behavior added, the flag
-  ``coerce_to_unicode=True`` flag can be passed to :func:`.create_engine`
-  which will add an unconditional unicode processor to cx_Oracle for all
-  string values::
-
-    engine = create_engine("oracle+cx_oracle://dsn", coerce_to_unicode=True)
-
-  The above approach will add significant latency to result-set fetches
-  of plain string values.
-
-Sending String Values as Unicode or Non-Unicode
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-As of SQLAlchemy 1.2.2, the cx_Oracle dialect unconditionally calls
-``setinputsizes()`` for bound values that are passed as Python unicode objects.
-In Python 3, all string values are Unicode; for cx_Oracle, this corresponds to
-``cx_Oracle.NCHAR`` being passed to ``setinputsizes()`` for that parameter.
-In some edge cases, such as passing format specifiers for
-the ``trunc()`` function, Oracle does not accept these as NCHAR::
-
-    from sqlalchemy import func
-
-    conn.execute(
-        func.trunc(func.sysdate(), 'dd')
-    )
-
-In these cases, an error as follows may be raised::
-
-    ORA-01899: bad precision specifier
-
-When this error is encountered, it may be necessary to pass the string value
-with an explicit non-unicode type::
-
-    from sqlalchemy import func
-    from sqlalchemy import literal
-    from sqlalchemy import String
-
-    conn.execute(
-        func.trunc(func.sysdate(), literal('dd', String))
-    )
-
-For full control over this ``setinputsizes()`` behavior, see the section
-:ref:`cx_oracle_setinputsizes`
 
 .. _cx_oracle_setinputsizes:
 
@@ -193,7 +163,6 @@ series.   This setting can be modified as follows::
             if dbapitype is CLOB:
                 del inputsizes[bindparam]
 
-
 .. _cx_oracle_returning:
 
 RETURNING Support
@@ -285,11 +254,11 @@ from .base import OracleCompiler, OracleDialect, OracleExecutionContext
 from . import base as oracle
 from ...engine import result as _result
 from sqlalchemy import types as sqltypes, util, exc, processors
+from ...util import compat
 import random
 import collections
 import decimal
 import re
-import time
 
 
 class _OracleInteger(sqltypes.Integer):
@@ -430,11 +399,26 @@ class _OracleChar(sqltypes.CHAR):
         return dbapi.FIXED_CHAR
 
 
-class _OracleNVarChar(sqltypes.NVARCHAR):
+class _OracleUnicodeStringNCHAR(oracle.NVARCHAR2):
     def get_dbapi_type(self, dbapi):
         return dbapi.NCHAR
 
 
+class _OracleUnicodeStringCHAR(sqltypes.Unicode):
+    def get_dbapi_type(self, dbapi):
+        return None
+
+
+class _OracleUnicodeTextNCLOB(oracle.NCLOB):
+    def get_dbapi_type(self, dbapi):
+        return dbapi.NCLOB
+
+
+class _OracleUnicodeTextCLOB(sqltypes.UnicodeText):
+    def get_dbapi_type(self, dbapi):
+        return dbapi.CLOB
+
+
 class _OracleText(sqltypes.Text):
     def get_dbapi_type(self, dbapi):
         return dbapi.CLOB
@@ -459,11 +443,6 @@ class _OracleEnum(sqltypes.Enum):
         return process
 
 
-class _OracleUnicodeText(sqltypes.UnicodeText):
-    def get_dbapi_type(self, dbapi):
-        return dbapi.NCLOB
-
-
 class _OracleBinary(sqltypes.LargeBinary):
     def get_dbapi_type(self, dbapi):
         return dbapi.BLOB
@@ -698,14 +677,16 @@ class OracleDialect_cx_oracle(OracleDialect):
         oracle.INTERVAL: _OracleInterval,
         sqltypes.Text: _OracleText,
         sqltypes.String: _OracleString,
-        sqltypes.UnicodeText: _OracleUnicodeText,
+        sqltypes.UnicodeText: _OracleUnicodeTextCLOB,
         sqltypes.CHAR: _OracleChar,
         sqltypes.Enum: _OracleEnum,
 
         oracle.LONG: _OracleLong,
         oracle.RAW: _OracleRaw,
-        sqltypes.Unicode: _OracleNVarChar,
-        sqltypes.NVARCHAR: _OracleNVarChar,
+        sqltypes.Unicode: _OracleUnicodeStringCHAR,
+        sqltypes.NVARCHAR: _OracleUnicodeStringNCHAR,
+        sqltypes.NCHAR: _OracleUnicodeStringNCHAR,
+        oracle.NCLOB: _OracleUnicodeTextNCLOB,
         oracle.ROWID: _OracleRowid,
     }
 
@@ -714,7 +695,7 @@ class OracleDialect_cx_oracle(OracleDialect):
     def __init__(self,
                  auto_convert_lobs=True,
                  threaded=True,
-                 coerce_to_unicode=False,
+                 coerce_to_unicode=True,
                  coerce_to_decimal=True,
                  arraysize=50,
                  **kwargs):
@@ -727,6 +708,10 @@ class OracleDialect_cx_oracle(OracleDialect):
         self.auto_convert_lobs = auto_convert_lobs
         self.coerce_to_unicode = coerce_to_unicode
         self.coerce_to_decimal = coerce_to_decimal
+        if self._use_nchar_for_unicode:
+            self.colspecs = self.colspecs.copy()
+            self.colspecs[sqltypes.Unicode] = _OracleUnicodeStringNCHAR
+            self.colspecs[sqltypes.UnicodeText] = _OracleUnicodeTextNCLOB
 
         cx_Oracle = self.dbapi
 
@@ -872,11 +857,36 @@ class OracleDialect_cx_oracle(OracleDialect):
             # allow all strings to come back natively as Unicode
             elif dialect.coerce_to_unicode and \
                     default_type in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
-                return cursor.var(
-                    util.text_type, size, cursor.arraysize
-                )
+                if compat.py2k:
+                    outconverter = processors.to_unicode_processor_factory(
+                        dialect.encoding, None)
+                    return cursor.var(
+                        cx_Oracle.STRING, size, cursor.arraysize,
+                        outconverter=outconverter
+                    )
+                else:
+                    return cursor.var(
+                        util.text_type, size, cursor.arraysize
+                    )
+
+            elif dialect.auto_convert_lobs and default_type in (
+                    cx_Oracle.CLOB, cx_Oracle.NCLOB
+            ):
+                if compat.py2k:
+                    outconverter = processors.to_unicode_processor_factory(
+                        dialect.encoding, None)
+                    return cursor.var(
+                        default_type, size, cursor.arraysize,
+                        outconverter=lambda value: outconverter(value.read())
+                    )
+                else:
+                    return cursor.var(
+                        default_type, size, cursor.arraysize,
+                        outconverter=lambda value: value.read()
+                    )
+
             elif dialect.auto_convert_lobs and default_type in (
-                    cx_Oracle.CLOB, cx_Oracle.NCLOB, cx_Oracle.BLOB
+                    cx_Oracle.BLOB,
             ):
                 return cursor.var(
                     default_type, size, cursor.arraysize,
index aa524cd511e97bb6b4e6bea42a7d84eda8b1e2ac..f48217a4e9cbbda27b4e4a894f4d920a58b5517c 100644 (file)
@@ -1135,6 +1135,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
             dialect_impl = bindparam.type._unwrapped_dialect_impl(self.dialect)
             dialect_impl_cls = type(dialect_impl)
             dbtype = dialect_impl.get_dbapi_type(self.dialect.dbapi)
+
             if dbtype is not None and (
                 not exclude_types or dbtype not in exclude_types and
                 dialect_impl_cls not in exclude_types
index fc6eba89a717654c089e9a4bea4c79b809ac3026..23725e4836a3ff1e6a1d57f2b468f12b5c8bb7d5 100644 (file)
@@ -179,13 +179,12 @@ class CompatFlagsTest(fixtures.TestBase, AssertsCompiledSQL):
 
         # before connect, assume modern DB
         assert dialect._supports_char_length
-        assert dialect._supports_nchar
         assert dialect.use_ansi
+        assert not dialect._use_nchar_for_unicode
 
         dialect.initialize(Mock())
         assert not dialect.implicit_returning
         assert not dialect._supports_char_length
-        assert not dialect._supports_nchar
         assert not dialect.use_ansi
         self.assert_compile(String(50), "VARCHAR2(50)", dialect=dialect)
         self.assert_compile(Unicode(50), "VARCHAR2(50)", dialect=dialect)
@@ -201,19 +200,29 @@ class CompatFlagsTest(fixtures.TestBase, AssertsCompiledSQL):
         dialect = self._dialect(None)
 
         assert dialect._supports_char_length
-        assert dialect._supports_nchar
+        assert not dialect._use_nchar_for_unicode
         assert dialect.use_ansi
         self.assert_compile(String(50), "VARCHAR2(50 CHAR)", dialect=dialect)
-        self.assert_compile(Unicode(50), "NVARCHAR2(50)", dialect=dialect)
-        self.assert_compile(UnicodeText(), "NCLOB", dialect=dialect)
+        self.assert_compile(Unicode(50), "VARCHAR2(50 CHAR)", dialect=dialect)
+        self.assert_compile(UnicodeText(), "CLOB", dialect=dialect)
 
     def test_ora10_flags(self):
         dialect = self._dialect((10, 2, 5))
 
         dialect.initialize(Mock())
         assert dialect._supports_char_length
-        assert dialect._supports_nchar
+        assert not dialect._use_nchar_for_unicode
         assert dialect.use_ansi
+        self.assert_compile(String(50), "VARCHAR2(50 CHAR)", dialect=dialect)
+        self.assert_compile(Unicode(50), "VARCHAR2(50 CHAR)", dialect=dialect)
+        self.assert_compile(UnicodeText(), "CLOB", dialect=dialect)
+
+    def test_use_nchar(self):
+        dialect = self._dialect((10, 2, 5), use_nchar_for_unicode=True)
+
+        dialect.initialize(Mock())
+        assert dialect._use_nchar_for_unicode
+
         self.assert_compile(String(50), "VARCHAR2(50 CHAR)", dialect=dialect)
         self.assert_compile(Unicode(50), "NVARCHAR2(50)", dialect=dialect)
         self.assert_compile(UnicodeText(), "NCLOB", dialect=dialect)
index f8e8d41f881bbb047722500d54877e22e7997896..9fbea61303a8cf58d58bf06448cb73bb6f2bb712 100644 (file)
@@ -75,14 +75,33 @@ class DialectTypesTest(fixtures.TestBase, AssertsCompiledSQL):
             (DATE(), cx_oracle._OracleDate),
             (oracle.DATE(), oracle.DATE),
             (String(50), cx_oracle._OracleString),
-            (Unicode(), cx_oracle._OracleNVarChar),
+            (Unicode(), cx_oracle._OracleUnicodeStringCHAR),
             (Text(), cx_oracle._OracleText),
-            (UnicodeText(), cx_oracle._OracleUnicodeText),
-            (NCHAR(), cx_oracle._OracleNVarChar),
+            (UnicodeText(), cx_oracle._OracleUnicodeTextCLOB),
+            (NCHAR(), cx_oracle._OracleUnicodeStringNCHAR),
+            (NVARCHAR(), cx_oracle._OracleUnicodeStringNCHAR),
             (oracle.RAW(50), cx_oracle._OracleRaw),
         ]:
-            assert isinstance(start.dialect_impl(dialect), test), \
-                    "wanted %r got %r" % (test, start.dialect_impl(dialect))
+            assert isinstance(
+                start.dialect_impl(dialect), test), \
+                "wanted %r got %r" % (test, start.dialect_impl(dialect))
+
+    def test_type_adapt_nchar(self):
+        dialect = cx_oracle.dialect(use_nchar_for_unicode=True)
+
+        for start, test in [
+            (String(), String),
+            (VARCHAR(), cx_oracle._OracleString),
+            (String(50), cx_oracle._OracleString),
+            (Unicode(), cx_oracle._OracleUnicodeStringNCHAR),
+            (Text(), cx_oracle._OracleText),
+            (UnicodeText(), cx_oracle._OracleUnicodeTextNCLOB),
+            (NCHAR(), cx_oracle._OracleUnicodeStringNCHAR),
+            (NVARCHAR(), cx_oracle._OracleUnicodeStringNCHAR),
+        ]:
+            assert isinstance(
+                start.dialect_impl(dialect), test), \
+                "wanted %r got %r" % (test, start.dialect_impl(dialect))
 
     def test_raw_compile(self):
         self.assert_compile(oracle.RAW(), "RAW")
@@ -100,6 +119,24 @@ class DialectTypesTest(fixtures.TestBase, AssertsCompiledSQL):
 
     def test_varchar_types(self):
         dialect = oracle.dialect()
+        for typ, exp in [
+            (String(50), "VARCHAR2(50 CHAR)"),
+            (Unicode(50), "VARCHAR2(50 CHAR)"),
+            (NVARCHAR(50), "NVARCHAR2(50)"),
+            (VARCHAR(50), "VARCHAR(50 CHAR)"),
+            (oracle.NVARCHAR2(50), "NVARCHAR2(50)"),
+            (oracle.VARCHAR2(50), "VARCHAR2(50 CHAR)"),
+            (String(), "VARCHAR2"),
+            (Unicode(), "VARCHAR2"),
+            (NVARCHAR(), "NVARCHAR2"),
+            (VARCHAR(), "VARCHAR"),
+            (oracle.NVARCHAR2(), "NVARCHAR2"),
+            (oracle.VARCHAR2(), "VARCHAR2"),
+        ]:
+            self.assert_compile(typ, exp, dialect=dialect)
+
+    def test_varchar_use_nchar_types(self):
+        dialect = oracle.dialect(use_nchar_for_unicode=True)
         for typ, exp in [
             (String(50), "VARCHAR2(50 CHAR)"),
             (Unicode(50), "NVARCHAR2(50)"),
@@ -587,12 +624,12 @@ class TypesTest(fixtures.TestBase):
         testing.requires.python3,
         "cx_oracle always returns unicode on py3k")
     def test_coerce_to_unicode(self):
-        engine = testing_engine(options=dict(coerce_to_unicode=True))
+        engine = testing_engine(options=dict(coerce_to_unicode=False))
         value = engine.scalar("SELECT 'hello' FROM DUAL")
-        assert isinstance(value, util.text_type)
+        assert isinstance(value, util.binary_type)
 
         value = testing.db.scalar("SELECT 'hello' FROM DUAL")
-        assert isinstance(value, util.binary_type)
+        assert isinstance(value, util.text_type)
 
     @testing.provide_metadata
     def test_reflect_dates(self):
@@ -651,11 +688,29 @@ class TypesTest(fixtures.TestBase):
         assert isinstance(t2.c.data.type, sqltypes.NVARCHAR)
 
         if testing.against('oracle+cx_oracle'):
-            # nvarchar returns unicode natively.  cx_oracle
-            # _OracleNVarChar type should be at play here.
             assert isinstance(
                 t2.c.data.type.dialect_impl(testing.db.dialect),
-                cx_oracle._OracleNVarChar)
+                cx_oracle._OracleUnicodeStringNCHAR)
+
+        data = u('m’a réveillé.')
+        t2.insert().execute(data=data)
+        res = t2.select().execute().first()['data']
+        eq_(res, data)
+        assert isinstance(res, util.text_type)
+
+    @testing.provide_metadata
+    def test_reflect_unicode_no_nvarchar(self):
+        metadata = self.metadata
+        Table('tnv', metadata, Column('data', sqltypes.Unicode(255)))
+        metadata.create_all()
+        m2 = MetaData(testing.db)
+        t2 = Table('tnv', m2, autoload=True)
+        assert isinstance(t2.c.data.type, sqltypes.VARCHAR)
+
+        if testing.against('oracle+cx_oracle'):
+            assert isinstance(
+                t2.c.data.type.dialect_impl(testing.db.dialect),
+                cx_oracle._OracleString)
 
         data = u('m’a réveillé.')
         t2.insert().execute(data=data)
@@ -901,7 +956,8 @@ class SetInputSizesTest(fixtures.TestBase):
     __backend__ = True
 
     @testing.provide_metadata
-    def _test_setinputsizes(self, datatype, value, sis_value):
+    def _test_setinputsizes(
+            self, datatype, value, sis_value, set_nchar_flag=False):
         class TestTypeDec(TypeDecorator):
             impl = NullType()
 
@@ -944,7 +1000,12 @@ class SetInputSizesTest(fixtures.TestBase):
             def __getattr__(self, key):
                 return getattr(self.cursor, key)
 
-        with testing.db.connect() as conn:
+        if set_nchar_flag:
+            engine = testing_engine(options={"use_nchar_for_unicode": True})
+        else:
+            engine = testing.db
+
+        with engine.connect() as conn:
             connection_fairy = conn.connection
             for tab in [t1, t2, t3]:
                 with mock.patch.object(
@@ -995,9 +1056,23 @@ class SetInputSizesTest(fixtures.TestBase):
             oracle.DOUBLE_PRECISION, 25.34534,
             None)
 
+    def test_unicode_nchar_mode(self):
+        self._test_setinputsizes(
+            Unicode(30), u("test"), testing.db.dialect.dbapi.NCHAR,
+            set_nchar_flag=True)
+
+    def test_unicodetext_nchar_mode(self):
+        self._test_setinputsizes(
+            UnicodeText(), u("test"), testing.db.dialect.dbapi.NCLOB,
+            set_nchar_flag=True)
+
     def test_unicode(self):
         self._test_setinputsizes(
-            Unicode(30), u("test"), testing.db.dialect.dbapi.NCHAR)
+            Unicode(30), u("test"), None)
+
+    def test_unicodetext(self):
+        self._test_setinputsizes(
+            UnicodeText(), u("test"), testing.db.dialect.dbapi.CLOB)
 
     def test_string(self):
         self._test_setinputsizes(String(30), "test", None)
index a70901208e02bcb049da21291df96c09f00bc332..ae7b5746ab7d7006711102f1d277988ad9e7f6e4 100644 (file)
@@ -1,15 +1,15 @@
 # /home/classic/dev/sqlalchemy/test/profiles.txt
 # This file is written out on a per-environment basis.
-# For each test in aaa_profiling, the corresponding function and 
+# For each test in aaa_profiling, the corresponding function and
 # environment is located within this file.  If it doesn't exist,
 # the test is skipped.
-# If a callcount does exist, it is compared to what we received. 
+# If a callcount does exist, it is compared to what we received.
 # assertions are raised if the counts do not match.
-# 
-# To add a new callcount test, apply the function_call_count 
-# decorator and re-run the tests using the --write-profiles 
+#
+# To add a new callcount test, apply the function_call_count
+# decorator and re-run the tests using the --write-profiles
 # option - this file will be rewritten including the new count.
-# 
+#
 
 # TEST: test.aaa_profiling.test_compiler.CompileTest.test_insert
 
@@ -27,14 +27,14 @@ test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dba
 test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 70,70
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mssql_pyodbc_dbapiunicode_cextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mssql_pyodbc_dbapiunicode_nocextensions 73,73
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_mysqldb_dbapiunicode_cextensions 73,73
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_mysqldb_dbapiunicode_nocextensions 73,73
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_mysqldb_dbapiunicode_cextensions 73,73,73,73
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_mysqldb_dbapiunicode_nocextensions 73,73,73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_pymysql_dbapiunicode_cextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_mysql_pymysql_dbapiunicode_nocextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_oracle_cx_oracle_dbapiunicode_cextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 73,73
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_postgresql_psycopg2_dbapiunicode_cextensions 73,73,78
-test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 73,73,78
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_postgresql_psycopg2_dbapiunicode_cextensions 73,73,73
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 73,73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_sqlite_pysqlite_dbapiunicode_cextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 73,73
 test.aaa_profiling.test_compiler.CompileTest.test_insert 3.7_mysql_mysqldb_dbapiunicode_cextensions 73,73,73,73
@@ -138,14 +138,14 @@ test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dba
 test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 82,80
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mssql_pyodbc_dbapiunicode_cextensions 83,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mssql_pyodbc_dbapiunicode_nocextensions 83,81
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_mysqldb_dbapiunicode_cextensions 83,81
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_mysqldb_dbapiunicode_nocextensions 83,81
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_mysqldb_dbapiunicode_cextensions 83,81,81,81
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_mysqldb_dbapiunicode_nocextensions 83,81,81,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_pymysql_dbapiunicode_cextensions 81,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_mysql_pymysql_dbapiunicode_nocextensions 81,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_oracle_cx_oracle_dbapiunicode_cextensions 83,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 83,81
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_postgresql_psycopg2_dbapiunicode_cextensions 83,81,77
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 83,81,77
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_postgresql_psycopg2_dbapiunicode_cextensions 83,81,81
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 83,81,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_sqlite_pysqlite_dbapiunicode_cextensions 83,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 83,81
 test.aaa_profiling.test_compiler.CompileTest.test_update 3.7_mysql_mysqldb_dbapiunicode_cextensions 83,81,81,81
@@ -388,8 +388,8 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_mssql_pyodbc_dbap
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_mssql_pyodbc_dbapiunicode_nocextensions 26212
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_mysql_mysqldb_dbapiunicode_cextensions 41220
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_mysql_mysqldb_dbapiunicode_nocextensions 50225
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_oracle_cx_oracle_dbapiunicode_cextensions 17287
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 26292
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_oracle_cx_oracle_dbapiunicode_cextensions 17299
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 44316
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_postgresql_psycopg2_dbapiunicode_cextensions 17199
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 26204
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_dbapiunicode_cextensions 17164
@@ -398,8 +398,8 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_mssql_pyodbc_dbap
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_mssql_pyodbc_dbapiunicode_nocextensions 27228
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_mysql_mysqldb_dbapiunicode_cextensions 30235
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_mysql_mysqldb_dbapiunicode_nocextensions 39242
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_oracle_cx_oracle_dbapiunicode_cextensions 18301
-test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 27308
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_oracle_cx_oracle_dbapiunicode_cextensions 18307
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 27314
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_postgresql_psycopg2_dbapiunicode_cextensions 18228
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 27235
 test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.6_sqlite_pysqlite_dbapiunicode_cextensions 18186
@@ -475,6 +475,7 @@ test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_postgresql_
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_cextensions 455708
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_build_query 3.7_sqlite_pysqlite_dbapiunicode_nocextensions 455708
 
+
 # TEST: test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results
 
 test.aaa_profiling.test_orm.JoinedEagerLoadTest.test_fetch_results 2.7_mssql_pyodbc_dbapiunicode_cextensions 466544
@@ -667,20 +668,20 @@ test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_mssql_pyodbc_dbapiunic
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_mssql_pyodbc_dbapiunicode_nocextensions 6780
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_mysql_mysqldb_dbapiunicode_cextensions 8020
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_mysql_mysqldb_dbapiunicode_nocextensions 8570
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_oracle_cx_oracle_dbapiunicode_cextensions 6280
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 6830
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_oracle_cx_oracle_dbapiunicode_cextensions 6360
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 8190
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_postgresql_psycopg2_dbapiunicode_cextensions 6150
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 6700
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_cextensions 6004
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 6554
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_mssql_pyodbc_dbapiunicode_cextensions 6404
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_mssql_pyodbc_dbapiunicode_nocextensions 6974
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_mysql_mysqldb_dbapiunicode_cextensions 7404
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_mysql_mysqldb_dbapiunicode_cextensions 7494
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_mysql_mysqldb_dbapiunicode_nocextensions 7974
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_oracle_cx_oracle_dbapiunicode_cextensions 6454
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 7024
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_oracle_cx_oracle_dbapiunicode_cextensions 6494
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 7154
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_postgresql_psycopg2_dbapiunicode_cextensions 6414
-test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 6984
+test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 7074
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_sqlite_pysqlite_dbapiunicode_cextensions 6186
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 6756
 test.aaa_profiling.test_orm.QueryTest.test_query_cols 3.7_mysql_mysqldb_dbapiunicode_cextensions 7433
@@ -863,8 +864,8 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 51
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mssql_pyodbc_dbapiunicode_cextensions 51
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mssql_pyodbc_dbapiunicode_nocextensions 55
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_mysqldb_dbapiunicode_cextensions 51,51
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_mysqldb_dbapiunicode_nocextensions 55,55
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_mysqldb_dbapiunicode_cextensions 51,51,51,51
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_mysqldb_dbapiunicode_nocextensions 55,55,55,55
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_pymysql_dbapiunicode_cextensions 51,51
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_mysql_pymysql_dbapiunicode_nocextensions 55,55
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.6_oracle_cx_oracle_dbapiunicode_cextensions 51
@@ -900,8 +901,8 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 90
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mssql_pyodbc_dbapiunicode_cextensions 90
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mssql_pyodbc_dbapiunicode_nocextensions 94
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_mysqldb_dbapiunicode_cextensions 90,90
-test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_mysqldb_dbapiunicode_nocextensions 94,94
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_mysqldb_dbapiunicode_cextensions 90,90,90,90
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_mysqldb_dbapiunicode_nocextensions 94,94,94,94
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_pymysql_dbapiunicode_cextensions 90,90
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_mysql_pymysql_dbapiunicode_nocextensions 94,94
 test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.6_oracle_cx_oracle_dbapiunicode_cextensions 90
@@ -937,8 +938,8 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mssql_pyodbc_dbapiunicode_cextensions 16
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mssql_pyodbc_dbapiunicode_nocextensions 16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_mysqldb_dbapiunicode_cextensions 16,16
-test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_mysqldb_dbapiunicode_nocextensions 16,16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_mysqldb_dbapiunicode_cextensions 16,16,16,16
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_mysqldb_dbapiunicode_nocextensions 16,16,16,16
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_pymysql_dbapiunicode_cextensions 16,16
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_mysql_pymysql_dbapiunicode_nocextensions 16,16
 test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.6_oracle_cx_oracle_dbapiunicode_cextensions 16
@@ -965,21 +966,21 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mssql_pyodbc
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 40304,40306,40306,40306
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 55326,55328,55328,55328
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122257,122262
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 137269,137269
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 355
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 15377
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 137259,137264
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 375
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 45417
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 290,292,292
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15312,15314,15314
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 246
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15268
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mssql_pyodbc_dbapiunicode_cextensions 245
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mssql_pyodbc_dbapiunicode_nocextensions 14249
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_mysqldb_dbapiunicode_cextensions 20285,20287
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34289,34291
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_pymysql_dbapiunicode_cextensions 88036,88066
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_pymysql_dbapiunicode_nocextensions 102040,102065
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_oracle_cx_oracle_dbapiunicode_cextensions 324
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14328
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_mysqldb_dbapiunicode_cextensions 20285,20287,20287,20287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34289,34291,34291,34291
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_pymysql_dbapiunicode_cextensions 88041,88046
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_mysql_pymysql_dbapiunicode_nocextensions 102045,102055
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_oracle_cx_oracle_dbapiunicode_cextensions 334
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14338
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_postgresql_psycopg2_dbapiunicode_cextensions 290,292,292
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14294,14296,14296
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_string 3.6_sqlite_pysqlite_dbapiunicode_cextensions 242
@@ -1001,22 +1002,22 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodb
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 15258
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 40304,40306,40306,40306
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 55326,55328,55328,55328
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122257,122257
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 137264,137264
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 20355
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 35377
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122257,122262
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 137274,137264
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 375
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 45417
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 290,292,292
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15312,15314,15314
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 246
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15268
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mssql_pyodbc_dbapiunicode_cextensions 245
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mssql_pyodbc_dbapiunicode_nocextensions 14249
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_mysqldb_dbapiunicode_cextensions 20285,20287
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34289,34291
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_pymysql_dbapiunicode_cextensions 88036,88056
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_pymysql_dbapiunicode_nocextensions 102040,102055
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_oracle_cx_oracle_dbapiunicode_cextensions 324
-test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14328
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_mysqldb_dbapiunicode_cextensions 20285,20287,20287,20287
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34289,34291,34291,34291
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_pymysql_dbapiunicode_cextensions 88041,88036
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_mysql_pymysql_dbapiunicode_nocextensions 102050,102050
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_oracle_cx_oracle_dbapiunicode_cextensions 334
+test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14338
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_postgresql_psycopg2_dbapiunicode_cextensions 290,292,292
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14294,14296,14296
 test.aaa_profiling.test_resultset.ResultSetTest.test_raw_unicode 3.6_sqlite_pysqlite_dbapiunicode_cextensions 242
@@ -1038,24 +1039,24 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dba
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mssql_pyodbc_dbapiunicode_nocextensions 15528
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_cextensions 40533,40535,40535,40535
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_mysqldb_dbapiunicode_nocextensions 55535,55537,55537,55537
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122506,122501
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 137498,137513
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 535
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 15537
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_cextensions 122506,122496
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_mysql_pymysql_dbapiunicode_nocextensions 137518,137508
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_cextensions 555
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 45577
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_cextensions 518,520,520
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15520,15522,15522
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_cextensions 460
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15462
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mssql_pyodbc_dbapiunicode_cextensions 530
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mssql_pyodbc_dbapiunicode_nocextensions 14534
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_mysqldb_dbapiunicode_cextensions 20538,20520
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34542,34524
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_pymysql_dbapiunicode_cextensions 88290,88275
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_pymysql_dbapiunicode_nocextensions 102294,102299
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_oracle_cx_oracle_dbapiunicode_cextensions 539
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14543
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_postgresql_psycopg2_dbapiunicode_cextensions 543,525,525
-test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14547,14529,14529
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_mysqldb_dbapiunicode_cextensions 20538,20540,20540,20540
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34542,34544,34544,34544
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_pymysql_dbapiunicode_cextensions 88295,88300
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_mysql_pymysql_dbapiunicode_nocextensions 102299,102304
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_oracle_cx_oracle_dbapiunicode_cextensions 549
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14553
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_postgresql_psycopg2_dbapiunicode_cextensions 543,545,545
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14547,14549,14549
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_sqlite_pysqlite_dbapiunicode_cextensions 480
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 14484
 test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.7_mysql_mysqldb_dbapiunicode_cextensions 20537,20539,20539,20539
@@ -1075,24 +1076,24 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_db
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mssql_pyodbc_dbapiunicode_nocextensions 15528
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_cextensions 40533,40535,40535,40535
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_mysqldb_dbapiunicode_nocextensions 55535,55537,55537,55537
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122501,122506
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_cextensions 122501,122496
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_mysql_pymysql_dbapiunicode_nocextensions 137503,137503
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 20535
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 55537
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_cextensions 555
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_oracle_cx_oracle_dbapiunicode_nocextensions 45577
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_cextensions 518,520,520
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_postgresql_psycopg2_dbapiunicode_nocextensions 15520,15522,15522
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_cextensions 460
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite_dbapiunicode_nocextensions 15462
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mssql_pyodbc_dbapiunicode_cextensions 530
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mssql_pyodbc_dbapiunicode_nocextensions 14534
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_mysqldb_dbapiunicode_cextensions 20538,20520
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34542,34524
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_pymysql_dbapiunicode_cextensions 88290,88275
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_pymysql_dbapiunicode_nocextensions 102294,102274
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_oracle_cx_oracle_dbapiunicode_cextensions 539
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14543
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_postgresql_psycopg2_dbapiunicode_cextensions 543,525,525
-test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14547,14529,14529
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_mysqldb_dbapiunicode_cextensions 20538,20540,20540,20540
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_mysqldb_dbapiunicode_nocextensions 34542,34544,34544,34544
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_pymysql_dbapiunicode_cextensions 88290,88295
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_mysql_pymysql_dbapiunicode_nocextensions 102299,102329
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_oracle_cx_oracle_dbapiunicode_cextensions 549
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_oracle_cx_oracle_dbapiunicode_nocextensions 14553
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_postgresql_psycopg2_dbapiunicode_cextensions 543,545,545
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_postgresql_psycopg2_dbapiunicode_nocextensions 14547,14549,14549
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_sqlite_pysqlite_dbapiunicode_cextensions 480
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.6_sqlite_pysqlite_dbapiunicode_nocextensions 14484
 test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.7_mysql_mysqldb_dbapiunicode_cextensions 20537,20539,20539,20539