Skip to content

Commit ebbca2d

Browse files
committed
Fix leaks with functions in DML
If plpgsql functions are used in DML queries then we were leaking 8KB for every invocation of that function. This can quickly add up. The issue was that the "CurTransactionContext" was not getting cleaned up after every invocation. The reason was that we were inadvertantly allocating a temporary list in that context. Postgres then thought that this CurTransactionContext needs to be re-used further and kept it around. We now use a proper memory context to avoid this. Fixes #7053
1 parent 1b7f109 commit ebbca2d

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

.unreleased/fix_7088

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixes: #7088 Fix leaks with functions in DML
2+
Thanks: @Kazmirchuk for reporting this

src/cache.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,16 @@ release_subtxn_pinned_caches(SubTransactionId subtxnid, bool abort)
229229
{
230230
ListCell *lc;
231231

232-
/* Need a copy because cache_release will modify pinned_caches */
232+
/*
233+
* Need a copy because cache_release will modify pinned_caches.
234+
*
235+
* This needs to be allocated in pinned cache memory context.
236+
* Otherwise leaks ensue if CurTransactionContext (which is the
237+
* CurrentMemoryContext) gets used!
238+
*/
239+
MemoryContext old = MemoryContextSwitchTo(pinned_caches_mctx);
233240
List *pinned_caches_copy = list_copy(pinned_caches);
241+
MemoryContextSwitchTo(old);
234242

235243
/* Only release caches created in subtxn */
236244
foreach (lc, pinned_caches_copy)

test/expected/copy_memory_usage.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,40 @@ select * from portal_memory_log where (
5959
----+-------
6060
(0 rows)
6161

62+
-- Test plpgsql leaks
63+
CREATE TABLE test_ht(tm timestamptz, val float8);
64+
SELECT * FROM create_hypertable('test_ht', 'tm');
65+
NOTICE: adding not-null constraint to column "tm"
66+
hypertable_id | schema_name | table_name | created
67+
---------------+-------------+------------+---------
68+
2 | public | test_ht | t
69+
(1 row)
70+
71+
-- Use a plpgsql function to insert into the hypertable
72+
CREATE OR REPLACE FUNCTION to_double(_in text, INOUT _out double precision)
73+
LANGUAGE plpgsql IMMUTABLE parallel safe
74+
AS $$
75+
BEGIN
76+
SELECT CAST(_in AS double precision) INTO _out;
77+
EXCEPTION WHEN others THEN
78+
--do nothing: _out already carries default
79+
END;
80+
$$;
81+
-- TopTransactionContext usage needs to remain the same after every insert
82+
-- There was a leak earlier in the child CurTransactionContext
83+
BEGIN;
84+
INSERT INTO test_ht VALUES ('1980-01-01 00:00:00-00', to_double('23.11', 0));
85+
SELECT sum(total_bytes) from pg_backend_memory_contexts where parent = 'TopTransactionContext';
86+
sum
87+
-------
88+
16384
89+
(1 row)
90+
91+
INSERT INTO test_ht VALUES ('1980-02-01 00:00:00-00', to_double('24.11', 0));
92+
SELECT sum(total_bytes) from pg_backend_memory_contexts where parent = 'TopTransactionContext';
93+
sum
94+
-------
95+
16384
96+
(1 row)
97+
98+
COMMIT;

test/sql/copy_memory_usage.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,25 @@ select * from portal_memory_log where (
5656
from portal_memory_log
5757
);
5858

59+
-- Test plpgsql leaks
60+
CREATE TABLE test_ht(tm timestamptz, val float8);
61+
SELECT * FROM create_hypertable('test_ht', 'tm');
62+
-- Use a plpgsql function to insert into the hypertable
63+
CREATE OR REPLACE FUNCTION to_double(_in text, INOUT _out double precision)
64+
LANGUAGE plpgsql IMMUTABLE parallel safe
65+
AS $$
66+
BEGIN
67+
SELECT CAST(_in AS double precision) INTO _out;
68+
EXCEPTION WHEN others THEN
69+
--do nothing: _out already carries default
70+
END;
71+
$$;
72+
73+
-- TopTransactionContext usage needs to remain the same after every insert
74+
-- There was a leak earlier in the child CurTransactionContext
75+
BEGIN;
76+
INSERT INTO test_ht VALUES ('1980-01-01 00:00:00-00', to_double('23.11', 0));
77+
SELECT sum(total_bytes) from pg_backend_memory_contexts where parent = 'TopTransactionContext';
78+
INSERT INTO test_ht VALUES ('1980-02-01 00:00:00-00', to_double('24.11', 0));
79+
SELECT sum(total_bytes) from pg_backend_memory_contexts where parent = 'TopTransactionContext';
80+
COMMIT;

0 commit comments

Comments
 (0)