Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Guard against table-AM-less relations in planner.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 17 Oct 2022 15:35:23 +0000 (11:35 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 17 Oct 2022 15:35:23 +0000 (11:35 -0400)
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

index bdb2f164295ccc4de8986491c1c7dacfd3f6bce3..d163cc45c4b89995567fa5c7094a8eae6f7c87bc 100644 (file)
@@ -123,6 +123,22 @@ 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.
+    */
+   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))));
+   }
+
    /* Temporary and unlogged relations are inaccessible during recovery. */
    if (!RelationNeedsWAL(relation) && RecoveryInProgress())
        ereport(ERROR,