]> granicus.if.org Git - postgresql/blob - src/backend/executor/functions.c
This is the final state of the rule system for 6.4 after the
[postgresql] / src / backend / executor / functions.c
1 /*-------------------------------------------------------------------------
2  *
3  * functions.c--
4  *        Routines to handle functions called from the executor
5  *        Putting this stuff in fmgr makes the postmaster a mess....
6  *
7  * Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.18 1998/08/24 01:37:48 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include <string.h>
16 #include "postgres.h"
17
18 #include "nodes/primnodes.h"
19 #include "nodes/relation.h"
20 #include "nodes/execnodes.h"
21 #include "nodes/plannodes.h"
22
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"
28 #include "fmgr.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"
40
41 #undef new
42
43 typedef enum
44 {
45         F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
46 } ExecStatus;
47
48 typedef struct local_es
49 {
50         QueryDesc  *qd;
51         EState     *estate;
52         struct local_es *next;
53         ExecStatus      status;
54 } execution_state;
55
56 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
57
58 /* non-export function prototypes */
59 static TupleDesc postquel_start(execution_state *es);
60 static execution_state *
61 init_execution_state(FunctionCachePtr fcache,
62                                          char *args[]);
63 static TupleTableSlot *postquel_getnext(execution_state *es);
64 static void postquel_end(execution_state *es);
65 static void
66 postquel_sub_params(execution_state *es, int nargs,
67                                         char *args[], bool *nullV);
68 static Datum
69 postquel_execute(execution_state *es, FunctionCachePtr fcache,
70                                  List *fTlist, char **args, bool *isNull);
71
72
73 Datum
74 ProjectAttribute(TupleDesc TD,
75                                  TargetEntry *tlist,
76                                  HeapTuple tup,
77                                  bool *isnullP)
78 {
79         Datum           val,
80                                 valueP;
81         Var                *attrVar = (Var *) tlist->expr;
82         AttrNumber      attrno = attrVar->varattno;
83
84
85         val = heap_getattr(tup, attrno, TD, isnullP);
86         if (*isnullP)
87                 return (Datum) NULL;
88
89         valueP = datumCopy(val,
90                                            TD->attrs[attrno - 1]->atttypid,
91                                            TD->attrs[attrno - 1]->attbyval,
92                                            (Size) TD->attrs[attrno - 1]->attlen);
93         return valueP;
94 }
95
96 static execution_state *
97 init_execution_state(FunctionCachePtr fcache,
98                                          char *args[])
99 {
100         execution_state *newes;
101         execution_state *nextes;
102         execution_state *preves;
103         QueryTreeList *queryTree_list;
104         int                     i;
105         List       *planTree_list;
106         int                     nargs;
107
108         nargs = fcache->nargs;
109
110         newes = (execution_state *) palloc(sizeof(execution_state));
111         nextes = newes;
112         preves = (execution_state *) NULL;
113
114
115         planTree_list = (List *)
116                 pg_parse_and_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None, FALSE);
117
118         for (i = 0; i < queryTree_list->len; i++)
119         {
120                 EState     *estate;
121                 Query      *queryTree = (Query *) (queryTree_list->qtrees[i]);
122                 Plan       *planTree = lfirst(planTree_list);
123
124                 if (!nextes)
125                         nextes = (execution_state *) palloc(sizeof(execution_state));
126                 if (preves)
127                         preves->next = nextes;
128
129                 nextes->next = NULL;
130                 nextes->status = F_EXEC_START;
131                 nextes->qd = CreateQueryDesc(queryTree,
132                                                                          planTree,
133                                                                          None);
134                 estate = CreateExecutorState();
135
136                 if (nargs > 0)
137                 {
138                         int                     i;
139                         ParamListInfo paramLI;
140
141                         paramLI =
142                                 (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
143
144                         MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
145
146                         estate->es_param_list_info = paramLI;
147
148                         for (i = 0; i < nargs; paramLI++, i++)
149                         {
150                                 paramLI->kind = PARAM_NUM;
151                                 paramLI->id = i + 1;
152                                 paramLI->isnull = false;
153                                 paramLI->value = (Datum) NULL;
154                         }
155                         paramLI->kind = PARAM_INVALID;
156                 }
157                 else
158                         estate->es_param_list_info = (ParamListInfo) NULL;
159                 nextes->estate = estate;
160                 preves = nextes;
161                 nextes = (execution_state *) NULL;
162
163                 planTree_list = lnext(planTree_list);
164         }
165
166         return newes;
167 }
168
169 static TupleDesc
170 postquel_start(execution_state *es)
171 {
172 #ifdef FUNC_UTIL_PATCH
173
174         /*
175          * Do nothing for utility commands. (create, destroy...)  DZ -
176          * 30-8-1996
177          */
178         if (es->qd->operation == CMD_UTILITY)
179                 return (TupleDesc) NULL;
180 #endif
181         return ExecutorStart(es->qd, es->estate);
182 }
183
184 static TupleTableSlot *
185 postquel_getnext(execution_state *es)
186 {
187         int                     feature;
188
189 #ifdef FUNC_UTIL_PATCH
190         if (es->qd->operation == CMD_UTILITY)
191         {
192
193                 /*
194                  * Process an utility command. (create, destroy...)  DZ -
195                  * 30-8-1996
196                  */
197                 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
198                 if (!LAST_POSTQUEL_COMMAND(es))
199                         CommandCounterIncrement();
200                 return (TupleTableSlot *) NULL;
201         }
202 #endif
203
204         feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
205
206         return ExecutorRun(es->qd, es->estate, feature, 0);
207 }
208
209 static void
210 postquel_end(execution_state *es)
211 {
212 #ifdef FUNC_UTIL_PATCH
213
214         /*
215          * Do nothing for utility commands. (create, destroy...)  DZ -
216          * 30-8-1996
217          */
218         if (es->qd->operation == CMD_UTILITY)
219                 return;
220 #endif
221         ExecutorEnd(es->qd, es->estate);
222 }
223
224 static void
225 postquel_sub_params(execution_state *es,
226                                         int nargs,
227                                         char *args[],
228                                         bool *nullV)
229 {
230         ParamListInfo paramLI;
231         EState     *estate;
232
233         estate = es->estate;
234         paramLI = estate->es_param_list_info;
235
236         while (paramLI->kind != PARAM_INVALID)
237         {
238                 if (paramLI->kind == PARAM_NUM)
239                 {
240                         Assert(paramLI->id <= nargs);
241                         paramLI->value = (Datum) args[(paramLI->id - 1)];
242                         paramLI->isnull = nullV[(paramLI->id - 1)];
243                 }
244                 paramLI++;
245         }
246 }
247
248 static TupleTableSlot *
249 copy_function_result(FunctionCachePtr fcache,
250                                          TupleTableSlot *resultSlot)
251 {
252         TupleTableSlot *funcSlot;
253         TupleDesc       resultTd;
254         HeapTuple       newTuple;
255         HeapTuple       oldTuple;
256
257         Assert(!TupIsNull(resultSlot));
258         oldTuple = resultSlot->val;
259
260         funcSlot = (TupleTableSlot *) fcache->funcSlot;
261
262         if (funcSlot == (TupleTableSlot *) NULL)
263                 return resultSlot;
264
265         resultTd = resultSlot->ttc_tupleDescriptor;
266
267         /*
268          * When the funcSlot is NULL we have to initialize the funcSlot's
269          * tuple descriptor.
270          */
271         if (TupIsNull(funcSlot))
272         {
273                 int                     i = 0;
274                 TupleDesc       funcTd = funcSlot->ttc_tupleDescriptor;
275
276                 while (i < oldTuple->t_natts)
277                 {
278                         funcTd->attrs[i] =
279                                 (AttributeTupleForm) palloc(ATTRIBUTE_TUPLE_SIZE);
280                         memmove(funcTd->attrs[i],
281                                         resultTd->attrs[i],
282                                         ATTRIBUTE_TUPLE_SIZE);
283                         i++;
284                 }
285         }
286
287         newTuple = heap_copytuple(oldTuple);
288
289         return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
290 }
291
292 static Datum
293 postquel_execute(execution_state *es,
294                                  FunctionCachePtr fcache,
295                                  List *fTlist,
296                                  char **args,
297                                  bool *isNull)
298 {
299         TupleTableSlot *slot;
300         Datum           value;
301
302         /*
303          * It's more right place to do it (before
304          * postquel_start->ExecutorStart). Now
305          * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
306          * note: I HOPE we can do it here). - vadim 01/22/97
307          */
308         if (fcache->nargs > 0)
309                 postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
310
311         if (es->status == F_EXEC_START)
312         {
313                 postquel_start(es);
314                 es->status = F_EXEC_RUN;
315         }
316
317         slot = postquel_getnext(es);
318
319         if (TupIsNull(slot))
320         {
321                 postquel_end(es);
322                 es->status = F_EXEC_DONE;
323                 *isNull = true;
324
325                 /*
326                  * If this isn't the last command for the function we have to
327                  * increment the command counter so that subsequent commands can
328                  * see changes made by previous ones.
329                  */
330                 if (!LAST_POSTQUEL_COMMAND(es))
331                         CommandCounterIncrement();
332                 return (Datum) NULL;
333         }
334
335         if (LAST_POSTQUEL_COMMAND(es))
336         {
337                 TupleTableSlot *resSlot;
338
339                 /*
340                  * Copy the result.  copy_function_result is smart enough to do
341                  * nothing when no action is called for.  This helps reduce the
342                  * logic and code redundancy here.
343                  */
344                 resSlot = copy_function_result(fcache, slot);
345                 if (fTlist != NIL)
346                 {
347                         HeapTuple       tup;
348                         TargetEntry *tle = lfirst(fTlist);
349
350                         tup = resSlot->val;
351                         value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
352                                                                          tle,
353                                                                          tup,
354                                                                          isNull);
355                 }
356                 else
357                 {
358                         value = (Datum) resSlot;
359                         *isNull = false;
360                 }
361
362                 /*
363                  * If this is a single valued function we have to end the function
364                  * execution now.
365                  */
366                 if (fcache->oneResult)
367                 {
368                         postquel_end(es);
369                         es->status = F_EXEC_DONE;
370                 }
371
372                 return value;
373         }
374
375         /*
376          * If this isn't the last command for the function, we don't return
377          * any results, but we have to increment the command counter so that
378          * subsequent commands can see changes made by previous ones.
379          */
380         CommandCounterIncrement();
381         return (Datum) NULL;
382 }
383
384 Datum
385 postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
386 {
387         execution_state *es;
388         Datum           result = 0;
389         FunctionCachePtr fcache = funcNode->func_fcache;
390         CommandId       savedId;
391
392         /*
393          * Before we start do anything we must save CurrentScanCommandId to
394          * restore it before return to upper Executor. Also, we have to set
395          * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
396          */
397         savedId = GetScanCommandId();
398         SetScanCommandId(GetCurrentCommandId());
399
400         es = (execution_state *) fcache->func_state;
401         if (es == NULL)
402         {
403                 es = init_execution_state(fcache, args);
404                 fcache->func_state = (char *) es;
405         }
406
407         while (es && es->status == F_EXEC_DONE)
408                 es = es->next;
409
410         Assert(es);
411
412         /*
413          * Execute each command in the function one after another until we're
414          * executing the final command and get a result or we run out of
415          * commands.
416          */
417         while (es != (execution_state *) NULL)
418         {
419                 result = postquel_execute(es,
420                                                                   fcache,
421                                                                   funcNode->func_tlist,
422                                                                   args,
423                                                                   isNull);
424                 if (es->status != F_EXEC_DONE)
425                         break;
426                 es = es->next;
427         }
428
429         /*
430          * If we've gone through every command in this function, we are done.
431          */
432         if (es == (execution_state *) NULL)
433         {
434
435                 /*
436                  * Reset the execution states to start over again
437                  */
438                 es = (execution_state *) fcache->func_state;
439                 while (es)
440                 {
441                         es->status = F_EXEC_START;
442                         es = es->next;
443                 }
444
445                 /*
446                  * Let caller know we're finished.
447                  */
448                 *isDone = true;
449                 SetScanCommandId(savedId);
450                 return (fcache->oneResult) ? result : (Datum) NULL;
451         }
452
453         /*
454          * If we got a result from a command within the function it has to be
455          * the final command.  All others shouldn't be returing anything.
456          */
457         Assert(LAST_POSTQUEL_COMMAND(es));
458         *isDone = false;
459
460         SetScanCommandId(savedId);
461         return result;
462 }