dbTalk Databases Forums  

[BUGS] Out-of-Memory with ROWTYPE assignment in PL/pgSQL FOR loop

mailing.database.pgsql-bugs mailing.database.pgsql-bugs


Discuss [BUGS] Out-of-Memory with ROWTYPE assignment in PL/pgSQL FOR loop in the mailing.database.pgsql-bugs forum.



Reply
 
Thread Tools Display Modes
  #1  
Old   
Bill Rugolsky Jr.
 
Posts: n/a

Default [BUGS] Out-of-Memory with ROWTYPE assignment in PL/pgSQL FOR loop - 06-20-2005 , 04:24 PM






Hello,

The PL/pgSQL FOR loop in the function consume_memory() defined below
will consume VM on each iteration until the process hits its ulimit.
The problem occurs with variables of ROWTYPE; there is no unbounded
allocation when using simple types such as integer or varchar. Before I
delve into the SPI code, perhaps someone familiar with PostgreSQL internals
can spot the resource leak.

Tested with 8.0.1 and CVS head as of 2005-06-20:

-- Start of test code

-- create a table with ten million rows
CREATE TEMPORARY TABLE ten ( n integer DEFAULT 0 ) ;
INSERT INTO ten VALUES (0);
INSERT INTO ten VALUES (1);
INSERT INTO ten VALUES (2);
INSERT INTO ten VALUES (3);
INSERT INTO ten VALUES (4);
INSERT INTO ten VALUES (5);
INSERT INTO ten VALUES (6);
INSERT INTO ten VALUES (7);
INSERT INTO ten VALUES (8);
INSERT INTO ten VALUES (9);
CREATE TEMPORARY TABLE thousand AS
SELECT 100*i.n + 10*j.n + k.n AS n FROM ten AS i, ten as j, ten as k ;
CREATE TEMPORARY TABLE tenmillion AS
SELECT 10000*i.n + 10*j.n + k.n AS n FROM thousand AS i, thousand as j, ten as k ;

-- a function to consume memory
CREATE OR REPLACE FUNCTION consume_memory()
RETURNS void AS $PROC$
DECLARE
rec tenmillion%ROWTYPE ;
prev tenmillion%ROWTYPE ;
BEGIN
FOR rec IN SELECT * FROM tenmillion LOOP
prev := rec ;
END LOOP ;
RETURN ;
END
$PROC$ LANGUAGE plpgsql;

-- Until this point, memory usage is approximately constant.
-- Evaluating the above function will rapidly consume VM.

SELECT consume_memory() ;

-- End of test code

Here's a record of Committed_AS from /proc/meminfo on a Linux 2.6 system,
over the course of the test:

rugolsky@ti64: while : ; do grep Committed_AS /proc/meminfo ; sleep 1 ; done
Committed_AS: 225592 kB
Committed_AS: 225592 kB
Committed_AS: 233692 kB <- Started
Committed_AS: 258280 kB
Committed_AS: 282868 kB
Committed_AS: 299260 kB
Committed_AS: 323848 kB
Committed_AS: 340232 kB
Committed_AS: 348436 kB
Committed_AS: 356632 kB
Committed_AS: 381220 kB
Committed_AS: 397612 kB
Committed_AS: 414004 kB
Committed_AS: 422200 kB
Committed_AS: 438592 kB
Committed_AS: 463180 kB
Committed_AS: 487768 kB
Committed_AS: 504160 kB
Committed_AS: 504160 kB
Committed_AS: 520552 kB
Committed_AS: 545140 kB
Committed_AS: 569728 kB
Committed_AS: 586120 kB
Committed_AS: 586120 kB
Committed_AS: 602512 kB
Committed_AS: 225640 kB <- Cancelled

Regards,

Bill Rugolsky

---------------------------(end of broadcast)---------------------------
TIP 9: the planner will ignore your desire to choose an index scan if your
joining column's datatypes do not match

Reply With Quote
  #2  
Old   
Tom Lane
 
Posts: n/a

Default Re: [BUGS] Out-of-Memory with ROWTYPE assignment in PL/pgSQL FOR loop - 06-20-2005 , 05:55 PM






"Bill Rugolsky Jr." <brugolsky (AT) telemetry-investments (DOT) com> writes:
Quote:
The PL/pgSQL FOR loop in the function consume_memory() defined below
will consume VM on each iteration until the process hits its ulimit.
The problem occurs with variables of ROWTYPE; there is no unbounded
allocation when using simple types such as integer or varchar.
Yeah, looks like I introduced a memory leak with the 8.0 changes for
better support of rowtype variables :-(. Here's the patch.

regards, tom lane


Index: pl_exec.c
================================================== =================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.127.4.2
diff -c -r1.127.4.2 pl_exec.c
*** pl_exec.c 20 Jun 2005 20:44:50 -0000 1.127.4.2
--- pl_exec.c 20 Jun 2005 22:46:14 -0000
***************
*** 2003,2013 ****
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;
- estate->eval_econtext = NULL;

estate->err_func = func;
estate->err_stmt = NULL;
estate->err_text = NULL;
}

/* ----------
--- 2003,2032 ----
estate->eval_tuptable = NULL;
estate->eval_processed = 0;
estate->eval_lastoid = InvalidOid;

estate->err_func = func;
estate->err_stmt = NULL;
estate->err_text = NULL;
+
+ /*
+ * Create an EState for evaluation of simple expressions, if there's
+ * not one already in the current transaction. The EState is made a
+ * child of TopTransactionContext so it will have the right lifespan.
+ */
+ if (simple_eval_estate == NULL)
+ {
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(TopTransactionContext);
+ simple_eval_estate = CreateExecutorState();
+ MemoryContextSwitchTo(oldcontext);
+ }
+
+ /*
+ * Create an expression context for simple expressions.
+ * This must be a child of simple_eval_estate.
+ */
+ estate->eval_econtext = CreateExprContext(simple_eval_estate);
}

/* ----------
***************
*** 3238,3243 ****
--- 3257,3264 ----
Datum *value,
bool *isnull)
{
+ MemoryContext oldcontext;
+
switch (datum->dtype)
{
case PLPGSQL_DTYPE_VAR:
***************
*** 3264,3272 ****
--- 3285,3295 ----
elog(ERROR, "row variable has no tupdesc");
/* Make sure we have a valid type/typmod setting */
BlessTupleDesc(row->rowtupdesc);
+ oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
tup = make_tuple_from_row(estate, row, row->rowtupdesc);
if (tup == NULL) /* should not happen */
elog(ERROR, "row not compatible with its own tupdesc");
+ MemoryContextSwitchTo(oldcontext);
*typeid = row->rowtupdesc->tdtypeid;
*value = HeapTupleGetDatum(tup);
*isnull = false;
***************
*** 3299,3308 ****
--- 3322,3333 ----
* fields. Copy the tuple body and insert the right
* values.
*/
+ oldcontext = MemoryContextSwitchTo(estate->eval_econtext->ecxt_per_tuple_memory);
heap_copytuple_with_tuple(rec->tup, &worktup);
HeapTupleHeaderSetDatumLength(worktup.t_data, worktup.t_len);
HeapTupleHeaderSetTypeId(worktup.t_data, rec->tupdesc->tdtypeid);
HeapTupleHeaderSetTypMod(worktup.t_data, rec->tupdesc->tdtypmod);
+ MemoryContextSwitchTo(oldcontext);
*typeid = rec->tupdesc->tdtypeid;
*value = HeapTupleGetDatum(&worktup);
*isnull = false;
***************
*** 3579,3585 ****
Oid *rettype)
{
Datum retval;
! ExprContext * volatile econtext;
ParamListInfo paramLI;
int i;
Snapshot saveActiveSnapshot;
--- 3604,3610 ----
Oid *rettype)
{
Datum retval;
! ExprContext *econtext = estate->eval_econtext;
ParamListInfo paramLI;
int i;
Snapshot saveActiveSnapshot;
***************
*** 3590,3609 ****
*rettype = expr->expr_simple_type;

/*
- * Create an EState for evaluation of simple expressions, if there's
- * not one already in the current transaction. The EState is made a
- * child of TopTransactionContext so it will have the right lifespan.
- */
- if (simple_eval_estate == NULL)
- {
- MemoryContext oldcontext;
-
- oldcontext = MemoryContextSwitchTo(TopTransactionContext);
- simple_eval_estate = CreateExecutorState();
- MemoryContextSwitchTo(oldcontext);
- }
-
- /*
* Prepare the expression for execution, if it's not been done already
* in the current transaction.
*/
--- 3615,3620 ----
***************
*** 3617,3634 ****
}

/*
- * Create an expression context for simple expressions, if there's not
- * one already in the current function call. This must be a child of
- * simple_eval_estate.
- */
- econtext = estate->eval_econtext;
- if (econtext == NULL)
- {
- econtext = CreateExprContext(simple_eval_estate);
- estate->eval_econtext = econtext;
- }
-
- /*
* Param list can live in econtext's temporary memory context.
*
* XXX think about avoiding repeated palloc's for param lists? Beware
--- 3628,3633 ----

---------------------------(end of broadcast)---------------------------
TIP 8: explain analyze is your friend


Reply With Quote
  #3  
Old   
Bill Rugolsky Jr.
 
Posts: n/a

Default Re: [BUGS] Out-of-Memory with ROWTYPE assignment in PL/pgSQL FOR loop - 06-20-2005 , 08:35 PM



On Mon, Jun 20, 2005 at 06:54:20PM -0400, Tom Lane wrote:
Quote:
"Bill Rugolsky Jr." <brugolsky (AT) telemetry-investments (DOT) com> writes:
The PL/pgSQL FOR loop in the function consume_memory() defined below
will consume VM on each iteration until the process hits its ulimit.
The problem occurs with variables of ROWTYPE; there is no unbounded
allocation when using simple types such as integer or varchar.

Yeah, looks like I introduced a memory leak with the 8.0 changes for
better support of rowtype variables :-(. Here's the patch.
Thank you for the quick reply; much appreciated!
Applied and tested against CVS head; that plugged the leak.

-Bill

---------------------------(end of broadcast)---------------------------
TIP 7: don't forget to increase your free space map settings


Reply With Quote
Reply




Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.3
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.