]> granicus.if.org Git - postgresql/blob - src/backend/executor/functions.c
Restructure command-completion-report code so that there is just one
[postgresql] / src / backend / executor / functions.c
1 /*-------------------------------------------------------------------------
2  *
3  * functions.c
4  *        Routines to handle functions called from the executor
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.48 2002/02/26 22:47:05 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include "access/heapam.h"
18 #include "catalog/pg_proc.h"
19 #include "catalog/pg_type.h"
20 #include "executor/execdefs.h"
21 #include "executor/executor.h"
22 #include "executor/functions.h"
23 #include "tcop/pquery.h"
24 #include "tcop/tcopprot.h"
25 #include "tcop/utility.h"
26 #include "utils/builtins.h"
27 #include "utils/syscache.h"
28
29
30 /*
31  * We have an execution_state record for each query in the function.
32  */
33 typedef enum
34 {
35         F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
36 } ExecStatus;
37
38 typedef struct local_es
39 {
40         QueryDesc  *qd;
41         EState     *estate;
42         struct local_es *next;
43         ExecStatus      status;
44 } execution_state;
45
46 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
47
48
49 /*
50  * An SQLFunctionCache record is built during the first call,
51  * and linked to from the fn_extra field of the FmgrInfo struct.
52  */
53
54 typedef struct
55 {
56         int                     typlen;                 /* length of the return type */
57         bool            typbyval;               /* true if return type is pass by value */
58         bool            returnsTuple;   /* true if return type is a tuple */
59
60         TupleTableSlot *funcSlot;       /* if one result we need to copy it before
61                                                                  * we end execution of the function and
62                                                                  * free stuff */
63
64         /* head of linked list of execution_state records */
65         execution_state *func_state;
66 } SQLFunctionCache;
67
68 typedef SQLFunctionCache *SQLFunctionCachePtr;
69
70
71 /* non-export function prototypes */
72 static execution_state *init_execution_state(char *src,
73                                          Oid *argOidVect, int nargs);
74 static void init_sql_fcache(FmgrInfo *finfo);
75 static void postquel_start(execution_state *es);
76 static TupleTableSlot *postquel_getnext(execution_state *es);
77 static void postquel_end(execution_state *es);
78 static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
79 static Datum postquel_execute(execution_state *es,
80                                  FunctionCallInfo fcinfo,
81                                  SQLFunctionCachePtr fcache);
82
83
84 static execution_state *
85 init_execution_state(char *src, Oid *argOidVect, int nargs)
86 {
87         execution_state *newes;
88         execution_state *nextes;
89         execution_state *preves;
90         List       *queryTree_list,
91                            *qtl_item;
92
93         newes = (execution_state *) palloc(sizeof(execution_state));
94         nextes = newes;
95         preves = (execution_state *) NULL;
96
97         queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
98
99         foreach(qtl_item, queryTree_list)
100         {
101                 Query      *queryTree = lfirst(qtl_item);
102                 Plan       *planTree;
103                 EState     *estate;
104
105                 planTree = pg_plan_query(queryTree);
106
107                 if (!nextes)
108                         nextes = (execution_state *) palloc(sizeof(execution_state));
109                 if (preves)
110                         preves->next = nextes;
111
112                 nextes->next = NULL;
113                 nextes->status = F_EXEC_START;
114                 nextes->qd = CreateQueryDesc(queryTree,
115                                                                          planTree,
116                                                                          None);
117                 estate = CreateExecutorState();
118
119                 if (nargs > 0)
120                 {
121                         int                     i;
122                         ParamListInfo paramLI;
123
124                         paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
125
126                         MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
127
128                         estate->es_param_list_info = paramLI;
129
130                         for (i = 0; i < nargs; paramLI++, i++)
131                         {
132                                 paramLI->kind = PARAM_NUM;
133                                 paramLI->id = i + 1;
134                                 paramLI->isnull = false;
135                                 paramLI->value = (Datum) NULL;
136                         }
137                         paramLI->kind = PARAM_INVALID;
138                 }
139                 else
140                         estate->es_param_list_info = (ParamListInfo) NULL;
141                 nextes->estate = estate;
142                 preves = nextes;
143                 nextes = (execution_state *) NULL;
144         }
145
146         return newes;
147 }
148
149
150 static void
151 init_sql_fcache(FmgrInfo *finfo)
152 {
153         Oid                     foid = finfo->fn_oid;
154         HeapTuple       procedureTuple;
155         HeapTuple       typeTuple;
156         Form_pg_proc procedureStruct;
157         Form_pg_type typeStruct;
158         SQLFunctionCachePtr fcache;
159         Oid                *argOidVect;
160         char       *src;
161         int                     nargs;
162         Datum           tmp;
163         bool            isNull;
164
165         /*
166          * get the procedure tuple corresponding to the given function Oid
167          */
168         procedureTuple = SearchSysCache(PROCOID,
169                                                                         ObjectIdGetDatum(foid),
170                                                                         0, 0, 0);
171         if (!HeapTupleIsValid(procedureTuple))
172                 elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
173                          foid);
174
175         procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
176
177         /*
178          * get the return type from the procedure tuple
179          */
180         typeTuple = SearchSysCache(TYPEOID,
181                                                    ObjectIdGetDatum(procedureStruct->prorettype),
182                                                            0, 0, 0);
183         if (!HeapTupleIsValid(typeTuple))
184                 elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
185                          procedureStruct->prorettype);
186
187         typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
188
189         fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache));
190         MemSet(fcache, 0, sizeof(SQLFunctionCache));
191
192         /*
193          * get the type length and by-value flag from the type tuple
194          */
195         fcache->typlen = typeStruct->typlen;
196         if (typeStruct->typrelid == InvalidOid)
197         {
198                 /* The return type is not a relation, so just use byval */
199                 fcache->typbyval = typeStruct->typbyval;
200                 fcache->returnsTuple = false;
201         }
202         else
203         {
204                 /*
205                  * This is a hack.      We assume here that any function returning a
206                  * tuple returns it by reference.  This needs to be fixed, since
207                  * actually the mechanism isn't quite like return-by-reference.
208                  */
209                 fcache->typbyval = false;
210                 fcache->returnsTuple = true;
211         }
212
213         /*
214          * If we are returning exactly one result then we have to copy tuples
215          * and by reference results because we have to end the execution
216          * before we return the results.  When you do this everything
217          * allocated by the executor (i.e. slots and tuples) is freed.
218          */
219         if (!finfo->fn_retset && !fcache->typbyval)
220                 fcache->funcSlot = MakeTupleTableSlot();
221         else
222                 fcache->funcSlot = NULL;
223
224         nargs = procedureStruct->pronargs;
225
226         if (nargs > 0)
227         {
228                 argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
229                 memcpy(argOidVect,
230                            procedureStruct->proargtypes,
231                            nargs * sizeof(Oid));
232         }
233         else
234                 argOidVect = (Oid *) NULL;
235
236         tmp = SysCacheGetAttr(PROCOID,
237                                                   procedureTuple,
238                                                   Anum_pg_proc_prosrc,
239                                                   &isNull);
240         if (isNull)
241                 elog(ERROR, "init_sql_fcache: null prosrc for procedure %u",
242                          foid);
243         src = DatumGetCString(DirectFunctionCall1(textout, tmp));
244
245         fcache->func_state = init_execution_state(src, argOidVect, nargs);
246
247         pfree(src);
248
249         ReleaseSysCache(typeTuple);
250         ReleaseSysCache(procedureTuple);
251
252         finfo->fn_extra = (void *) fcache;
253 }
254
255
256 static void
257 postquel_start(execution_state *es)
258 {
259         /*
260          * Do nothing for utility commands. (create, destroy...)  DZ -
261          * 30-8-1996
262          */
263         if (es->qd->operation == CMD_UTILITY)
264                 return;
265         ExecutorStart(es->qd, es->estate);
266 }
267
268 static TupleTableSlot *
269 postquel_getnext(execution_state *es)
270 {
271         int                     feature;
272
273         if (es->qd->operation == CMD_UTILITY)
274         {
275                 /*
276                  * Process a utility command. (create, destroy...)      DZ - 30-8-1996
277                  */
278                 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
279                 if (!LAST_POSTQUEL_COMMAND(es))
280                         CommandCounterIncrement();
281                 return (TupleTableSlot *) NULL;
282         }
283
284         feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
285
286         return ExecutorRun(es->qd, es->estate, feature, 0L);
287 }
288
289 static void
290 postquel_end(execution_state *es)
291 {
292         /*
293          * Do nothing for utility commands. (create, destroy...)  DZ -
294          * 30-8-1996
295          */
296         if (es->qd->operation == CMD_UTILITY)
297                 return;
298         ExecutorEnd(es->qd, es->estate);
299 }
300
301 static void
302 postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
303 {
304         EState     *estate;
305         ParamListInfo paramLI;
306
307         estate = es->estate;
308         paramLI = estate->es_param_list_info;
309
310         while (paramLI->kind != PARAM_INVALID)
311         {
312                 if (paramLI->kind == PARAM_NUM)
313                 {
314                         Assert(paramLI->id <= fcinfo->nargs);
315                         paramLI->value = fcinfo->arg[paramLI->id - 1];
316                         paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
317                 }
318                 paramLI++;
319         }
320 }
321
322 static TupleTableSlot *
323 copy_function_result(SQLFunctionCachePtr fcache,
324                                          TupleTableSlot *resultSlot)
325 {
326         TupleTableSlot *funcSlot;
327         TupleDesc       resultTd;
328         HeapTuple       resultTuple;
329         HeapTuple       newTuple;
330
331         Assert(!TupIsNull(resultSlot));
332         resultTuple = resultSlot->val;
333
334         funcSlot = fcache->funcSlot;
335
336         if (funcSlot == NULL)
337                 return resultSlot;              /* no need to copy result */
338
339         /*
340          * If first time through, we have to initialize the funcSlot's tuple
341          * descriptor.
342          */
343         if (funcSlot->ttc_tupleDescriptor == NULL)
344         {
345                 resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
346                 ExecSetSlotDescriptor(funcSlot, resultTd, true);
347                 ExecSetSlotDescriptorIsNew(funcSlot, true);
348         }
349
350         newTuple = heap_copytuple(resultTuple);
351
352         return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
353 }
354
355 static Datum
356 postquel_execute(execution_state *es,
357                                  FunctionCallInfo fcinfo,
358                                  SQLFunctionCachePtr fcache)
359 {
360         TupleTableSlot *slot;
361         Datum           value;
362
363         /*
364          * It's more right place to do it (before
365          * postquel_start->ExecutorStart). Now
366          * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
367          * note: I HOPE we can do it here). - vadim 01/22/97
368          */
369         if (fcinfo->nargs > 0)
370                 postquel_sub_params(es, fcinfo);
371
372         if (es->status == F_EXEC_START)
373         {
374                 postquel_start(es);
375                 es->status = F_EXEC_RUN;
376         }
377
378         slot = postquel_getnext(es);
379
380         if (TupIsNull(slot))
381         {
382                 postquel_end(es);
383                 es->status = F_EXEC_DONE;
384                 fcinfo->isnull = true;
385
386                 /*
387                  * If this isn't the last command for the function we have to
388                  * increment the command counter so that subsequent commands can
389                  * see changes made by previous ones.
390                  */
391                 if (!LAST_POSTQUEL_COMMAND(es))
392                         CommandCounterIncrement();
393                 return (Datum) NULL;
394         }
395
396         if (LAST_POSTQUEL_COMMAND(es))
397         {
398                 TupleTableSlot *resSlot;
399
400                 /*
401                  * Copy the result.  copy_function_result is smart enough to do
402                  * nothing when no action is called for.  This helps reduce the
403                  * logic and code redundancy here.
404                  */
405                 resSlot = copy_function_result(fcache, slot);
406
407                 /*
408                  * If we are supposed to return a tuple, we return the tuple slot
409                  * pointer converted to Datum.  If we are supposed to return a
410                  * simple value, then project out the first attribute of the
411                  * result tuple (ie, take the first result column of the final
412                  * SELECT).
413                  */
414                 if (fcache->returnsTuple)
415                 {
416                         /*
417                          * XXX do we need to remove junk attrs from the result tuple?
418                          * Probably OK to leave them, as long as they are at the end.
419                          */
420                         value = PointerGetDatum(resSlot);
421                         fcinfo->isnull = false;
422                 }
423                 else
424                 {
425                         value = heap_getattr(resSlot->val,
426                                                                  1,
427                                                                  resSlot->ttc_tupleDescriptor,
428                                                                  &(fcinfo->isnull));
429
430                         /*
431                          * Note: if result type is pass-by-reference then we are
432                          * returning a pointer into the tuple copied by
433                          * copy_function_result.  This is OK.
434                          */
435                 }
436
437                 /*
438                  * If this is a single valued function we have to end the function
439                  * execution now.
440                  */
441                 if (!fcinfo->flinfo->fn_retset)
442                 {
443                         postquel_end(es);
444                         es->status = F_EXEC_DONE;
445                 }
446
447                 return value;
448         }
449
450         /*
451          * If this isn't the last command for the function, we don't return
452          * any results, but we have to increment the command counter so that
453          * subsequent commands can see changes made by previous ones.
454          */
455         CommandCounterIncrement();
456         return (Datum) NULL;
457 }
458
459 Datum
460 fmgr_sql(PG_FUNCTION_ARGS)
461 {
462         MemoryContext oldcontext;
463         SQLFunctionCachePtr fcache;
464         execution_state *es;
465         Datum           result = 0;
466         CommandId       savedId;
467
468         /*
469          * Switch to context in which the fcache lives.  This ensures that
470          * parsetrees, plans, etc, will have sufficient lifetime.  The
471          * sub-executor is responsible for deleting per-tuple information.
472          */
473         oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
474
475         /*
476          * Before we start do anything we must save CurrentScanCommandId to
477          * restore it before return to upper Executor. Also, we have to set
478          * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
479          */
480         savedId = GetScanCommandId();
481         SetScanCommandId(GetCurrentCommandId());
482
483         /*
484          * Initialize fcache and execution state if first time through.
485          */
486         fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
487         if (fcache == NULL)
488         {
489                 init_sql_fcache(fcinfo->flinfo);
490                 fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
491         }
492         es = fcache->func_state;
493         Assert(es);
494
495         /*
496          * Find first unfinished query in function.
497          */
498         while (es && es->status == F_EXEC_DONE)
499                 es = es->next;
500
501         Assert(es);
502
503         /*
504          * Execute each command in the function one after another until we're
505          * executing the final command and get a result or we run out of
506          * commands.
507          */
508         while (es != (execution_state *) NULL)
509         {
510                 result = postquel_execute(es, fcinfo, fcache);
511                 if (es->status != F_EXEC_DONE)
512                         break;
513                 es = es->next;
514         }
515
516         /*
517          * Restore outer command ID.
518          */
519         SetScanCommandId(savedId);
520
521         /*
522          * If we've gone through every command in this function, we are done.
523          */
524         if (es == (execution_state *) NULL)
525         {
526                 /*
527                  * Reset the execution states to start over again
528                  */
529                 es = fcache->func_state;
530                 while (es)
531                 {
532                         es->status = F_EXEC_START;
533                         es = es->next;
534                 }
535
536                 /*
537                  * Let caller know we're finished.
538                  */
539                 if (fcinfo->flinfo->fn_retset)
540                 {
541                         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
542
543                         if (rsi && IsA(rsi, ReturnSetInfo))
544                                 rsi->isDone = ExprEndResult;
545                         else
546                                 elog(ERROR, "Set-valued function called in context that cannot accept a set");
547                         fcinfo->isnull = true;
548                         result = (Datum) 0;
549                 }
550
551                 MemoryContextSwitchTo(oldcontext);
552
553                 return result;
554         }
555
556         /*
557          * If we got a result from a command within the function it has to be
558          * the final command.  All others shouldn't be returning anything.
559          */
560         Assert(LAST_POSTQUEL_COMMAND(es));
561
562         /*
563          * Let caller know we're not finished.
564          */
565         if (fcinfo->flinfo->fn_retset)
566         {
567                 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
568
569                 if (rsi && IsA(rsi, ReturnSetInfo))
570                         rsi->isDone = ExprMultipleResult;
571                 else
572                         elog(ERROR, "Set-valued function called in context that cannot accept a set");
573         }
574
575         MemoryContextSwitchTo(oldcontext);
576
577         return result;
578 }