aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/pl_exec.c216
-rw-r--r--src/pl/plpgsql/src/pl_handler.c5
-rw-r--r--src/pl/plpgsql/src/plpgsql.h19
3 files changed, 160 insertions, 80 deletions
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f76dd952ac8..824d638c7e7 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.91 2003/09/25 23:02:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.92 2003/09/28 23:37:45 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -57,6 +57,19 @@
static const char *const raise_skip_msg = "RAISE";
+/*
+ * All plpgsql function executions within a single transaction share
+ * the same executor EState for evaluating "simple" expressions. Each
+ * function call creates its own "eval_econtext" ExprContext within this
+ * estate. We destroy the estate at transaction shutdown to ensure there
+ * is no permanent leakage of memory (especially for xact abort case).
+ *
+ * If a simple PLpgSQL_expr has been used in the current xact, it is
+ * linked into the active_simple_exprs list.
+ */
+static EState *simple_eval_estate = NULL;
+static PLpgSQL_expr *active_simple_exprs = NULL;
+
/************************************************************
* Local function forward declarations
************************************************************/
@@ -135,7 +148,7 @@ static void exec_eval_datum(PLpgSQL_execstate * estate,
Oid *typeid,
Datum *value,
bool *isnull);
-static int exec_eval_subscript(PLpgSQL_execstate * estate,
+static int exec_eval_integer(PLpgSQL_execstate * estate,
PLpgSQL_expr * expr,
bool *isNull);
static Datum exec_eval_expr(PLpgSQL_execstate * estate,
@@ -381,6 +394,9 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
}
/* Clean up any leftover temporary memory */
+ if (estate.eval_econtext != NULL)
+ FreeExprContext(estate.eval_econtext);
+ estate.eval_econtext = NULL;
exec_eval_cleanup(&estate);
/*
@@ -653,6 +669,9 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
}
/* Clean up any leftover temporary memory */
+ if (estate.eval_econtext != NULL)
+ FreeExprContext(estate.eval_econtext);
+ estate.eval_econtext = NULL;
exec_eval_cleanup(&estate);
/*
@@ -1915,10 +1934,9 @@ exec_eval_cleanup(PLpgSQL_execstate * estate)
SPI_freetuptable(estate->eval_tuptable);
estate->eval_tuptable = NULL;
- /* Clear result of exec_eval_simple_expr */
+ /* Clear result of exec_eval_simple_expr (but keep the econtext) */
if (estate->eval_econtext != NULL)
- FreeExprContext(estate->eval_econtext);
- estate->eval_econtext = NULL;
+ ResetExprContext(estate->eval_econtext);
}
@@ -1962,7 +1980,7 @@ exec_prepare_plan(PLpgSQL_execstate * estate,
expr->plan = SPI_saveplan(plan);
spi_plan = (_SPI_plan *) expr->plan;
expr->plan_argtypes = spi_plan->argtypes;
- expr->plan_simple_expr = NULL;
+ expr->expr_simple_expr = NULL;
exec_simple_check_plan(expr);
SPI_freeplan(plan);
@@ -2931,9 +2949,9 @@ exec_assign_value(PLpgSQL_execstate * estate,
bool subisnull;
subscriptvals[i] =
- exec_eval_subscript(estate,
- subscripts[nsubscripts - 1 - i],
- &subisnull);
+ exec_eval_integer(estate,
+ subscripts[nsubscripts - 1 - i],
+ &subisnull);
havenullsubscript |= subisnull;
}
@@ -3065,7 +3083,7 @@ exec_eval_datum(PLpgSQL_execstate * estate,
case PLPGSQL_DTYPE_TRIGARG:
trigarg = (PLpgSQL_trigarg *) datum;
*typeid = TEXTOID;
- tgargno = exec_eval_subscript(estate, trigarg->argnum, isnull);
+ tgargno = exec_eval_integer(estate, trigarg->argnum, isnull);
if (*isnull || tgargno < 0 || tgargno >= estate->trig_nargs)
{
*value = (Datum) 0;
@@ -3089,33 +3107,28 @@ exec_eval_datum(PLpgSQL_execstate * estate,
}
/* ----------
- * exec_eval_subscript Hack to allow subscripting of result variables.
+ * exec_eval_integer Evaluate an expression, coerce result to int4
*
- * The caller may already have an open eval_econtext, which we have to
- * save and restore around the call of exec_eval_expr.
+ * Note we do not do exec_eval_cleanup here; the caller must do it at
+ * some later point. (We do this because the caller may be holding the
+ * results of other, pass-by-reference, expression evaluations, such as
+ * an array value to be subscripted. Also see notes in exec_eval_simple_expr
+ * about allocation of the parameter array.)
* ----------
*/
static int
-exec_eval_subscript(PLpgSQL_execstate * estate,
- PLpgSQL_expr * expr,
- bool *isNull)
+exec_eval_integer(PLpgSQL_execstate * estate,
+ PLpgSQL_expr * expr,
+ bool *isNull)
{
- ExprContext *save_econtext;
- Datum subscriptdatum;
- Oid subscripttypeid;
- int result;
+ Datum exprdatum;
+ Oid exprtypeid;
- save_econtext = estate->eval_econtext;
- estate->eval_econtext = NULL;
- subscriptdatum = exec_eval_expr(estate, expr, isNull, &subscripttypeid);
- subscriptdatum = exec_simple_cast_value(subscriptdatum,
- subscripttypeid,
- INT4OID, -1,
- isNull);
- result = DatumGetInt32(subscriptdatum);
- exec_eval_cleanup(estate);
- estate->eval_econtext = save_econtext;
- return result;
+ exprdatum = exec_eval_expr(estate, expr, isNull, &exprtypeid);
+ exprdatum = exec_simple_cast_value(exprdatum, exprtypeid,
+ INT4OID, -1,
+ isNull);
+ return DatumGetInt32(exprdatum);
}
/* ----------
@@ -3143,7 +3156,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
* If this is a simple expression, bypass SPI and use the executor
* directly
*/
- if (expr->plan_simple_expr != NULL)
+ if (expr->expr_simple_expr != NULL)
return exec_eval_simple_expr(estate, expr, isNull, rettype);
rc = exec_run_select(estate, expr, 2, NULL);
@@ -3262,6 +3275,10 @@ exec_run_select(PLpgSQL_execstate * estate,
/* ----------
* exec_eval_simple_expr - Evaluate a simple expression returning
* a Datum by directly calling ExecEvalExpr().
+ *
+ * Note: if pass-by-reference, the result is in the eval_econtext's
+ * temporary memory context. It will be freed when exec_eval_cleanup
+ * is done.
* ----------
*/
static Datum
@@ -3271,65 +3288,98 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
Oid *rettype)
{
Datum retval;
- int i;
ExprContext *econtext;
ParamListInfo paramLI;
+ int i;
+
+ /*
+ * Pass back previously-determined result type.
+ */
+ *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.
+ */
+ if (expr->expr_simple_state == NULL)
+ {
+ expr->expr_simple_state = ExecPrepareExpr(expr->expr_simple_expr,
+ simple_eval_estate);
+ /* Add it to list for cleanup */
+ expr->expr_simple_next = active_simple_exprs;
+ active_simple_exprs = expr;
+ }
/*
- * Create an expression context to hold the arguments and the result
- * of this expression evaluation. This must be a child of the EState
- * we created in the SPI plan's context.
+ * 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 = CreateExprContext(expr->plan_simple_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 however that this routine is re-entrant: exec_eval_datum()
+ * can call it back for subscript evaluation, and so there can be a
+ * need to have more than one active param list.
*/
paramLI = (ParamListInfo)
MemoryContextAlloc(econtext->ecxt_per_tuple_memory,
(expr->nparams + 1) * sizeof(ParamListInfoData));
- econtext->ecxt_param_list_info = paramLI;
/*
- * Put the parameter values into the parameter list info of the
- * expression context.
+ * Put the parameter values into the parameter list entries.
*/
- for (i = 0; i < expr->nparams; i++, paramLI++)
+ for (i = 0; i < expr->nparams; i++)
{
PLpgSQL_datum *datum = estate->datums[expr->params[i]];
Oid paramtypeid;
- paramLI->kind = PARAM_NUM;
- paramLI->id = i + 1;
+ paramLI[i].kind = PARAM_NUM;
+ paramLI[i].id = i + 1;
exec_eval_datum(estate, datum, expr->plan_argtypes[i],
- &paramtypeid, &paramLI->value, &paramLI->isnull);
+ &paramtypeid,
+ &paramLI[i].value, &paramLI[i].isnull);
}
- paramLI->kind = PARAM_INVALID;
+ paramLI[i].kind = PARAM_INVALID;
/*
- * Initialize things
+ * Now we can safely make the econtext point to the param list.
*/
- *rettype = expr->plan_simple_type;
+ econtext->ecxt_param_list_info = paramLI;
/*
* Now call the executor to evaluate the expression
*/
SPI_push();
- retval = ExecEvalExprSwitchContext(expr->plan_simple_expr,
+ retval = ExecEvalExprSwitchContext(expr->expr_simple_state,
econtext,
isNull,
NULL);
SPI_pop();
/*
- * Note: if pass-by-reference, the result is in the econtext's
- * temporary memory context. It will be freed when exec_eval_cleanup
- * is done.
- */
- Assert(estate->eval_econtext == NULL);
- estate->eval_econtext = econtext;
-
- /*
* That's it.
*/
return retval;
@@ -3795,9 +3845,8 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
_SPI_plan *spi_plan = (_SPI_plan *) expr->plan;
Plan *plan;
TargetEntry *tle;
- MemoryContext oldcontext;
- expr->plan_simple_expr = NULL;
+ expr->expr_simple_expr = NULL;
/*
* 1. We can only evaluate queries that resulted in one single
@@ -3842,21 +3891,14 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
return;
/*
- * Yes - this is a simple expression. Prepare to execute it. We need
- * an EState and an expression state tree, which we'll put into the
- * plan context so they will have appropriate lifespan.
+ * Yes - this is a simple expression. Mark it as such, and initialize
+ * state to "not executing".
*/
- oldcontext = MemoryContextSwitchTo(spi_plan->plancxt);
-
- expr->plan_simple_estate = CreateExecutorState();
-
- expr->plan_simple_expr = ExecPrepareExpr(tle->expr,
- expr->plan_simple_estate);
-
- MemoryContextSwitchTo(oldcontext);
-
+ expr->expr_simple_expr = tle->expr;
+ expr->expr_simple_state = NULL;
+ expr->expr_simple_next = NULL;
/* Also stash away the expression result type */
- expr->plan_simple_type = exprType((Node *) tle->expr);
+ expr->expr_simple_type = exprType((Node *) tle->expr);
}
/*
@@ -3893,3 +3935,35 @@ exec_set_found(PLpgSQL_execstate * estate, bool state)
var->value = (Datum) state;
var->isnull = false;
}
+
+/*
+ * plpgsql_eoxact --- post-transaction-commit-or-abort cleanup
+ *
+ * If a simple_eval_estate was created in the current transaction,
+ * it has to be cleaned up, and we have to mark all active PLpgSQL_expr
+ * structs that are using it as no longer active.
+ */
+void
+plpgsql_eoxact(bool isCommit, void *arg)
+{
+ PLpgSQL_expr *expr;
+ PLpgSQL_expr *enext;
+
+ /* Mark all active exprs as inactive */
+ for (expr = active_simple_exprs; expr; expr = enext)
+ {
+ enext = expr->expr_simple_next;
+ expr->expr_simple_state = NULL;
+ expr->expr_simple_next = NULL;
+ }
+ active_simple_exprs = NULL;
+ /*
+ * If we are doing a clean transaction shutdown, free the EState
+ * (so that any remaining resources will be released correctly).
+ * In an abort, we expect the regular abort recovery procedures to
+ * release everything of interest.
+ */
+ if (isCommit && simple_eval_estate)
+ FreeExecutorState(simple_eval_estate);
+ simple_eval_estate = NULL;
+}
diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c
index 1705d878cc7..25f729e9ce1 100644
--- a/src/pl/plpgsql/src/pl_handler.c
+++ b/src/pl/plpgsql/src/pl_handler.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.17 2003/08/04 00:43:33 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.18 2003/09/28 23:37:45 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -46,7 +46,6 @@
static int plpgsql_firstcall = 1;
-void plpgsql_init(void);
static void plpgsql_init_all(void);
@@ -64,6 +63,8 @@ plpgsql_init(void)
plpgsql_HashTableInit();
+ RegisterEOXactCallback(plpgsql_eoxact, NULL);
+
plpgsql_firstcall = 0;
}
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 80e79df30b8..ea3ce2e1bb4 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.41 2003/09/25 23:02:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.42 2003/09/28 23:37:45 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -166,18 +166,22 @@ typedef struct
} PLpgSQL_datum;
-typedef struct
+typedef struct PLpgSQL_expr
{ /* SQL Query to plan and execute */
int dtype;
int exprno;
char *query;
void *plan;
- ExprState *plan_simple_expr;
- EState *plan_simple_estate;
- Oid plan_simple_type;
Oid *plan_argtypes;
+ /* fields for "simple expression" fast-path execution: */
+ Expr *expr_simple_expr; /* NULL means not a simple expr */
+ Oid expr_simple_type;
+ /* if expr is simple AND in use in current xact, these fields are set: */
+ ExprState *expr_simple_state;
+ struct PLpgSQL_expr *expr_simple_next;
+ /* params to pass to expr */
int nparams;
- int params[1];
+ int params[1]; /* VARIABLE SIZE ARRAY ... must be last */
} PLpgSQL_expr;
@@ -636,6 +640,7 @@ extern void plpgsql_HashTableInit(void);
* Functions in pl_handler.c
* ----------
*/
+extern void plpgsql_init(void);
extern Datum plpgsql_call_handler(PG_FUNCTION_ARGS);
/* ----------
@@ -646,7 +651,7 @@ extern Datum plpgsql_exec_function(PLpgSQL_function * func,
FunctionCallInfo fcinfo);
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function * func,
TriggerData *trigdata);
-
+extern void plpgsql_eoxact(bool isCommit, void *arg);
/* ----------
* Functions for the dynamic string handling in pl_funcs.c