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

Commitc255148

Browse files
committed
Fix temporary object cleanup failing due to toast access without snapshot.
When cleaning up temporary objects during process exit the cleanup could failwith: FATAL: cannot fetch toast data without an active snapshotThe bug is caused by RemoveTempRelationsCallback() not setting up asnapshot. If an object with toasted catalog data needs to be cleaned up,init_toast_snapshot() could fail with the above error.Most of the time however the the problem is masked due to cached catalogsnapshots being returned by GetOldestSnapshot(). But dropping an object cancause catalog invalidations to be emitted. If no further catalog accesses arenecessary between the invalidation processing and the next toast datumdeletion, the bug becomes visible.It's easy to miss this bug because it typically happens after clientsdisconnect and the FATAL error just ends up in the log.Luckily temporary table cleanup at the next use of the same temporary schemaor during DISCARD ALL does not have the same problem.Fix the bug by pushing a snapshot in RemoveTempRelationsCallback(). Also addisolation tests for temporary object cleanup, including objects with toastedcatalog data.A future HEAD only commit will add more assertions.Reported-By: Miles DelahuntyAuthor: Andres FreundDiscussion:https://postgr.es/m/CAOFAq3BU5Mf2TTvu8D9n_ZOoFAeQswuzk7yziAb7xuw_qyw5gw@mail.gmail.comBackpatch: 10-
1 parenta5716ba commitc255148

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

‎src/backend/catalog/namespace.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include"utils/inval.h"
5656
#include"utils/lsyscache.h"
5757
#include"utils/memutils.h"
58+
#include"utils/snapmgr.h"
5859
#include"utils/syscache.h"
5960
#include"utils/varlena.h"
6061

@@ -4247,9 +4248,11 @@ RemoveTempRelationsCallback(int code, Datum arg)
42474248
/* Need to ensure we have a usable transaction. */
42484249
AbortOutOfAnyTransaction();
42494250
StartTransactionCommand();
4251+
PushActiveSnapshot(GetTransactionSnapshot());
42504252

42514253
RemoveTempRelations(myTempNamespace);
42524254

4255+
PopActiveSnapshot();
42534256
CommitTransactionCommand();
42544257
}
42554258
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
Parsed test spec with 2 sessions
2+
3+
starting permutation: s1_create_temp_objects s1_discard_temp s2_check_schema
4+
step s1_create_temp_objects:
5+
6+
-- create function large enough to be toasted, to ensure we correctly clean those up, a prior bug
7+
-- https://postgr.es/m/CAOFAq3BU5Mf2TTvu8D9n_ZOoFAeQswuzk7yziAb7xuw_qyw5gw%40mail.gmail.com
8+
SELECT exec(format($outer$
9+
CREATE OR REPLACE FUNCTION pg_temp.long() RETURNS text LANGUAGE sql AS $body$ SELECT %L; $body$$outer$,
10+
(SELECT string_agg(g.i::text||':'||random()::text, '|') FROM generate_series(1, 100) g(i))));
11+
12+
-- The above bug requirs function removal to happen after a catalog
13+
-- invalidation. dependency.c sorts objects in descending oid order so
14+
-- that newer objects are deleted before older objects, so create a
15+
-- table after.
16+
CREATE TEMPORARY TABLE invalidate_catalog_cache();
17+
18+
-- test non-temp function is dropped when depending on temp table
19+
CREATE TEMPORARY TABLE just_give_me_a_type(id serial primary key);
20+
21+
CREATE FUNCTION uses_a_temp_type(just_give_me_a_type) RETURNS int LANGUAGE sql AS $$SELECT 1;$$;
22+
23+
exec
24+
----
25+
26+
(1 row)
27+
28+
step s1_discard_temp:
29+
DISCARD TEMP;
30+
31+
step s2_check_schema:
32+
SELECT oid::regclass FROM pg_class WHERE relnamespace = (SELECT oid FROM s1_temp_schema);
33+
SELECT oid::regproc FROM pg_proc WHERE pronamespace = (SELECT oid FROM s1_temp_schema);
34+
SELECT oid::regproc FROM pg_type WHERE typnamespace = (SELECT oid FROM s1_temp_schema);
35+
36+
oid
37+
---
38+
(0 rows)
39+
40+
oid
41+
---
42+
(0 rows)
43+
44+
oid
45+
---
46+
(0 rows)
47+
48+
49+
starting permutation: s1_advisory s2_advisory s1_create_temp_objects s1_exit s2_check_schema
50+
step s1_advisory:
51+
SELECT pg_advisory_lock('pg_namespace'::regclass::int8);
52+
53+
pg_advisory_lock
54+
----------------
55+
56+
(1 row)
57+
58+
step s2_advisory:
59+
SELECT pg_advisory_lock('pg_namespace'::regclass::int8);
60+
<waiting ...>
61+
step s1_create_temp_objects:
62+
63+
-- create function large enough to be toasted, to ensure we correctly clean those up, a prior bug
64+
-- https://postgr.es/m/CAOFAq3BU5Mf2TTvu8D9n_ZOoFAeQswuzk7yziAb7xuw_qyw5gw%40mail.gmail.com
65+
SELECT exec(format($outer$
66+
CREATE OR REPLACE FUNCTION pg_temp.long() RETURNS text LANGUAGE sql AS $body$ SELECT %L; $body$$outer$,
67+
(SELECT string_agg(g.i::text||':'||random()::text, '|') FROM generate_series(1, 100) g(i))));
68+
69+
-- The above bug requirs function removal to happen after a catalog
70+
-- invalidation. dependency.c sorts objects in descending oid order so
71+
-- that newer objects are deleted before older objects, so create a
72+
-- table after.
73+
CREATE TEMPORARY TABLE invalidate_catalog_cache();
74+
75+
-- test non-temp function is dropped when depending on temp table
76+
CREATE TEMPORARY TABLE just_give_me_a_type(id serial primary key);
77+
78+
CREATE FUNCTION uses_a_temp_type(just_give_me_a_type) RETURNS int LANGUAGE sql AS $$SELECT 1;$$;
79+
80+
exec
81+
----
82+
83+
(1 row)
84+
85+
step s1_exit:
86+
SELECT pg_terminate_backend(pg_backend_pid());
87+
88+
FATAL: terminating connection due to administrator command
89+
server closed the connection unexpectedly
90+
This probably means the server terminated abnormally
91+
before or while processing the request.
92+
93+
step s2_advisory: <... completed>
94+
pg_advisory_lock
95+
----------------
96+
97+
(1 row)
98+
99+
step s2_check_schema:
100+
SELECT oid::regclass FROM pg_class WHERE relnamespace = (SELECT oid FROM s1_temp_schema);
101+
SELECT oid::regproc FROM pg_proc WHERE pronamespace = (SELECT oid FROM s1_temp_schema);
102+
SELECT oid::regproc FROM pg_type WHERE typnamespace = (SELECT oid FROM s1_temp_schema);
103+
104+
oid
105+
---
106+
(0 rows)
107+
108+
oid
109+
---
110+
(0 rows)
111+
112+
oid
113+
---
114+
(0 rows)
115+

‎src/test/isolation/isolation_schedule

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ test: eval-plan-qual-trigger
3434
test: lock-update-delete
3535
test: lock-update-traversal
3636
test: inherit-temp
37+
test: temp-schema-cleanup
3738
test: insert-conflict-do-nothing
3839
test: insert-conflict-do-nothing-2
3940
test: insert-conflict-do-update
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Test cleanup of objects in temporary schema.
2+
3+
setup{
4+
CREATETABLEs1_temp_schema(oidoid);
5+
--tohelpcreatealongfunction
6+
CREATEFUNCTIONexec(p_footext)RETURNSvoidLANGUAGEplpgsqlAS $$BEGINEXECUTEp_foo; END;$$;
7+
}
8+
9+
teardown{
10+
DROPTABLEs1_temp_schema;
11+
DROPFUNCTIONexec(text);
12+
}
13+
14+
session"s1"
15+
setup{
16+
CREATETEMPORARYTABLEjust_to_create_temp_schema();
17+
DROPTABLEjust_to_create_temp_schema;
18+
INSERTINTOs1_temp_schemaSELECTpg_my_temp_schema();
19+
}
20+
21+
steps1_advisory{
22+
SELECTpg_advisory_lock('pg_namespace'::regclass::int8);
23+
}
24+
25+
steps1_create_temp_objects{
26+
27+
--createfunctionlargeenoughtobetoasted,toensurewecorrectlycleanthoseup,apriorbug
28+
--https://postgr.es/m/CAOFAq3BU5Mf2TTvu8D9n_ZOoFAeQswuzk7yziAb7xuw_qyw5gw%40mail.gmail.com
29+
SELECTexec(format($outer$
30+
CREATEORREPLACEFUNCTIONpg_temp.long()RETURNStextLANGUAGEsqlAS $body$SELECT %L; $body$$outer$,
31+
(SELECTstring_agg(g.i::text||':'||random()::text,'|')FROMgenerate_series(1,100)g(i))));
32+
33+
--Theabovebugrequirsfunctionremovaltohappenafteracatalog
34+
--invalidation.dependency.csortsobjectsindescendingoidorderso
35+
--thatnewerobjectsaredeletedbeforeolderobjects,socreatea
36+
--tableafter.
37+
CREATETEMPORARYTABLEinvalidate_catalog_cache();
38+
39+
--testnon-tempfunctionisdroppedwhendependingontemptable
40+
CREATETEMPORARYTABLEjust_give_me_a_type(idserialprimarykey);
41+
42+
CREATEFUNCTIONuses_a_temp_type(just_give_me_a_type)RETURNSintLANGUAGEsqlAS $$SELECT1;$$;
43+
}
44+
45+
steps1_discard_temp{
46+
DISCARDTEMP;
47+
}
48+
49+
steps1_exit{
50+
SELECTpg_terminate_backend(pg_backend_pid());
51+
}
52+
53+
54+
session"s2"
55+
56+
steps2_advisory{
57+
SELECTpg_advisory_lock('pg_namespace'::regclass::int8);
58+
}
59+
60+
steps2_check_schema{
61+
SELECToid::regclassFROMpg_classWHERErelnamespace=(SELECToidFROMs1_temp_schema);
62+
SELECToid::regprocFROMpg_procWHEREpronamespace=(SELECToidFROMs1_temp_schema);
63+
SELECToid::regprocFROMpg_typeWHEREtypnamespace=(SELECToidFROMs1_temp_schema);
64+
}
65+
66+
67+
# Test temporary object cleanup during DISCARD.
68+
permutation
69+
s1_create_temp_objects
70+
s1_discard_temp
71+
s2_check_schema
72+
73+
# Test temporary object cleanup during process exit.
74+
#
75+
# To check (in s2) if temporary objects (in s1) have properly been removed we
76+
# need to wait for s1 to finish cleaning up. Luckily session level advisory
77+
# locks are released only after temp table cleanup.
78+
permutation
79+
s1_advisory
80+
s2_advisory
81+
s1_create_temp_objects
82+
s1_exit
83+
s2_check_schema
84+
85+
# Can't run further tests here, because s1's connection is dead

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp