aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/functions.c
diff options
context:
space:
mode:
authorMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
committerMarc G. Fournier <scrappy@hub.org>1996-07-09 06:22:35 +0000
commitd31084e9d1118b25fd16580d9d8c2924b5740dff (patch)
tree3179e66307d54df9c7b966543550e601eb55e668 /src/backend/executor/functions.c
downloadpostgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.tar.gz
postgresql-d31084e9d1118b25fd16580d9d8c2924b5740dff.zip
Postgres95 1.01 Distribution - Virgin SourcesPG95-1_01
Diffstat (limited to 'src/backend/executor/functions.c')
-rw-r--r--src/backend/executor/functions.c388
1 files changed, 388 insertions, 0 deletions
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
new file mode 100644
index 00000000000..2f6e29d8277
--- /dev/null
+++ b/src/backend/executor/functions.c
@@ -0,0 +1,388 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.1.1.1 1996/07/09 06:21:25 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 "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; i<nargs; paramLI++, i++) {
+ paramLI->kind = 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)
+{
+ return ExecutorStart(es->qd, es->estate);
+}
+
+static TupleTableSlot *
+postquel_getnext(execution_state *es)
+{
+ int feature;
+
+ feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
+
+ return ExecutorRun(es->qd, es->estate, feature, 0);
+}
+
+static void
+postquel_end(execution_state *es)
+{
+ 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;
+ 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;
+}