]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeSubplan.c
Use Params, rather than run-time-modified Const nodes, to handle
[postgresql] / src / backend / executor / nodeSubplan.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeSubplan.c
4  *        routines to support subselects
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.34 2002/11/26 03:01:57 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 /*
15  *       INTERFACE ROUTINES
16  *              ExecSubPlan  - process a subselect
17  *              ExecInitSubPlan - initialize a subselect
18  *              ExecEndSubPlan  - shut down a subselect
19  */
20 #include "postgres.h"
21
22 #include "access/heapam.h"
23 #include "executor/executor.h"
24 #include "executor/nodeSubplan.h"
25 #include "tcop/pquery.h"
26
27
28 /* ----------------------------------------------------------------
29  *              ExecSubPlan(node)
30  *
31  * ----------------------------------------------------------------
32  */
33 Datum
34 ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
35 {
36         Plan       *plan = node->plan;
37         SubLink    *sublink = node->sublink;
38         SubLinkType subLinkType = sublink->subLinkType;
39         bool            useor = sublink->useor;
40         MemoryContext oldcontext;
41         TupleTableSlot *slot;
42         Datum           result;
43         bool            found = false;  /* TRUE if got at least one subplan tuple */
44         List       *lst;
45
46         /*
47          * We are probably in a short-lived expression-evaluation context.
48          * Switch to longer-lived per-query context.
49          */
50         oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
51
52         if (node->setParam != NIL)
53                 elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
54
55         /*
56          * Set Params of this plan from parent plan correlation Vars
57          */
58         if (node->parParam != NIL)
59         {
60                 foreach(lst, node->parParam)
61                 {
62                         ParamExecData *prm;
63
64                         prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
65                         Assert(pvar != NIL);
66                         prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
67                                                                                                    econtext,
68                                                                                                    &(prm->isnull),
69                                                                                                    NULL);
70                         pvar = lnext(pvar);
71                 }
72                 plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
73         }
74         Assert(pvar == NIL);
75
76         ExecReScan(plan, NULL, NULL);
77
78         /*
79          * For all sublink types except EXPR_SUBLINK, the result is boolean as
80          * are the results of the combining operators.  We combine results
81          * within a tuple (if there are multiple columns) using OR semantics
82          * if "useor" is true, AND semantics if not.  We then combine results
83          * across tuples (if the subplan produces more than one) using OR
84          * semantics for ANY_SUBLINK or AND semantics for ALL_SUBLINK.
85          * (MULTIEXPR_SUBLINK doesn't allow multiple tuples from the subplan.)
86          * NULL results from the combining operators are handled according to
87          * the usual SQL semantics for OR and AND.      The result for no input
88          * tuples is FALSE for ANY_SUBLINK, TRUE for ALL_SUBLINK, NULL for
89          * MULTIEXPR_SUBLINK.
90          *
91          * For EXPR_SUBLINK we require the subplan to produce no more than one
92          * tuple, else an error is raised.      If zero tuples are produced, we
93          * return NULL.  Assuming we get a tuple, we just return its first
94          * column (there can be only one non-junk column in this case).
95          */
96         result = BoolGetDatum(subLinkType == ALL_SUBLINK);
97         *isNull = false;
98
99         for (slot = ExecProcNode(plan, NULL);
100                  !TupIsNull(slot);
101                  slot = ExecProcNode(plan, NULL))
102         {
103                 HeapTuple       tup = slot->val;
104                 TupleDesc       tdesc = slot->ttc_tupleDescriptor;
105                 Datum           rowresult = BoolGetDatum(!useor);
106                 bool            rownull = false;
107                 int                     col = 1;
108
109                 if (subLinkType == EXISTS_SUBLINK)
110                 {
111                         found = true;
112                         result = BoolGetDatum(true);
113                         break;
114                 }
115
116                 if (subLinkType == EXPR_SUBLINK)
117                 {
118                         /* cannot allow multiple input tuples for EXPR sublink */
119                         if (found)
120                                 elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
121                         found = true;
122
123                         /*
124                          * We need to copy the subplan's tuple in case the result is
125                          * of pass-by-ref type --- our return value will point into
126                          * this copied tuple!  Can't use the subplan's instance of the
127                          * tuple since it won't still be valid after next
128                          * ExecProcNode() call. node->curTuple keeps track of the
129                          * copied tuple for eventual freeing.
130                          */
131                         tup = heap_copytuple(tup);
132                         if (node->curTuple)
133                                 heap_freetuple(node->curTuple);
134                         node->curTuple = tup;
135                         result = heap_getattr(tup, col, tdesc, isNull);
136                         /* keep scanning subplan to make sure there's only one tuple */
137                         continue;
138                 }
139
140                 /* cannot allow multiple input tuples for MULTIEXPR sublink either */
141                 if (subLinkType == MULTIEXPR_SUBLINK && found)
142                         elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
143
144                 found = true;
145
146                 /*
147                  * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
148                  * operators for columns of tuple.
149                  */
150                 foreach(lst, sublink->oper)
151                 {
152                         Expr       *expr = (Expr *) lfirst(lst);
153                         Param      *prm = lsecond(expr->args);
154                         ParamExecData *prmdata;
155                         Datum           expresult;
156                         bool            expnull;
157
158                         /*
159                          * The righthand side of the expression should be either a
160                          * Param or a function call or RelabelType node taking a Param
161                          * as arg (these nodes represent run-time type coercions
162                          * inserted by the parser to get to the input type needed by
163                          * the operator). Find the Param node and insert the actual
164                          * righthand-side value into the param's econtext slot.
165                          *
166                          * XXX possible improvement: could make a list of the ParamIDs
167                          * at startup time, instead of repeating this check at each row.
168                          */
169                         if (!IsA(prm, Param))
170                         {
171                                 switch (nodeTag(prm))
172                                 {
173                                         case T_Expr:
174                                                 prm = lfirst(((Expr *) prm)->args);
175                                                 break;
176                                         case T_RelabelType:
177                                                 prm = (Param *) (((RelabelType *) prm)->arg);
178                                                 break;
179                                         default:
180                                                 /* will fail below */
181                                                 break;
182                                 }
183                                 if (!IsA(prm, Param))
184                                         elog(ERROR, "ExecSubPlan: failed to find placeholder for subplan result");
185                         }
186                         Assert(prm->paramkind == PARAM_EXEC);
187                         prmdata = &(econtext->ecxt_param_exec_vals[prm->paramid]);
188                         Assert(prmdata->execPlan == NULL);
189                         prmdata->value = heap_getattr(tup, col, tdesc,
190                                                                                   &(prmdata->isnull));
191
192                         /*
193                          * Now we can eval the combining operator for this column.
194                          */
195                         expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
196                                                                                                   &expnull, NULL);
197
198                         /*
199                          * Combine the result into the row result as appropriate.
200                          */
201                         if (col == 1)
202                         {
203                                 rowresult = expresult;
204                                 rownull = expnull;
205                         }
206                         else if (useor)
207                         {
208                                 /* combine within row per OR semantics */
209                                 if (expnull)
210                                         rownull = true;
211                                 else if (DatumGetBool(expresult))
212                                 {
213                                         rowresult = BoolGetDatum(true);
214                                         rownull = false;
215                                         break;          /* needn't look at any more columns */
216                                 }
217                         }
218                         else
219                         {
220                                 /* combine within row per AND semantics */
221                                 if (expnull)
222                                         rownull = true;
223                                 else if (!DatumGetBool(expresult))
224                                 {
225                                         rowresult = BoolGetDatum(false);
226                                         rownull = false;
227                                         break;          /* needn't look at any more columns */
228                                 }
229                         }
230                         col++;
231                 }
232
233                 if (subLinkType == ANY_SUBLINK)
234                 {
235                         /* combine across rows per OR semantics */
236                         if (rownull)
237                                 *isNull = true;
238                         else if (DatumGetBool(rowresult))
239                         {
240                                 result = BoolGetDatum(true);
241                                 *isNull = false;
242                                 break;                  /* needn't look at any more rows */
243                         }
244                 }
245                 else if (subLinkType == ALL_SUBLINK)
246                 {
247                         /* combine across rows per AND semantics */
248                         if (rownull)
249                                 *isNull = true;
250                         else if (!DatumGetBool(rowresult))
251                         {
252                                 result = BoolGetDatum(false);
253                                 *isNull = false;
254                                 break;                  /* needn't look at any more rows */
255                         }
256                 }
257                 else
258                 {
259                         /* must be MULTIEXPR_SUBLINK */
260                         result = rowresult;
261                         *isNull = rownull;
262                 }
263         }
264
265         if (!found)
266         {
267                 /*
268                  * deal with empty subplan result.      result/isNull were previously
269                  * initialized correctly for all sublink types except EXPR and
270                  * MULTIEXPR; for those, return NULL.
271                  */
272                 if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
273                 {
274                         result = (Datum) 0;
275                         *isNull = true;
276                 }
277         }
278
279         MemoryContextSwitchTo(oldcontext);
280
281         return result;
282 }
283
284 /* ----------------------------------------------------------------
285  *              ExecInitSubPlan
286  *
287  * ----------------------------------------------------------------
288  */
289 bool
290 ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
291 {
292         EState     *sp_estate = CreateExecutorState();
293
294         sp_estate->es_range_table = node->rtable;
295         sp_estate->es_param_list_info = estate->es_param_list_info;
296         sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
297         sp_estate->es_tupleTable =
298                 ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
299         sp_estate->es_snapshot = estate->es_snapshot;
300
301         node->needShutdown = false;
302         node->curTuple = NULL;
303
304         if (!ExecInitNode(node->plan, sp_estate, parent))
305                 return false;
306
307         node->needShutdown = true;      /* now we need to shutdown the subplan */
308
309         /*
310          * If this plan is un-correlated or undirect correlated one and want
311          * to set params for parent plan then prepare parameters.
312          */
313         if (node->setParam != NIL)
314         {
315                 List       *lst;
316
317                 foreach(lst, node->setParam)
318                 {
319                         ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
320
321                         prm->execPlan = node;
322                 }
323
324                 /*
325                  * Note that in the case of un-correlated subqueries we don't care
326                  * about setting parent->chgParam here: indices take care about
327                  * it, for others - it doesn't matter...
328                  */
329         }
330
331         return true;
332 }
333
334 /* ----------------------------------------------------------------
335  *              ExecSetParamPlan
336  *
337  *              Executes an InitPlan subplan and sets its output parameters.
338  *
339  * This is called from ExecEvalParam() when the value of a PARAM_EXEC
340  * parameter is requested and the param's execPlan field is set (indicating
341  * that the param has not yet been evaluated).  This allows lazy evaluation
342  * of initplans: we don't run the subplan until/unless we need its output.
343  * Note that this routine MUST clear the execPlan fields of the plan's
344  * output parameters after evaluating them!
345  * ----------------------------------------------------------------
346  */
347 void
348 ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
349 {
350         Plan       *plan = node->plan;
351         SubLink    *sublink = node->sublink;
352         MemoryContext oldcontext;
353         TupleTableSlot *slot;
354         List       *lst;
355         bool            found = false;
356
357         /*
358          * We are probably in a short-lived expression-evaluation context.
359          * Switch to longer-lived per-query context.
360          */
361         oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
362
363         if (sublink->subLinkType == ANY_SUBLINK ||
364                 sublink->subLinkType == ALL_SUBLINK)
365                 elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
366
367         if (plan->chgParam != NULL)
368                 ExecReScan(plan, NULL, NULL);
369
370         for (slot = ExecProcNode(plan, NULL);
371                  !TupIsNull(slot);
372                  slot = ExecProcNode(plan, NULL))
373         {
374                 HeapTuple       tup = slot->val;
375                 TupleDesc       tdesc = slot->ttc_tupleDescriptor;
376                 int                     i = 1;
377
378                 if (sublink->subLinkType == EXISTS_SUBLINK)
379                 {
380                         ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
381
382                         prm->execPlan = NULL;
383                         prm->value = BoolGetDatum(true);
384                         prm->isnull = false;
385                         found = true;
386                         break;
387                 }
388
389                 if (found &&
390                         (sublink->subLinkType == EXPR_SUBLINK ||
391                          sublink->subLinkType == MULTIEXPR_SUBLINK))
392                         elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
393
394                 found = true;
395
396                 /*
397                  * We need to copy the subplan's tuple in case any of the params
398                  * are pass-by-ref type --- the pointers stored in the param
399                  * structs will point at this copied tuple!  node->curTuple keeps
400                  * track of the copied tuple for eventual freeing.
401                  */
402                 tup = heap_copytuple(tup);
403                 if (node->curTuple)
404                         heap_freetuple(node->curTuple);
405                 node->curTuple = tup;
406
407                 foreach(lst, node->setParam)
408                 {
409                         ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
410
411                         prm->execPlan = NULL;
412                         prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
413                         i++;
414                 }
415         }
416
417         if (!found)
418         {
419                 if (sublink->subLinkType == EXISTS_SUBLINK)
420                 {
421                         ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
422
423                         prm->execPlan = NULL;
424                         prm->value = BoolGetDatum(false);
425                         prm->isnull = false;
426                 }
427                 else
428                 {
429                         foreach(lst, node->setParam)
430                         {
431                                 ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
432
433                                 prm->execPlan = NULL;
434                                 prm->value = (Datum) 0;
435                                 prm->isnull = true;
436                         }
437                 }
438         }
439
440         if (plan->extParam == NULL) /* un-correlated ... */
441         {
442                 ExecEndNode(plan, NULL);
443                 node->needShutdown = false;
444         }
445
446         MemoryContextSwitchTo(oldcontext);
447 }
448
449 /* ----------------------------------------------------------------
450  *              ExecEndSubPlan
451  * ----------------------------------------------------------------
452  */
453 void
454 ExecEndSubPlan(SubPlan *node)
455 {
456         if (node->needShutdown)
457         {
458                 ExecEndNode(node->plan, NULL);
459                 node->needShutdown = false;
460         }
461         if (node->curTuple)
462         {
463                 heap_freetuple(node->curTuple);
464                 node->curTuple = NULL;
465         }
466 }
467
468 void
469 ExecReScanSetParamPlan(SubPlan *node, Plan *parent)
470 {
471         Plan       *plan = node->plan;
472         List       *lst;
473
474         if (node->parParam != NULL)
475                 elog(ERROR, "ExecReScanSetParamPlan: direct correlated subquery unsupported, yet");
476         if (node->setParam == NULL)
477                 elog(ERROR, "ExecReScanSetParamPlan: setParam list is NULL");
478         if (plan->extParam == NULL)
479                 elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL");
480
481         /*
482          * Don't actual re-scan: ExecSetParamPlan does re-scan if
483          * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, NULL);
484          */
485
486         /*
487          * Mark this subplan's output parameters as needing recalculation
488          */
489         foreach(lst, node->setParam)
490         {
491                 ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
492
493                 prm->execPlan = node;
494         }
495
496         parent->chgParam = nconc(parent->chgParam, listCopy(node->setParam));
497
498 }