summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2017-05-25 16:24:25 -0400
committerGerrit Code Review <gerrit@awstats.zzzcomputing.com>2017-05-25 16:24:25 -0400
commit915012950c26ca5ed68917088761b73047691113 (patch)
treedbd37503841a00a64841d2c8b7e73432ed6bf8d0
parentf8a3f14e4f862a4bf0be591699f1f72815c72514 (diff)
parenta78718b9340e9840a470300932af178ce57c0f7d (diff)
downloadsqlalchemy-915012950c26ca5ed68917088761b73047691113.tar.gz
Merge "Raise if ForeignKeyConstraint created with different numbers of local and remote columns."
-rw-r--r--doc/build/changelog/changelog_11.rst12
-rw-r--r--lib/sqlalchemy/sql/schema.py16
-rw-r--r--test/sql/test_metadata.py23
3 files changed, 51 insertions, 0 deletions
diff --git a/doc/build/changelog/changelog_11.rst b/doc/build/changelog/changelog_11.rst
index 89d42f473..66f6bb45a 100644
--- a/doc/build/changelog/changelog_11.rst
+++ b/doc/build/changelog/changelog_11.rst
@@ -67,6 +67,18 @@
fail for cx_Oracle version 6.0b1 due to the "b" character. Version
string parsing is now via a regexp rather than a simple split.
+ .. change:: 3949
+ :tags: bug, schema
+ :versions: 1.2.0b1
+ :tickets: 3949
+
+ An :class:`.ArgumentError` is now raised if a
+ :class:`.ForeignKeyConstraint` object is created with a mismatched
+ number of "local" and "remote" columns, which otherwise causes the
+ internal state of the constraint to be incorrect. Note that this
+ also impacts the condition where a dialect's reflection process
+ produces a mismatched set of columns for a foreign key constraint.
+
.. change:: 3980
:tags: bug, ext
:versions: 1.2.0b1
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index a9aee5883..4eb095196 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -2883,6 +2883,22 @@ class ForeignKeyConstraint(ColumnCollectionConstraint):
self.use_alter = use_alter
self.match = match
+ if len(set(columns)) != len(refcolumns):
+ if len(set(columns)) != len(columns):
+ # e.g. FOREIGN KEY (a, a) REFERENCES r (b, c)
+ raise exc.ArgumentError(
+ "ForeignKeyConstraint with duplicate source column "
+ "references are not supported."
+ )
+ else:
+ # e.g. FOREIGN KEY (a) REFERENCES r (b, c)
+ # paraphrasing https://www.postgresql.org/docs/9.2/static/\
+ # ddl-constraints.html
+ raise exc.ArgumentError(
+ "ForeignKeyConstraint number "
+ "of constrained columns must match the number of "
+ "referenced columns.")
+
# standalone ForeignKeyConstraint - create
# associated ForeignKey objects which will be applied to hosted
# Column objects (in col.foreign_keys), either now or when attached
diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py
index 6d0df3b5f..61fbbc57b 100644
--- a/test/sql/test_metadata.py
+++ b/test/sql/test_metadata.py
@@ -348,6 +348,29 @@ class MetaDataTest(fixtures.TestBase, ComparesTables):
getattr, list(a.foreign_keys)[0], "column"
)
+ def test_fk_mismatched_local_remote_cols(self):
+
+ assert_raises_message(
+ exc.ArgumentError,
+ "ForeignKeyConstraint number of constrained columns must "
+ "match the number of referenced columns.",
+ ForeignKeyConstraint, ['a'], ['b.a', 'b.b']
+ )
+
+ assert_raises_message(
+ exc.ArgumentError,
+ "ForeignKeyConstraint number of constrained columns "
+ "must match the number of referenced columns.",
+ ForeignKeyConstraint, ['a', 'b'], ['b.a']
+ )
+
+ assert_raises_message(
+ exc.ArgumentError,
+ "ForeignKeyConstraint with duplicate source column "
+ "references are not supported.",
+ ForeignKeyConstraint, ['a', 'a'], ['b.a', 'b.b']
+ )
+
def test_pickle_metadata_sequence_restated(self):
m1 = MetaData()
Table('a', m1,