On Sun, Dec 3, 2023 at 2:11 AM Paul Jungwirth <p...@illuminatedcomputing.com> wrote: > > v19 patch series attached, rebased to a11c9c42ea. >
this TODO: * TODO: It sounds like FOR PORTION OF might need to do something here too? based on comments on ExprContext. I refactor a bit, and solved this TODO. tring to the following TODO: // TODO: Need to save context->mtstate->mt_transition_capture? (See comment on ExecInsert) but failed. I also attached the trial, and also added the related test. You can also use the test to check portion update with insert trigger with "referencing old table as old_table new table as new_table" situation.
From 8c97db9391900d766db99834c23c5e4b60cadf01 Mon Sep 17 00:00:00 2001 From: pgaddict <jian.universal...@gmail.com> Date: Tue, 5 Dec 2023 17:59:43 +0800 Subject: [PATCH v1 1/1] set eval targetrange in per-output-tuple exprcontext --- src/backend/executor/nodeModifyTable.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 88eda012..fe57e592 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -3780,7 +3780,6 @@ ExecModifyTable(PlanState *pstate) * Reset per-tuple memory context used for processing on conflict and * returning clauses, to free any expression evaluation storage * allocated in the previous cycle. - * TODO: It sounds like FOR PORTION OF might need to do something here too? */ if (pstate->ps_ExprContext) ResetExprContext(pstate->ps_ExprContext); @@ -4472,9 +4471,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) forPortionOf = (ForPortionOfExpr *) node->forPortionOf; /* Eval the FOR PORTION OF target */ - if (mtstate->ps.ps_ExprContext == NULL) - ExecAssignExprContext(estate, &mtstate->ps); - econtext = mtstate->ps.ps_ExprContext; + econtext = GetPerTupleExprContext(estate); exprState = ExecPrepareExpr((Expr *) forPortionOf->targetRange, estate); targetRange = ExecEvalExpr(exprState, econtext, &isNull); -- 2.34.1
From cda2f8324ef920c807008e2763f97c6503c17a94 Mon Sep 17 00:00:00 2001 From: pgaddict <jian.universal...@gmail.com> Date: Wed, 6 Dec 2023 20:58:08 +0800 Subject: [PATCH v1 1/1] trying to save mt_transition_capture while ExecInsert --- src/backend/executor/nodeModifyTable.c | 21 ++++- src/test/regress/expected/for_portion_of.out | 80 ++++++++++++++++++++ src/test/regress/sql/for_portion_of.sql | 72 ++++++++++++++++++ 3 files changed, 171 insertions(+), 2 deletions(-) diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index fe57e592..c9d14dab 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -167,7 +167,8 @@ static bool ExecMergeMatched(ModifyTableContext *context, static void ExecMergeNotMatched(ModifyTableContext *context, ResultRelInfo *resultRelInfo, bool canSetTag); - +static void +ExecSetupTransitionCaptureState(ModifyTableState *mtstate, EState *estate); /* * Verify that the tuples to be produced by INSERT match the @@ -1259,7 +1260,7 @@ ExecForPortionOfLeftovers(ModifyTableContext *context, TupleTableSlot *leftoverTuple2 = fpoState->fp_Leftover2; HeapTuple oldtuple = NULL; bool shouldFree = false; - + int i; /* original cmd type */ /* * Get the range of the old pre-UPDATE/DELETE tuple, * so we can intersect it with the FOR PORTION OF target @@ -1312,6 +1313,7 @@ ExecForPortionOfLeftovers(ModifyTableContext *context, * TODO: Really? What if you update the partition key? */ + i = context->mtstate->operation; if (!RangeIsEmpty(leftoverRangeType1)) { oldtuple = ExecFetchSlotHeapTuple(oldtupleSlot, false, &shouldFree); @@ -1320,6 +1322,10 @@ ExecForPortionOfLeftovers(ModifyTableContext *context, set_leftover_tuple_bounds(leftoverTuple1, forPortionOf, typcache, leftoverRangeType1); ExecMaterializeSlot(leftoverTuple1); + context->mtstate->operation = CMD_INSERT; + context->mtstate->mt_transition_capture = NULL; + ExecSetupTransitionCaptureState(context->mtstate, estate); + // TODO: Need to save context->mtstate->mt_transition_capture? (See comment on ExecInsert) ExecInsert(context, resultRelInfo, leftoverTuple1, node->canSetTag, NULL, NULL); } @@ -1340,10 +1346,21 @@ ExecForPortionOfLeftovers(ModifyTableContext *context, set_leftover_tuple_bounds(leftoverTuple2, forPortionOf, typcache, leftoverRangeType2); ExecMaterializeSlot(leftoverTuple2); + context->mtstate->operation = CMD_INSERT; + context->mtstate->mt_transition_capture = NULL; + ExecSetupTransitionCaptureState(context->mtstate, estate); + // TODO: Need to save context->mtstate->mt_transition_capture? (See comment on ExecInsert) ExecInsert(context, resultRelInfo, leftoverTuple2, node->canSetTag, NULL, NULL); } + if (!RangeIsEmpty(leftoverRangeType2) || !RangeIsEmpty(leftoverRangeType1)) + { + /* if any of the above branch executed then */ + context->mtstate->operation = i; + context->mtstate->mt_transition_capture = NULL; + ExecSetupTransitionCaptureState(context->mtstate, estate); + } if (shouldFree) heap_freetuple(oldtuple); } diff --git a/src/test/regress/expected/for_portion_of.out b/src/test/regress/expected/for_portion_of.out index 64f7bc7e..04202aa0 100644 --- a/src/test/regress/expected/for_portion_of.out +++ b/src/test/regress/expected/for_portion_of.out @@ -441,3 +441,83 @@ SELECT * FROM for_portion_of_test2 ORDER BY id, valid_at; DROP TABLE for_portion_of_test2; DROP TYPE mydaterange; +DROP TABLE IF EXISTS for_portion_of_test; +CREATE TABLE for_portion_of_test (id int4range,valid_at daterange, name text); +INSERT INTO for_portion_of_test VALUES ('[1,1]', '[2018-01-01,2020-01-01)', 'one'); +create or replace function dump_trigger() returns trigger language plpgsql as +$$ + BEGIN + raise notice 'TG_OP = %', TG_OP; + IF TG_OP = 'INSERT' then + raise notice 'trigger = %, TG_LEVEL %, new table = %', + TG_NAME, TG_LEVEL, + (select string_agg(new_table::text, ', ' order by id) from new_table); + ELSIF TG_OP = 'UPDATE' then + raise notice 'trigger = %, TG_LEVEL %, old table = %, new table = %', + TG_NAME, TG_LEVEL, + (select string_agg(old_table::text, ', ' order by id) from old_table), + (select string_agg(new_table::text, ', ' order by id) from new_table); + ELSIF TG_OP = 'DELETE' then + raise notice 'trigger = %,TG_LEVEL %, old table = %', + TG_NAME, TG_LEVEL, + (select string_agg(old_table::text, ', ' order by id) from old_table); + END IF; + RETURN NULL; + END; +$$; +create or replace trigger for_portion_of_test_insert_trig +after insert on for_portion_of_test +referencing new table as new_table +for each row execute procedure dump_trigger(); +create or replace trigger for_portion_of_test_insert_trig_stmt +after insert on for_portion_of_test +referencing new table as new_table +for each statement execute procedure dump_trigger(); +create or replace trigger for_portion_of_test_update_trig +after update on for_portion_of_test +referencing old table as old_table new table as new_table +for each row execute procedure dump_trigger(); +create or replace trigger for_portion_of_test_update_trig_stmt +after update on for_portion_of_test +referencing old table as old_table new table as new_table +for each statement execute procedure dump_trigger(); +create or replace trigger for_portion_of_test_del_trig +after delete on for_portion_of_test +referencing old table as old_table +for each row execute procedure dump_trigger(); +create or replace trigger for_portion_of_test_del_trig_stmt +after delete on for_portion_of_test +referencing old table as old_table +for each statement execute procedure dump_trigger(); +begin; +UPDATE for_portion_of_test FOR PORTION OF valid_at + FROM '2018-01-15' TO '2019-01-01' SET name = '2018-01-15_to_2019-01-01'; +NOTICE: TG_OP = INSERT +NOTICE: trigger = for_portion_of_test_insert_trig, TG_LEVEL ROW, new table = ("[1,2)","[01-01-2018,01-15-2018)",one), ("[1,2)","[01-01-2019,01-01-2020)",one) +NOTICE: TG_OP = INSERT +NOTICE: trigger = for_portion_of_test_insert_trig, TG_LEVEL ROW, new table = ("[1,2)","[01-01-2018,01-15-2018)",one), ("[1,2)","[01-01-2019,01-01-2020)",one) +NOTICE: TG_OP = UPDATE +NOTICE: trigger = for_portion_of_test_update_trig, TG_LEVEL ROW, old table = ("[1,2)","[01-01-2018,01-01-2020)",one), new table = ("[1,2)","[01-15-2018,01-01-2019)",2018-01-15_to_2019-01-01) +NOTICE: TG_OP = UPDATE +NOTICE: trigger = for_portion_of_test_update_trig_stmt, TG_LEVEL STATEMENT, old table = ("[1,2)","[01-01-2018,01-01-2020)",one), new table = ("[1,2)","[01-15-2018,01-01-2019)",2018-01-15_to_2019-01-01) +rollback; +begin; +delete from for_portion_of_test FOR PORTION OF valid_at FROM NULL TO '2018-01-21'; +NOTICE: TG_OP = INSERT +NOTICE: trigger = for_portion_of_test_insert_trig, TG_LEVEL ROW, new table = ("[1,2)","[01-21-2018,01-01-2020)",one) +NOTICE: TG_OP = DELETE +NOTICE: trigger = for_portion_of_test_del_trig,TG_LEVEL ROW, old table = ("[1,2)","[01-01-2018,01-01-2020)",one) +NOTICE: TG_OP = DELETE +NOTICE: trigger = for_portion_of_test_del_trig_stmt,TG_LEVEL STATEMENT, old table = ("[1,2)","[01-01-2018,01-01-2020)",one) +rollback; +begin; +UPDATE for_portion_of_test FOR PORTION OF valid_at + FROM NULL TO '2018-01-02' SET name = 'NULL_to_2018-01-01'; +NOTICE: TG_OP = INSERT +NOTICE: trigger = for_portion_of_test_insert_trig, TG_LEVEL ROW, new table = ("[1,2)","[01-02-2018,01-01-2020)",one) +NOTICE: TG_OP = UPDATE +NOTICE: trigger = for_portion_of_test_update_trig, TG_LEVEL ROW, old table = ("[1,2)","[01-01-2018,01-01-2020)",one), new table = ("[1,2)","[01-01-2018,01-02-2018)",NULL_to_2018-01-01) +NOTICE: TG_OP = UPDATE +NOTICE: trigger = for_portion_of_test_update_trig_stmt, TG_LEVEL STATEMENT, old table = ("[1,2)","[01-01-2018,01-01-2020)",one), new table = ("[1,2)","[01-01-2018,01-02-2018)",NULL_to_2018-01-01) +rollback; +DROP TABLE IF EXISTS for_portion_of_test; diff --git a/src/test/regress/sql/for_portion_of.sql b/src/test/regress/sql/for_portion_of.sql index 677b8e38..e86819c4 100644 --- a/src/test/regress/sql/for_portion_of.sql +++ b/src/test/regress/sql/for_portion_of.sql @@ -341,3 +341,75 @@ SELECT * FROM for_portion_of_test2 ORDER BY id, valid_at; DROP TABLE for_portion_of_test2; DROP TYPE mydaterange; + +DROP TABLE IF EXISTS for_portion_of_test; +CREATE TABLE for_portion_of_test (id int4range,valid_at daterange, name text); +INSERT INTO for_portion_of_test VALUES ('[1,1]', '[2018-01-01,2020-01-01)', 'one'); + +create or replace function dump_trigger() returns trigger language plpgsql as +$$ + BEGIN + raise notice 'TG_OP = %', TG_OP; + IF TG_OP = 'INSERT' then + raise notice 'trigger = %, TG_LEVEL %, new table = %', + TG_NAME, TG_LEVEL, + (select string_agg(new_table::text, ', ' order by id) from new_table); + ELSIF TG_OP = 'UPDATE' then + raise notice 'trigger = %, TG_LEVEL %, old table = %, new table = %', + TG_NAME, TG_LEVEL, + (select string_agg(old_table::text, ', ' order by id) from old_table), + (select string_agg(new_table::text, ', ' order by id) from new_table); + ELSIF TG_OP = 'DELETE' then + raise notice 'trigger = %,TG_LEVEL %, old table = %', + TG_NAME, TG_LEVEL, + (select string_agg(old_table::text, ', ' order by id) from old_table); + END IF; + RETURN NULL; + END; +$$; + +create or replace trigger for_portion_of_test_insert_trig +after insert on for_portion_of_test +referencing new table as new_table +for each row execute procedure dump_trigger(); + +create or replace trigger for_portion_of_test_insert_trig_stmt +after insert on for_portion_of_test +referencing new table as new_table +for each statement execute procedure dump_trigger(); + +create or replace trigger for_portion_of_test_update_trig +after update on for_portion_of_test +referencing old table as old_table new table as new_table +for each row execute procedure dump_trigger(); + +create or replace trigger for_portion_of_test_update_trig_stmt +after update on for_portion_of_test +referencing old table as old_table new table as new_table +for each statement execute procedure dump_trigger(); + +create or replace trigger for_portion_of_test_del_trig +after delete on for_portion_of_test +referencing old table as old_table +for each row execute procedure dump_trigger(); + +create or replace trigger for_portion_of_test_del_trig_stmt +after delete on for_portion_of_test +referencing old table as old_table +for each statement execute procedure dump_trigger(); + +begin; +UPDATE for_portion_of_test FOR PORTION OF valid_at + FROM '2018-01-15' TO '2019-01-01' SET name = '2018-01-15_to_2019-01-01'; +rollback; + +begin; +delete from for_portion_of_test FOR PORTION OF valid_at FROM NULL TO '2018-01-21'; +rollback; + +begin; +UPDATE for_portion_of_test FOR PORTION OF valid_at + FROM NULL TO '2018-01-02' SET name = 'NULL_to_2018-01-01'; +rollback; + +DROP TABLE IF EXISTS for_portion_of_test; \ No newline at end of file -- 2.34.1