summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2006-08-04 21:33:36 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2006-08-04 21:33:36 +0000
commitc68489863c91a23821c4fcbfd9cfb5bce3220e8f (patch)
tree89fa9cd063fc5d1927e78c3c78d81d4cfb352a94 /src/backend
parentbf7b205e161a8f11bb3cea39a03d63f79decfc20 (diff)
downloadpostgresql-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.c71
-rw-r--r--src/backend/utils/adt/domains.c59
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);
}