]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- oracle + firebird: "case sensitivity" feature will detect an all-lowercase
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Jan 2010 18:41:30 +0000 (18:41 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 24 Jan 2010 18:41:30 +0000 (18:41 +0000)
case-sensitive column name during reflect and add
"quote=True" to the generated Column, so that proper
quoting is maintained.

CHANGES
lib/sqlalchemy/dialects/firebird/base.py
lib/sqlalchemy/dialects/oracle/base.py
lib/sqlalchemy/engine/reflection.py
lib/sqlalchemy/test/requires.py
test/engine/test_reflection.py

diff --git a/CHANGES b/CHANGES
index e322df15de9e0f43835e8de404e239713dde62bf..021cd4a236bb830d7a8cd9f01950d445b1fba523 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -711,6 +711,11 @@ CHANGES
     
     - using types.BigInteger with Oracle will generate
       NUMBER(19) [ticket:1125]
+    
+    - "case sensitivity" feature will detect an all-lowercase
+      case-sensitive column name during reflect and add 
+      "quote=True" to the generated Column, so that proper
+      quoting is maintained.
       
 - firebird
     - the keys() method of RowProxy() now returns the result
@@ -723,6 +728,11 @@ CHANGES
     - using new dialect.initialize() feature to set up
       version-dependent behavior.
 
+    - "case sensitivity" feature will detect an all-lowercase
+      case-sensitive column name during reflect and add 
+      "quote=True" to the generated Column, so that proper
+      quoting is maintained.
+
 - mssql
     - MSSQL + Pyodbc + FreeTDS now works for the most part,
       with possible exceptions regarding binary data as well as
index aab217450298044c7a9aeb67b946d5ba82a69e14..e4a2c568a4ea7ec324c0800862485a8809d1fc41 100644 (file)
@@ -484,7 +484,8 @@ class FBDialect(default.DefaultDialect):
             if row is None:
                 break
             name = self.normalize_name(row['fname'])
-
+            orig_colname = row['fname']
+            
             # get the data type
             colspec = row['ftype'].rstrip()
             coltype = self.ischema_names.get(colspec)
@@ -523,6 +524,9 @@ class FBDialect(default.DefaultDialect):
                 'nullable' :  not bool(row['null_flag']),
                 'default' : defvalue
             }
+            
+            if orig_colname.lower() == orig_colname:
+                col_d['quote'] = True
 
             # if the PK is a single field, try to see if its linked to
             # a sequence thru a trigger
index 92679696109ec39844987bc6e1f7d47f00d7da4b..bb9ed325069692189dcf0a1ba707584437650080 100644 (file)
@@ -718,8 +718,8 @@ class OracleDialect(default.DefaultDialect):
                                table_name=table_name, owner=schema)
 
         for row in c:
-            (colname, coltype, length, precision, scale, nullable, default) = \
-                (self.normalize_name(row[0]), row[1], row[2], row[3], row[4], row[5]=='Y', row[6])
+            (colname, orig_colname, coltype, length, precision, scale, nullable, default) = \
+                (self.normalize_name(row[0]), row[0], row[1], row[2], row[3], row[4], row[5]=='Y', row[6])
 
             if coltype == 'NUMBER' :
                 coltype = NUMBER(precision, scale)
@@ -740,6 +740,9 @@ class OracleDialect(default.DefaultDialect):
                 'nullable': nullable,
                 'default': default,
             }
+            if orig_colname.lower() == orig_colname:
+                cdict['quote'] = True
+
             columns.append(cdict)
         return columns
 
index 0d49b38bcd3fd4c96487d9ad7deb8fb5a383a33c..57f2205c167bc6da40473f81a33bd68f434ef1c5 100644 (file)
@@ -292,7 +292,9 @@ class Inspector(object):
             }
             if 'autoincrement' in col_d:
                 col_kw['autoincrement'] = col_d['autoincrement']
-            
+            if 'quote' in col_d:
+                col_kw['quote'] = col_d['quote']
+                
             colargs = []
             if col_d.get('default') is not None:
                 # the "default" value is assumed to be a literal SQL expression,
index be6ae9594b329822bb78f59f69f7bd2ee8f7afea..4f6c81a204564a8dc71c714599d2ab241d6af3b5 100644 (file)
@@ -9,8 +9,10 @@ from testing import \
      _block_unconditionally as no_support, \
      _chain_decorators_on, \
      exclude, \
-     emits_warning_on
+     emits_warning_on,\
+     skip_if
 
+import testing
 
 def deferrable_constraints(fn):
     """Target database must support derferable constraints."""
@@ -106,6 +108,11 @@ def savepoints(fn):
         exclude('mysql', '<', (5, 0, 3), 'not supported by database'),
         )
 
+def denormalized_names(fn):
+    """Target database must have 'denormalized', i.e. UPPERCASE as case insensitive names."""
+    
+    return skip_if(lambda: not testing.db.dialect.requires_name_normalize)(fn)
+    
 def schemas(fn):
     """Target database must support external schemas, and have one named 'test_schema'."""
     
index 95f985db3d587b49fb536b64a8c30692418a3f89..1582c86e4a2675c4e04c9867f9171d7e176484ed 100644 (file)
@@ -7,7 +7,8 @@ from sqlalchemy import MetaData
 from sqlalchemy.test.schema import Table
 from sqlalchemy.test.schema import Column
 import sqlalchemy as sa
-from sqlalchemy.test import TestBase, ComparesTables, testing, engines
+from sqlalchemy.test import TestBase, ComparesTables, \
+                            testing, engines, AssertsCompiledSQL
 
 create_inspector = Inspector.from_engine
 
@@ -995,6 +996,31 @@ def dropViews(con, schema=None):
         con.execute(sa.sql.text(query))
 
 
+class ReverseCasingReflectTest(TestBase, AssertsCompiledSQL):
+
+    @testing.requires.denormalized_names
+    def setup(self):
+        testing.db.execute("""
+        CREATE TABLE weird_casing(
+                col1 char(20),
+                "Col2" char(20),
+                "col3" char(20)
+        )
+        """)
+
+    @testing.requires.denormalized_names
+    def teardown(self):
+        testing.db.execute("drop table weird_casing")
+
+    @testing.requires.denormalized_names
+    def test_direct_quoting(self):
+        m = MetaData(testing.db)
+        t = Table("weird_casing", m, autoload=True)
+        self.assert_compile(
+            t.select(),
+            'SELECT weird_casing.col1, weird_casing."Col2", weird_casing."col3" FROM weird_casing'
+        )
+
 class ComponentReflectionTest(TestBase):
 
     @testing.requires.schemas