diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index db8db75b6e383c8aaabd1b3a83947ef0d8199371..84ce6b3125493e628044432baa33efb43f590e7a 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -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
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 8c47babb6dcc9951db4243065e61712eff563efb..044881af711ff50186a69715b16334b1832d4420 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -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;
diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql
index 53276e4d673b96b0e184439101584bbe5da2b00d..85c9d04d64f11ba234f0a20d6d9bb39c1f32ec01 100644
--- a/src/test/regress/sql/foreign_key.sql
+++ b/src/test/regress/sql/foreign_key.sql
@@ -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;