]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- [feature] An explicit error is raised when
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Sep 2012 16:42:38 +0000 (12:42 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 23 Sep 2012 16:42:38 +0000 (12:42 -0400)
    a ForeignKeyConstraint() that was
    constructed to refer to multiple remote tables
    is first used. [ticket:2455]

CHANGES
lib/sqlalchemy/schema.py
test/sql/test_metadata.py

diff --git a/CHANGES b/CHANGES
index 8d5cf36cede061e97855a3a88b3fa4ebd8952602..be72bcab758b52884595329fa052ea66b3261ac8 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -587,6 +587,11 @@ underneath "0.7.xx".
     column object itself, consistent with the behavior
     of label(column, None).  [ticket:2168]
 
+  - [feature] An explicit error is raised when
+    a ForeignKeyConstraint() that was
+    constructed to refer to multiple remote tables
+    is first used. [ticket:2455]
+
 - sqlite
   - [feature] the SQLite date and time types
     have been overhauled to support a more open
index 1c535461a930c15100a4b5830ce8030d22bf8866..fdff37d3fe9f7db95e7700ee1fc72dc4b7e2a34e 100644 (file)
@@ -1391,10 +1391,10 @@ class ForeignKey(SchemaItem):
                 schema = parenttable.metadata.schema
 
             if (len(m) == 1):
-                tname   = m.pop()
+                tname = m.pop()
             else:
                 colname = m.pop()
-                tname   = m.pop()
+                tname = m.pop()
 
             if (len(m) > 0):
                 schema = '.'.join(m)
@@ -1403,11 +1403,24 @@ class ForeignKey(SchemaItem):
                 raise exc.NoReferencedTableError(
                     "Foreign key associated with column '%s' could not find "
                     "table '%s' with which to generate a "
-                    "foreign key to target column '%s'" % (self.parent, tname, colname),
+                    "foreign key to target column '%s'" %
+                    (self.parent, tname, colname),
                     tname)
             table = Table(tname, parenttable.metadata,
                           mustexist=True, schema=schema)
 
+            if not hasattr(self.constraint, '_referred_table'):
+                self.constraint._referred_table = table
+            elif self.constraint._referred_table is not table:
+                raise exc.ArgumentError(
+                    'ForeignKeyConstraint on %s(%s) refers to '
+                    'multiple remote tables: %s and %s' % (
+                    parenttable,
+                    self.constraint._col_description,
+                    self.constraint._referred_table,
+                    table
+                ))
+
             _column = None
             if colname is None:
                 # colname is None in the case that ForeignKey argument
@@ -2167,6 +2180,10 @@ class ForeignKeyConstraint(Constraint):
             self._set_parent_with_dispatch(columns[0].table)
 
 
+    @property
+    def _col_description(self):
+        return ", ".join(self._elements)
+
     @property
     def columns(self):
         return self._elements.keys()
@@ -2177,6 +2194,7 @@ class ForeignKeyConstraint(Constraint):
 
     def _set_parent(self, table):
         super(ForeignKeyConstraint, self)._set_parent(table)
+
         for col, fk in self._elements.iteritems():
             # string-specified column names now get
             # resolved to Column objects
index 3891f58c69552a835c25360e956105cd1e71c5d3..c7321d7d96a3b3b8f6117653fdf27df97647a5df 100644 (file)
@@ -1057,6 +1057,34 @@ class ConstraintTest(fixtures.TestBase):
         assert s1.c.a.references(t1.c.a)
         assert not s1.c.a.references(t1.c.b)
 
+    def test_invalid_composite_fk_check(self):
+        m = MetaData()
+        t1 = Table('t1', m, Column('x', Integer), Column('y', Integer),
+            ForeignKeyConstraint(['x', 'y'], ['t2.x', 't3.y'])
+        )
+        t2 = Table('t2', m, Column('x', Integer))
+        t3 = Table('t3', m, Column('y', Integer))
+
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            t1.join, t2
+        )
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            t1.join, t3
+        )
+
+        assert_raises_message(
+            exc.ArgumentError,
+            r"ForeignKeyConstraint on t1\(x, y\) refers to "
+                "multiple remote tables: t2 and t3",
+            schema.CreateTable(t1).compile
+        )
+
 class ColumnDefinitionTest(AssertsCompiledSQL, fixtures.TestBase):
     """Test Column() construction."""