diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-07 09:40:26 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2022-06-07 12:34:47 -0400 |
| commit | 93bc7ed534f12934528c0cbf5489417ddc025e40 (patch) | |
| tree | b56bb45f62a25adf7f7305de9a0cd39c3098fe61 /test/sql | |
| parent | 938c5d1033085289b4cbbd4b9229eaa3ad90b66d (diff) | |
| download | sqlalchemy-93bc7ed534f12934528c0cbf5489417ddc025e40.tar.gz | |
graceful degrade for FKs not reflectable
Fixed bugs involving the :paramref:`.Table.include_columns` and the
:paramref:`.Table.resolve_fks` parameters on :class:`.Table`; these
little-used parameters were apparently not working for columns that refer
to foreign key constraints.
In the first case, not-included columns that refer to foreign keys would
still attempt to create a :class:`.ForeignKey` object, producing errors
when attempting to resolve the columns for the foreign key constraint
within reflection; foreign key constraints that refer to skipped columns
are now omitted from the table reflection process in the same way as
occurs for :class:`.Index` and :class:`.UniqueConstraint` objects with the
same conditions. No warning is produced however, as we likely want to
remove the include_columns warnings for all constraints in 2.0.
In the latter case, the production of table aliases or subqueries would
fail on an FK related table not found despite the presence of
``resolve_fks=False``; the logic has been repaired so that if a related
table is not found, the :class:`.ForeignKey` object is still proxied to the
aliased table or subquery (these :class:`.ForeignKey` objects are normally
used in the production of join conditions), but it is sent with a flag that
it's not resolvable. The aliased table / subquery will then work normally,
with the exception that it cannot be used to generate a join condition
automatically, as the foreign key information is missing. This was already
the behavior for such foreign key constraints produced using non-reflection
methods, such as joining :class:`.Table` objects from different
:class:`.MetaData` collections.
Fixes: #8100
Fixes: #8101
Change-Id: Ifa37a91bd1f1785fca85ef163eec031660d9ea4d
Diffstat (limited to 'test/sql')
| -rw-r--r-- | test/sql/test_selectable.py | 62 |
1 files changed, 54 insertions, 8 deletions
diff --git a/test/sql/test_selectable.py b/test/sql/test_selectable.py index d05d7ad8b..3ecbfca27 100644 --- a/test/sql/test_selectable.py +++ b/test/sql/test_selectable.py @@ -1478,21 +1478,67 @@ class SelectableTest( assert j4.corresponding_column(j2.c.aid) is j4.c.aid assert j4.corresponding_column(a.c.id) is j4.c.id - def test_two_metadata_join_raises(self): + @testing.combinations(True, False) + def test_two_metadata_join_raises(self, include_a_joining_table): + """test case from 2008 enhanced as of #8101, more specific failure + modes for non-resolvable FKs + + """ m = MetaData() m2 = MetaData() t1 = Table("t1", m, Column("id", Integer), Column("id2", Integer)) - t2 = Table("t2", m, Column("id", Integer, ForeignKey("t1.id"))) + + if include_a_joining_table: + t2 = Table("t2", m, Column("id", Integer, ForeignKey("t1.id"))) + t3 = Table("t3", m2, Column("id", Integer, ForeignKey("t1.id2"))) - s = ( - select(t2, t3) - .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) - .subquery() - ) + with expect_raises_message( + exc.NoReferencedTableError, + "Foreign key associated with column 't3.id'", + ): + t3.join(t1) - assert_raises(exc.NoReferencedTableError, s.join, t1) + if include_a_joining_table: + s = ( + select(t2, t3) + .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) + .subquery() + ) + else: + s = ( + select(t3) + .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) + .subquery() + ) + + with expect_raises_message( + exc.NoReferencedTableError, + "Foreign key associated with column 'anon_1.t3_id' could not " + "find table 't1' with which to generate a foreign key to target " + "column 'id2'", + ): + select(s.join(t1)), + + # manual join is OK. using select().join() here is also exercising + # that join() does not need to resolve FKs if we provided the + # ON clause + if include_a_joining_table: + self.assert_compile( + select(s).join( + t1, and_(s.c.t2_id == t1.c.id, s.c.t3_id == t1.c.id) + ), + "SELECT anon_1.t2_id, anon_1.t3_id FROM (SELECT " + "t2.id AS t2_id, t3.id AS t3_id FROM t2, t3) AS anon_1 " + "JOIN t1 ON anon_1.t2_id = t1.id AND anon_1.t3_id = t1.id", + ) + else: + self.assert_compile( + select(s).join(t1, s.c.t3_id == t1.c.id), + "SELECT anon_1.t3_id FROM (SELECT t3.id AS t3_id FROM t3) " + "AS anon_1 JOIN t1 ON anon_1.t3_id = t1.id", + ) def test_multi_label_chain_naming_col(self): # See [ticket:2167] for this one. |
