Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit8b51083

Browse files
committed
Restructure plpgsql's caching of 'simple' expression evaluation trees
to be less dangerous, and often faster as well. ExprState trees arenot kept across transaction boundaries; this eliminates problems withresource leakage in failed transactions. But by keeping them in aper-transaction EState, we can safely arrange for a single ExprStateto be shared by all the expression evaluations done in a given plpgsqlfunction call. (Formerly it seemed necessary to create and destroy anExprState for each exec_eval_simple_expr() call.) This saves time inany scenario where a plpgsql function executes more than one expression.Seems to be about as fast as 7.3 for simple cases, and significantlyfaster for functions that do a lot of calculations.
1 parent8934790 commit8b51083

File tree

3 files changed

+160
-80
lines changed

3 files changed

+160
-80
lines changed

‎src/pl/plpgsql/src/pl_exec.c

Lines changed: 145 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.91 2003/09/25 23:02:12 tgl Exp $
6+
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.92 2003/09/28 23:37:45 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -57,6 +57,19 @@
5757

5858
staticconstchar*constraise_skip_msg="RAISE";
5959

60+
/*
61+
* All plpgsql function executions within a single transaction share
62+
* the same executor EState for evaluating "simple" expressions. Each
63+
* function call creates its own "eval_econtext" ExprContext within this
64+
* estate. We destroy the estate at transaction shutdown to ensure there
65+
* is no permanent leakage of memory (especially for xact abort case).
66+
*
67+
* If a simple PLpgSQL_expr has been used in the current xact, it is
68+
* linked into the active_simple_exprs list.
69+
*/
70+
staticEState*simple_eval_estate=NULL;
71+
staticPLpgSQL_expr*active_simple_exprs=NULL;
72+
6073
/************************************************************
6174
* Local function forward declarations
6275
************************************************************/
@@ -135,7 +148,7 @@ static void exec_eval_datum(PLpgSQL_execstate * estate,
135148
Oid*typeid,
136149
Datum*value,
137150
bool*isnull);
138-
staticintexec_eval_subscript(PLpgSQL_execstate*estate,
151+
staticintexec_eval_integer(PLpgSQL_execstate*estate,
139152
PLpgSQL_expr*expr,
140153
bool*isNull);
141154
staticDatumexec_eval_expr(PLpgSQL_execstate*estate,
@@ -381,6 +394,9 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
381394
}
382395

383396
/* Clean up any leftover temporary memory */
397+
if (estate.eval_econtext!=NULL)
398+
FreeExprContext(estate.eval_econtext);
399+
estate.eval_econtext=NULL;
384400
exec_eval_cleanup(&estate);
385401

386402
/*
@@ -653,6 +669,9 @@ plpgsql_exec_trigger(PLpgSQL_function * func,
653669
}
654670

655671
/* Clean up any leftover temporary memory */
672+
if (estate.eval_econtext!=NULL)
673+
FreeExprContext(estate.eval_econtext);
674+
estate.eval_econtext=NULL;
656675
exec_eval_cleanup(&estate);
657676

658677
/*
@@ -1915,10 +1934,9 @@ exec_eval_cleanup(PLpgSQL_execstate * estate)
19151934
SPI_freetuptable(estate->eval_tuptable);
19161935
estate->eval_tuptable=NULL;
19171936

1918-
/* Clear result of exec_eval_simple_expr */
1937+
/* Clear result of exec_eval_simple_expr(but keep the econtext)*/
19191938
if (estate->eval_econtext!=NULL)
1920-
FreeExprContext(estate->eval_econtext);
1921-
estate->eval_econtext=NULL;
1939+
ResetExprContext(estate->eval_econtext);
19221940
}
19231941

19241942

@@ -1962,7 +1980,7 @@ exec_prepare_plan(PLpgSQL_execstate * estate,
19621980
expr->plan=SPI_saveplan(plan);
19631981
spi_plan= (_SPI_plan*)expr->plan;
19641982
expr->plan_argtypes=spi_plan->argtypes;
1965-
expr->plan_simple_expr=NULL;
1983+
expr->expr_simple_expr=NULL;
19661984
exec_simple_check_plan(expr);
19671985

19681986
SPI_freeplan(plan);
@@ -2931,9 +2949,9 @@ exec_assign_value(PLpgSQL_execstate * estate,
29312949
boolsubisnull;
29322950

29332951
subscriptvals[i]=
2934-
exec_eval_subscript(estate,
2935-
subscripts[nsubscripts-1-i],
2936-
&subisnull);
2952+
exec_eval_integer(estate,
2953+
subscripts[nsubscripts-1-i],
2954+
&subisnull);
29372955
havenullsubscript |=subisnull;
29382956
}
29392957

@@ -3065,7 +3083,7 @@ exec_eval_datum(PLpgSQL_execstate * estate,
30653083
casePLPGSQL_DTYPE_TRIGARG:
30663084
trigarg= (PLpgSQL_trigarg*)datum;
30673085
*typeid=TEXTOID;
3068-
tgargno=exec_eval_subscript(estate,trigarg->argnum,isnull);
3086+
tgargno=exec_eval_integer(estate,trigarg->argnum,isnull);
30693087
if (*isnull||tgargno<0||tgargno >=estate->trig_nargs)
30703088
{
30713089
*value= (Datum)0;
@@ -3089,33 +3107,28 @@ exec_eval_datum(PLpgSQL_execstate * estate,
30893107
}
30903108

30913109
/* ----------
3092-
*exec_eval_subscriptHack to allow subscripting ofresultvariables.
3110+
*exec_eval_integerEvaluate an expression, coerceresultto int4
30933111
*
3094-
* The caller may already have an open eval_econtext, which we have to
3095-
* save and restore around the call of exec_eval_expr.
3112+
* Note we do not do exec_eval_cleanup here; the caller must do it at
3113+
* some later point. (We do this because the caller may be holding the
3114+
* results of other, pass-by-reference, expression evaluations, such as
3115+
* an array value to be subscripted. Also see notes in exec_eval_simple_expr
3116+
* about allocation of the parameter array.)
30963117
* ----------
30973118
*/
30983119
staticint
3099-
exec_eval_subscript(PLpgSQL_execstate*estate,
3100-
PLpgSQL_expr*expr,
3101-
bool*isNull)
3120+
exec_eval_integer(PLpgSQL_execstate*estate,
3121+
PLpgSQL_expr*expr,
3122+
bool*isNull)
31023123
{
3103-
ExprContext*save_econtext;
3104-
Datumsubscriptdatum;
3105-
Oidsubscripttypeid;
3106-
intresult;
3124+
Datumexprdatum;
3125+
Oidexprtypeid;
31073126

3108-
save_econtext=estate->eval_econtext;
3109-
estate->eval_econtext=NULL;
3110-
subscriptdatum=exec_eval_expr(estate,expr,isNull,&subscripttypeid);
3111-
subscriptdatum=exec_simple_cast_value(subscriptdatum,
3112-
subscripttypeid,
3113-
INT4OID,-1,
3114-
isNull);
3115-
result=DatumGetInt32(subscriptdatum);
3116-
exec_eval_cleanup(estate);
3117-
estate->eval_econtext=save_econtext;
3118-
returnresult;
3127+
exprdatum=exec_eval_expr(estate,expr,isNull,&exprtypeid);
3128+
exprdatum=exec_simple_cast_value(exprdatum,exprtypeid,
3129+
INT4OID,-1,
3130+
isNull);
3131+
returnDatumGetInt32(exprdatum);
31193132
}
31203133

31213134
/* ----------
@@ -3143,7 +3156,7 @@ exec_eval_expr(PLpgSQL_execstate * estate,
31433156
* If this is a simple expression, bypass SPI and use the executor
31443157
* directly
31453158
*/
3146-
if (expr->plan_simple_expr!=NULL)
3159+
if (expr->expr_simple_expr!=NULL)
31473160
returnexec_eval_simple_expr(estate,expr,isNull,rettype);
31483161

31493162
rc=exec_run_select(estate,expr,2,NULL);
@@ -3262,6 +3275,10 @@ exec_run_select(PLpgSQL_execstate * estate,
32623275
/* ----------
32633276
* exec_eval_simple_expr -Evaluate a simple expression returning
32643277
*a Datum by directly calling ExecEvalExpr().
3278+
*
3279+
* Note: if pass-by-reference, the result is in the eval_econtext's
3280+
* temporary memory context. It will be freed when exec_eval_cleanup
3281+
* is done.
32653282
* ----------
32663283
*/
32673284
staticDatum
@@ -3271,64 +3288,97 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate,
32713288
Oid*rettype)
32723289
{
32733290
Datumretval;
3274-
inti;
32753291
ExprContext*econtext;
32763292
ParamListInfoparamLI;
3293+
inti;
3294+
3295+
/*
3296+
* Pass back previously-determined result type.
3297+
*/
3298+
*rettype=expr->expr_simple_type;
3299+
3300+
/*
3301+
* Create an EState for evaluation of simple expressions, if there's
3302+
* not one already in the current transaction. The EState is made a
3303+
* child of TopTransactionContext so it will have the right lifespan.
3304+
*/
3305+
if (simple_eval_estate==NULL)
3306+
{
3307+
MemoryContextoldcontext;
3308+
3309+
oldcontext=MemoryContextSwitchTo(TopTransactionContext);
3310+
simple_eval_estate=CreateExecutorState();
3311+
MemoryContextSwitchTo(oldcontext);
3312+
}
3313+
3314+
/*
3315+
* Prepare the expression for execution, if it's not been done already
3316+
* in the current transaction.
3317+
*/
3318+
if (expr->expr_simple_state==NULL)
3319+
{
3320+
expr->expr_simple_state=ExecPrepareExpr(expr->expr_simple_expr,
3321+
simple_eval_estate);
3322+
/* Add it to list for cleanup */
3323+
expr->expr_simple_next=active_simple_exprs;
3324+
active_simple_exprs=expr;
3325+
}
32773326

32783327
/*
3279-
* Create an expression contextto hold the arguments and the result
3280-
*of this expression evaluation. This must be a child of the EState
3281-
*we created in the SPI plan's context.
3328+
* Create an expression contextfor simple expressions, if there's
3329+
*not one already in the current function call. This must be a
3330+
*child of simple_eval_estate.
32823331
*/
3283-
econtext=CreateExprContext(expr->plan_simple_estate);
3332+
econtext=estate->eval_econtext;
3333+
if (econtext==NULL)
3334+
{
3335+
econtext=CreateExprContext(simple_eval_estate);
3336+
estate->eval_econtext=econtext;
3337+
}
32843338

32853339
/*
32863340
* Param list can live in econtext's temporary memory context.
3341+
*
3342+
* XXX think about avoiding repeated palloc's for param lists?
3343+
* Beware however that this routine is re-entrant: exec_eval_datum()
3344+
* can call it back for subscript evaluation, and so there can be a
3345+
* need to have more than one active param list.
32873346
*/
32883347
paramLI= (ParamListInfo)
32893348
MemoryContextAlloc(econtext->ecxt_per_tuple_memory,
32903349
(expr->nparams+1)*sizeof(ParamListInfoData));
3291-
econtext->ecxt_param_list_info=paramLI;
32923350

32933351
/*
3294-
* Put the parameter values into the parameter list info of the
3295-
* expression context.
3352+
* Put the parameter values into the parameter list entries.
32963353
*/
3297-
for (i=0;i<expr->nparams;i++,paramLI++)
3354+
for (i=0;i<expr->nparams;i++)
32983355
{
32993356
PLpgSQL_datum*datum=estate->datums[expr->params[i]];
33003357
Oidparamtypeid;
33013358

3302-
paramLI->kind=PARAM_NUM;
3303-
paramLI->id=i+1;
3359+
paramLI[i].kind=PARAM_NUM;
3360+
paramLI[i].id=i+1;
33043361
exec_eval_datum(estate,datum,expr->plan_argtypes[i],
3305-
&paramtypeid,&paramLI->value,&paramLI->isnull);
3362+
&paramtypeid,
3363+
&paramLI[i].value,&paramLI[i].isnull);
33063364
}
3307-
paramLI->kind=PARAM_INVALID;
3365+
paramLI[i].kind=PARAM_INVALID;
33083366

33093367
/*
3310-
*Initialize things
3368+
*Now we can safely make the econtext point to the param list.
33113369
*/
3312-
*rettype=expr->plan_simple_type;
3370+
econtext->ecxt_param_list_info=paramLI;
33133371

33143372
/*
33153373
* Now call the executor to evaluate the expression
33163374
*/
33173375
SPI_push();
3318-
retval=ExecEvalExprSwitchContext(expr->plan_simple_expr,
3376+
retval=ExecEvalExprSwitchContext(expr->expr_simple_state,
33193377
econtext,
33203378
isNull,
33213379
NULL);
33223380
SPI_pop();
33233381

3324-
/*
3325-
* Note: if pass-by-reference, the result is in the econtext's
3326-
* temporary memory context. It will be freed when exec_eval_cleanup
3327-
* is done.
3328-
*/
3329-
Assert(estate->eval_econtext==NULL);
3330-
estate->eval_econtext=econtext;
3331-
33323382
/*
33333383
* That's it.
33343384
*/
@@ -3795,9 +3845,8 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
37953845
_SPI_plan*spi_plan= (_SPI_plan*)expr->plan;
37963846
Plan*plan;
37973847
TargetEntry*tle;
3798-
MemoryContextoldcontext;
37993848

3800-
expr->plan_simple_expr=NULL;
3849+
expr->expr_simple_expr=NULL;
38013850

38023851
/*
38033852
* 1. We can only evaluate queries that resulted in one single
@@ -3842,21 +3891,14 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
38423891
return;
38433892

38443893
/*
3845-
* Yes - this is a simple expression. Prepare to execute it. We need
3846-
* an EState and an expression state tree, which we'll put into the
3847-
* plan context so they will have appropriate lifespan.
3894+
* Yes - this is a simple expression. Mark it as such, and initialize
3895+
* state to "not executing".
38483896
*/
3849-
oldcontext=MemoryContextSwitchTo(spi_plan->plancxt);
3850-
3851-
expr->plan_simple_estate=CreateExecutorState();
3852-
3853-
expr->plan_simple_expr=ExecPrepareExpr(tle->expr,
3854-
expr->plan_simple_estate);
3855-
3856-
MemoryContextSwitchTo(oldcontext);
3857-
3897+
expr->expr_simple_expr=tle->expr;
3898+
expr->expr_simple_state=NULL;
3899+
expr->expr_simple_next=NULL;
38583900
/* Also stash away the expression result type */
3859-
expr->plan_simple_type=exprType((Node*)tle->expr);
3901+
expr->expr_simple_type=exprType((Node*)tle->expr);
38603902
}
38613903

38623904
/*
@@ -3893,3 +3935,35 @@ exec_set_found(PLpgSQL_execstate * estate, bool state)
38933935
var->value= (Datum)state;
38943936
var->isnull= false;
38953937
}
3938+
3939+
/*
3940+
* plpgsql_eoxact --- post-transaction-commit-or-abort cleanup
3941+
*
3942+
* If a simple_eval_estate was created in the current transaction,
3943+
* it has to be cleaned up, and we have to mark all active PLpgSQL_expr
3944+
* structs that are using it as no longer active.
3945+
*/
3946+
void
3947+
plpgsql_eoxact(boolisCommit,void*arg)
3948+
{
3949+
PLpgSQL_expr*expr;
3950+
PLpgSQL_expr*enext;
3951+
3952+
/* Mark all active exprs as inactive */
3953+
for (expr=active_simple_exprs;expr;expr=enext)
3954+
{
3955+
enext=expr->expr_simple_next;
3956+
expr->expr_simple_state=NULL;
3957+
expr->expr_simple_next=NULL;
3958+
}
3959+
active_simple_exprs=NULL;
3960+
/*
3961+
* If we are doing a clean transaction shutdown, free the EState
3962+
* (so that any remaining resources will be released correctly).
3963+
* In an abort, we expect the regular abort recovery procedures to
3964+
* release everything of interest.
3965+
*/
3966+
if (isCommit&&simple_eval_estate)
3967+
FreeExecutorState(simple_eval_estate);
3968+
simple_eval_estate=NULL;
3969+
}

‎src/pl/plpgsql/src/pl_handler.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.17 2003/08/04 00:43:33 momjian Exp $
6+
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.18 2003/09/28 23:37:45 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -46,7 +46,6 @@
4646

4747
staticintplpgsql_firstcall=1;
4848

49-
voidplpgsql_init(void);
5049
staticvoidplpgsql_init_all(void);
5150

5251

@@ -64,6 +63,8 @@ plpgsql_init(void)
6463

6564
plpgsql_HashTableInit();
6665

66+
RegisterEOXactCallback(plpgsql_eoxact,NULL);
67+
6768
plpgsql_firstcall=0;
6869
}
6970

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp