diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 0ebf3dff25c..d744a4bfad3 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -2240,6 +2240,14 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree) * the snapshot, rangetable, result-rel info, and external Param info. * They need their own copies of local state, including a tuple table, * es_param_exec_vals, etc. + * + * The ResultRelInfo array management is trickier than it looks. We + * create a fresh array for the child but copy all the content from the + * parent. This is because it's okay for the child to share any + * per-relation state the parent has already created --- but if the child + * sets up any ResultRelInfo fields, such as its own junkfilter, that + * state must *not* propagate back to the parent. (For one thing, the + * pointed-to data is in a memory context that won't last long enough.) */ estate->es_direction = ForwardScanDirection; estate->es_snapshot = parentestate->es_snapshot; @@ -2248,9 +2256,19 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree) estate->es_plannedstmt = parentestate->es_plannedstmt; estate->es_junkFilter = parentestate->es_junkFilter; estate->es_output_cid = parentestate->es_output_cid; - estate->es_result_relations = parentestate->es_result_relations; - estate->es_num_result_relations = parentestate->es_num_result_relations; - estate->es_result_relation_info = parentestate->es_result_relation_info; + if (parentestate->es_num_result_relations > 0) + { + int numResultRelations = parentestate->es_num_result_relations; + ResultRelInfo *resultRelInfos; + + resultRelInfos = (ResultRelInfo *) + palloc(numResultRelations * sizeof(ResultRelInfo)); + memcpy(resultRelInfos, parentestate->es_result_relations, + numResultRelations * sizeof(ResultRelInfo)); + estate->es_result_relations = resultRelInfos; + estate->es_num_result_relations = numResultRelations; + } + /* es_result_relation_info must NOT be copied */ /* es_trig_target_relations must NOT be copied */ estate->es_rowMarks = parentestate->es_rowMarks; estate->es_top_eflags = parentestate->es_top_eflags; diff --git a/src/test/isolation/expected/eval-plan-qual.out b/src/test/isolation/expected/eval-plan-qual.out index 0f6595fcb1b..433533e6117 100644 --- a/src/test/isolation/expected/eval-plan-qual.out +++ b/src/test/isolation/expected/eval-plan-qual.out @@ -72,3 +72,35 @@ c2 (0,1) 1 0 0 c3 (0,1) 2 0 0 c3 (0,4) 2 1 0 step c2: COMMIT; + +starting permutation: writep2 returningp1 c1 c2 +step writep2: UPDATE p SET b = -b WHERE a = 1 AND c = 0; +step returningp1: + WITH u AS ( UPDATE p SET b = b WHERE a > 0 RETURNING * ) + SELECT * FROM u; + +step c1: COMMIT; +step returningp1: <... completed> +a b c + +1 0 0 +1 0 1 +1 0 2 +1 -1 0 +1 1 1 +1 1 2 +1 -2 0 +1 2 1 +1 2 2 +1 -3 0 +2 0 0 +2 0 1 +2 0 2 +2 1 0 +2 1 1 +2 1 2 +2 2 0 +2 2 1 +2 2 2 +2 3 0 +step c2: COMMIT; diff --git a/src/test/isolation/specs/eval-plan-qual.spec b/src/test/isolation/specs/eval-plan-qual.spec index 876e5470dba..6fb24322863 100644 --- a/src/test/isolation/specs/eval-plan-qual.spec +++ b/src/test/isolation/specs/eval-plan-qual.spec @@ -39,11 +39,15 @@ step "upsert1" { INSERT INTO accounts SELECT 'savings', 500 WHERE NOT EXISTS (SELECT 1 FROM upsert); } -# tests with table p check inheritance cases, specifically a bug where -# nodeLockRows did the wrong thing when the first updated tuple was in -# a non-first child table + +# tests with table p check inheritance cases: +# readp1/writep1/readp2 tests a bug where nodeLockRows did the wrong thing +# when the first updated tuple was in a non-first child table. +# writep2/returningp1 tests a memory allocation issue + step "readp1" { SELECT tableoid::regclass, ctid, * FROM p WHERE b IN (0, 1) AND c = 0 FOR UPDATE; } step "writep1" { UPDATE p SET b = -1 WHERE a = 1 AND b = 1 AND c = 0; } +step "writep2" { UPDATE p SET b = -b WHERE a = 1 AND c = 0; } step "c1" { COMMIT; } session "s2" @@ -59,6 +63,10 @@ step "upsert2" { WHERE NOT EXISTS (SELECT 1 FROM upsert); } step "readp2" { SELECT tableoid::regclass, ctid, * FROM p WHERE b IN (0, 1) AND c = 0 FOR UPDATE; } +step "returningp1" { + WITH u AS ( UPDATE p SET b = b WHERE a > 0 RETURNING * ) + SELECT * FROM u; +} step "c2" { COMMIT; } session "s3" @@ -70,3 +78,4 @@ permutation "wx1" "wx2" "c1" "c2" "read" permutation "wy1" "wy2" "c1" "c2" "read" permutation "upsert1" "upsert2" "c1" "c2" "read" permutation "readp1" "writep1" "readp2" "c1" "c2" +permutation "writep2" "returningp1" "c1" "c2"