v01_fix_memory_leak_in_hashed_subplan_node.patch

application/octet-stream

Filename: v01_fix_memory_leak_in_hashed_subplan_node.patch
Type: application/octet-stream
Part: 0
Message: Re:BUG #19040: Memory leak in hashed subplan node due to missing hashtempcxt reset

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v1
File+
src/backend/executor/nodeSubplan.c 31 12
src/test/regress/expected/subselect.out 19 0
src/test/regress/sql/subselect.sql 11 0
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);