From 2e3326929b0ba9f421f2ab1270c57b294c208a99 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 17 Oct 2022 11:35:23 -0400 Subject: [PATCH] Guard against table-AM-less relations in planner. The executor will dump core if it's asked to execute a seqscan on a relation having no table AM, such as a view. While that shouldn't really happen, it's possible to get there via catalog corruption, such as a missing ON SELECT rule. It seems worth installing a defense against that. There are multiple plausible places for such a defense, but I picked the planner's get_relation_info(). Per discussion of bug #17646 from Kui Liu. Back-patch to v12 where the tableam APIs were introduced; in older versions you won't get a SIGSEGV, so it seems less pressing. Discussion: https://postgr.es/m/17646-70c93cfa40365776@postgresql.org --- src/backend/optimizer/util/plancat.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 5012bfe142..c328a2f912 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -126,6 +126,24 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, */ relation = table_open(relationObjectId, NoLock); + /* + * Relations without a table AM can be used in a query only if they are of + * special-cased relkinds. This check prevents us from crashing later if, + * for example, a view's ON SELECT rule has gone missing. Note that + * table_open() already rejected indexes and composite types; spell the + * error the same way it does. + */ + if (!relation->rd_tableam) + { + if (!(relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE || + relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot open relation \"%s\"", + RelationGetRelationName(relation)), + errdetail_relkind_not_supported(relation->rd_rel->relkind))); + } + /* Temporary and unlogged relations are inaccessible during recovery. */ if (!RelationIsPermanent(relation) && RecoveryInProgress()) ereport(ERROR,