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

Commit8c17c86

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 parent4a9150f commit8c17c86

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
@@ -4340,11 +4340,13 @@ GetAfterTriggersStoreSlot(AfterTriggersTableData *table,
43404340
MemoryContextoldcxt;
43414341

43424342
/*
4343-
* We only need this slot only until AfterTriggerEndQuery, but making
4344-
* it last till end-of-subxact is good enough. It'll be freed by
4345-
* AfterTriggerFreeQuery().
4343+
* We need this slot only until AfterTriggerEndQuery, but making it
4344+
* last till end-of-subxact is good enough. It'll be freed by
4345+
* AfterTriggerFreeQuery(). However, the passed-in tupdesc might have
4346+
* a different lifespan, so we'd better make a copy of that.
43464347
*/
43474348
oldcxt=MemoryContextSwitchTo(CurTransactionContext);
4349+
tupdesc=CreateTupleDescCopy(tupdesc);
43484350
table->storeslot=MakeSingleTupleTableSlot(tupdesc,&TTSOpsVirtual);
43494351
MemoryContextSwitchTo(oldcxt);
43504352
}
@@ -4640,7 +4642,12 @@ AfterTriggerFreeQuery(AfterTriggersQueryData *qs)
46404642
if (ts)
46414643
tuplestore_end(ts);
46424644
if (table->storeslot)
4643-
ExecDropSingleTupleTableSlot(table->storeslot);
4645+
{
4646+
TupleTableSlot*slot=table->storeslot;
4647+
4648+
table->storeslot=NULL;
4649+
ExecDropSingleTupleTableSlot(slot);
4650+
}
46444651
}
46454652

46464653
/*

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

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3305,7 +3305,7 @@ insert into convslot_test_parent(col1) values ('1');
33053305
insert into convslot_test_child(col1) values ('1');
33063306
insert into convslot_test_parent(col1) values ('3');
33073307
insert into convslot_test_child(col1) values ('3');
3308-
createor replacefunctiontrigger_function1()
3308+
create functionconvslot_trig1()
33093309
returns trigger
33103310
language plpgsql
33113311
AS $$
@@ -3315,7 +3315,7 @@ raise notice 'trigger = %, old_table = %',
33153315
(select string_agg(old_table::text, ', ' order by col1) from old_table);
33163316
return null;
33173317
end; $$;
3318-
createor replacefunctiontrigger_function2()
3318+
create functionconvslot_trig2()
33193319
returns trigger
33203320
language plpgsql
33213321
AS $$
@@ -3327,10 +3327,10 @@ return null;
33273327
end; $$;
33283328
create trigger but_trigger after update on convslot_test_child
33293329
referencing new table as new_table
3330-
for each statement execute functiontrigger_function2();
3330+
for each statement execute functionconvslot_trig2();
33313331
update convslot_test_parent set col1 = col1 || '1';
33323332
NOTICE: trigger = but_trigger, new table = (11,tutu), (31,tutu)
3333-
createor replacefunctiontrigger_function3()
3333+
create functionconvslot_trig3()
33343334
returns trigger
33353335
language plpgsql
33363336
AS $$
@@ -3343,13 +3343,39 @@ return null;
33433343
end; $$;
33443344
create trigger but_trigger2 after update on convslot_test_child
33453345
referencing old table as old_table new table as new_table
3346-
for each statement execute functiontrigger_function3();
3346+
for each statement execute functionconvslot_trig3();
33473347
update convslot_test_parent set col1 = col1 || '1';
33483348
NOTICE: trigger = but_trigger, new table = (111,tutu), (311,tutu)
33493349
NOTICE: trigger = but_trigger2, old_table = (11,tutu), (31,tutu), new table = (111,tutu), (311,tutu)
33503350
create trigger bdt_trigger after delete on convslot_test_child
33513351
referencing old table as old_table
3352-
for each statement execute functiontrigger_function1();
3352+
for each statement execute functionconvslot_trig1();
33533353
delete from convslot_test_parent;
33543354
NOTICE: trigger = bdt_trigger, old_table = (111,tutu), (311,tutu)
33553355
drop table convslot_test_child, convslot_test_parent;
3356+
drop function convslot_trig1();
3357+
drop function convslot_trig2();
3358+
drop function convslot_trig3();
3359+
-- Bug #17607: variant of above in which trigger function raises an error;
3360+
-- we don't see any ill effects unless trigger tuple requires mapping
3361+
create table convslot_test_parent (id int primary key, val int)
3362+
partition by range (id);
3363+
create table convslot_test_part (val int, id int not null);
3364+
alter table convslot_test_parent
3365+
attach partition convslot_test_part for values from (1) to (1000);
3366+
create function convslot_trig4() returns trigger as
3367+
$$begin raise exception 'BOOM!'; end$$ language plpgsql;
3368+
create trigger convslot_test_parent_update
3369+
after update on convslot_test_parent
3370+
referencing old table as old_rows new table as new_rows
3371+
for each statement execute procedure convslot_trig4();
3372+
insert into convslot_test_parent (id, val) values (1, 2);
3373+
begin;
3374+
savepoint svp;
3375+
update convslot_test_parent set val = 3; -- error expected
3376+
ERROR: BOOM!
3377+
CONTEXT: PL/pgSQL function convslot_trig4() line 1 at RAISE
3378+
rollback to savepoint svp;
3379+
rollback;
3380+
drop table convslot_test_parent;
3381+
drop function convslot_trig4();

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2448,7 +2448,7 @@ insert into convslot_test_child(col1) values ('1');
24482448
insert into convslot_test_parent(col1)values ('3');
24492449
insert into convslot_test_child(col1)values ('3');
24502450

2451-
createor replacefunctiontrigger_function1()
2451+
createfunctionconvslot_trig1()
24522452
returns trigger
24532453
language plpgsql
24542454
AS $$
@@ -2459,7 +2459,7 @@ raise notice 'trigger = %, old_table = %',
24592459
returnnull;
24602460
end; $$;
24612461

2462-
createor replacefunctiontrigger_function2()
2462+
createfunctionconvslot_trig2()
24632463
returns trigger
24642464
language plpgsql
24652465
AS $$
@@ -2472,11 +2472,11 @@ end; $$;
24722472

24732473
createtriggerbut_trigger afterupdateon convslot_test_child
24742474
referencing new tableas new_table
2475-
for each statement execute functiontrigger_function2();
2475+
for each statement execute functionconvslot_trig2();
24762476

24772477
update convslot_test_parentset col1= col1||'1';
24782478

2479-
createor replacefunctiontrigger_function3()
2479+
createfunctionconvslot_trig3()
24802480
returns trigger
24812481
language plpgsql
24822482
AS $$
@@ -2490,12 +2490,45 @@ end; $$;
24902490

24912491
createtriggerbut_trigger2 afterupdateon convslot_test_child
24922492
referencing old tableas old_table new tableas new_table
2493-
for each statement execute functiontrigger_function3();
2493+
for each statement execute functionconvslot_trig3();
24942494
update convslot_test_parentset col1= col1||'1';
24952495

24962496
createtriggerbdt_trigger afterdeleteon convslot_test_child
24972497
referencing old tableas old_table
2498-
for each statement execute functiontrigger_function1();
2498+
for each statement execute functionconvslot_trig1();
24992499
deletefrom convslot_test_parent;
25002500

25012501
droptable convslot_test_child, convslot_test_parent;
2502+
dropfunction convslot_trig1();
2503+
dropfunction convslot_trig2();
2504+
dropfunction convslot_trig3();
2505+
2506+
-- Bug #17607: variant of above in which trigger function raises an error;
2507+
-- we don't see any ill effects unless trigger tuple requires mapping
2508+
2509+
createtableconvslot_test_parent (idintprimary key, valint)
2510+
partition by range (id);
2511+
2512+
createtableconvslot_test_part (valint, idintnot null);
2513+
2514+
altertable convslot_test_parent
2515+
attach partition convslot_test_part forvaluesfrom (1) to (1000);
2516+
2517+
createfunctionconvslot_trig4() returns triggeras
2518+
$$begin raise exception'BOOM!'; end$$ language plpgsql;
2519+
2520+
createtriggerconvslot_test_parent_update
2521+
afterupdateon convslot_test_parent
2522+
referencing old tableas old_rows new tableas new_rows
2523+
for each statement execute procedure convslot_trig4();
2524+
2525+
insert into convslot_test_parent (id, val)values (1,2);
2526+
2527+
begin;
2528+
savepoint svp;
2529+
update convslot_test_parentset val=3;-- error expected
2530+
rollback to savepoint svp;
2531+
rollback;
2532+
2533+
droptable convslot_test_parent;
2534+
dropfunction convslot_trig4();

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp