]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
further fixes to case sensitive logic
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2006 23:05:36 +0000 (23:05 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 4 Sep 2006 23:05:36 +0000 (23:05 +0000)
lib/sqlalchemy/ansisql.py
lib/sqlalchemy/schema.py
test/sql/quote.py

index d053f738983377ef70c03370d61829fecd138241..c44595f36a3f375ec2bf095758de7253645ba153 100644 (file)
@@ -775,14 +775,15 @@ class ANSIIdentifierPreparer(object):
         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)
index 5c7ec72b6cb6a29ee7f3ccbcd78e47b1673f3799..4b6b8d6be049c490a8b54998669079351854fda9 100644 (file)
@@ -43,16 +43,31 @@ class SchemaItem(object):
         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
@@ -204,10 +219,10 @@ class Table(SchemaItem, sql.TableClause):
 
     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):
@@ -396,6 +411,7 @@ class Column(SchemaItem, sql.ColumnClause):
         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
@@ -458,19 +474,22 @@ class Column(SchemaItem, sql.ColumnClause):
         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:
@@ -479,6 +498,12 @@ class Column(SchemaItem, sql.ColumnClause):
             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."""
@@ -730,7 +755,7 @@ class PrimaryKeyConstraint(Constraint):
         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):
index 3e1e95a2664ea0d25758d71be68ca2b76dcfc919..6438586f6b314fc2f3b38e29942745f759e7c280 100644 (file)
@@ -90,6 +90,35 @@ class QuoteTest(PersistTest):
         """
         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()