1 /*-------------------------------------------------------------------------
4 * Routines to handle functions called from the executor
5 * Putting this stuff in fmgr makes the postmaster a mess....
7 * Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.22 1999/02/03 21:16:12 momjian Exp $
13 *-------------------------------------------------------------------------
18 #include "nodes/primnodes.h"
19 #include "nodes/relation.h"
20 #include "nodes/execnodes.h"
21 #include "nodes/plannodes.h"
23 #include "catalog/pg_proc.h"
24 #include "tcop/pquery.h"
25 #include "tcop/tcopprot.h"
26 #include "tcop/utility.h"
27 #include "nodes/params.h"
29 #include "utils/fcache.h"
30 #include "utils/datum.h"
31 #include "utils/elog.h"
32 #include "utils/palloc.h"
33 #include "utils/syscache.h"
34 #include "catalog/pg_language.h"
35 #include "access/heapam.h"
36 #include "access/xact.h"
37 #include "executor/executor.h"
38 #include "executor/execdefs.h"
39 #include "executor/functions.h"
45 F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
48 typedef struct local_es
52 struct local_es *next;
56 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
58 /* non-export function prototypes */
59 static TupleDesc postquel_start(execution_state *es);
60 static execution_state *init_execution_state(FunctionCachePtr fcache,
62 static TupleTableSlot *postquel_getnext(execution_state *es);
63 static void postquel_end(execution_state *es);
64 static void postquel_sub_params(execution_state *es, int nargs,
65 char *args[], bool *nullV);
66 static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
67 List *fTlist, char **args, bool *isNull);
71 ProjectAttribute(TupleDesc TD,
78 Var *attrVar = (Var *) tlist->expr;
79 AttrNumber attrno = attrVar->varattno;
82 val = heap_getattr(tup, attrno, TD, isnullP);
86 valueP = datumCopy(val,
87 TD->attrs[attrno - 1]->atttypid,
88 TD->attrs[attrno - 1]->attbyval,
89 (Size) TD->attrs[attrno - 1]->attlen);
93 static execution_state *
94 init_execution_state(FunctionCachePtr fcache,
97 execution_state *newes;
98 execution_state *nextes;
99 execution_state *preves;
100 QueryTreeList *queryTree_list;
105 nargs = fcache->nargs;
107 newes = (execution_state *) palloc(sizeof(execution_state));
109 preves = (execution_state *) NULL;
112 planTree_list = (List *)
113 pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None, FALSE);
115 for (i = 0; i < queryTree_list->len; i++)
118 Query *queryTree = (Query *) (queryTree_list->qtrees[i]);
119 Plan *planTree = lfirst(planTree_list);
122 nextes = (execution_state *) palloc(sizeof(execution_state));
124 preves->next = nextes;
127 nextes->status = F_EXEC_START;
128 nextes->qd = CreateQueryDesc(queryTree,
131 estate = CreateExecutorState();
136 ParamListInfo paramLI;
138 paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
140 MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
142 estate->es_param_list_info = paramLI;
144 for (i = 0; i < nargs; paramLI++, i++)
146 paramLI->kind = PARAM_NUM;
148 paramLI->isnull = false;
149 paramLI->value = (Datum) NULL;
151 paramLI->kind = PARAM_INVALID;
154 estate->es_param_list_info = (ParamListInfo) NULL;
155 nextes->estate = estate;
157 nextes = (execution_state *) NULL;
159 planTree_list = lnext(planTree_list);
166 postquel_start(execution_state *es)
168 #ifdef FUNC_UTIL_PATCH
171 * Do nothing for utility commands. (create, destroy...) DZ -
174 if (es->qd->operation == CMD_UTILITY)
175 return (TupleDesc) NULL;
177 return ExecutorStart(es->qd, es->estate);
180 static TupleTableSlot *
181 postquel_getnext(execution_state *es)
185 #ifdef FUNC_UTIL_PATCH
186 if (es->qd->operation == CMD_UTILITY)
190 * Process an utility command. (create, destroy...) DZ -
193 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
194 if (!LAST_POSTQUEL_COMMAND(es))
195 CommandCounterIncrement();
196 return (TupleTableSlot *) NULL;
200 feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
202 return ExecutorRun(es->qd, es->estate, feature, 0);
206 postquel_end(execution_state *es)
208 #ifdef FUNC_UTIL_PATCH
211 * Do nothing for utility commands. (create, destroy...) DZ -
214 if (es->qd->operation == CMD_UTILITY)
217 ExecutorEnd(es->qd, es->estate);
221 postquel_sub_params(execution_state *es,
226 ParamListInfo paramLI;
230 paramLI = estate->es_param_list_info;
232 while (paramLI->kind != PARAM_INVALID)
234 if (paramLI->kind == PARAM_NUM)
236 Assert(paramLI->id <= nargs);
237 paramLI->value = (Datum) args[(paramLI->id - 1)];
238 paramLI->isnull = nullV[(paramLI->id - 1)];
244 static TupleTableSlot *
245 copy_function_result(FunctionCachePtr fcache,
246 TupleTableSlot *resultSlot)
248 TupleTableSlot *funcSlot;
253 Assert(!TupIsNull(resultSlot));
254 oldTuple = resultSlot->val;
256 funcSlot = (TupleTableSlot *) fcache->funcSlot;
258 if (funcSlot == (TupleTableSlot *) NULL)
261 resultTd = resultSlot->ttc_tupleDescriptor;
264 * When the funcSlot is NULL we have to initialize the funcSlot's
267 if (TupIsNull(funcSlot))
270 TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
272 while (i < oldTuple->t_data->t_natts)
274 funcTd->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
275 memmove(funcTd->attrs[i],
277 ATTRIBUTE_TUPLE_SIZE);
282 newTuple = heap_copytuple(oldTuple);
284 return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
288 postquel_execute(execution_state *es,
289 FunctionCachePtr fcache,
294 TupleTableSlot *slot;
298 * It's more right place to do it (before
299 * postquel_start->ExecutorStart). Now
300 * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
301 * note: I HOPE we can do it here). - vadim 01/22/97
303 if (fcache->nargs > 0)
304 postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
306 if (es->status == F_EXEC_START)
309 es->status = F_EXEC_RUN;
312 slot = postquel_getnext(es);
317 es->status = F_EXEC_DONE;
321 * If this isn't the last command for the function we have to
322 * increment the command counter so that subsequent commands can
323 * see changes made by previous ones.
325 if (!LAST_POSTQUEL_COMMAND(es))
326 CommandCounterIncrement();
330 if (LAST_POSTQUEL_COMMAND(es))
332 TupleTableSlot *resSlot;
335 * Copy the result. copy_function_result is smart enough to do
336 * nothing when no action is called for. This helps reduce the
337 * logic and code redundancy here.
339 resSlot = copy_function_result(fcache, slot);
342 TargetEntry *tle = lfirst(fTlist);
344 value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
351 value = (Datum) resSlot;
356 * If this is a single valued function we have to end the function
359 if (fcache->oneResult)
362 es->status = F_EXEC_DONE;
369 * If this isn't the last command for the function, we don't return
370 * any results, but we have to increment the command counter so that
371 * subsequent commands can see changes made by previous ones.
373 CommandCounterIncrement();
378 postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
382 FunctionCachePtr fcache = funcNode->func_fcache;
386 * Before we start do anything we must save CurrentScanCommandId to
387 * restore it before return to upper Executor. Also, we have to set
388 * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
390 savedId = GetScanCommandId();
391 SetScanCommandId(GetCurrentCommandId());
393 es = (execution_state *) fcache->func_state;
396 es = init_execution_state(fcache, args);
397 fcache->func_state = (char *) es;
400 while (es && es->status == F_EXEC_DONE)
406 * Execute each command in the function one after another until we're
407 * executing the final command and get a result or we run out of
410 while (es != (execution_state *) NULL)
412 result = postquel_execute(es,
414 funcNode->func_tlist,
417 if (es->status != F_EXEC_DONE)
423 * If we've gone through every command in this function, we are done.
425 if (es == (execution_state *) NULL)
429 * Reset the execution states to start over again
431 es = (execution_state *) fcache->func_state;
434 es->status = F_EXEC_START;
439 * Let caller know we're finished.
442 SetScanCommandId(savedId);
443 return (fcache->oneResult) ? result : (Datum) NULL;
447 * If we got a result from a command within the function it has to be
448 * the final command. All others shouldn't be returing anything.
450 Assert(LAST_POSTQUEL_COMMAND(es));
453 SetScanCommandId(savedId);