/*------------------------------------------------------------------------- * * functions.c-- * Routines to handle functions called from the executor * Putting this stuff in fmgr makes the postmaster a mess.... * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.3 1996/10/26 04:13:20 scrappy Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "nodes/primnodes.h" #include "nodes/relation.h" #include "nodes/execnodes.h" #include "nodes/plannodes.h" #include "catalog/pg_proc.h" #include "parser/parse_query.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" #include "nodes/params.h" #include "fmgr.h" #include "utils/fcache.h" #include "utils/datum.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/syscache.h" #include "catalog/pg_language.h" #include "access/heapam.h" #include "access/xact.h" #include "executor/executor.h" #include "executor/functions.h" #undef new typedef enum {F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE} ExecStatus; typedef struct local_es { QueryDesc *qd; EState *estate; struct local_es *next; ExecStatus status; } execution_state; #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL) /* non-export function prototypes */ static TupleDesc postquel_start(execution_state *es); static execution_state *init_execution_state(FunctionCachePtr fcache, char *args[]); static TupleTableSlot *postquel_getnext(execution_state *es); static void postquel_end(execution_state *es); static void postquel_sub_params(execution_state *es, int nargs, char *args[], bool *nullV); static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache, List *fTlist, char **args, bool *isNull); Datum ProjectAttribute(TupleDesc TD, TargetEntry *tlist, HeapTuple tup, bool *isnullP) { Datum val,valueP; Var *attrVar = (Var *)tlist->expr; AttrNumber attrno = attrVar->varattno; val = PointerGetDatum(heap_getattr(tup, InvalidBuffer, attrno, TD, isnullP)); if (*isnullP) return (Datum) NULL; valueP = datumCopy(val, TD->attrs[attrno-1]->atttypid, TD->attrs[attrno-1]->attbyval, (Size) TD->attrs[attrno-1]->attlen); return valueP; } static execution_state * init_execution_state(FunctionCachePtr fcache, char *args[]) { execution_state *newes; execution_state *nextes; execution_state *preves; QueryTreeList *queryTree_list; int i; List *planTree_list; int nargs; nargs = fcache->nargs; newes = (execution_state *) palloc(sizeof(execution_state)); nextes = newes; preves = (execution_state *)NULL; planTree_list = (List *) pg_plan(fcache->src, fcache->argOidVect, nargs, &queryTree_list, None); for (i=0; i < queryTree_list->len; i++) { EState *estate; Query *queryTree = (Query*) (queryTree_list->qtrees[i]); Plan *planTree = lfirst(planTree_list); if (!nextes) nextes = (execution_state *) palloc(sizeof(execution_state)); if (preves) preves->next = nextes; nextes->next = NULL; nextes->status = F_EXEC_START; nextes->qd = CreateQueryDesc(queryTree, planTree, None); estate = CreateExecutorState(); if (nargs > 0) { int i; ParamListInfo paramLI; paramLI = (ParamListInfo)palloc((nargs+1)*sizeof(ParamListInfoData)); memset(paramLI, 0, nargs*sizeof(ParamListInfoData)); estate->es_param_list_info = paramLI; for (i=0; ikind = PARAM_NUM; paramLI->id = i+1; paramLI->isnull = false; paramLI->value = (Datum) NULL; } paramLI->kind = PARAM_INVALID; } else estate->es_param_list_info = (ParamListInfo)NULL; nextes->estate = estate; preves = nextes; nextes = (execution_state *)NULL; planTree_list = lnext(planTree_list); } return newes; } static TupleDesc postquel_start(execution_state *es) { #ifdef FUNC_UTIL_PATCH /* * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 */ if (es->qd->operation == CMD_UTILITY) { return (TupleDesc) NULL; } #endif return ExecutorStart(es->qd, es->estate); } static TupleTableSlot * postquel_getnext(execution_state *es) { int feature; #ifdef FUNC_UTIL_PATCH if (es->qd->operation == CMD_UTILITY) { /* * Process an utility command. (create, destroy...) DZ - 30-8-1996 */ ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest); if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); return (TupleTableSlot*) NULL; } #endif feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN; return ExecutorRun(es->qd, es->estate, feature, 0); } static void postquel_end(execution_state *es) { #ifdef FUNC_UTIL_PATCH /* * Do nothing for utility commands. (create, destroy...) DZ - 30-8-1996 */ if (es->qd->operation == CMD_UTILITY) { return; } #endif ExecutorEnd(es->qd, es->estate); } static void postquel_sub_params(execution_state *es, int nargs, char *args[], bool *nullV) { ParamListInfo paramLI; EState *estate; estate = es->estate; paramLI = estate->es_param_list_info; while (paramLI->kind != PARAM_INVALID) { if (paramLI->kind == PARAM_NUM) { Assert(paramLI->id <= nargs); paramLI->value = (Datum)args[(paramLI->id - 1)]; paramLI->isnull = nullV[(paramLI->id - 1)]; } paramLI++; } } static TupleTableSlot * copy_function_result(FunctionCachePtr fcache, TupleTableSlot *resultSlot) { TupleTableSlot *funcSlot; TupleDesc resultTd; HeapTuple newTuple; HeapTuple oldTuple; Assert(! TupIsNull(resultSlot)); oldTuple = resultSlot->val; funcSlot = (TupleTableSlot*)fcache->funcSlot; if (funcSlot == (TupleTableSlot*)NULL) return resultSlot; resultTd = resultSlot->ttc_tupleDescriptor; /* * When the funcSlot is NULL we have to initialize the funcSlot's * tuple descriptor. */ if (TupIsNull(funcSlot)) { int i= 0; TupleDesc funcTd = funcSlot->ttc_tupleDescriptor; while (i < oldTuple->t_natts) { funcTd->attrs[i] = (AttributeTupleForm)palloc(ATTRIBUTE_TUPLE_SIZE); memmove(funcTd->attrs[i], resultTd->attrs[i], ATTRIBUTE_TUPLE_SIZE); i++; } } newTuple = heap_copytuple(oldTuple); return ExecStoreTuple(newTuple,funcSlot,InvalidBuffer,true); } static Datum postquel_execute(execution_state *es, FunctionCachePtr fcache, List *fTlist, char **args, bool *isNull) { TupleTableSlot *slot; Datum value; if (es->status == F_EXEC_START) { (void) postquel_start(es); es->status = F_EXEC_RUN; } if (fcache->nargs > 0) postquel_sub_params(es, fcache->nargs, args, fcache->nullVect); slot = postquel_getnext(es); if (TupIsNull(slot)) { postquel_end(es); es->status = F_EXEC_DONE; *isNull = true; /* * If this isn't the last command for the function * we have to increment the command * counter so that subsequent commands can see changes made * by previous ones. */ if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); return (Datum)NULL; } if (LAST_POSTQUEL_COMMAND(es)) { TupleTableSlot *resSlot; /* * Copy the result. copy_function_result is smart enough * to do nothing when no action is called for. This helps * reduce the logic and code redundancy here. */ resSlot = copy_function_result(fcache, slot); if (fTlist != NIL) { HeapTuple tup; TargetEntry *tle = lfirst(fTlist); tup = resSlot->val; value = ProjectAttribute(resSlot->ttc_tupleDescriptor, tle, tup, isNull); }else { value = (Datum)resSlot; *isNull = false; } /* * If this is a single valued function we have to end the * function execution now. */ if (fcache->oneResult) { postquel_end(es); es->status = F_EXEC_DONE; } return value; } /* * If this isn't the last command for the function, we don't * return any results, but we have to increment the command * counter so that subsequent commands can see changes made * by previous ones. */ CommandCounterIncrement(); return (Datum)NULL; } Datum postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone) { execution_state *es; Datum result = 0; FunctionCachePtr fcache = funcNode->func_fcache; es = (execution_state *) fcache->func_state; if (es == NULL) { es = init_execution_state(fcache, args); fcache->func_state = (char *) es; } while (es && es->status == F_EXEC_DONE) es = es->next; Assert(es); /* * Execute each command in the function one after another until we're * executing the final command and get a result or we run out of * commands. */ while (es != (execution_state *)NULL) { result = postquel_execute(es, fcache, funcNode->func_tlist, args, isNull); if (es->status != F_EXEC_DONE) break; es = es->next; } /* * If we've gone through every command in this function, we are done. */ if (es == (execution_state *)NULL) { /* * Reset the execution states to start over again */ es = (execution_state *)fcache->func_state; while (es) { es->status = F_EXEC_START; es = es->next; } /* * Let caller know we're finished. */ *isDone = true; return (fcache->oneResult) ? result : (Datum)NULL; } /* * If we got a result from a command within the function it has * to be the final command. All others shouldn't be returing * anything. */ Assert ( LAST_POSTQUEL_COMMAND(es) ); *isDone = false; return result; }