Skip to content

Commit cf31205

Browse files
author
Daniil Anisimov
committed
[PGPRO-17014] Fix the issue with early reset of AQOCacheMemCtx by ATX
The autonomous transaction can be aborted in nested levels of plPgSql. Check this level in aqo_free_callback.
1 parent dad793d commit cf31205

5 files changed

Lines changed: 151 additions & 1 deletion

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ REGRESS = aqo_disabled \
1616
aqo_intelligent \
1717
aqo_forced \
1818
aqo_learn \
19+
aqo_atx \
1920
schema \
2021
aqo_fdw \
2122
aqo_CVE-2020-14350 \

aqo.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "access/relation.h"
1414
#include "access/table.h"
15+
#include "access/xact.h"
1516
#include "catalog/pg_extension.h"
1617
#include "commands/extension.h"
1718
#include "miscadmin.h"
@@ -113,7 +114,11 @@ aqo_free_callback(ResourceReleasePhase phase,
113114
if (phase != RESOURCE_RELEASE_AFTER_LOCKS)
114115
return;
115116

116-
if (isTopLevel)
117+
if (isTopLevel
118+
#ifdef PGPRO_EE
119+
&& getNestLevelATX() == 0
120+
#endif
121+
)
117122
{
118123
MemoryContextReset(AQOCacheMemCtx);
119124
cur_classes = NIL;

expected/aqo_atx.out

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
-- Test for early reset of AQOCache memory context by ATX
2+
SELECT (count(*) = 0) AS skip_test FROM pg_settings
3+
WHERE name = 'max_autonomous_transactions' \gset
4+
\if :skip_test
5+
\quit
6+
\endif
7+
CREATE EXTENSION IF NOT EXISTS aqo;
8+
SELECT true AS success FROM aqo_reset();
9+
success
10+
---------
11+
t
12+
(1 row)
13+
14+
CREATE TABLE t1 (a int, b text, c int);
15+
CREATE unique index on t1 (a, b);
16+
CREATE TABLE t2 AS SELECT 0 AS a, 'cero' AS b, 1 AS c;
17+
CREATE FUNCTION t1_update_func() RETURNS trigger AS $$
18+
BEGIN
19+
PERFORM * FROM t2 WHERE a >= 0;
20+
RETURN new;
21+
END;
22+
$$ LANGUAGE plpgsql;
23+
CREATE TRIGGER t1_update
24+
AFTER UPDATE ON t1
25+
FOR EACH STATEMENT
26+
EXECUTE PROCEDURE t1_update_func();
27+
CREATE FUNCTION f2()
28+
RETURNS int AS $$
29+
BEGIN
30+
BEGIN AUTONOMOUS
31+
INSERT INTO t1 SELECT a,b,c FROM t2 WHERE t2.a >= 0
32+
ON conflict (a,b) do UPDATE SET c = t1.c + 1;
33+
SELECT 1/0;
34+
EXCEPTION WHEN division_by_zero THEN
35+
NULL;
36+
END;
37+
38+
INSERT INTO t1 SELECT a,b,c FROM t2 WHERE t2.a >= 0
39+
ON conflict (a,b) do UPDATE SET c = t1.c + 1;
40+
41+
RETURN 0;
42+
END;
43+
$$ LANGUAGE plpgsql security definer;
44+
SET aqo.join_threshold = 0;
45+
SET aqo.mode = learn;
46+
PREPARE prep1 AS SELECT f2();
47+
EXECUTE prep1;
48+
f2
49+
----
50+
0
51+
(1 row)
52+
53+
EXECUTE prep1;
54+
f2
55+
----
56+
0
57+
(1 row)
58+
59+
EXECUTE prep1;
60+
f2
61+
----
62+
0
63+
(1 row)
64+
65+
EXECUTE prep1;
66+
f2
67+
----
68+
0
69+
(1 row)
70+
71+
EXECUTE prep1;
72+
f2
73+
----
74+
0
75+
(1 row)
76+
77+
DROP TABLE t1, t2 CASCADE;
78+
DROP FUNCTION t1_update_func, f2;
79+
DROP EXTENSION aqo;

expected/aqo_atx_1.out

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Test for early reset of AQOCache memory context by ATX
2+
SELECT (count(*) = 0) AS skip_test FROM pg_settings
3+
WHERE name = 'max_autonomous_transactions' \gset
4+
\if :skip_test
5+
\quit

sql/aqo_atx.sql

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
-- Test for early reset of AQOCache memory context by ATX
2+
SELECT (count(*) = 0) AS skip_test FROM pg_settings
3+
WHERE name = 'max_autonomous_transactions' \gset
4+
5+
\if :skip_test
6+
\quit
7+
\endif
8+
9+
CREATE EXTENSION IF NOT EXISTS aqo;
10+
SELECT true AS success FROM aqo_reset();
11+
12+
CREATE TABLE t1 (a int, b text, c int);
13+
CREATE unique index on t1 (a, b);
14+
15+
CREATE TABLE t2 AS SELECT 0 AS a, 'cero' AS b, 1 AS c;
16+
17+
CREATE FUNCTION t1_update_func() RETURNS trigger AS $$
18+
BEGIN
19+
PERFORM * FROM t2 WHERE a >= 0;
20+
RETURN new;
21+
END;
22+
$$ LANGUAGE plpgsql;
23+
24+
CREATE TRIGGER t1_update
25+
AFTER UPDATE ON t1
26+
FOR EACH STATEMENT
27+
EXECUTE PROCEDURE t1_update_func();
28+
29+
CREATE FUNCTION f2()
30+
RETURNS int AS $$
31+
BEGIN
32+
BEGIN AUTONOMOUS
33+
INSERT INTO t1 SELECT a,b,c FROM t2 WHERE t2.a >= 0
34+
ON conflict (a,b) do UPDATE SET c = t1.c + 1;
35+
SELECT 1/0;
36+
EXCEPTION WHEN division_by_zero THEN
37+
NULL;
38+
END;
39+
40+
INSERT INTO t1 SELECT a,b,c FROM t2 WHERE t2.a >= 0
41+
ON conflict (a,b) do UPDATE SET c = t1.c + 1;
42+
43+
RETURN 0;
44+
END;
45+
$$ LANGUAGE plpgsql security definer;
46+
47+
SET aqo.join_threshold = 0;
48+
SET aqo.mode = learn;
49+
50+
PREPARE prep1 AS SELECT f2();
51+
EXECUTE prep1;
52+
EXECUTE prep1;
53+
EXECUTE prep1;
54+
EXECUTE prep1;
55+
EXECUTE prep1;
56+
57+
DROP TABLE t1, t2 CASCADE;
58+
DROP FUNCTION t1_update_func, f2;
59+
60+
DROP EXTENSION aqo;

0 commit comments

Comments
 (0)