]> granicus.if.org Git - postgresql/blob - src/backend/executor/functions.c
Update copyrights to 2003.
[postgresql] / src / backend / executor / functions.c
1 /*-------------------------------------------------------------------------
2  *
3  * functions.c
4  *        Execution of SQL-language functions
5  *
6  * Portions Copyright (c) 1996-2003, 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.71 2003/08/04 02:39:58 momjian 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/lsyscache.h"
28 #include "utils/syscache.h"
29
30
31 /*
32  * We have an execution_state record for each query in a function.      Each
33  * record contains a querytree and plantree for its query.      If the query
34  * is currently in F_EXEC_RUN state then there's a QueryDesc too.
35  */
36 typedef enum
37 {
38         F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
39 } ExecStatus;
40
41 typedef struct local_es
42 {
43         struct local_es *next;
44         ExecStatus      status;
45         Query      *query;
46         Plan       *plan;
47         QueryDesc  *qd;                         /* null unless status == RUN */
48 } execution_state;
49
50 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
51
52
53 /*
54  * An SQLFunctionCache record is built during the first call,
55  * and linked to from the fn_extra field of the FmgrInfo struct.
56  */
57 typedef struct
58 {
59         int                     typlen;                 /* length of the return type */
60         bool            typbyval;               /* true if return type is pass by value */
61         bool            returnsTuple;   /* true if return type is a tuple */
62         bool            shutdown_reg;   /* true if registered shutdown callback */
63
64         TupleTableSlot *funcSlot;       /* if one result we need to copy it before
65                                                                  * we end execution of the function and
66                                                                  * free stuff */
67
68         ParamListInfo paramLI;          /* Param list representing current args */
69
70         /* head of linked list of execution_state records */
71         execution_state *func_state;
72 } SQLFunctionCache;
73
74 typedef SQLFunctionCache *SQLFunctionCachePtr;
75
76
77 /* non-export function prototypes */
78 static execution_state *init_execution_state(char *src,
79                                          Oid *argOidVect, int nargs,
80                                          Oid rettype, bool haspolyarg);
81 static void init_sql_fcache(FmgrInfo *finfo);
82 static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
83 static TupleTableSlot *postquel_getnext(execution_state *es);
84 static void postquel_end(execution_state *es);
85 static void postquel_sub_params(SQLFunctionCachePtr fcache,
86                                         FunctionCallInfo fcinfo);
87 static Datum postquel_execute(execution_state *es,
88                                  FunctionCallInfo fcinfo,
89                                  SQLFunctionCachePtr fcache);
90 static void sql_exec_error_callback(void *arg);
91 static void ShutdownSQLFunction(Datum arg);
92
93
94 static execution_state *
95 init_execution_state(char *src, Oid *argOidVect, int nargs,
96                                          Oid rettype, bool haspolyarg)
97 {
98         execution_state *firstes;
99         execution_state *preves;
100         List       *queryTree_list,
101                            *qtl_item;
102
103         queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
104
105         /*
106          * If the function has any arguments declared as polymorphic types,
107          * then it wasn't type-checked at definition time; must do so now.
108          */
109         if (haspolyarg)
110                 check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
111
112         firstes = NULL;
113         preves = NULL;
114
115         foreach(qtl_item, queryTree_list)
116         {
117                 Query      *queryTree = lfirst(qtl_item);
118                 Plan       *planTree;
119                 execution_state *newes;
120
121                 planTree = pg_plan_query(queryTree);
122
123                 newes = (execution_state *) palloc(sizeof(execution_state));
124                 if (preves)
125                         preves->next = newes;
126                 else
127                         firstes = newes;
128
129                 newes->next = NULL;
130                 newes->status = F_EXEC_START;
131                 newes->query = queryTree;
132                 newes->plan = planTree;
133                 newes->qd = NULL;
134
135                 preves = newes;
136         }
137
138         return firstes;
139 }
140
141
142 static void
143 init_sql_fcache(FmgrInfo *finfo)
144 {
145         Oid                     foid = finfo->fn_oid;
146         Oid                     rettype;
147         HeapTuple       procedureTuple;
148         HeapTuple       typeTuple;
149         Form_pg_proc procedureStruct;
150         Form_pg_type typeStruct;
151         SQLFunctionCachePtr fcache;
152         Oid                *argOidVect;
153         bool            haspolyarg;
154         char       *src;
155         int                     nargs;
156         Datum           tmp;
157         bool            isNull;
158
159         fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
160
161         /*
162          * get the procedure tuple corresponding to the given function Oid
163          */
164         procedureTuple = SearchSysCache(PROCOID,
165                                                                         ObjectIdGetDatum(foid),
166                                                                         0, 0, 0);
167         if (!HeapTupleIsValid(procedureTuple))
168                 elog(ERROR, "cache lookup failed for function %u", foid);
169         procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
170
171         /*
172          * get the result type from the procedure tuple, and check for
173          * polymorphic result type; if so, find out the actual result type.
174          */
175         rettype = procedureStruct->prorettype;
176
177         if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
178         {
179                 rettype = get_fn_expr_rettype(finfo);
180                 if (rettype == InvalidOid)              /* this probably should not happen */
181                         ereport(ERROR,
182                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
183                                          errmsg("could not determine actual result type for function declared %s",
184                                                   format_type_be(procedureStruct->prorettype))));
185         }
186
187         /* Now look up the actual result type */
188         typeTuple = SearchSysCache(TYPEOID,
189                                                            ObjectIdGetDatum(rettype),
190                                                            0, 0, 0);
191         if (!HeapTupleIsValid(typeTuple))
192                 elog(ERROR, "cache lookup failed for type %u", rettype);
193         typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
194
195         /*
196          * get the type length and by-value flag from the type tuple
197          */
198         fcache->typlen = typeStruct->typlen;
199
200         if (typeStruct->typtype != 'c' && rettype != RECORDOID)
201         {
202                 /* The return type is not a composite type, so just use byval */
203                 fcache->typbyval = typeStruct->typbyval;
204                 fcache->returnsTuple = false;
205         }
206         else
207         {
208                 /*
209                  * This is a hack.      We assume here that any function returning a
210                  * tuple returns it by reference.  This needs to be fixed, since
211                  * actually the mechanism isn't quite like return-by-reference.
212                  */
213                 fcache->typbyval = false;
214                 fcache->returnsTuple = true;
215         }
216
217         /*
218          * If we are returning exactly one result then we have to copy tuples
219          * and by reference results because we have to end the execution
220          * before we return the results.  When you do this everything
221          * allocated by the executor (i.e. slots and tuples) is freed.
222          */
223         if (!finfo->fn_retset && !fcache->typbyval)
224                 fcache->funcSlot = MakeTupleTableSlot();
225         else
226                 fcache->funcSlot = NULL;
227
228         /*
229          * Parse and plan the queries.  We need the argument type info to pass
230          * to the parser.
231          */
232         nargs = procedureStruct->pronargs;
233         haspolyarg = false;
234
235         if (nargs > 0)
236         {
237                 int                     argnum;
238
239                 argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
240                 memcpy(argOidVect,
241                            procedureStruct->proargtypes,
242                            nargs * sizeof(Oid));
243                 /* Resolve any polymorphic argument types */
244                 for (argnum = 0; argnum < nargs; argnum++)
245                 {
246                         Oid                     argtype = argOidVect[argnum];
247
248                         if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
249                         {
250                                 argtype = get_fn_expr_argtype(finfo, argnum);
251                                 if (argtype == InvalidOid)
252                                         ereport(ERROR,
253                                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
254                                                          errmsg("could not determine actual type of argument declared %s",
255                                                                         format_type_be(argOidVect[argnum]))));
256                                 argOidVect[argnum] = argtype;
257                                 haspolyarg = true;
258                         }
259                 }
260         }
261         else
262                 argOidVect = (Oid *) NULL;
263
264         tmp = SysCacheGetAttr(PROCOID,
265                                                   procedureTuple,
266                                                   Anum_pg_proc_prosrc,
267                                                   &isNull);
268         if (isNull)
269                 elog(ERROR, "null prosrc for function %u", foid);
270         src = DatumGetCString(DirectFunctionCall1(textout, tmp));
271
272         fcache->func_state = init_execution_state(src, argOidVect, nargs,
273                                                                                           rettype, haspolyarg);
274
275         pfree(src);
276
277         ReleaseSysCache(typeTuple);
278         ReleaseSysCache(procedureTuple);
279
280         finfo->fn_extra = (void *) fcache;
281 }
282
283
284 static void
285 postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
286 {
287         Assert(es->qd == NULL);
288         es->qd = CreateQueryDesc(es->query, es->plan,
289                                                          None_Receiver,
290                                                          fcache->paramLI, false);
291
292         /* Utility commands don't need Executor. */
293         if (es->qd->operation != CMD_UTILITY)
294                 ExecutorStart(es->qd, false);
295
296         es->status = F_EXEC_RUN;
297 }
298
299 static TupleTableSlot *
300 postquel_getnext(execution_state *es)
301 {
302         long            count;
303
304         if (es->qd->operation == CMD_UTILITY)
305         {
306                 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
307                 return (TupleTableSlot *) NULL;
308         }
309
310         /*
311          * If it's the function's last command, and it's a SELECT, fetch one
312          * row at a time so we can return the results.  Otherwise just run it
313          * to completion.
314          */
315         if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)
316                 count = 1L;
317         else
318                 count = 0L;
319
320         return ExecutorRun(es->qd, ForwardScanDirection, count);
321 }
322
323 static void
324 postquel_end(execution_state *es)
325 {
326         /* mark status done to ensure we don't do ExecutorEnd twice */
327         es->status = F_EXEC_DONE;
328
329         /* Utility commands don't need Executor. */
330         if (es->qd->operation != CMD_UTILITY)
331                 ExecutorEnd(es->qd);
332
333         FreeQueryDesc(es->qd);
334         es->qd = NULL;
335 }
336
337 /* Build ParamListInfo array representing current arguments */
338 static void
339 postquel_sub_params(SQLFunctionCachePtr fcache,
340                                         FunctionCallInfo fcinfo)
341 {
342         ParamListInfo paramLI;
343         int                     nargs = fcinfo->nargs;
344
345         if (nargs > 0)
346         {
347                 int                     i;
348
349                 paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));
350
351                 for (i = 0; i < nargs; i++)
352                 {
353                         paramLI[i].kind = PARAM_NUM;
354                         paramLI[i].id = i + 1;
355                         paramLI[i].value = fcinfo->arg[i];
356                         paramLI[i].isnull = fcinfo->argnull[i];
357                 }
358                 paramLI[nargs].kind = PARAM_INVALID;
359         }
360         else
361                 paramLI = (ParamListInfo) NULL;
362
363         if (fcache->paramLI)
364                 pfree(fcache->paramLI);
365
366         fcache->paramLI = paramLI;
367 }
368
369 static TupleTableSlot *
370 copy_function_result(SQLFunctionCachePtr fcache,
371                                          TupleTableSlot *resultSlot)
372 {
373         TupleTableSlot *funcSlot;
374         TupleDesc       resultTd;
375         HeapTuple       resultTuple;
376         HeapTuple       newTuple;
377
378         Assert(!TupIsNull(resultSlot));
379         resultTuple = resultSlot->val;
380
381         funcSlot = fcache->funcSlot;
382
383         if (funcSlot == NULL)
384                 return resultSlot;              /* no need to copy result */
385
386         /*
387          * If first time through, we have to initialize the funcSlot's tuple
388          * descriptor.
389          */
390         if (funcSlot->ttc_tupleDescriptor == NULL)
391         {
392                 resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
393                 ExecSetSlotDescriptor(funcSlot, resultTd, true);
394                 ExecSetSlotDescriptorIsNew(funcSlot, true);
395         }
396
397         newTuple = heap_copytuple(resultTuple);
398
399         return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
400 }
401
402 static Datum
403 postquel_execute(execution_state *es,
404                                  FunctionCallInfo fcinfo,
405                                  SQLFunctionCachePtr fcache)
406 {
407         TupleTableSlot *slot;
408         Datum           value;
409
410         if (es->status == F_EXEC_START)
411                 postquel_start(es, fcache);
412
413         slot = postquel_getnext(es);
414
415         if (TupIsNull(slot))
416         {
417                 postquel_end(es);
418                 fcinfo->isnull = true;
419
420                 /*
421                  * If this isn't the last command for the function we have to
422                  * increment the command counter so that subsequent commands can
423                  * see changes made by previous ones.
424                  */
425                 if (!LAST_POSTQUEL_COMMAND(es))
426                         CommandCounterIncrement();
427                 return (Datum) NULL;
428         }
429
430         if (LAST_POSTQUEL_COMMAND(es))
431         {
432                 TupleTableSlot *resSlot;
433
434                 /*
435                  * Copy the result.  copy_function_result is smart enough to do
436                  * nothing when no action is called for.  This helps reduce the
437                  * logic and code redundancy here.
438                  */
439                 resSlot = copy_function_result(fcache, slot);
440
441                 /*
442                  * If we are supposed to return a tuple, we return the tuple slot
443                  * pointer converted to Datum.  If we are supposed to return a
444                  * simple value, then project out the first attribute of the
445                  * result tuple (ie, take the first result column of the final
446                  * SELECT).
447                  */
448                 if (fcache->returnsTuple)
449                 {
450                         /*
451                          * XXX do we need to remove junk attrs from the result tuple?
452                          * Probably OK to leave them, as long as they are at the end.
453                          */
454                         value = PointerGetDatum(resSlot);
455                         fcinfo->isnull = false;
456                 }
457                 else
458                 {
459                         value = heap_getattr(resSlot->val,
460                                                                  1,
461                                                                  resSlot->ttc_tupleDescriptor,
462                                                                  &(fcinfo->isnull));
463
464                         /*
465                          * Note: if result type is pass-by-reference then we are
466                          * returning a pointer into the tuple copied by
467                          * copy_function_result.  This is OK.
468                          */
469                 }
470
471                 /*
472                  * If this is a single valued function we have to end the function
473                  * execution now.
474                  */
475                 if (!fcinfo->flinfo->fn_retset)
476                         postquel_end(es);
477
478                 return value;
479         }
480
481         /*
482          * If this isn't the last command for the function, we don't return
483          * any results, but we have to increment the command counter so that
484          * subsequent commands can see changes made by previous ones.
485          */
486         CommandCounterIncrement();
487         return (Datum) NULL;
488 }
489
490 Datum
491 fmgr_sql(PG_FUNCTION_ARGS)
492 {
493         MemoryContext oldcontext;
494         SQLFunctionCachePtr fcache;
495         ErrorContextCallback sqlerrcontext;
496         execution_state *es;
497         Datum           result = 0;
498
499         /*
500          * Switch to context in which the fcache lives.  This ensures that
501          * parsetrees, plans, etc, will have sufficient lifetime.  The
502          * sub-executor is responsible for deleting per-tuple information.
503          */
504         oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
505
506         /*
507          * Setup error traceback support for ereport()
508          */
509         sqlerrcontext.callback = sql_exec_error_callback;
510         sqlerrcontext.arg = fcinfo->flinfo;
511         sqlerrcontext.previous = error_context_stack;
512         error_context_stack = &sqlerrcontext;
513
514         /*
515          * Initialize fcache (build plans) if first time through.
516          */
517         fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
518         if (fcache == NULL)
519         {
520                 init_sql_fcache(fcinfo->flinfo);
521                 fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
522         }
523         es = fcache->func_state;
524
525         /*
526          * Convert params to appropriate format if starting a fresh execution.
527          * (If continuing execution, we can re-use prior params.)
528          */
529         if (es && es->status == F_EXEC_START)
530                 postquel_sub_params(fcache, fcinfo);
531
532         /*
533          * Find first unfinished query in function.
534          */
535         while (es && es->status == F_EXEC_DONE)
536                 es = es->next;
537
538         /*
539          * Execute each command in the function one after another until we're
540          * executing the final command and get a result or we run out of
541          * commands.
542          */
543         while (es)
544         {
545                 result = postquel_execute(es, fcinfo, fcache);
546                 if (es->status != F_EXEC_DONE)
547                         break;
548                 es = es->next;
549         }
550
551         /*
552          * If we've gone through every command in this function, we are done.
553          */
554         if (es == (execution_state *) NULL)
555         {
556                 /*
557                  * Reset the execution states to start over again on next call.
558                  */
559                 es = fcache->func_state;
560                 while (es)
561                 {
562                         es->status = F_EXEC_START;
563                         es = es->next;
564                 }
565
566                 /*
567                  * Let caller know we're finished.
568                  */
569                 if (fcinfo->flinfo->fn_retset)
570                 {
571                         ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
572
573                         if (rsi && IsA(rsi, ReturnSetInfo))
574                                 rsi->isDone = ExprEndResult;
575                         else
576                                 ereport(ERROR,
577                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
578                                                  errmsg("set-valued function called in context that cannot accept a set")));
579                         fcinfo->isnull = true;
580                         result = (Datum) 0;
581
582                         /* Deregister shutdown callback, if we made one */
583                         if (fcache->shutdown_reg)
584                         {
585                                 UnregisterExprContextCallback(rsi->econtext,
586                                                                                           ShutdownSQLFunction,
587                                                                                           PointerGetDatum(fcache));
588                                 fcache->shutdown_reg = false;
589                         }
590                 }
591
592                 error_context_stack = sqlerrcontext.previous;
593
594                 MemoryContextSwitchTo(oldcontext);
595
596                 return result;
597         }
598
599         /*
600          * If we got a result from a command within the function it has to be
601          * the final command.  All others shouldn't be returning anything.
602          */
603         Assert(LAST_POSTQUEL_COMMAND(es));
604
605         /*
606          * Let caller know we're not finished.
607          */
608         if (fcinfo->flinfo->fn_retset)
609         {
610                 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
611
612                 if (rsi && IsA(rsi, ReturnSetInfo))
613                         rsi->isDone = ExprMultipleResult;
614                 else
615                         ereport(ERROR,
616                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
617                                          errmsg("set-valued function called in context that cannot accept a set")));
618
619                 /*
620                  * Ensure we will get shut down cleanly if the exprcontext is not
621                  * run to completion.
622                  */
623                 if (!fcache->shutdown_reg)
624                 {
625                         RegisterExprContextCallback(rsi->econtext,
626                                                                                 ShutdownSQLFunction,
627                                                                                 PointerGetDatum(fcache));
628                         fcache->shutdown_reg = true;
629                 }
630         }
631
632         error_context_stack = sqlerrcontext.previous;
633
634         MemoryContextSwitchTo(oldcontext);
635
636         return result;
637 }
638
639
640 /*
641  * error context callback to let us supply a call-stack traceback
642  */
643 static void
644 sql_exec_error_callback(void *arg)
645 {
646         FmgrInfo   *flinfo = (FmgrInfo *) arg;
647         SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;
648         char       *fn_name;
649
650         fn_name = get_func_name(flinfo->fn_oid);
651         /* safety check, shouldn't happen */
652         if (fn_name == NULL)
653                 return;
654
655         /*
656          * Try to determine where in the function we failed.  If there is a
657          * query with non-null QueryDesc, finger it.  (We check this rather
658          * than looking for F_EXEC_RUN state, so that errors during
659          * ExecutorStart or ExecutorEnd are blamed on the appropriate query;
660          * see postquel_start and postquel_end.)
661          */
662         if (fcache)
663         {
664                 execution_state *es;
665                 int                     query_num;
666
667                 es = fcache->func_state;
668                 query_num = 1;
669                 while (es)
670                 {
671                         if (es->qd)
672                         {
673                                 errcontext("SQL function \"%s\" query %d",
674                                                    fn_name, query_num);
675                                 break;
676                         }
677                         es = es->next;
678                         query_num++;
679                 }
680                 if (es == NULL)
681                 {
682                         /*
683                          * couldn't identify a running query; might be function entry,
684                          * function exit, or between queries.
685                          */
686                         errcontext("SQL function \"%s\"", fn_name);
687                 }
688         }
689         else
690         {
691                 /* must have failed during init_sql_fcache() */
692                 errcontext("SQL function \"%s\" during startup", fn_name);
693         }
694
695         /* free result of get_func_name (in case this is only a notice) */
696         pfree(fn_name);
697 }
698
699
700 /*
701  * callback function in case a function-returning-set needs to be shut down
702  * before it has been run to completion
703  */
704 static void
705 ShutdownSQLFunction(Datum arg)
706 {
707         SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
708         execution_state *es = fcache->func_state;
709
710         while (es != NULL)
711         {
712                 /* Shut down anything still running */
713                 if (es->status == F_EXEC_RUN)
714                         postquel_end(es);
715                 /* Reset states to START in case we're called again */
716                 es->status = F_EXEC_START;
717                 es = es->next;
718         }
719
720         /* execUtils will deregister the callback... */
721         fcache->shutdown_reg = false;
722 }