I've run into what seems to be a bug in ExecInsert() that causes a crash
when inserting multiple rows into a partitioned table that each go into
different partitions with different tuple descriptors.  Crash occurs if
ExecInsert() returns without resetting estate->es_result_relation_info
back to the root table's resultRelInfo.  For example, if a BR trigger on a
partition returns NULL for a row.

Crashing example:

create table p (a int, b text) partition by list (a);
create table p12 (b text, a int);

-- p12 has different tuple descriptor than p
alter table p attach partition p12 for values in (1, 2);

create table p4 partition of p for values in (4);

create function blackhole () returns trigger as $$ begin return NULL; end;
$$ language plpgsql;
create trigger blackhole before insert on p12 for each row execute
procedure blackhole();

insert into p values (1, 'b'), (4, 'a');
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.

Crash is caused because we enter into ExecFindPartition with p12's
resultRelInfo as if it correponds to the root table.  That happens because
we didn't reset estate->es_result_relation_info, which had been set to
p12's resultRelInfo to point back to the original resultRelInfo (that is,
p's) before returning like below:

       slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);

        if (slot == NULL)       /* "do nothing" */
            return NULL;

There are other places where we prematurely return like that.

Attached a patch to fix that, which would need to be back-patched to 10.

Thanks,
Amit
diff --git a/src/backend/executor/nodeModifyTable.c 
b/src/backend/executor/nodeModifyTable.c
index c32928d9bd..0d6326d1de 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -281,7 +281,7 @@ ExecInsert(ModifyTableState *mtstate,
        /*
         * get information on the (current) result relation
         */
-       resultRelInfo = estate->es_result_relation_info;
+       saved_resultRelInfo = resultRelInfo = estate->es_result_relation_info;
 
        /* Determine the partition to heap_insert the tuple into */
        if (mtstate->mt_partition_tuple_routing)
@@ -408,7 +408,10 @@ ExecInsert(ModifyTableState *mtstate,
                slot = ExecBRInsertTriggers(estate, resultRelInfo, slot);
 
                if (slot == NULL)               /* "do nothing" */
-                       return NULL;
+               {
+                       result = NULL;
+                       goto restore_estate_result_rel;
+               }
 
                /* trigger might have changed tuple */
                tuple = ExecMaterializeSlot(slot);
@@ -421,7 +424,10 @@ ExecInsert(ModifyTableState *mtstate,
                slot = ExecIRInsertTriggers(estate, resultRelInfo, slot);
 
                if (slot == NULL)               /* "do nothing" */
-                       return NULL;
+               {
+                       result = NULL;
+                       goto restore_estate_result_rel;
+               }
 
                /* trigger might have changed tuple */
                tuple = ExecMaterializeSlot(slot);
@@ -439,7 +445,10 @@ ExecInsert(ModifyTableState *mtstate,
                                                                                
                                           planSlot);
 
                if (slot == NULL)               /* "do nothing" */
-                       return NULL;
+               {
+                       result = NULL;
+                       goto restore_estate_result_rel;
+               }
 
                /* FDW might have changed tuple */
                tuple = ExecMaterializeSlot(slot);
@@ -495,7 +504,7 @@ ExecInsert(ModifyTableState *mtstate,
                 * No need though if the tuple has been routed, and a BR trigger
                 * doesn't exist.
                 */
-               if (saved_resultRelInfo != NULL &&
+               if (saved_resultRelInfo != resultRelInfo &&
                        !(resultRelInfo->ri_TrigDesc &&
                          resultRelInfo->ri_TrigDesc->trig_insert_before_row))
                        check_partition_constr = false;
@@ -544,7 +553,8 @@ ExecInsert(ModifyTableState *mtstate,
                                                                                
         estate, canSetTag, &returning))
                                        {
                                                
InstrCountFiltered2(&mtstate->ps, 1);
-                                               return returning;
+                                               result = returning;
+                                               goto restore_estate_result_rel;
                                        }
                                        else
                                                goto vlock;
@@ -559,7 +569,8 @@ ExecInsert(ModifyTableState *mtstate,
                                        Assert(onconflict == 
ONCONFLICT_NOTHING);
                                        ExecCheckTIDVisible(estate, 
resultRelInfo, &conflictTid);
                                        InstrCountFiltered2(&mtstate->ps, 1);
-                                       return NULL;
+                                       result = NULL;
+                                       goto restore_estate_result_rel;
                                }
                        }
 
@@ -686,6 +697,7 @@ ExecInsert(ModifyTableState *mtstate,
        if (resultRelInfo->ri_projectReturning)
                result = ExecProcessReturning(resultRelInfo, slot, planSlot);
 
+restore_estate_result_rel:
        if (saved_resultRelInfo)
                estate->es_result_relation_info = saved_resultRelInfo;
 

Reply via email to