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