1 /*-------------------------------------------------------------------------
4 * Execution of SQL-language functions
6 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.71 2003/08/04 02:39:58 momjian Exp $
13 *-------------------------------------------------------------------------
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"
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.
38 F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
41 typedef struct local_es
43 struct local_es *next;
47 QueryDesc *qd; /* null unless status == RUN */
50 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
54 * An SQLFunctionCache record is built during the first call,
55 * and linked to from the fn_extra field of the FmgrInfo struct.
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 */
64 TupleTableSlot *funcSlot; /* if one result we need to copy it before
65 * we end execution of the function and
68 ParamListInfo paramLI; /* Param list representing current args */
70 /* head of linked list of execution_state records */
71 execution_state *func_state;
74 typedef SQLFunctionCache *SQLFunctionCachePtr;
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);
94 static execution_state *
95 init_execution_state(char *src, Oid *argOidVect, int nargs,
96 Oid rettype, bool haspolyarg)
98 execution_state *firstes;
99 execution_state *preves;
100 List *queryTree_list,
103 queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
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.
110 check_sql_fn_retval(rettype, get_typtype(rettype), queryTree_list);
115 foreach(qtl_item, queryTree_list)
117 Query *queryTree = lfirst(qtl_item);
119 execution_state *newes;
121 planTree = pg_plan_query(queryTree);
123 newes = (execution_state *) palloc(sizeof(execution_state));
125 preves->next = newes;
130 newes->status = F_EXEC_START;
131 newes->query = queryTree;
132 newes->plan = planTree;
143 init_sql_fcache(FmgrInfo *finfo)
145 Oid foid = finfo->fn_oid;
147 HeapTuple procedureTuple;
149 Form_pg_proc procedureStruct;
150 Form_pg_type typeStruct;
151 SQLFunctionCachePtr fcache;
159 fcache = (SQLFunctionCachePtr) palloc0(sizeof(SQLFunctionCache));
162 * get the procedure tuple corresponding to the given function Oid
164 procedureTuple = SearchSysCache(PROCOID,
165 ObjectIdGetDatum(foid),
167 if (!HeapTupleIsValid(procedureTuple))
168 elog(ERROR, "cache lookup failed for function %u", foid);
169 procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
172 * get the result type from the procedure tuple, and check for
173 * polymorphic result type; if so, find out the actual result type.
175 rettype = procedureStruct->prorettype;
177 if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
179 rettype = get_fn_expr_rettype(finfo);
180 if (rettype == InvalidOid) /* this probably should not happen */
182 (errcode(ERRCODE_DATATYPE_MISMATCH),
183 errmsg("could not determine actual result type for function declared %s",
184 format_type_be(procedureStruct->prorettype))));
187 /* Now look up the actual result type */
188 typeTuple = SearchSysCache(TYPEOID,
189 ObjectIdGetDatum(rettype),
191 if (!HeapTupleIsValid(typeTuple))
192 elog(ERROR, "cache lookup failed for type %u", rettype);
193 typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
196 * get the type length and by-value flag from the type tuple
198 fcache->typlen = typeStruct->typlen;
200 if (typeStruct->typtype != 'c' && rettype != RECORDOID)
202 /* The return type is not a composite type, so just use byval */
203 fcache->typbyval = typeStruct->typbyval;
204 fcache->returnsTuple = false;
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.
213 fcache->typbyval = false;
214 fcache->returnsTuple = true;
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.
223 if (!finfo->fn_retset && !fcache->typbyval)
224 fcache->funcSlot = MakeTupleTableSlot();
226 fcache->funcSlot = NULL;
229 * Parse and plan the queries. We need the argument type info to pass
232 nargs = procedureStruct->pronargs;
239 argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
241 procedureStruct->proargtypes,
242 nargs * sizeof(Oid));
243 /* Resolve any polymorphic argument types */
244 for (argnum = 0; argnum < nargs; argnum++)
246 Oid argtype = argOidVect[argnum];
248 if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
250 argtype = get_fn_expr_argtype(finfo, argnum);
251 if (argtype == InvalidOid)
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;
262 argOidVect = (Oid *) NULL;
264 tmp = SysCacheGetAttr(PROCOID,
269 elog(ERROR, "null prosrc for function %u", foid);
270 src = DatumGetCString(DirectFunctionCall1(textout, tmp));
272 fcache->func_state = init_execution_state(src, argOidVect, nargs,
273 rettype, haspolyarg);
277 ReleaseSysCache(typeTuple);
278 ReleaseSysCache(procedureTuple);
280 finfo->fn_extra = (void *) fcache;
285 postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
287 Assert(es->qd == NULL);
288 es->qd = CreateQueryDesc(es->query, es->plan,
290 fcache->paramLI, false);
292 /* Utility commands don't need Executor. */
293 if (es->qd->operation != CMD_UTILITY)
294 ExecutorStart(es->qd, false);
296 es->status = F_EXEC_RUN;
299 static TupleTableSlot *
300 postquel_getnext(execution_state *es)
304 if (es->qd->operation == CMD_UTILITY)
306 ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
307 return (TupleTableSlot *) NULL;
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
315 if (LAST_POSTQUEL_COMMAND(es) && es->qd->operation == CMD_SELECT)
320 return ExecutorRun(es->qd, ForwardScanDirection, count);
324 postquel_end(execution_state *es)
326 /* mark status done to ensure we don't do ExecutorEnd twice */
327 es->status = F_EXEC_DONE;
329 /* Utility commands don't need Executor. */
330 if (es->qd->operation != CMD_UTILITY)
333 FreeQueryDesc(es->qd);
337 /* Build ParamListInfo array representing current arguments */
339 postquel_sub_params(SQLFunctionCachePtr fcache,
340 FunctionCallInfo fcinfo)
342 ParamListInfo paramLI;
343 int nargs = fcinfo->nargs;
349 paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));
351 for (i = 0; i < nargs; i++)
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];
358 paramLI[nargs].kind = PARAM_INVALID;
361 paramLI = (ParamListInfo) NULL;
364 pfree(fcache->paramLI);
366 fcache->paramLI = paramLI;
369 static TupleTableSlot *
370 copy_function_result(SQLFunctionCachePtr fcache,
371 TupleTableSlot *resultSlot)
373 TupleTableSlot *funcSlot;
375 HeapTuple resultTuple;
378 Assert(!TupIsNull(resultSlot));
379 resultTuple = resultSlot->val;
381 funcSlot = fcache->funcSlot;
383 if (funcSlot == NULL)
384 return resultSlot; /* no need to copy result */
387 * If first time through, we have to initialize the funcSlot's tuple
390 if (funcSlot->ttc_tupleDescriptor == NULL)
392 resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
393 ExecSetSlotDescriptor(funcSlot, resultTd, true);
394 ExecSetSlotDescriptorIsNew(funcSlot, true);
397 newTuple = heap_copytuple(resultTuple);
399 return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
403 postquel_execute(execution_state *es,
404 FunctionCallInfo fcinfo,
405 SQLFunctionCachePtr fcache)
407 TupleTableSlot *slot;
410 if (es->status == F_EXEC_START)
411 postquel_start(es, fcache);
413 slot = postquel_getnext(es);
418 fcinfo->isnull = true;
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.
425 if (!LAST_POSTQUEL_COMMAND(es))
426 CommandCounterIncrement();
430 if (LAST_POSTQUEL_COMMAND(es))
432 TupleTableSlot *resSlot;
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.
439 resSlot = copy_function_result(fcache, slot);
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
448 if (fcache->returnsTuple)
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.
454 value = PointerGetDatum(resSlot);
455 fcinfo->isnull = false;
459 value = heap_getattr(resSlot->val,
461 resSlot->ttc_tupleDescriptor,
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.
472 * If this is a single valued function we have to end the function
475 if (!fcinfo->flinfo->fn_retset)
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.
486 CommandCounterIncrement();
491 fmgr_sql(PG_FUNCTION_ARGS)
493 MemoryContext oldcontext;
494 SQLFunctionCachePtr fcache;
495 ErrorContextCallback sqlerrcontext;
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.
504 oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
507 * Setup error traceback support for ereport()
509 sqlerrcontext.callback = sql_exec_error_callback;
510 sqlerrcontext.arg = fcinfo->flinfo;
511 sqlerrcontext.previous = error_context_stack;
512 error_context_stack = &sqlerrcontext;
515 * Initialize fcache (build plans) if first time through.
517 fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
520 init_sql_fcache(fcinfo->flinfo);
521 fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
523 es = fcache->func_state;
526 * Convert params to appropriate format if starting a fresh execution.
527 * (If continuing execution, we can re-use prior params.)
529 if (es && es->status == F_EXEC_START)
530 postquel_sub_params(fcache, fcinfo);
533 * Find first unfinished query in function.
535 while (es && es->status == F_EXEC_DONE)
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
545 result = postquel_execute(es, fcinfo, fcache);
546 if (es->status != F_EXEC_DONE)
552 * If we've gone through every command in this function, we are done.
554 if (es == (execution_state *) NULL)
557 * Reset the execution states to start over again on next call.
559 es = fcache->func_state;
562 es->status = F_EXEC_START;
567 * Let caller know we're finished.
569 if (fcinfo->flinfo->fn_retset)
571 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
573 if (rsi && IsA(rsi, ReturnSetInfo))
574 rsi->isDone = ExprEndResult;
577 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
578 errmsg("set-valued function called in context that cannot accept a set")));
579 fcinfo->isnull = true;
582 /* Deregister shutdown callback, if we made one */
583 if (fcache->shutdown_reg)
585 UnregisterExprContextCallback(rsi->econtext,
587 PointerGetDatum(fcache));
588 fcache->shutdown_reg = false;
592 error_context_stack = sqlerrcontext.previous;
594 MemoryContextSwitchTo(oldcontext);
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.
603 Assert(LAST_POSTQUEL_COMMAND(es));
606 * Let caller know we're not finished.
608 if (fcinfo->flinfo->fn_retset)
610 ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
612 if (rsi && IsA(rsi, ReturnSetInfo))
613 rsi->isDone = ExprMultipleResult;
616 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
617 errmsg("set-valued function called in context that cannot accept a set")));
620 * Ensure we will get shut down cleanly if the exprcontext is not
623 if (!fcache->shutdown_reg)
625 RegisterExprContextCallback(rsi->econtext,
627 PointerGetDatum(fcache));
628 fcache->shutdown_reg = true;
632 error_context_stack = sqlerrcontext.previous;
634 MemoryContextSwitchTo(oldcontext);
641 * error context callback to let us supply a call-stack traceback
644 sql_exec_error_callback(void *arg)
646 FmgrInfo *flinfo = (FmgrInfo *) arg;
647 SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra;
650 fn_name = get_func_name(flinfo->fn_oid);
651 /* safety check, shouldn't happen */
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.)
667 es = fcache->func_state;
673 errcontext("SQL function \"%s\" query %d",
683 * couldn't identify a running query; might be function entry,
684 * function exit, or between queries.
686 errcontext("SQL function \"%s\"", fn_name);
691 /* must have failed during init_sql_fcache() */
692 errcontext("SQL function \"%s\" during startup", fn_name);
695 /* free result of get_func_name (in case this is only a notice) */
701 * callback function in case a function-returning-set needs to be shut down
702 * before it has been run to completion
705 ShutdownSQLFunction(Datum arg)
707 SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
708 execution_state *es = fcache->func_state;
712 /* Shut down anything still running */
713 if (es->status == F_EXEC_RUN)
715 /* Reset states to START in case we're called again */
716 es->status = F_EXEC_START;
720 /* execUtils will deregister the callback... */
721 fcache->shutdown_reg = false;