functions.c
上传用户:blenddy
上传日期:2007-01-07
资源大小:6495k
文件大小:10k
- /*-------------------------------------------------------------------------
- *
- * 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: /usr/local/cvsroot/pgsql/src/backend/executor/functions.c,v 1.26 1999/05/25 16:08:39 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
- #include <string.h>
- #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 "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/execdefs.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 = heap_getattr(tup, 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;
- List *queryTree_list,
- *planTree_list,
- *qtl_item;
- int nargs = fcache->nargs;
- newes = (execution_state *) palloc(sizeof(execution_state));
- nextes = newes;
- preves = (execution_state *) NULL;
- planTree_list = pg_parse_and_plan(fcache->src, fcache->argOidVect,
- nargs, &queryTree_list, None, FALSE);
- foreach(qtl_item, queryTree_list)
- {
- Query *queryTree = lfirst(qtl_item);
- Plan *planTree = lfirst(planTree_list);
- EState *estate;
- 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 (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
- elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
- 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)
- {
- #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, (Node *) NULL, (Node *) NULL);
- }
- 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_data->t_natts)
- {
- funcTd->attrs[i] = (Form_pg_attribute) 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;
- /*
- * It's more right place to do it (before
- * postquel_start->ExecutorStart). Now
- * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
- * note: I HOPE we can do it here). - vadim 01/22/97
- */
- if (fcache->nargs > 0)
- postquel_sub_params(es, fcache->nargs, args, fcache->nullVect);
- if (es->status == F_EXEC_START)
- {
- postquel_start(es);
- es->status = F_EXEC_RUN;
- }
- 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)
- {
- TargetEntry *tle = lfirst(fTlist);
- value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
- tle,
- resSlot->val,
- 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;
- CommandId savedId;
- /*
- * Before we start do anything we must save CurrentScanCommandId to
- * restore it before return to upper Executor. Also, we have to set
- * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97
- */
- savedId = GetScanCommandId();
- SetScanCommandId(GetCurrentCommandId());
- 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;
- SetScanCommandId(savedId);
- 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;
- SetScanCommandId(savedId);
- return result;
- }