summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2010-01-24 18:41:30 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2010-01-24 18:41:30 +0000
commitdd01f817b738a81bf9e5e4632387454f0f177dd6 (patch)
tree4102b7da0d399469b0f0648f84ff2cfe5b558d43
parent9806d81675ef62363753a028ada43bc460728cf5 (diff)
downloadsqlalchemy-dd01f817b738a81bf9e5e4632387454f0f177dd6.tar.gz
- oracle + firebird: "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.
-rw-r--r--CHANGES10
-rw-r--r--lib/sqlalchemy/dialects/firebird/base.py6
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py7
-rw-r--r--lib/sqlalchemy/engine/reflection.py4
-rw-r--r--lib/sqlalchemy/test/requires.py9
-rw-r--r--test/engine/test_reflection.py28
6 files changed, 58 insertions, 6 deletions
diff --git a/CHANGES b/CHANGES
index e322df15d..021cd4a23 100644
--- 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
diff --git a/lib/sqlalchemy/dialects/firebird/base.py b/lib/sqlalchemy/dialects/firebird/base.py
index aab217450..e4a2c568a 100644
--- a/lib/sqlalchemy/dialects/firebird/base.py
+++ b/lib/sqlalchemy/dialects/firebird/base.py
@@ -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
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 926796961..bb9ed3250 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -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
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py
index 0d49b38bc..57f2205c1 100644
--- a/lib/sqlalchemy/engine/reflection.py
+++ b/lib/sqlalchemy/engine/reflection.py
@@ -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,
diff --git a/lib/sqlalchemy/test/requires.py b/lib/sqlalchemy/test/requires.py
index be6ae9594..4f6c81a20 100644
--- a/lib/sqlalchemy/test/requires.py
+++ b/lib/sqlalchemy/test/requires.py
@@ -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'."""
diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py
index 95f985db3..1582c86e4 100644
--- a/test/engine/test_reflection.py
+++ b/test/engine/test_reflection.py
@@ -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