Defend against unsupported partition relkind in logical replication worker.
authorTom Lane <[email protected]>
Wed, 2 Nov 2022 16:29:39 +0000 (12:29 -0400)
committerTom Lane <[email protected]>
Wed, 2 Nov 2022 16:29:39 +0000 (12:29 -0400)
Since partitions can be foreign tables not only plain tables, but
logical replication only supports plain tables, we'd better check the
relkind of a partition after we find it.  (There was some discussion
of checking this when adding a partitioned table to a subscription;
but that would be inadequate since the troublesome partition could be
added later.)  Without this, the situation leads to a segfault or
assertion failure.

In passing, add a separate variable for the target Relation of
a cross-partition UPDATE; reusing partrel seemed mighty confusing
and error-prone.

Shi Yu and Tom Lane, per report from Ilya Gladyshev.  Back-patch
to v13 where logical replication into partitioned tables became
a thing.

Discussion: https://postgr.es/m/6b93e3748ba43298694f376ca8797279d7945e29[email protected]

src/backend/replication/logical/worker.c

index 4c4165da1eb6413e5b723acca5cf5b2700a5d76d..0cd61d04a806d52af80df6e0417bd061fabb1b30 100644 (file)
@@ -1095,6 +1095,15 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
    Assert(partrelinfo != NULL);
    partrel = partrelinfo->ri_RelationDesc;
 
+   /*
+    * Check for supported relkind.  We need this since partitions might be of
+    * unsupported relkinds; and the set of partitions can change, so checking
+    * at CREATE/ALTER SUBSCRIPTION would be insufficient.
+    */
+   CheckSubscriptionRelkind(partrel->rd_rel->relkind,
+                            get_namespace_name(RelationGetNamespace(partrel)),
+                            RelationGetRelationName(partrel));
+
    /*
     * To perform any of the operations below, the tuple must match the
     * partition's rowtype. Convert if needed or just copy, using a dedicated
@@ -1151,6 +1160,7 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
            {
                TupleTableSlot *localslot;
                ResultRelInfo *partrelinfo_new;
+               Relation    partrel_new;
                bool        found;
 
                /* Get the matching local tuple from the partition. */
@@ -1235,7 +1245,6 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
                        slot_getallattrs(remoteslot);
                    }
 
-
                    /* Find the new partition. */
                    oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
                    partrelinfo_new = ExecFindPartition(mtstate, relinfo,
@@ -1243,6 +1252,12 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
                                                        estate);
                    MemoryContextSwitchTo(oldctx);
                    Assert(partrelinfo_new != partrelinfo);
+                   partrel_new = partrelinfo_new->ri_RelationDesc;
+
+                   /* Check that new partition also has supported relkind. */
+                   CheckSubscriptionRelkind(partrel_new->rd_rel->relkind,
+                                            get_namespace_name(RelationGetNamespace(partrel_new)),
+                                            RelationGetRelationName(partrel_new));
 
                    /* DELETE old tuple found in the old partition. */
                    estate->es_result_relation_info = partrelinfo;
@@ -1257,11 +1272,10 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
                     * partition rowtype.
                     */
                    oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
-                   partrel = partrelinfo_new->ri_RelationDesc;
                    partinfo = partrelinfo_new->ri_PartitionInfo;
                    remoteslot_part = partinfo->pi_PartitionTupleSlot;
                    if (remoteslot_part == NULL)
-                       remoteslot_part = table_slot_create(partrel,
+                       remoteslot_part = table_slot_create(partrel_new,
                                                            &estate->es_tupleTable);
                    map = partinfo->pi_RootToPartitionMap;
                    if (map != NULL)