Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit216f9c1

Browse files
committed
Fix tupdesc lifespan bug with AfterTriggersTableData.storeslot.
Commit25936fd adjusted things so that the "storeslot" we usefor remapping trigger tuples would have adequate lifespan, but itneglected to consider the lifespan of the tuple descriptor thatthe slot depends on. It turns out that in at least some cases, thetupdesc we are passing is a refcounted tupdesc, and the refcount forthe slot's reference can get assigned to a resource owner havingdifferent lifespan than the slot does. That leads to an error like"tupdesc reference 0x7fdef236a1b8 is not owned by resource ownerSubTransaction". Worse, because of a second oversight in the samecommit, we'd try to free the same tupdesc refcount again whilecleaning up after that error, leading to recursive errors and an"ERRORDATA_STACK_SIZE exceeded" PANIC.To fix the initial problem, let's just make a non-refcounted copyof the tupdesc we're supposed to use. That seems likely to guardagainst additional problems, since there's no strong reason forthis code to assume that what it's given is a refcounted tupdesc;in which case there's an independent hazard of the tupdesc havingshorter lifespan than the slot does. (I didn't bother trying tofree said copy, since it should go away anyway when the (sub)transaction context is cleaned up.)The other issue can be fixed by making the code added toAfterTriggerFreeQuery work like the rest of that function, ie besure that it doesn't try to free the same slot twice in the eventof recursive error cleanup.While here, also clean up minor stylistic issues in the test caseadded by25936fd: don't use "create or replace function", as anyname collision within the tests is likely to have ill effectsthat that won't mask; and don't use function names as generic astrigger_function1, especially if you're not going to drop themat the end of the test stanza.Per bug #17607 from Thomas Mc Kay. Back-patch to v12, as theprevious fix was.Discussion:https://postgr.es/m/17607-bd8ccc81226f7f80@postgresql.org
1 parent1d2fec9 commit216f9c1

File tree

3 files changed

+82
-16
lines changed

3 files changed

+82
-16
lines changed

‎src/backend/commands/trigger.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4775,11 +4775,13 @@ GetAfterTriggersStoreSlot(AfterTriggersTableData *table,
47754775
MemoryContextoldcxt;
47764776

47774777
/*
4778-
* We only need this slot only until AfterTriggerEndQuery, but making
4779-
* it last till end-of-subxact is good enough. It'll be freed by
4780-
* AfterTriggerFreeQuery().
4778+
* We need this slot only until AfterTriggerEndQuery, but making it
4779+
* last till end-of-subxact is good enough. It'll be freed by
4780+
* AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4781+
* a different lifespan, so we'd better make a copy of that.
47814782
*/
47824783
oldcxt=MemoryContextSwitchTo(CurTransactionContext);
4784+
tupdesc=CreateTupleDescCopy(tupdesc);
47834785
table->storeslot=MakeSingleTupleTableSlot(tupdesc,&TTSOpsVirtual);
47844786
MemoryContextSwitchTo(oldcxt);
47854787
}
@@ -5098,7 +5100,12 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
50985100
if (ts)
50995101
tuplestore_end(ts);
51005102
if (table->storeslot)
5101-
ExecDropSingleTupleTableSlot(table->storeslot);
5103+
{
5104+
TupleTableSlot*slot=table->storeslot;
5105+
5106+
table->storeslot=NULL;
5107+
ExecDropSingleTupleTableSlot(slot);
5108+
}
51025109
}
51035110

51045111
/*

‎src/test/regress/expected/triggers.out

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3468,7 +3468,7 @@ insert into convslot_test_parent(col1) values ('1');
34683468
insert into convslot_test_child(col1) values ('1');
34693469
insert into convslot_test_parent(col1) values ('3');
34703470
insert into convslot_test_child(col1) values ('3');
3471-
createor replacefunctiontrigger_function1()
3471+
create functionconvslot_trig1()
34723472
returns trigger
34733473
language plpgsql
34743474
AS $$
@@ -3478,7 +3478,7 @@ raise notice 'trigger = %, old_table = %',
34783478
(select string_agg(old_table::text, ', ' order by col1) from old_table);
34793479
return null;
34803480
end; $$;
3481-
createor replacefunctiontrigger_function2()
3481+
create functionconvslot_trig2()
34823482
returns trigger
34833483
language plpgsql
34843484
AS $$
@@ -3490,10 +3490,10 @@ return null;
34903490
end; $$;
34913491
create trigger but_trigger after update on convslot_test_child
34923492
referencing new table as new_table
3493-
for each statement execute functiontrigger_function2();
3493+
for each statement execute functionconvslot_trig2();
34943494
update convslot_test_parent set col1 = col1 || '1';
34953495
NOTICE: trigger = but_trigger, new table = (11,tutu), (31,tutu)
3496-
createor replacefunctiontrigger_function3()
3496+
create functionconvslot_trig3()
34973497
returns trigger
34983498
language plpgsql
34993499
AS $$
@@ -3506,16 +3506,42 @@ return null;
35063506
end; $$;
35073507
create trigger but_trigger2 after update on convslot_test_child
35083508
referencing old table as old_table new table as new_table
3509-
for each statement execute functiontrigger_function3();
3509+
for each statement execute functionconvslot_trig3();
35103510
update convslot_test_parent set col1 = col1 || '1';
35113511
NOTICE: trigger = but_trigger, new table = (111,tutu), (311,tutu)
35123512
NOTICE: trigger = but_trigger2, old_table = (11,tutu), (31,tutu), new table = (111,tutu), (311,tutu)
35133513
create trigger bdt_trigger after delete on convslot_test_child
35143514
referencing old table as old_table
3515-
for each statement execute functiontrigger_function1();
3515+
for each statement execute functionconvslot_trig1();
35163516
delete from convslot_test_parent;
35173517
NOTICE: trigger = bdt_trigger, old_table = (111,tutu), (311,tutu)
35183518
drop table convslot_test_child, convslot_test_parent;
3519+
drop function convslot_trig1();
3520+
drop function convslot_trig2();
3521+
drop function convslot_trig3();
3522+
-- Bug #17607: variant of above in which trigger function raises an error;
3523+
-- we don't see any ill effects unless trigger tuple requires mapping
3524+
create table convslot_test_parent (id int primary key, val int)
3525+
partition by range (id);
3526+
create table convslot_test_part (val int, id int not null);
3527+
alter table convslot_test_parent
3528+
attach partition convslot_test_part for values from (1) to (1000);
3529+
create function convslot_trig4() returns trigger as
3530+
$$begin raise exception 'BOOM!'; end$$ language plpgsql;
3531+
create trigger convslot_test_parent_update
3532+
after update on convslot_test_parent
3533+
referencing old table as old_rows new table as new_rows
3534+
for each statement execute procedure convslot_trig4();
3535+
insert into convslot_test_parent (id, val) values (1, 2);
3536+
begin;
3537+
savepoint svp;
3538+
update convslot_test_parent set val = 3; -- error expected
3539+
ERROR: BOOM!
3540+
CONTEXT: PL/pgSQL function convslot_trig4() line 1 at RAISE
3541+
rollback to savepoint svp;
3542+
rollback;
3543+
drop table convslot_test_parent;
3544+
drop function convslot_trig4();
35193545
-- Test trigger renaming on partitioned tables
35203546
create table grandparent (id int, primary key (id)) partition by range (id);
35213547
create table middle partition of grandparent for values from (1) to (10)

‎src/test/regress/sql/triggers.sql

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,7 +2615,7 @@ insert into convslot_test_child(col1) values ('1');
26152615
insert into convslot_test_parent(col1)values ('3');
26162616
insert into convslot_test_child(col1)values ('3');
26172617

2618-
createor replacefunctiontrigger_function1()
2618+
createfunctionconvslot_trig1()
26192619
returns trigger
26202620
language plpgsql
26212621
AS $$
@@ -2626,7 +2626,7 @@ raise notice 'trigger = %, old_table = %',
26262626
returnnull;
26272627
end; $$;
26282628

2629-
createor replacefunctiontrigger_function2()
2629+
createfunctionconvslot_trig2()
26302630
returns trigger
26312631
language plpgsql
26322632
AS $$
@@ -2639,11 +2639,11 @@ end; $$;
26392639

26402640
createtriggerbut_trigger afterupdateon convslot_test_child
26412641
referencing new tableas new_table
2642-
for each statement execute functiontrigger_function2();
2642+
for each statement execute functionconvslot_trig2();
26432643

26442644
update convslot_test_parentset col1= col1||'1';
26452645

2646-
createor replacefunctiontrigger_function3()
2646+
createfunctionconvslot_trig3()
26472647
returns trigger
26482648
language plpgsql
26492649
AS $$
@@ -2657,15 +2657,48 @@ end; $$;
26572657

26582658
createtriggerbut_trigger2 afterupdateon convslot_test_child
26592659
referencing old tableas old_table new tableas new_table
2660-
for each statement execute functiontrigger_function3();
2660+
for each statement execute functionconvslot_trig3();
26612661
update convslot_test_parentset col1= col1||'1';
26622662

26632663
createtriggerbdt_trigger afterdeleteon convslot_test_child
26642664
referencing old tableas old_table
2665-
for each statement execute functiontrigger_function1();
2665+
for each statement execute functionconvslot_trig1();
26662666
deletefrom convslot_test_parent;
26672667

26682668
droptable convslot_test_child, convslot_test_parent;
2669+
dropfunction convslot_trig1();
2670+
dropfunction convslot_trig2();
2671+
dropfunction convslot_trig3();
2672+
2673+
-- Bug #17607: variant of above in which trigger function raises an error;
2674+
-- we don't see any ill effects unless trigger tuple requires mapping
2675+
2676+
createtableconvslot_test_parent (idintprimary key, valint)
2677+
partition by range (id);
2678+
2679+
createtableconvslot_test_part (valint, idintnot null);
2680+
2681+
altertable convslot_test_parent
2682+
attach partition convslot_test_part forvaluesfrom (1) to (1000);
2683+
2684+
createfunctionconvslot_trig4() returns triggeras
2685+
$$begin raise exception'BOOM!'; end$$ language plpgsql;
2686+
2687+
createtriggerconvslot_test_parent_update
2688+
afterupdateon convslot_test_parent
2689+
referencing old tableas old_rows new tableas new_rows
2690+
for each statement execute procedure convslot_trig4();
2691+
2692+
insert into convslot_test_parent (id, val)values (1,2);
2693+
2694+
begin;
2695+
savepoint svp;
2696+
update convslot_test_parentset val=3;-- error expected
2697+
rollback to savepoint svp;
2698+
rollback;
2699+
2700+
droptable convslot_test_parent;
2701+
dropfunction convslot_trig4();
26692702

26702703
-- Test trigger renaming on partitioned tables
26712704
createtablegrandparent (idint,primary key (id)) partition by range (id);

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp