- 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
- 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
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)
'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
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)
'nullable': nullable,
'default': default,
}
+ if orig_colname.lower() == orig_colname:
+ cdict['quote'] = True
+
columns.append(cdict)
return columns
}
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,
_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."""
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'."""
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
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