]> granicus.if.org Git - postgresql/blob - src/backend/executor/functions.c
Fix bug noted by Bruce: FETCH in an already-aborted transaction block
[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  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  *
11  * IDENTIFICATION
12  *        $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.32 2000/04/04 21:44:39 tgl Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
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"
26
27 #undef new
28
29 typedef enum
30 {
31         F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
32 } ExecStatus;
33
34 typedef struct local_es
35 {
36         QueryDesc  *qd;
37         EState     *estate;
38         struct local_es *next;
39         ExecStatus      status;
40 } execution_state;
41
42 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
43
44 /* non-export function prototypes */
45 static TupleDesc postquel_start(execution_state *es);
46 static execution_state *init_execution_state(FunctionCachePtr fcache,
47                                          char *args[]);
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);
54
55
56 Datum
57 ProjectAttribute(TupleDesc TD,
58                                  TargetEntry *tlist,
59                                  HeapTuple tup,
60                                  bool *isnullP)
61 {
62         Datum           val,
63                                 valueP;
64         Var                *attrVar = (Var *) tlist->expr;
65         AttrNumber      attrno = attrVar->varattno;
66
67
68         val = heap_getattr(tup, attrno, TD, isnullP);
69         if (*isnullP)
70                 return (Datum) NULL;
71
72         valueP = datumCopy(val,
73                                            TD->attrs[attrno - 1]->atttypid,
74                                            TD->attrs[attrno - 1]->attbyval,
75                                            (Size) TD->attrs[attrno - 1]->attlen);
76         return valueP;
77 }
78
79 static execution_state *
80 init_execution_state(FunctionCachePtr fcache,
81                                          char *args[])
82 {
83         execution_state *newes;
84         execution_state *nextes;
85         execution_state *preves;
86         List       *queryTree_list,
87                            *qtl_item;
88         int                     nargs = fcache->nargs;
89
90         newes = (execution_state *) palloc(sizeof(execution_state));
91         nextes = newes;
92         preves = (execution_state *) NULL;
93
94         queryTree_list = pg_parse_and_rewrite(fcache->src, fcache->argOidVect,
95                                                                                   nargs, FALSE);
96
97         foreach(qtl_item, queryTree_list)
98         {
99                 Query      *queryTree = lfirst(qtl_item);
100                 Plan       *planTree;
101                 EState     *estate;
102
103                 planTree = pg_plan_query(queryTree);
104
105                 if (!nextes)
106                         nextes = (execution_state *) palloc(sizeof(execution_state));
107                 if (preves)
108                         preves->next = nextes;
109
110                 nextes->next = NULL;
111                 nextes->status = F_EXEC_START;
112                 nextes->qd = CreateQueryDesc(queryTree,
113                                                                          planTree,
114                                                                          None);
115                 estate = CreateExecutorState();
116
117                 if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
118                         elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
119
120                 if (nargs > 0)
121                 {
122                         int                     i;
123                         ParamListInfo paramLI;
124
125                         paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
126
127                         MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
128
129                         estate->es_param_list_info = paramLI;
130
131                         for (i = 0; i < nargs; paramLI++, i++)
132                         {
133                                 paramLI->kind = PARAM_NUM;
134                                 paramLI->id = i + 1;
135                                 paramLI->isnull = false;
136                                 paramLI->value = (Datum) NULL;
137                         }
138                         paramLI->kind = PARAM_INVALID;
139                 }
140                 else
141                         estate->es_param_list_info = (ParamListInfo) NULL;
142                 nextes->estate = estate;
143                 preves = nextes;
144                 nextes = (execution_state *) NULL;
145         }
146
147         return newes;
148 }
149
150 static TupleDesc
151 postquel_start(execution_state *es)
152 {
153         /*
154          * Do nothing for utility commands. (create, destroy...)  DZ -
155          * 30-8-1996
156          */
157         if (es->qd->operation == CMD_UTILITY)
158                 return (TupleDesc) NULL;
159         return ExecutorStart(es->qd, es->estate);
160 }
161
162 static TupleTableSlot *
163 postquel_getnext(execution_state *es)
164 {
165         int                     feature;
166
167         if (es->qd->operation == CMD_UTILITY)
168         {
169                 /*
170                  * Process a utility command. (create, destroy...)  DZ -
171                  * 30-8-1996
172                  */
173                 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
174                 if (!LAST_POSTQUEL_COMMAND(es))
175                         CommandCounterIncrement();
176                 return (TupleTableSlot *) NULL;
177         }
178
179         feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
180
181         return ExecutorRun(es->qd, es->estate, feature, (Node *) NULL, (Node *) NULL);
182 }
183
184 static void
185 postquel_end(execution_state *es)
186 {
187         /*
188          * Do nothing for utility commands. (create, destroy...)  DZ -
189          * 30-8-1996
190          */
191         if (es->qd->operation == CMD_UTILITY)
192                 return;
193         ExecutorEnd(es->qd, es->estate);
194 }
195
196 static void
197 postquel_sub_params(execution_state *es,
198                                         int nargs,
199                                         char *args[],
200                                         bool *nullV)
201 {
202         ParamListInfo paramLI;
203         EState     *estate;
204
205         estate = es->estate;
206         paramLI = estate->es_param_list_info;
207
208         while (paramLI->kind != PARAM_INVALID)
209         {
210                 if (paramLI->kind == PARAM_NUM)
211                 {
212                         Assert(paramLI->id <= nargs);
213                         paramLI->value = (Datum) args[(paramLI->id - 1)];
214                         paramLI->isnull = nullV[(paramLI->id - 1)];
215                 }
216                 paramLI++;
217         }
218 }
219
220 static TupleTableSlot *
221 copy_function_result(FunctionCachePtr fcache,
222                                          TupleTableSlot *resultSlot)
223 {
224         TupleTableSlot *funcSlot;
225         TupleDesc       resultTd;
226         HeapTuple       newTuple;
227         HeapTuple       oldTuple;
228
229         Assert(!TupIsNull(resultSlot));
230         oldTuple = resultSlot->val;
231
232         funcSlot = (TupleTableSlot *) fcache->funcSlot;
233
234         if (funcSlot == (TupleTableSlot *) NULL)
235                 return resultSlot;
236
237         resultTd = resultSlot->ttc_tupleDescriptor;
238
239         /*
240          * When the funcSlot is NULL we have to initialize the funcSlot's
241          * tuple descriptor.
242          */
243         if (TupIsNull(funcSlot))
244         {
245                 int                     i = 0;
246                 TupleDesc       funcTd = funcSlot->ttc_tupleDescriptor;
247
248                 while (i < oldTuple->t_data->t_natts)
249                 {
250                         funcTd->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
251                         memmove(funcTd->attrs[i],
252                                         resultTd->attrs[i],
253                                         ATTRIBUTE_TUPLE_SIZE);
254                         i++;
255                 }
256         }
257
258         newTuple = heap_copytuple(oldTuple);
259
260         return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
261 }
262
263 static Datum
264 postquel_execute(execution_state *es,
265                                  FunctionCachePtr fcache,
266                                  List *fTlist,
267                                  char **args,
268                                  bool *isNull)
269 {
270         TupleTableSlot *slot;
271         Datum           value;
272
273         /*
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
278          */
279         if (fcache->nargs > 0)
280                 postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
281
282         if (es->status == F_EXEC_START)
283         {
284                 postquel_start(es);
285                 es->status = F_EXEC_RUN;
286         }
287
288         slot = postquel_getnext(es);
289
290         if (TupIsNull(slot))
291         {
292                 postquel_end(es);
293                 es->status = F_EXEC_DONE;
294                 *isNull = true;
295
296                 /*
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.
300                  */
301                 if (!LAST_POSTQUEL_COMMAND(es))
302                         CommandCounterIncrement();
303                 return (Datum) NULL;
304         }
305
306         if (LAST_POSTQUEL_COMMAND(es))
307         {
308                 TupleTableSlot *resSlot;
309
310                 /*
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.
314                  */
315                 resSlot = copy_function_result(fcache, slot);
316                 if (fTlist != NIL)
317                 {
318                         TargetEntry *tle = lfirst(fTlist);
319
320                         value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
321                                                                          tle,
322                                                                          resSlot->val,
323                                                                          isNull);
324                 }
325                 else
326                 {
327                         value = (Datum) resSlot;
328                         *isNull = false;
329                 }
330
331                 /*
332                  * If this is a single valued function we have to end the function
333                  * execution now.
334                  */
335                 if (fcache->oneResult)
336                 {
337                         postquel_end(es);
338                         es->status = F_EXEC_DONE;
339                 }
340
341                 return value;
342         }
343
344         /*
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.
348          */
349         CommandCounterIncrement();
350         return (Datum) NULL;
351 }
352
353 Datum
354 postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
355 {
356         execution_state *es;
357         Datum           result = 0;
358         FunctionCachePtr fcache = funcNode->func_fcache;
359         CommandId       savedId;
360
361         /*
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
365          */
366         savedId = GetScanCommandId();
367         SetScanCommandId(GetCurrentCommandId());
368
369         es = (execution_state *) fcache->func_state;
370         if (es == NULL)
371         {
372                 es = init_execution_state(fcache, args);
373                 fcache->func_state = (char *) es;
374         }
375
376         while (es && es->status == F_EXEC_DONE)
377                 es = es->next;
378
379         Assert(es);
380
381         /*
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
384          * commands.
385          */
386         while (es != (execution_state *) NULL)
387         {
388                 result = postquel_execute(es,
389                                                                   fcache,
390                                                                   funcNode->func_tlist,
391                                                                   args,
392                                                                   isNull);
393                 if (es->status != F_EXEC_DONE)
394                         break;
395                 es = es->next;
396         }
397
398         /*
399          * If we've gone through every command in this function, we are done.
400          */
401         if (es == (execution_state *) NULL)
402         {
403
404                 /*
405                  * Reset the execution states to start over again
406                  */
407                 es = (execution_state *) fcache->func_state;
408                 while (es)
409                 {
410                         es->status = F_EXEC_START;
411                         es = es->next;
412                 }
413
414                 /*
415                  * Let caller know we're finished.
416                  */
417                 *isDone = true;
418                 SetScanCommandId(savedId);
419                 return (fcache->oneResult) ? result : (Datum) NULL;
420         }
421
422         /*
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.
425          */
426         Assert(LAST_POSTQUEL_COMMAND(es));
427         *isDone = false;
428
429         SetScanCommandId(savedId);
430         return result;
431 }