Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Defend against unsupported partition relkind in logical replication worker.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 2 Nov 2022 16:29:39 +0000 (12:29 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
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.camel@gmail.com

src/backend/replication/logical/worker.c

index 6c97ef02809090b080f108ea0691bb3eb4b2079e..1825daf903e7a3e3ed0a15c7e3d4427eca98f0e1 100644 (file)
@@ -2151,6 +2151,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
@@ -2204,6 +2213,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. */
@@ -2289,7 +2299,6 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
                        slot_getallattrs(remoteslot);
                    }
 
-
                    /* Find the new partition. */
                    oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
                    partrelinfo_new = ExecFindPartition(mtstate, relinfo,
@@ -2297,6 +2306,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. */
                    apply_handle_delete_internal(edata, partrelinfo,
@@ -2309,10 +2324,9 @@ apply_handle_tuple_routing(ApplyExecutionData *edata,
                     * partition rowtype.
                     */
                    oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
-                   partrel = partrelinfo_new->ri_RelationDesc;
                    remoteslot_part = partrelinfo_new->ri_PartitionTupleSlot;
                    if (remoteslot_part == NULL)
-                       remoteslot_part = table_slot_create(partrel,
+                       remoteslot_part = table_slot_create(partrel_new,
                                                            &estate->es_tupleTable);
                    map = partrelinfo_new->ri_RootToPartitionMap;
                    if (map != NULL)