1 /*-------------------------------------------------------------------------
4 * Routines to handle functions called from the executor
5 * Putting this stuff in fmgr makes the postmaster a mess....
7 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.32 2000/04/04 21:44:39 tgl Exp $
14 *-------------------------------------------------------------------------
18 #include "access/heapam.h"
19 #include "executor/execdefs.h"
20 #include "executor/executor.h"
21 #include "executor/functions.h"
22 #include "tcop/pquery.h"
23 #include "tcop/tcopprot.h"
24 #include "tcop/utility.h"
25 #include "utils/datum.h"
31 F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
34 typedef struct local_es
38 struct local_es *next;
42 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
44 /* non-export function prototypes */
45 static TupleDesc postquel_start(execution_state *es);
46 static execution_state *init_execution_state(FunctionCachePtr fcache,
48 static TupleTableSlot *postquel_getnext(execution_state *es);
49 static void postquel_end(execution_state *es);
50 static void postquel_sub_params(execution_state *es, int nargs,
51 char *args[], bool *nullV);
52 static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache,
53 List *fTlist, char **args, bool *isNull);
57 ProjectAttribute(TupleDesc TD,
64 Var *attrVar = (Var *) tlist->expr;
65 AttrNumber attrno = attrVar->varattno;
68 val = heap_getattr(tup, attrno, TD, isnullP);
72 valueP = datumCopy(val,
73 TD->attrs[attrno - 1]->atttypid,
74 TD->attrs[attrno - 1]->attbyval,
75 (Size) TD->attrs[attrno - 1]->attlen);
79 static execution_state *
80 init_execution_state(FunctionCachePtr fcache,
83 execution_state *newes;
84 execution_state *nextes;
85 execution_state *preves;
88 int nargs = fcache->nargs;
90 newes = (execution_state *) palloc(sizeof(execution_state));
92 preves = (execution_state *) NULL;
94 queryTree_list = pg_parse_and_rewrite(fcache->src, fcache->argOidVect,
97 foreach(qtl_item, queryTree_list)
99 Query *queryTree = lfirst(qtl_item);
103 planTree = pg_plan_query(queryTree);
106 nextes = (execution_state *) palloc(sizeof(execution_state));
108 preves->next = nextes;
111 nextes->status = F_EXEC_START;
112 nextes->qd = CreateQueryDesc(queryTree,
115 estate = CreateExecutorState();
117 if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
118 elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
123 ParamListInfo paramLI;
125 paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
127 MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
129 estate->es_param_list_info = paramLI;
131 for (i = 0; i < nargs; paramLI++, i++)
133 paramLI->kind = PARAM_NUM;
135 paramLI->isnull = false;
136 paramLI->value = (Datum) NULL;
138 paramLI->kind = PARAM_INVALID;
141 estate->es_param_list_info = (ParamListInfo) NULL;
142 nextes->estate = estate;
144 nextes = (execution_state *) NULL;
151 postquel_start(execution_state *es)
154 * Do nothing for utility commands. (create, destroy...) DZ -
157 if (es->qd->operation == CMD_UTILITY)
158 return (TupleDesc) NULL;
159 return ExecutorStart(es->qd, es->estate);
162 static TupleTableSlot *
163 postquel_getnext(execution_state *es)
167 if (es->qd->operation == CMD_UTILITY)
170 * Process a utility command. (create, destroy...) DZ -
173 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
174 if (!LAST_POSTQUEL_COMMAND(es))
175 CommandCounterIncrement();
176 return (TupleTableSlot *) NULL;
179 feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
181 return ExecutorRun(es->qd, es->estate, feature, (Node *) NULL, (Node *) NULL);
185 postquel_end(execution_state *es)
188 * Do nothing for utility commands. (create, destroy...) DZ -
191 if (es->qd->operation == CMD_UTILITY)
193 ExecutorEnd(es->qd, es->estate);
197 postquel_sub_params(execution_state *es,
202 ParamListInfo paramLI;
206 paramLI = estate->es_param_list_info;
208 while (paramLI->kind != PARAM_INVALID)
210 if (paramLI->kind == PARAM_NUM)
212 Assert(paramLI->id <= nargs);
213 paramLI->value = (Datum) args[(paramLI->id - 1)];
214 paramLI->isnull = nullV[(paramLI->id - 1)];
220 static TupleTableSlot *
221 copy_function_result(FunctionCachePtr fcache,
222 TupleTableSlot *resultSlot)
224 TupleTableSlot *funcSlot;
229 Assert(!TupIsNull(resultSlot));
230 oldTuple = resultSlot->val;
232 funcSlot = (TupleTableSlot *) fcache->funcSlot;
234 if (funcSlot == (TupleTableSlot *) NULL)
237 resultTd = resultSlot->ttc_tupleDescriptor;
240 * When the funcSlot is NULL we have to initialize the funcSlot's
243 if (TupIsNull(funcSlot))
246 TupleDesc funcTd = funcSlot->ttc_tupleDescriptor;
248 while (i < oldTuple->t_data->t_natts)
250 funcTd->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
251 memmove(funcTd->attrs[i],
253 ATTRIBUTE_TUPLE_SIZE);
258 newTuple = heap_copytuple(oldTuple);
260 return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
264 postquel_execute(execution_state *es,
265 FunctionCachePtr fcache,
270 TupleTableSlot *slot;
274 * It's more right place to do it (before
275 * postquel_start->ExecutorStart). Now
276 * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
277 * note: I HOPE we can do it here). - vadim 01/22/97
279 if (fcache->nargs > 0)
280 postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
282 if (es->status == F_EXEC_START)
285 es->status = F_EXEC_RUN;
288 slot = postquel_getnext(es);
293 es->status = F_EXEC_DONE;
297 * If this isn't the last command for the function we have to
298 * increment the command counter so that subsequent commands can
299 * see changes made by previous ones.
301 if (!LAST_POSTQUEL_COMMAND(es))
302 CommandCounterIncrement();
306 if (LAST_POSTQUEL_COMMAND(es))
308 TupleTableSlot *resSlot;
311 * Copy the result. copy_function_result is smart enough to do
312 * nothing when no action is called for. This helps reduce the
313 * logic and code redundancy here.
315 resSlot = copy_function_result(fcache, slot);
318 TargetEntry *tle = lfirst(fTlist);
320 value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
327 value = (Datum) resSlot;
332 * If this is a single valued function we have to end the function
335 if (fcache->oneResult)
338 es->status = F_EXEC_DONE;
345 * If this isn't the last command for the function, we don't return
346 * any results, but we have to increment the command counter so that
347 * subsequent commands can see changes made by previous ones.
349 CommandCounterIncrement();
354 postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
358 FunctionCachePtr fcache = funcNode->func_fcache;
362 * Before we start do anything we must save CurrentScanCommandId to
363 * restore it before return to upper Executor. Also, we have to set
364 * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
366 savedId = GetScanCommandId();
367 SetScanCommandId(GetCurrentCommandId());
369 es = (execution_state *) fcache->func_state;
372 es = init_execution_state(fcache, args);
373 fcache->func_state = (char *) es;
376 while (es && es->status == F_EXEC_DONE)
382 * Execute each command in the function one after another until we're
383 * executing the final command and get a result or we run out of
386 while (es != (execution_state *) NULL)
388 result = postquel_execute(es,
390 funcNode->func_tlist,
393 if (es->status != F_EXEC_DONE)
399 * If we've gone through every command in this function, we are done.
401 if (es == (execution_state *) NULL)
405 * Reset the execution states to start over again
407 es = (execution_state *) fcache->func_state;
410 es->status = F_EXEC_START;
415 * Let caller know we're finished.
418 SetScanCommandId(savedId);
419 return (fcache->oneResult) ? result : (Datum) NULL;
423 * If we got a result from a command within the function it has to be
424 * the final command. All others shouldn't be returing anything.
426 Assert(LAST_POSTQUEL_COMMAND(es));
429 SetScanCommandId(savedId);