diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2006-08-04 21:33:36 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2006-08-04 21:33:36 +0000 |
commit | c68489863c91a23821c4fcbfd9cfb5bce3220e8f (patch) | |
tree | 89fa9cd063fc5d1927e78c3c78d81d4cfb352a94 /src/backend | |
parent | bf7b205e161a8f11bb3cea39a03d63f79decfc20 (diff) | |
download | postgresql-c68489863c91a23821c4fcbfd9cfb5bce3220e8f.tar.gz |
Fix domain_in() bug exhibited by Darcy Buskermolen. The idea of an EState
that's shorter-lived than the expression state being evaluated in it really
doesn't work :-( --- we end up with fn_extra caches getting deleted while
still in use. Rather than abandon the notion of caching expression state
across domain_in calls altogether, I chose to make domain_in a bit cozier
with ExprContext. All we really need for evaluating variable-free
expressions is an ExprContext, not an EState, so I invented the notion of a
"standalone" ExprContext. domain_in can prevent resource leakages by doing
a ReScanExprContext on this rather than having to free it entirely; so we
can make the ExprContext have the same lifespan (and particularly the same
per_query memory context) as the expression state structs.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/executor/execUtils.c | 71 | ||||
-rw-r--r-- | src/backend/utils/adt/domains.c | 59 |
2 files changed, 101 insertions, 29 deletions
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index c879de3bbc..aa5aeb57f3 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.138 2006/07/31 20:09:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.139 2006/08/04 21:33:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ * CreateExecutorState Create/delete executor working state * FreeExecutorState * CreateExprContext + * CreateStandaloneExprContext * FreeExprContext * ReScanExprContext * @@ -332,6 +333,68 @@ CreateExprContext(EState *estate) } /* ---------------- + * CreateStandaloneExprContext + * + * Create a context for standalone expression evaluation. + * + * An ExprContext made this way can be used for evaluation of expressions + * that contain no Params, subplans, or Var references (it might work to + * put tuple references into the scantuple field, but it seems unwise). + * + * The ExprContext struct is allocated in the caller's current memory + * context, which also becomes its "per query" context. + * + * It is caller's responsibility to free the ExprContext when done, + * or at least ensure that any shutdown callbacks have been called + * (ReScanExprContext() is suitable). Otherwise, non-memory resources + * might be leaked. + * ---------------- + */ +ExprContext * +CreateStandaloneExprContext(void) +{ + ExprContext *econtext; + + /* Create the ExprContext node within the caller's memory context */ + econtext = makeNode(ExprContext); + + /* Initialize fields of ExprContext */ + econtext->ecxt_scantuple = NULL; + econtext->ecxt_innertuple = NULL; + econtext->ecxt_outertuple = NULL; + + econtext->ecxt_per_query_memory = CurrentMemoryContext; + + /* + * Create working memory for expression evaluation in this context. + */ + econtext->ecxt_per_tuple_memory = + AllocSetContextCreate(CurrentMemoryContext, + "ExprContext", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + econtext->ecxt_param_exec_vals = NULL; + econtext->ecxt_param_list_info = NULL; + + econtext->ecxt_aggvalues = NULL; + econtext->ecxt_aggnulls = NULL; + + econtext->caseValue_datum = (Datum) 0; + econtext->caseValue_isNull = true; + + econtext->domainValue_datum = (Datum) 0; + econtext->domainValue_isNull = true; + + econtext->ecxt_estate = NULL; + + econtext->ecxt_callbacks = NULL; + + return econtext; +} + +/* ---------------- * FreeExprContext * * Free an expression context, including calling any remaining @@ -352,9 +415,11 @@ FreeExprContext(ExprContext *econtext) ShutdownExprContext(econtext); /* And clean up the memory used */ MemoryContextDelete(econtext->ecxt_per_tuple_memory); - /* Unlink self from owning EState */ + /* Unlink self from owning EState, if any */ estate = econtext->ecxt_estate; - estate->es_exprcontexts = list_delete_ptr(estate->es_exprcontexts, econtext); + if (estate) + estate->es_exprcontexts = list_delete_ptr(estate->es_exprcontexts, + econtext); /* And delete the ExprContext node */ pfree(econtext); } diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c index 7c13175e45..2126c6b87b 100644 --- a/src/backend/utils/adt/domains.c +++ b/src/backend/utils/adt/domains.c @@ -11,17 +11,13 @@ * * The overhead required for constraint checking can be high, since examining * the catalogs to discover the constraints for a given domain is not cheap. - * We have two mechanisms for minimizing this cost: + * We have three mechanisms for minimizing this cost: * 1. In a nest of domains, we flatten the checking of all the levels * into just one operation. * 2. We cache the list of constraint items in the FmgrInfo struct * passed by the caller. - * - * We also have to create an EState to evaluate CHECK expressions in. - * Creating and destroying an EState is somewhat expensive, and so it's - * tempting to cache the EState too. However, that would mean that the - * EState never gets an explicit FreeExecutorState call, which is a bad idea - * because it risks leaking non-memory resources. + * 3. If there are CHECK constraints, we cache a standalone ExprContext + * to evaluate them in. * * * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group @@ -29,7 +25,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.2 2006/07/14 14:52:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.3 2006/08/04 21:33:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -54,6 +50,10 @@ typedef struct DomainIOData FmgrInfo proc; /* List of constraint items to check */ List *constraint_list; + /* Context for evaluating CHECK constraints in */ + ExprContext *econtext; + /* Memory context this cache is in */ + MemoryContext mcxt; } DomainIOData; @@ -95,6 +95,10 @@ domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary, my_extra->constraint_list = GetDomainConstraints(domainType); MemoryContextSwitchTo(oldcontext); + /* We don't make an ExprContext until needed */ + my_extra->econtext = NULL; + my_extra->mcxt = mcxt; + /* Mark cache valid */ my_extra->domain_type = domainType; } @@ -107,7 +111,7 @@ domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary, static void domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) { - EState *estate = NULL; + ExprContext *econtext = my_extra->econtext; ListCell *l; foreach(l, my_extra->constraint_list) @@ -125,25 +129,26 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) break; case DOM_CONSTRAINT_CHECK: { - ExprContext *econtext; Datum conResult; bool conIsNull; - Datum save_datum; - bool save_isNull; - if (estate == NULL) - estate = CreateExecutorState(); - econtext = GetPerTupleExprContext(estate); + /* Make the econtext if we didn't already */ + if (econtext == NULL) + { + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(my_extra->mcxt); + econtext = CreateStandaloneExprContext(); + MemoryContextSwitchTo(oldcontext); + my_extra->econtext = econtext; + } /* * Set up value to be returned by CoerceToDomainValue - * nodes. We must save and restore prior setting of - * econtext's domainValue fields, in case this node is - * itself within a check expression for another domain. + * nodes. Unlike ExecEvalCoerceToDomain, this econtext + * couldn't be shared with anything else, so no need + * to save and restore fields. */ - save_datum = econtext->domainValue_datum; - save_isNull = econtext->domainValue_isNull; - econtext->domainValue_datum = value; econtext->domainValue_isNull = isnull; @@ -158,9 +163,6 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) errmsg("value for domain %s violates check constraint \"%s\"", format_type_be(my_extra->domain_type), con->name))); - econtext->domainValue_datum = save_datum; - econtext->domainValue_isNull = save_isNull; - break; } default: @@ -170,8 +172,13 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra) } } - if (estate) - FreeExecutorState(estate); + /* + * Before exiting, call any shutdown callbacks and reset econtext's + * per-tuple memory. This avoids leaking non-memory resources, + * if anything in the expression(s) has any. + */ + if (econtext) + ReScanExprContext(econtext); } |