1996-07-09 08:22:35 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* functions.c
|
1997-09-07 07:04:48 +02:00
|
|
|
* Routines to handle functions called from the executor
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
2000-01-26 06:58:53 +01:00
|
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2000-08-24 05:29:15 +02:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
|
1996-07-09 08:22:35 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "access/heapam.h"
|
2000-08-24 05:29:15 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
|
|
|
#include "catalog/pg_type.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "executor/execdefs.h"
|
|
|
|
#include "executor/executor.h"
|
|
|
|
#include "executor/functions.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "tcop/pquery.h"
|
|
|
|
#include "tcop/tcopprot.h"
|
1996-10-26 06:15:05 +02:00
|
|
|
#include "tcop/utility.h"
|
2000-08-24 05:29:15 +02:00
|
|
|
#include "utils/builtins.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
#include "utils/datum.h"
|
2000-08-24 05:29:15 +02:00
|
|
|
#include "utils/syscache.h"
|
1996-07-09 08:22:35 +02:00
|
|
|
|
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
/*
|
|
|
|
* We have an execution_state record for each query in the function.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE
|
1997-09-08 23:56:23 +02:00
|
|
|
} ExecStatus;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
typedef struct local_es
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
QueryDesc *qd;
|
|
|
|
EState *estate;
|
1997-09-07 07:04:48 +02:00
|
|
|
struct local_es *next;
|
1997-09-08 04:41:22 +02:00
|
|
|
ExecStatus status;
|
1997-09-08 23:56:23 +02:00
|
|
|
} execution_state;
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-05-28 19:56:29 +02:00
|
|
|
#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/* non-export function prototypes */
|
2000-08-24 05:29:15 +02:00
|
|
|
static execution_state *init_execution_state(char *src,
|
|
|
|
Oid *argOidVect, int nargs);
|
|
|
|
static void init_sql_fcache(FmgrInfo *finfo);
|
1997-09-08 23:56:23 +02:00
|
|
|
static TupleDesc postquel_start(execution_state *es);
|
|
|
|
static TupleTableSlot *postquel_getnext(execution_state *es);
|
|
|
|
static void postquel_end(execution_state *es);
|
2000-05-28 19:56:29 +02:00
|
|
|
static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
|
|
|
|
static Datum postquel_execute(execution_state *es,
|
|
|
|
FunctionCallInfo fcinfo,
|
2000-08-24 05:29:15 +02:00
|
|
|
SQLFunctionCachePtr fcache);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
|
2000-08-08 17:43:12 +02:00
|
|
|
static Datum
|
|
|
|
ProjectAttribute(HeapTuple tup,
|
|
|
|
AttrNumber attrno,
|
|
|
|
TupleDesc TD,
|
1997-09-08 23:56:23 +02:00
|
|
|
bool *isnullP)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-07-12 04:37:39 +02:00
|
|
|
Datum val;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1998-01-31 05:39:26 +01:00
|
|
|
val = heap_getattr(tup, attrno, TD, isnullP);
|
2000-07-12 04:37:39 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (*isnullP)
|
2000-08-08 17:43:12 +02:00
|
|
|
return val;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
return datumCopy(val,
|
|
|
|
TD->attrs[attrno - 1]->attbyval,
|
|
|
|
TD->attrs[attrno - 1]->attlen);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static execution_state *
|
2000-08-24 05:29:15 +02:00
|
|
|
init_execution_state(char *src, Oid *argOidVect, int nargs)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
execution_state *newes;
|
|
|
|
execution_state *nextes;
|
|
|
|
execution_state *preves;
|
1999-05-13 09:29:22 +02:00
|
|
|
List *queryTree_list,
|
|
|
|
*qtl_item;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
newes = (execution_state *) palloc(sizeof(execution_state));
|
|
|
|
nextes = newes;
|
|
|
|
preves = (execution_state *) NULL;
|
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
foreach(qtl_item, queryTree_list)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
1999-05-13 09:29:22 +02:00
|
|
|
Query *queryTree = lfirst(qtl_item);
|
2000-04-04 23:44:40 +02:00
|
|
|
Plan *planTree;
|
1999-05-13 09:29:22 +02:00
|
|
|
EState *estate;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-04-04 23:44:40 +02:00
|
|
|
planTree = pg_plan_query(queryTree);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
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();
|
|
|
|
|
1999-02-08 15:14:32 +01:00
|
|
|
if (queryTree->limitOffset != NULL || queryTree->limitCount != NULL)
|
|
|
|
elog(ERROR, "LIMIT clause from SQL functions not yet implemented");
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
if (nargs > 0)
|
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int i;
|
|
|
|
ParamListInfo paramLI;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1999-02-03 22:18:02 +01:00
|
|
|
paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-09-18 22:22:58 +02:00
|
|
|
MemSet(paramLI, 0, nargs * sizeof(ParamListInfoData));
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return newes;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
*
|
|
|
|
* NB: use SearchSysCacheTupleCopy to ensure tuple lives long enough
|
|
|
|
* ----------------
|
|
|
|
*/
|
|
|
|
procedureTuple = SearchSysCacheTupleCopy(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 = SearchSysCacheTuple(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)
|
|
|
|
{
|
|
|
|
TupleTableSlot *slot;
|
|
|
|
|
|
|
|
slot = makeNode(TupleTableSlot);
|
|
|
|
slot->val = (HeapTuple) NULL;
|
|
|
|
slot->ttc_shouldFree = true;
|
|
|
|
slot->ttc_descIsNew = true;
|
|
|
|
slot->ttc_tupleDescriptor = (TupleDesc) NULL;
|
|
|
|
slot->ttc_buffer = InvalidBuffer;
|
|
|
|
slot->ttc_whichplan = -1;
|
|
|
|
|
|
|
|
fcache->funcSlot = slot;
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
|
|
|
|
heap_freetuple(procedureTuple);
|
|
|
|
|
|
|
|
finfo->fn_extra = (void *) fcache;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static TupleDesc
|
1997-09-08 23:56:23 +02:00
|
|
|
postquel_start(execution_state *es)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* Do nothing for utility commands. (create, destroy...) DZ -
|
|
|
|
* 30-8-1996
|
|
|
|
*/
|
|
|
|
if (es->qd->operation == CMD_UTILITY)
|
|
|
|
return (TupleDesc) NULL;
|
|
|
|
return ExecutorStart(es->qd, es->estate);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static TupleTableSlot *
|
1997-09-08 23:56:23 +02:00
|
|
|
postquel_getnext(execution_state *es)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
int feature;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (es->qd->operation == CMD_UTILITY)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Process a utility command. (create, destroy...) DZ - 30-8-1996
|
1997-09-07 07:04:48 +02:00
|
|
|
*/
|
|
|
|
ProcessUtility(es->qd->parsetree->utilityStmt, es->qd->dest);
|
|
|
|
if (!LAST_POSTQUEL_COMMAND(es))
|
|
|
|
CommandCounterIncrement();
|
|
|
|
return (TupleTableSlot *) NULL;
|
|
|
|
}
|
1996-09-16 07:36:38 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
feature = (LAST_POSTQUEL_COMMAND(es)) ? EXEC_RETONE : EXEC_RUN;
|
|
|
|
|
1999-05-25 18:15:34 +02:00
|
|
|
return ExecutorRun(es->qd, es->estate, feature, (Node *) NULL, (Node *) NULL);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
1997-09-08 23:56:23 +02:00
|
|
|
postquel_end(execution_state *es)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* Do nothing for utility commands. (create, destroy...) DZ -
|
|
|
|
* 30-8-1996
|
|
|
|
*/
|
|
|
|
if (es->qd->operation == CMD_UTILITY)
|
|
|
|
return;
|
|
|
|
ExecutorEnd(es->qd, es->estate);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2000-05-28 19:56:29 +02:00
|
|
|
postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-08 04:41:22 +02:00
|
|
|
EState *estate;
|
2000-05-28 19:56:29 +02:00
|
|
|
ParamListInfo paramLI;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
estate = es->estate;
|
|
|
|
paramLI = estate->es_param_list_info;
|
|
|
|
|
|
|
|
while (paramLI->kind != PARAM_INVALID)
|
|
|
|
{
|
|
|
|
if (paramLI->kind == PARAM_NUM)
|
|
|
|
{
|
2000-05-28 19:56:29 +02:00
|
|
|
Assert(paramLI->id <= fcinfo->nargs);
|
|
|
|
paramLI->value = fcinfo->arg[paramLI->id - 1];
|
|
|
|
paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
|
|
|
paramLI++;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static TupleTableSlot *
|
2000-08-24 05:29:15 +02:00
|
|
|
copy_function_result(SQLFunctionCachePtr fcache,
|
1997-09-08 23:56:23 +02:00
|
|
|
TupleTableSlot *resultSlot)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
TupleTableSlot *funcSlot;
|
1997-09-08 04:41:22 +02:00
|
|
|
TupleDesc resultTd;
|
2000-08-08 17:43:12 +02:00
|
|
|
HeapTuple resultTuple;
|
1997-09-08 04:41:22 +02:00
|
|
|
HeapTuple newTuple;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
Assert(!TupIsNull(resultSlot));
|
2000-08-08 17:43:12 +02:00
|
|
|
resultTuple = resultSlot->val;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
funcSlot = fcache->funcSlot;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
if (funcSlot == NULL)
|
|
|
|
return resultSlot; /* no need to copy result */
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
2000-08-08 17:43:12 +02:00
|
|
|
* If first time through, we have to initialize the funcSlot's
|
1997-09-07 07:04:48 +02:00
|
|
|
* tuple descriptor.
|
|
|
|
*/
|
|
|
|
if (TupIsNull(funcSlot))
|
|
|
|
{
|
2000-08-08 17:43:12 +02:00
|
|
|
resultTd = resultSlot->ttc_tupleDescriptor;
|
|
|
|
funcSlot->ttc_tupleDescriptor = CreateTupleDescCopy(resultTd);
|
|
|
|
funcSlot->ttc_descIsNew = true;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-08 17:43:12 +02:00
|
|
|
newTuple = heap_copytuple(resultTuple);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
1997-09-08 04:41:22 +02:00
|
|
|
static Datum
|
1997-09-08 23:56:23 +02:00
|
|
|
postquel_execute(execution_state *es,
|
2000-05-28 19:56:29 +02:00
|
|
|
FunctionCallInfo fcinfo,
|
2000-08-24 05:29:15 +02:00
|
|
|
SQLFunctionCachePtr fcache)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
TupleTableSlot *slot;
|
1997-09-08 04:41:22 +02:00
|
|
|
Datum value;
|
1997-01-22 06:26:50 +01:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*/
|
2000-05-28 19:56:29 +02:00
|
|
|
if (fcinfo->nargs > 0)
|
|
|
|
postquel_sub_params(es, fcinfo);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
if (es->status == F_EXEC_START)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
postquel_start(es);
|
|
|
|
es->status = F_EXEC_RUN;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
slot = postquel_getnext(es);
|
|
|
|
|
|
|
|
if (TupIsNull(slot))
|
|
|
|
{
|
|
|
|
postquel_end(es);
|
|
|
|
es->status = F_EXEC_DONE;
|
2000-05-28 19:56:29 +02:00
|
|
|
fcinfo->isnull = true;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2000-08-08 17:43:12 +02:00
|
|
|
/*
|
|
|
|
* 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)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
2000-08-08 17:43:12 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-05-28 19:56:29 +02:00
|
|
|
value = PointerGetDatum(resSlot);
|
|
|
|
fcinfo->isnull = false;
|
1997-09-07 07:04:48 +02:00
|
|
|
}
|
2000-08-08 17:43:12 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
value = ProjectAttribute(resSlot->val,
|
|
|
|
1,
|
|
|
|
resSlot->ttc_tupleDescriptor,
|
|
|
|
&fcinfo->isnull);
|
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a single valued function we have to end the function
|
|
|
|
* execution now.
|
|
|
|
*/
|
2000-08-24 05:29:15 +02:00
|
|
|
if (!fcinfo->flinfo->fn_retset)
|
1997-09-07 07:04:48 +02:00
|
|
|
{
|
|
|
|
postquel_end(es);
|
|
|
|
es->status = F_EXEC_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
1996-07-09 08:22:35 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* 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.
|
1996-07-09 08:22:35 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
CommandCounterIncrement();
|
|
|
|
return (Datum) NULL;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
2000-08-24 05:29:15 +02:00
|
|
|
fmgr_sql(PG_FUNCTION_ARGS)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-07-12 04:37:39 +02:00
|
|
|
MemoryContext oldcontext;
|
2000-08-24 05:29:15 +02:00
|
|
|
SQLFunctionCachePtr fcache;
|
1997-09-07 07:04:48 +02:00
|
|
|
execution_state *es;
|
1997-09-08 04:41:22 +02:00
|
|
|
Datum result = 0;
|
|
|
|
CommandId savedId;
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-08-24 05:29:15 +02:00
|
|
|
oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
|
2000-07-12 04:37:39 +02:00
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
/*
|
|
|
|
* 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());
|
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
/*
|
|
|
|
* Initialize fcache and execution state if first time through.
|
|
|
|
*/
|
|
|
|
fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
|
|
|
|
if (fcache == NULL)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-24 05:29:15 +02:00
|
|
|
init_sql_fcache(fcinfo->flinfo);
|
|
|
|
fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
2000-08-24 05:29:15 +02:00
|
|
|
es = fcache->func_state;
|
|
|
|
Assert(es);
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
/*
|
|
|
|
* Find first unfinished query in function.
|
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
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)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
2000-08-08 17:43:12 +02:00
|
|
|
result = postquel_execute(es, fcinfo, fcache);
|
1997-09-07 07:04:48 +02:00
|
|
|
if (es->status != F_EXEC_DONE)
|
|
|
|
break;
|
|
|
|
es = es->next;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
2000-05-28 19:56:29 +02:00
|
|
|
/*
|
|
|
|
* Restore outer command ID.
|
|
|
|
*/
|
|
|
|
SetScanCommandId(savedId);
|
|
|
|
|
1997-08-29 11:05:57 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If we've gone through every command in this function, we are done.
|
1997-08-29 11:05:57 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
if (es == (execution_state *) NULL)
|
1996-07-09 08:22:35 +02:00
|
|
|
{
|
1997-09-07 07:04:48 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the execution states to start over again
|
|
|
|
*/
|
2000-08-24 05:29:15 +02:00
|
|
|
es = fcache->func_state;
|
1997-09-07 07:04:48 +02:00
|
|
|
while (es)
|
|
|
|
{
|
|
|
|
es->status = F_EXEC_START;
|
|
|
|
es = es->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Let caller know we're finished.
|
|
|
|
*/
|
2000-08-24 05:29:15 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2000-07-12 04:37:39 +02:00
|
|
|
MemoryContextSwitchTo(oldcontext);
|
2000-08-24 05:29:15 +02:00
|
|
|
|
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|
1997-09-07 07:04:48 +02:00
|
|
|
|
1997-08-29 11:05:57 +02:00
|
|
|
/*
|
1997-09-07 07:04:48 +02:00
|
|
|
* If we got a result from a command within the function it has to be
|
2000-05-28 19:56:29 +02:00
|
|
|
* the final command. All others shouldn't be returning anything.
|
1997-08-29 11:05:57 +02:00
|
|
|
*/
|
1997-09-07 07:04:48 +02:00
|
|
|
Assert(LAST_POSTQUEL_COMMAND(es));
|
|
|
|
|
2000-08-24 05:29:15 +02:00
|
|
|
/*
|
|
|
|
* 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");
|
|
|
|
}
|
2000-07-12 04:37:39 +02:00
|
|
|
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
|
|
|
1997-09-07 07:04:48 +02:00
|
|
|
return result;
|
1996-07-09 08:22:35 +02:00
|
|
|
}
|