Skip to content

Commit c684898

Browse files
committed
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.
1 parent bf7b205 commit c684898

File tree

4 files changed

+105
-32
lines changed

4 files changed

+105
-32
lines changed

src/backend/executor/execUtils.c

Lines changed: 68 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.138 2006/07/31 20:09:04 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/execUtils.c,v 1.139 2006/08/04 21:33:36 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -17,6 +17,7 @@
1717
* CreateExecutorState Create/delete executor working state
1818
* FreeExecutorState
1919
* CreateExprContext
20+
* CreateStandaloneExprContext
2021
* FreeExprContext
2122
* ReScanExprContext
2223
*
@@ -331,6 +332,68 @@ CreateExprContext(EState *estate)
331332
return econtext;
332333
}
333334

335+
/* ----------------
336+
* CreateStandaloneExprContext
337+
*
338+
* Create a context for standalone expression evaluation.
339+
*
340+
* An ExprContext made this way can be used for evaluation of expressions
341+
* that contain no Params, subplans, or Var references (it might work to
342+
* put tuple references into the scantuple field, but it seems unwise).
343+
*
344+
* The ExprContext struct is allocated in the caller's current memory
345+
* context, which also becomes its "per query" context.
346+
*
347+
* It is caller's responsibility to free the ExprContext when done,
348+
* or at least ensure that any shutdown callbacks have been called
349+
* (ReScanExprContext() is suitable). Otherwise, non-memory resources
350+
* might be leaked.
351+
* ----------------
352+
*/
353+
ExprContext *
354+
CreateStandaloneExprContext(void)
355+
{
356+
ExprContext *econtext;
357+
358+
/* Create the ExprContext node within the caller's memory context */
359+
econtext = makeNode(ExprContext);
360+
361+
/* Initialize fields of ExprContext */
362+
econtext->ecxt_scantuple = NULL;
363+
econtext->ecxt_innertuple = NULL;
364+
econtext->ecxt_outertuple = NULL;
365+
366+
econtext->ecxt_per_query_memory = CurrentMemoryContext;
367+
368+
/*
369+
* Create working memory for expression evaluation in this context.
370+
*/
371+
econtext->ecxt_per_tuple_memory =
372+
AllocSetContextCreate(CurrentMemoryContext,
373+
"ExprContext",
374+
ALLOCSET_DEFAULT_MINSIZE,
375+
ALLOCSET_DEFAULT_INITSIZE,
376+
ALLOCSET_DEFAULT_MAXSIZE);
377+
378+
econtext->ecxt_param_exec_vals = NULL;
379+
econtext->ecxt_param_list_info = NULL;
380+
381+
econtext->ecxt_aggvalues = NULL;
382+
econtext->ecxt_aggnulls = NULL;
383+
384+
econtext->caseValue_datum = (Datum) 0;
385+
econtext->caseValue_isNull = true;
386+
387+
econtext->domainValue_datum = (Datum) 0;
388+
econtext->domainValue_isNull = true;
389+
390+
econtext->ecxt_estate = NULL;
391+
392+
econtext->ecxt_callbacks = NULL;
393+
394+
return econtext;
395+
}
396+
334397
/* ----------------
335398
* FreeExprContext
336399
*
@@ -352,9 +415,11 @@ FreeExprContext(ExprContext *econtext)
352415
ShutdownExprContext(econtext);
353416
/* And clean up the memory used */
354417
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
355-
/* Unlink self from owning EState */
418+
/* Unlink self from owning EState, if any */
356419
estate = econtext->ecxt_estate;
357-
estate->es_exprcontexts = list_delete_ptr(estate->es_exprcontexts, econtext);
420+
if (estate)
421+
estate->es_exprcontexts = list_delete_ptr(estate->es_exprcontexts,
422+
econtext);
358423
/* And delete the ExprContext node */
359424
pfree(econtext);
360425
}

src/backend/utils/adt/domains.c

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,21 @@
1111
*
1212
* The overhead required for constraint checking can be high, since examining
1313
* the catalogs to discover the constraints for a given domain is not cheap.
14-
* We have two mechanisms for minimizing this cost:
14+
* We have three mechanisms for minimizing this cost:
1515
* 1. In a nest of domains, we flatten the checking of all the levels
1616
* into just one operation.
1717
* 2. We cache the list of constraint items in the FmgrInfo struct
1818
* passed by the caller.
19-
*
20-
* We also have to create an EState to evaluate CHECK expressions in.
21-
* Creating and destroying an EState is somewhat expensive, and so it's
22-
* tempting to cache the EState too. However, that would mean that the
23-
* EState never gets an explicit FreeExecutorState call, which is a bad idea
24-
* because it risks leaking non-memory resources.
19+
* 3. If there are CHECK constraints, we cache a standalone ExprContext
20+
* to evaluate them in.
2521
*
2622
*
2723
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
2824
* Portions Copyright (c) 1994, Regents of the University of California
2925
*
3026
*
3127
* IDENTIFICATION
32-
* $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.2 2006/07/14 14:52:24 momjian Exp $
28+
* $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.3 2006/08/04 21:33:36 tgl Exp $
3329
*
3430
*-------------------------------------------------------------------------
3531
*/
@@ -54,6 +50,10 @@ typedef struct DomainIOData
5450
FmgrInfo proc;
5551
/* List of constraint items to check */
5652
List *constraint_list;
53+
/* Context for evaluating CHECK constraints in */
54+
ExprContext *econtext;
55+
/* Memory context this cache is in */
56+
MemoryContext mcxt;
5757
} DomainIOData;
5858

5959

@@ -95,6 +95,10 @@ domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
9595
my_extra->constraint_list = GetDomainConstraints(domainType);
9696
MemoryContextSwitchTo(oldcontext);
9797

98+
/* We don't make an ExprContext until needed */
99+
my_extra->econtext = NULL;
100+
my_extra->mcxt = mcxt;
101+
98102
/* Mark cache valid */
99103
my_extra->domain_type = domainType;
100104
}
@@ -107,7 +111,7 @@ domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
107111
static void
108112
domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
109113
{
110-
EState *estate = NULL;
114+
ExprContext *econtext = my_extra->econtext;
111115
ListCell *l;
112116

113117
foreach(l, my_extra->constraint_list)
@@ -125,25 +129,26 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
125129
break;
126130
case DOM_CONSTRAINT_CHECK:
127131
{
128-
ExprContext *econtext;
129132
Datum conResult;
130133
bool conIsNull;
131-
Datum save_datum;
132-
bool save_isNull;
133134

134-
if (estate == NULL)
135-
estate = CreateExecutorState();
136-
econtext = GetPerTupleExprContext(estate);
135+
/* Make the econtext if we didn't already */
136+
if (econtext == NULL)
137+
{
138+
MemoryContext oldcontext;
139+
140+
oldcontext = MemoryContextSwitchTo(my_extra->mcxt);
141+
econtext = CreateStandaloneExprContext();
142+
MemoryContextSwitchTo(oldcontext);
143+
my_extra->econtext = econtext;
144+
}
137145

138146
/*
139147
* Set up value to be returned by CoerceToDomainValue
140-
* nodes. We must save and restore prior setting of
141-
* econtext's domainValue fields, in case this node is
142-
* itself within a check expression for another domain.
148+
* nodes. Unlike ExecEvalCoerceToDomain, this econtext
149+
* couldn't be shared with anything else, so no need
150+
* to save and restore fields.
143151
*/
144-
save_datum = econtext->domainValue_datum;
145-
save_isNull = econtext->domainValue_isNull;
146-
147152
econtext->domainValue_datum = value;
148153
econtext->domainValue_isNull = isnull;
149154

@@ -158,9 +163,6 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
158163
errmsg("value for domain %s violates check constraint \"%s\"",
159164
format_type_be(my_extra->domain_type),
160165
con->name)));
161-
econtext->domainValue_datum = save_datum;
162-
econtext->domainValue_isNull = save_isNull;
163-
164166
break;
165167
}
166168
default:
@@ -170,8 +172,13 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
170172
}
171173
}
172174

173-
if (estate)
174-
FreeExecutorState(estate);
175+
/*
176+
* Before exiting, call any shutdown callbacks and reset econtext's
177+
* per-tuple memory. This avoids leaking non-memory resources,
178+
* if anything in the expression(s) has any.
179+
*/
180+
if (econtext)
181+
ReScanExprContext(econtext);
175182
}
176183

177184

src/include/executor/executor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.127 2006/06/16 18:42:23 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/executor/executor.h,v 1.128 2006/08/04 21:33:36 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -224,6 +224,7 @@ extern void end_tup_output(TupOutputState *tstate);
224224
extern EState *CreateExecutorState(void);
225225
extern void FreeExecutorState(EState *estate);
226226
extern ExprContext *CreateExprContext(EState *estate);
227+
extern ExprContext *CreateStandaloneExprContext(void);
227228
extern void FreeExprContext(ExprContext *econtext);
228229
extern void ReScanExprContext(ExprContext *econtext);
229230

src/include/nodes/execnodes.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
88
* Portions Copyright (c) 1994, Regents of the University of California
99
*
10-
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.157 2006/08/02 18:58:21 tgl Exp $
10+
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.158 2006/08/04 21:33:36 tgl Exp $
1111
*
1212
*-------------------------------------------------------------------------
1313
*/
@@ -118,7 +118,7 @@ typedef struct ExprContext
118118
Datum domainValue_datum;
119119
bool domainValue_isNull;
120120

121-
/* Link to containing EState */
121+
/* Link to containing EState (NULL if a standalone ExprContext) */
122122
struct EState *ecxt_estate;
123123

124124
/* Functions to call back when ExprContext is shut down */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy