]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Added preliminary support for Oracle's WITH_UNICODE
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 12 Mar 2010 21:05:53 +0000 (21:05 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 12 Mar 2010 21:05:53 +0000 (21:05 +0000)
mode.  At the very least this establishes initial
support for cx_Oracle with Python 3.
[ticket:1670]

CHANGES
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/test/config.py

diff --git a/CHANGES b/CHANGES
index 4335efd8a3225327c6ac566630ec27b5979ae6dd..b8ca5a0a7edaff3a1c55308a6962203138383a4f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -306,12 +306,18 @@ CHANGES
    - "out" parameters require a type that is supported by
      cx_oracle.  An error will be raised if no cx_oracle
      type can be found.
+
    - Oracle 'DATE' now does not perform any result processing,
      as the DATE type in Oracle stores full date+time objects,
      that's what you'll get.  Note that the generic types.Date
      type *will* still call value.date() on incoming values, 
      however.  When reflecting a table, the reflected type
      will be 'DATE'.
+
+   - Added preliminary support for Oracle's WITH_UNICODE
+     mode.  At the very least this establishes initial
+     support for cx_Oracle with Python 3.
+     [ticket:1670]
      
 - sqlite
    - Added "native_datetime=True" flag to create_engine().
index 3107c8b6c9b523f072b19643bcd4b1bf351b69ac..ffadc84f87d0580b565063ca130828ec25f5eb52 100644 (file)
@@ -590,22 +590,31 @@ class OracleDialect(default.DefaultDialect):
     def normalize_name(self, name):
         if name is None:
             return None
-        elif (name.upper() == name and
-              not self.identifier_preparer._requires_quotes(name.lower().decode(self.encoding))):
-            return name.lower().decode(self.encoding)
+        # Py2K
+        if isinstance(name, str):
+            name = name.decode(self.encoding)
+        # end Py2K
+        if name.upper() == name and \
+              not self.identifier_preparer._requires_quotes(name.lower()):
+            return name.lower()
         else:
-            return name.decode(self.encoding)
+            return name
 
     def denormalize_name(self, name):
         if name is None:
             return None
         elif name.lower() == name and not self.identifier_preparer._requires_quotes(name.lower()):
-            return name.upper().encode(self.encoding)
+            name = name.upper()
+        # Py2K
+        if not self.supports_unicode_binds:
+            name = name.encode(self.encoding)
         else:
-            return name.encode(self.encoding)
+            name = unicode(name)
+        # end Py2K
+        return name
 
     def _get_default_schema_name(self, connection):
-        return self.normalize_name(connection.execute('SELECT USER FROM DUAL').scalar())
+        return self.normalize_name(connection.execute(u'SELECT USER FROM DUAL').scalar())
 
     def table_names(self, connection, schema):
         # note that table_names() isnt loading DBLINKed or synonym'ed tables
@@ -664,7 +673,11 @@ class OracleDialect(default.DefaultDialect):
                                  resolve_synonyms=False, dblink='', **kw):
 
         if resolve_synonyms:
-            actual_name, owner, dblink, synonym = self._resolve_synonym(connection, desired_owner=self.denormalize_name(schema), desired_synonym=self.denormalize_name(table_name))
+            actual_name, owner, dblink, synonym = self._resolve_synonym(
+                                                         connection, 
+                                                         desired_owner=self.denormalize_name(schema), 
+                                                         desired_synonym=self.denormalize_name(table_name)
+                                                   )
         else:
             actual_name, owner, dblink, synonym = None, None, None, None
         if not actual_name:
index f4c2e295f2ef42ec6c1052431520a31ba44c672c..c5e24cbb35dbbcb02ed12cd1da149aeedb42171f 100644 (file)
@@ -119,7 +119,9 @@ class _NativeUnicodeMixin(object):
     def result_processor(self, dialect, coltype):
         # if we know cx_Oracle will return unicode,
         # don't process results
-        if self.convert_unicode != 'force' and \
+        if dialect._cx_oracle_with_unicode:
+            return None
+        elif self.convert_unicode != 'force' and \
                     dialect._cx_oracle_native_nvarchar and \
                     coltype == dialect.dbapi.UNICODE:
             return None
@@ -227,9 +229,8 @@ class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
             # on String, including that outparams/RETURNING
             # breaks for varchars
             self.set_input_sizes(quoted_bind_names, 
-                                     exclude_types=[
-                                              self.dialect.dbapi.STRING, 
-                                              self.dialect.dbapi.UNICODE])
+                                     exclude_types=self.dialect._cx_oracle_string_types
+                                )
             
         if len(self.compiled_parameters) == 1:
             for key in self.compiled.binds:
@@ -266,7 +267,7 @@ class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
         if self.cursor.description is not None:
             for column in self.cursor.description:
                 type_code = column[1]
-                if type_code in self.dialect.ORACLE_BINARY_TYPES:
+                if type_code in self.dialect._cx_oracle_binary_types:
                     result = base.BufferedColumnResultProxy(self)
         
         if result is None:
@@ -347,10 +348,26 @@ class Oracle_cx_oracle(OracleDialect):
             cx_oracle_ver = vers(self.dbapi.version)
             self.supports_unicode_binds = cx_oracle_ver >= (5, 0)
             self._cx_oracle_native_nvarchar = cx_oracle_ver >= (5, 0)
-            
-        if self.dbapi is None or not self.auto_convert_lobs or not 'CLOB' in self.dbapi.__dict__:
+           
+        if self.dbapi is not None and not hasattr(self.dbapi, 'UNICODE'):
+             # cx_Oracle WITH_UNICODE mode.  *only* python
+             # unicode objects accepted for anything
+             self._cx_oracle_string_types = set([self.dbapi.STRING])
+             self.supports_unicode_statements = True
+             self.supports_unicode_binds = True
+             self._cx_oracle_with_unicode = True
+        else:
+             self._cx_oracle_with_unicode = False
+             if self.dbapi is not None:
+                 self._cx_oracle_string_types = set([self.dbapi.UNICODE, self.dbapi.STRING])
+             else:
+                 self._cx_oracle_string_types = set()
+        if self.dbapi is None or \
+                    not self.auto_convert_lobs or \
+                    not hasattr(self.dbapi, 'CLOB'):
             self.dbapi_type_map = {}
-            self.ORACLE_BINARY_TYPES = []
+            self._cx_oracle_binary_types = set()
         else:
             # only use this for LOB objects.  using it for strings, dates
             # etc. leads to a little too much magic, reflection doesn't know if it should
@@ -361,7 +378,9 @@ class Oracle_cx_oracle(OracleDialect):
                 self.dbapi.BLOB: oracle.BLOB(),
                 self.dbapi.BINARY: oracle.RAW(),
             }
-            self.ORACLE_BINARY_TYPES = [getattr(self.dbapi, k) for k in ["BFILE", "CLOB", "NCLOB", "BLOB"] if hasattr(self.dbapi, k)]
+            self._cx_oracle_binary_types = set([getattr(self.dbapi, k) for k in 
+                                          ["BFILE", "CLOB", "NCLOB", "BLOB"] 
+                                          if hasattr(self.dbapi, k)])
     
     @classmethod
     def dbapi(cls):
@@ -395,6 +414,14 @@ class Oracle_cx_oracle(OracleDialect):
             threaded=self.threaded,
             twophase=self.allow_twophase,
             )
+
+        # Py2K
+        if self._cx_oracle_with_unicode:
+            for k, v in opts.items():
+                if isinstance(v, str):
+                    opts[k] = unicode(v)
+        # end Py2K
+
         if 'mode' in url.query:
             opts['mode'] = url.query['mode']
             if isinstance(opts['mode'], basestring):
index 0776279490466914b71382bd5742c9a14e218ab0..cfab01dc4237e56fb57bf5950a1ec7bfaadf2a56 100644 (file)
@@ -138,9 +138,17 @@ class DefaultDialect(base.Dialect):
     
     def _check_unicode_returns(self, connection):
         cursor = connection.connection.cursor()
+        # Py2K
+        if self.supports_unicode_statements:
+            cast_to = unicode
+        else:
+            cast_to = str
+        # end Py2K
+        # Py3K
+        #cast_to = str
         def check_unicode(type_):
             cursor.execute(
-                str(
+                cast_to(
                     expression.select( 
                     [expression.cast(
                         expression.literal_column("'test unicode returns'"), type_)
index eec962d807af2b6304d718e46082f10f90c9e188..efbe00fef2a1d0084505c43befd7aacdc652668f 100644 (file)
@@ -1,5 +1,6 @@
 import optparse, os, sys, re, ConfigParser, time, warnings
 
+
 # 2to3
 import StringIO