Fix match_foreign_keys_to_quals for FKs linking to unused rtable entries.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 29 Jun 2016 20:02:08 +0000 (16:02 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 29 Jun 2016 20:02:08 +0000 (16:02 -0400)
Since get_relation_foreign_keys doesn't try to determine whether RTEs
are actually part of the query semantics, it might make FK info records
linking to RTEs that won't have a RelOptInfo at all.  Cope with that.
Per bug #14219 from Andrew Gierth.

Report: <20160629183338.1397.43514@wrigleys.postgresql.org>

src/backend/optimizer/plan/initsplan.c
src/test/regress/expected/foreign_key.out
src/test/regress/sql/foreign_key.sql

index db8db75b6e383c8aaabd1b3a83947ef0d8199371..84ce6b3125493e628044432baa33efb43f590e7a 100644 (file)
@@ -2329,10 +2329,25 @@ match_foreign_keys_to_quals(PlannerInfo *root)
        foreach(lc, root->fkey_list)
        {
                ForeignKeyOptInfo *fkinfo = (ForeignKeyOptInfo *) lfirst(lc);
-               RelOptInfo *con_rel = find_base_rel(root, fkinfo->con_relid);
-               RelOptInfo *ref_rel = find_base_rel(root, fkinfo->ref_relid);
+               RelOptInfo *con_rel;
+               RelOptInfo *ref_rel;
                int                     colno;
 
+               /*
+                * Either relid might identify a rel that is in the query's rtable but
+                * isn't referenced by the jointree so won't have a RelOptInfo.  Hence
+                * don't use find_base_rel() here.  We can ignore such FKs.
+                */
+               if (fkinfo->con_relid >= root->simple_rel_array_size ||
+                       fkinfo->ref_relid >= root->simple_rel_array_size)
+                       continue;                       /* just paranoia */
+               con_rel = root->simple_rel_array[fkinfo->con_relid];
+               if (con_rel == NULL)
+                       continue;
+               ref_rel = root->simple_rel_array[fkinfo->ref_relid];
+               if (ref_rel == NULL)
+                       continue;
+
                /*
                 * Ignore FK unless both rels are baserels.  This gets rid of FKs that
                 * link to inheritance child rels (otherrels) and those that link to
index 8c47babb6dcc9951db4243065e61712eff563efb..044881af711ff50186a69715b16334b1832d4420 100644 (file)
@@ -1359,3 +1359,25 @@ update pp set f1=f1+1; -- fail
 ERROR:  update or delete on table "pp" violates foreign key constraint "cc_f1_fkey" on table "cc"
 DETAIL:  Key (f1)=(13) is still referenced from table "cc".
 drop table pp, cc;
+--
+-- Test interaction of foreign-key optimization with rules (bug #14219)
+--
+create temp table t1 (a integer primary key, b text);
+create temp table t2 (a integer primary key, b integer references t1);
+create rule r1 as on delete to t1 do delete from t2 where t2.b = old.a;
+explain (costs off) delete from t1 where a = 1;
+                 QUERY PLAN                 
+--------------------------------------------
+ Delete on t2
+   ->  Nested Loop
+         ->  Index Scan using t1_pkey on t1
+               Index Cond: (a = 1)
+         ->  Seq Scan on t2
+               Filter: (b = 1)
+ Delete on t1
+   ->  Index Scan using t1_pkey on t1
+         Index Cond: (a = 1)
+(10 rows)
+
+delete from t1 where a = 1;
index 53276e4d673b96b0e184439101584bbe5da2b00d..85c9d04d64f11ba234f0a20d6d9bb39c1f32ec01 100644 (file)
@@ -1009,3 +1009,13 @@ update pp set f1=f1+1;
 insert into cc values(13);
 update pp set f1=f1+1; -- fail
 drop table pp, cc;
+
+--
+-- Test interaction of foreign-key optimization with rules (bug #14219)
+--
+create temp table t1 (a integer primary key, b text);
+create temp table t2 (a integer primary key, b integer references t1);
+create rule r1 as on delete to t1 do delete from t2 where t2.b = old.a;
+
+explain (costs off) delete from t1 where a = 1;
+delete from t1 where a = 1;