if getattr(obj, 'quote', False):
return self._quote_identifier(ident)
if self.dialect.cache_identifiers:
+ case_sens = getattr(obj, 'case_sensitive', None)
try:
- return self.__strings[ident]
+ return self.__strings[(ident, case_sens)]
except KeyError:
if self._requires_quotes(ident, getattr(obj, 'case_sensitive', ident == ident.lower())):
- self.__strings[ident] = self._quote_identifier(ident)
+ self.__strings[(ident, case_sens)] = self._quote_identifier(ident)
else:
- self.__strings[ident] = ident
- return self.__strings[ident]
+ self.__strings[(ident, case_sens)] = ident
+ return self.__strings[(ident, case_sens)]
else:
if self._requires_quotes(ident, getattr(obj, 'case_sensitive', ident == ident.lower())):
return self._quote_identifier(ident)
return self._derived_metadata().engine
def _set_casing_strategy(self, name, kwargs, keyname='case_sensitive'):
- setattr(self, '_SchemaItem__%s_setting' % keyname, kwargs.pop(keyname, None))
+ """set the "case_sensitive" argument sent via keywords to the item's constructor.
+
+ for the purposes of Table's 'schema' property, the name of the variable is
+ optionally configurable."""
+ setattr(self, '_%s_setting' % keyname, kwargs.pop(keyname, None))
def _determine_case_sensitive(self, name, keyname='case_sensitive'):
- local = getattr(self, '_SchemaItem__%s_setting' % keyname, None)
+ """determine the "case_sensitive" value for this item.
+
+ for the purposes of Table's 'schema' property, the name of the variable is
+ optionally configurable.
+
+ a local non-None value overrides all others. after that, the parent item
+ (i.e. Column for a Sequence, Table for a Column, MetaData for a Table) is
+ searched for a non-None setting, traversing each parent until none are found.
+ finally, case_sensitive is set to True if and only if the name of this item
+ is not all lowercase.
+ """
+ local = getattr(self, '_%s_setting' % keyname, None)
if local is not None:
return local
parent = self
while parent is not None:
parent = parent._get_parent()
if parent is not None:
- parentval = getattr(parent, '_SchemaItem__case_sensitive_setting', None)
+ parentval = getattr(parent, '_case_sensitive_setting', None)
if parentval is not None:
return parentval
return name is not None and name.lower() != name
def _get_case_sensitive_schema(self):
try:
- return getattr(self, '_SchemaItem__case_sensitive_schema')
+ return getattr(self, '_case_sensitive_schema')
except AttributeError:
- setattr(self, '_SchemaItem__case_sensitive_schema', self._determine_case_sensitive(self.schema or '', keyname='case_sensitive_schema'))
- return getattr(self, '_SchemaItem__case_sensitive_schema')
+ setattr(self, '_case_sensitive_schema', self._determine_case_sensitive(self.schema or '', keyname='case_sensitive_schema'))
+ return getattr(self, '_case_sensitive_schema')
case_sensitive_schema = property(_get_case_sensitive_schema)
def _set_primary_key(self, pk):
self.quote = kwargs.pop('quote', False)
self._set_casing_strategy(name, kwargs)
self.onupdate = kwargs.pop('onupdate', None)
+ self.__originating_column = self
if self.index is not None and self.unique is not None:
raise exceptions.ArgumentError("Column may not define both index and unique")
self._foreign_key = None
self.args = None
def copy(self):
- """creates a copy of this Column, unitialized"""
- return Column(self.name, self.type, self.default, key = self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, case_sensitive=self.case_sensitive, quote=self.quote)
+ """creates a copy of this Column, unitialized. this is used in Table.tometadata."""
+ return Column(self.name, self.type, self.default, key = self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, case_sensitive=self._case_sensitive_setting, quote=self.quote)
def _make_proxy(self, selectable, name = None):
- """creates a copy of this Column, initialized the way this Column is"""
+ """create a "proxy" for this column.
+
+ This is a copy of this Column referenced
+ by a different parent (such as an alias or select statement)"""
if self.foreign_key is None:
fk = None
else:
fk = self.foreign_key.copy()
- c = Column(name or self.name, self.type, fk, self.default, key = name or self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, case_sensitive=self.case_sensitive, quote=self.quote)
+ c = Column(name or self.name, self.type, fk, self.default, key = name or self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, quote=self.quote)
c.table = selectable
c.orig_set = self.orig_set
- c._parent = self
+ c.__originating_column = self.__originating_column
if not c.hidden:
selectable.columns[c.key] = c
if self.primary_key:
c._init_items(fk)
return c
+ def _case_sens(self):
+ """redirect the 'case_sensitive' accessor to use the ultimate parent column which created
+ this one."""
+ return self.__originating_column._get_case_sensitive()
+ case_sensitive = property(_case_sens)
+
def accept_schema_visitor(self, visitor):
"""traverses the given visitor to this Column's default and foreign key object,
then calls visit_column on the visitor."""
self.columns.append(col)
col.primary_key=True
def copy(self):
- return PrimaryKeyConstraint(name=self.name, *[c.name for c in self])
+ return PrimaryKeyConstraint(name=self.name, *[c.key for c in self])
class UniqueConstraint(Constraint):
def __init__(self, name=None, *columns):
"""
x = table1.select(distinct=True).alias("LaLa").select().scalar()
+ def testlabelsnocase(self):
+ metadata = MetaData()
+ table1 = Table('SomeCase1', metadata,
+ Column('lowercase', Integer, primary_key=True),
+ Column('UPPERCASE', Integer),
+ Column('MixedCase', Integer))
+ table2 = Table('SomeCase2', metadata,
+ Column('id', Integer, primary_key=True, key='d123'),
+ Column('col2', Integer, key='u123'),
+ Column('MixedCase', Integer))
+
+ # first test case sensitive tables migrating via tometadata
+ meta = BoundMetaData(testbase.db, case_sensitive=False)
+ lc_table1 = table1.tometadata(meta)
+ lc_table2 = table2.tometadata(meta)
+ assert lc_table1.case_sensitive is False
+ assert lc_table1.c.UPPERCASE.case_sensitive is False
+ s = lc_table1.select()
+ assert hasattr(s.c.UPPERCASE, "case_sensitive")
+ assert s.c.UPPERCASE.case_sensitive is False
+
+ # now, the aliases etc. should be case-insensitive. PG will screw up if this doesnt work.
+ # also, if this test is run in the context of the other tests, we also test that the dialect properly
+ # caches identifiers with "case_sensitive" and "not case_sensitive" separately.
+ meta.create_all()
+ try:
+ x = lc_table1.select(distinct=True).alias("lala").select().scalar()
+ finally:
+ meta.drop_all()
if __name__ == "__main__":
testbase.main()