summaryrefslogtreecommitdiff
path: root/src/backend/executor/functions.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/functions.c')
-rw-r--r--src/backend/executor/functions.c611
1 files changed, 0 insertions, 611 deletions
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
deleted file mode 100644
index c7b7c398eb2..00000000000
--- a/src/backend/executor/functions.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * functions.c
- * Routines to handle functions called from the executor
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.52 2002/06/20 20:29:28 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "access/heapam.h"
-#include "catalog/pg_proc.h"
-#include "catalog/pg_type.h"
-#include "executor/execdefs.h"
-#include "executor/executor.h"
-#include "executor/functions.h"
-#include "tcop/pquery.h"
-#include "tcop/tcopprot.h"
-#include "tcop/utility.h"
-#include "utils/builtins.h"
-#include "utils/syscache.h"
-
-
-/*
- * We have an execution_state record for each query in a function.
- */
-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)
-
-
-/*
- * An SQLFunctionCache record is built during the first call,
- * and linked to from the fn_extra field of the FmgrInfo struct.
- */
-
-typedef struct
-{
- int typlen; /* length of the return type */
- bool typbyval; /* true if return type is pass by value */
- bool returnsTuple; /* true if return type is a tuple */
- bool shutdown_reg; /* true if registered shutdown callback */
-
- TupleTableSlot *funcSlot; /* if one result we need to copy it before
- * we end execution of the function and
- * free stuff */
-
- /* head of linked list of execution_state records */
- execution_state *func_state;
-} SQLFunctionCache;
-
-typedef SQLFunctionCache *SQLFunctionCachePtr;
-
-
-/* non-export function prototypes */
-static execution_state *init_execution_state(char *src,
- Oid *argOidVect, int nargs);
-static void init_sql_fcache(FmgrInfo *finfo);
-static void postquel_start(execution_state *es);
-static TupleTableSlot *postquel_getnext(execution_state *es);
-static void postquel_end(execution_state *es);
-static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
-static Datum postquel_execute(execution_state *es,
- FunctionCallInfo fcinfo,
- SQLFunctionCachePtr fcache);
-static void ShutdownSQLFunction(Datum arg);
-
-
-static execution_state *
-init_execution_state(char *src, Oid *argOidVect, int nargs)
-{
- execution_state *newes;
- execution_state *nextes;
- execution_state *preves;
- List *queryTree_list,
- *qtl_item;
-
- newes = (execution_state *) palloc(sizeof(execution_state));
- nextes = newes;
- preves = (execution_state *) NULL;
-
- queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
-
- foreach(qtl_item, queryTree_list)
- {
- Query *queryTree = lfirst(qtl_item);
- Plan *planTree;
- EState *estate;
-
- planTree = pg_plan_query(queryTree);
-
- 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, NULL);
- 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;
- }
-
- return newes;
-}
-
-
-static void
-init_sql_fcache(FmgrInfo *finfo)
-{
- Oid foid = finfo->fn_oid;
- HeapTuple procedureTuple;
- HeapTuple typeTuple;
- Form_pg_proc procedureStruct;
- Form_pg_type typeStruct;
- SQLFunctionCachePtr fcache;
- Oid *argOidVect;
- char *src;
- int nargs;
- Datum tmp;
- bool isNull;
-
- /*
- * get the procedure tuple corresponding to the given function Oid
- */
- procedureTuple = SearchSysCache(PROCOID,
- ObjectIdGetDatum(foid),
- 0, 0, 0);
- if (!HeapTupleIsValid(procedureTuple))
- elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u",
- foid);
-
- procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
-
- /*
- * get the return type from the procedure tuple
- */
- typeTuple = SearchSysCache(TYPEOID,
- ObjectIdGetDatum(procedureStruct->prorettype),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u",
- procedureStruct->prorettype);
-
- typeStruct = (Form_pg_type) GETSTRUCT(typeTuple);
-
- fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache));
- MemSet(fcache, 0, sizeof(SQLFunctionCache));
-
- /*
- * get the type length and by-value flag from the type tuple
- */
- fcache->typlen = typeStruct->typlen;
- if (typeStruct->typrelid == InvalidOid)
- {
- /* The return type is not a relation, so just use byval */
- fcache->typbyval = typeStruct->typbyval;
- fcache->returnsTuple = false;
- }
- else
- {
- /*
- * This is a hack. We assume here that any function returning a
- * tuple returns it by reference. This needs to be fixed, since
- * actually the mechanism isn't quite like return-by-reference.
- */
- fcache->typbyval = false;
- fcache->returnsTuple = true;
- }
-
- /*
- * If we are returning exactly one result then we have to copy tuples
- * and by reference results because we have to end the execution
- * before we return the results. When you do this everything
- * allocated by the executor (i.e. slots and tuples) is freed.
- */
- if (!finfo->fn_retset && !fcache->typbyval)
- fcache->funcSlot = MakeTupleTableSlot();
- else
- fcache->funcSlot = NULL;
-
- nargs = procedureStruct->pronargs;
-
- if (nargs > 0)
- {
- argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
- memcpy(argOidVect,
- procedureStruct->proargtypes,
- nargs * sizeof(Oid));
- }
- else
- argOidVect = (Oid *) NULL;
-
- tmp = SysCacheGetAttr(PROCOID,
- procedureTuple,
- Anum_pg_proc_prosrc,
- &isNull);
- if (isNull)
- elog(ERROR, "init_sql_fcache: null prosrc for procedure %u",
- foid);
- src = DatumGetCString(DirectFunctionCall1(textout, tmp));
-
- fcache->func_state = init_execution_state(src, argOidVect, nargs);
-
- pfree(src);
-
- ReleaseSysCache(typeTuple);
- ReleaseSysCache(procedureTuple);
-
- finfo->fn_extra = (void *) fcache;
-}
-
-
-static void
-postquel_start(execution_state *es)
-{
- /*
- * Do nothing for utility commands. (create, destroy...) DZ -
- * 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY)
- return;
- ExecutorStart(es->qd, es->estate);
-}
-
-static TupleTableSlot *
-postquel_getnext(execution_state *es)
-{
- long count;
-
- if (es->qd->operation == CMD_UTILITY)
- {
- /*
- * Process a utility command. (create, destroy...) DZ - 30-8-1996
- */
- ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest, NULL);
- if (!LAST_POSTQUEL_COMMAND(es))
- CommandCounterIncrement();
- return (TupleTableSlot *) NULL;
- }
-
- /* If it's not the last command, just run it to completion */
- count = (LAST_POSTQUEL_COMMAND(es)) ? 1L : 0L;
-
- return ExecutorRun(es->qd, es->estate, ForwardScanDirection, count);
-}
-
-static void
-postquel_end(execution_state *es)
-{
- /*
- * Do nothing for utility commands. (create, destroy...) DZ -
- * 30-8-1996
- */
- if (es->qd->operation == CMD_UTILITY)
- return;
- ExecutorEnd(es->qd, es->estate);
-}
-
-static void
-postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
-{
- EState *estate;
- ParamListInfo paramLI;
-
- estate = es->estate;
- paramLI = estate->es_param_list_info;
-
- while (paramLI->kind != PARAM_INVALID)
- {
- if (paramLI->kind == PARAM_NUM)
- {
- Assert(paramLI->id <= fcinfo->nargs);
- paramLI->value = fcinfo->arg[paramLI->id - 1];
- paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
- }
- paramLI++;
- }
-}
-
-static TupleTableSlot *
-copy_function_result(SQLFunctionCachePtr fcache,
- TupleTableSlot *resultSlot)
-{
- TupleTableSlot *funcSlot;
- TupleDesc resultTd;
- HeapTuple resultTuple;
- HeapTuple newTuple;
-
- Assert(!TupIsNull(resultSlot));
- resultTuple = resultSlot->val;
-
- funcSlot = fcache->funcSlot;
-
- if (funcSlot == NULL)
- return resultSlot; /* no need to copy result */
-
- /*
- * If first time through, we have to initialize the funcSlot's tuple
- * descriptor.
- */
- if (funcSlot->ttc_tupleDescriptor == NULL)
- {
- resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
- ExecSetSlotDescriptor(funcSlot, resultTd, true);
- ExecSetSlotDescriptorIsNew(funcSlot, true);
- }
-
- newTuple = heap_copytuple(resultTuple);
-
- return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
-}
-
-static Datum
-postquel_execute(execution_state *es,
- FunctionCallInfo fcinfo,
- SQLFunctionCachePtr fcache)
-{
- 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 (fcinfo->nargs > 0)
- postquel_sub_params(es, fcinfo);
-
- 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;
- fcinfo->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 we are supposed to return a tuple, we return the tuple slot
- * pointer converted to Datum. If we are supposed to return a
- * simple value, then project out the first attribute of the
- * result tuple (ie, take the first result column of the final
- * SELECT).
- */
- if (fcache->returnsTuple)
- {
- /*
- * XXX do we need to remove junk attrs from the result tuple?
- * Probably OK to leave them, as long as they are at the end.
- */
- value = PointerGetDatum(resSlot);
- fcinfo->isnull = false;
- }
- else
- {
- value = heap_getattr(resSlot->val,
- 1,
- resSlot->ttc_tupleDescriptor,
- &(fcinfo->isnull));
-
- /*
- * Note: if result type is pass-by-reference then we are
- * returning a pointer into the tuple copied by
- * copy_function_result. This is OK.
- */
- }
-
- /*
- * If this is a single valued function we have to end the function
- * execution now.
- */
- if (!fcinfo->flinfo->fn_retset)
- {
- 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
-fmgr_sql(PG_FUNCTION_ARGS)
-{
- MemoryContext oldcontext;
- SQLFunctionCachePtr fcache;
- execution_state *es;
- Datum result = 0;
-
- /*
- * Switch to context in which the fcache lives. This ensures that
- * parsetrees, plans, etc, will have sufficient lifetime. The
- * sub-executor is responsible for deleting per-tuple information.
- */
- oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-
- /*
- * Initialize fcache and execution state if first time through.
- */
- fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
- if (fcache == NULL)
- {
- init_sql_fcache(fcinfo->flinfo);
- fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
- }
- es = fcache->func_state;
- Assert(es);
-
- /*
- * Find first unfinished query in function.
- */
- 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, fcinfo, fcache);
- 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 = fcache->func_state;
- while (es)
- {
- es->status = F_EXEC_START;
- es = es->next;
- }
-
- /*
- * Let caller know we're finished.
- */
- if (fcinfo->flinfo->fn_retset)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = ExprEndResult;
- else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
- fcinfo->isnull = true;
- result = (Datum) 0;
-
- /* Deregister shutdown callback, if we made one */
- if (fcache->shutdown_reg)
- {
- UnregisterExprContextCallback(rsi->econtext,
- ShutdownSQLFunction,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = false;
- }
- }
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
- }
-
- /*
- * If we got a result from a command within the function it has to be
- * the final command. All others shouldn't be returning anything.
- */
- Assert(LAST_POSTQUEL_COMMAND(es));
-
- /*
- * Let caller know we're not finished.
- */
- if (fcinfo->flinfo->fn_retset)
- {
- ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
-
- if (rsi && IsA(rsi, ReturnSetInfo))
- rsi->isDone = ExprMultipleResult;
- else
- elog(ERROR, "Set-valued function called in context that cannot accept a set");
-
- /*
- * Ensure we will get shut down cleanly if the exprcontext is
- * not run to completion.
- */
- if (!fcache->shutdown_reg)
- {
- RegisterExprContextCallback(rsi->econtext,
- ShutdownSQLFunction,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = true;
- }
- }
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
-}
-
-/*
- * callback function in case a function-returning-set needs to be shut down
- * before it has been run to completion
- */
-static void
-ShutdownSQLFunction(Datum arg)
-{
- SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg);
- execution_state *es = fcache->func_state;
-
- while (es != NULL)
- {
- /* Shut down anything still running */
- if (es->status == F_EXEC_RUN)
- postquel_end(es);
- /* Reset states to START in case we're called again */
- es->status = F_EXEC_START;
- es = es->next;
- }
-
- /* execUtils will deregister the callback... */
- fcache->shutdown_reg = false;
-}