diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index f7f6fc2da0b..94968c9dbfc 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -156,21 +156,36 @@ ExecHashSubPlan(SubPlanState *node, */ if (slotNoNulls(slot)) { - if (node->havehashrows && - FindTupleHashEntry(node->hashtable, - slot, - node->cur_eq_comp, - node->lhs_hash_expr) != NULL) + if (node->havehashrows) { - ExecClearTuple(slot); - return BoolGetDatum(true); + TupleHashEntry entry = FindTupleHashEntry(node->hashtable, + slot, + node->cur_eq_comp, + node->lhs_hash_expr); + + + /* Must reset temp context after each hashtable lookup */ + MemoryContextReset(node->hashtable->tempcxt); + + if (entry != NULL) + { + ExecClearTuple(slot); + return BoolGetDatum(true); + } } - if (node->havenullrows && - findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs)) + if (node->havenullrows) { - ExecClearTuple(slot); - *isNull = true; - return BoolGetDatum(false); + bool found = findPartialMatch(node->hashnulls, slot, node->cur_eq_funcs); + + /* Must reset temp context after each hashtable lookup */ + MemoryContextReset(node->hashnulls->tempcxt); + + if (found) + { + ExecClearTuple(slot); + *isNull = true; + return BoolGetDatum(false); + } } ExecClearTuple(slot); return BoolGetDatum(false); @@ -629,11 +644,15 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext) if (slotNoNulls(slot)) { (void) LookupTupleHashEntry(node->hashtable, slot, &isnew, NULL); + /* Must reset temp context after each hashtable lookup */ + MemoryContextReset(node->hashtable->tempcxt); node->havehashrows = true; } else if (node->hashnulls) { (void) LookupTupleHashEntry(node->hashnulls, slot, &isnew, NULL); + /* Must reset temp context after each hashtable lookup */ + MemoryContextReset(node->hashnulls->tempcxt); node->havenullrows = true; } diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out index 40d8056fcea..394568dbe4c 100644 --- a/src/test/regress/expected/subselect.out +++ b/src/test/regress/expected/subselect.out @@ -889,6 +889,25 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- Another test case for cross-type hashed subplans: comparison of -- inner-side values must be done with appropriate operator -- +-- +-- Test case for memory leak in hashed subplan node +-- +create temp table memory_leak (c1 numeric, c2 int4); +insert into memory_leak +select i, i from generate_series(1, 3000000) i; +explain (verbose, costs off) +select c1, c2 from memory_leak where c1 not in( select i from generate_series(1, 10000) i); + QUERY PLAN +----------------------------------------------------------------------------- + Seq Scan on pg_temp.memory_leak + Output: memory_leak.c1, memory_leak.c2 + Filter: (NOT (ANY (memory_leak.c1 = ((hashed SubPlan 1).col1)::numeric))) + SubPlan 1 + -> Function Scan on pg_catalog.generate_series i + Output: i.i + Function Call: generate_series(1, 10000) +(7 rows) + explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name); QUERY PLAN diff --git a/src/test/regress/sql/subselect.sql b/src/test/regress/sql/subselect.sql index fec38ef85a6..9fd3d997e62 100644 --- a/src/test/regress/sql/subselect.sql +++ b/src/test/regress/sql/subselect.sql @@ -503,6 +503,17 @@ select * from outer_text where (f1, f2) not in (select * from inner_text); -- inner-side values must be done with appropriate operator -- +-- +-- Test case for memory leak in hashed subplan node +-- +create temp table memory_leak (c1 numeric, c2 int4); + +insert into memory_leak +select i, i from generate_series(1, 3000000) i; + +explain (verbose, costs off) +select c1, c2 from memory_leak where c1 not in( select i from generate_series(1, 10000) i); + explain (verbose, costs off) select 'foo'::text in (select 'bar'::name union all select 'bar'::name);