1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +03:00

First round of changes for new fmgr interface. fmgr itself and the

key call sites are changed, but most called functions are still oldstyle.
An exception is that the PL managers are updated (so, for example, NULL
handling now behaves as expected in plperl and plpgsql functions).
NOTE initdb is forced due to added column in pg_proc.
This commit is contained in:
Tom Lane
2000-05-28 17:56:29 +00:00
parent 5005bb060b
commit 0a7fb4e918
80 changed files with 3779 additions and 2908 deletions

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.69 2000/04/12 17:15:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.70 2000/05/28 17:55:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -41,6 +41,7 @@
#include "executor/functions.h"
#include "executor/nodeSubplan.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/fcache2.h"
@ -64,7 +65,8 @@ static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
bool *isNull, bool *isDone);
static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
List *argList, Datum argV[], bool *argIsDone);
List *argList, FunctionCallInfo fcinfo,
bool *argIsDone);
static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
bool *isNull);
@ -614,15 +616,12 @@ static void
ExecEvalFuncArgs(FunctionCachePtr fcache,
ExprContext *econtext,
List *argList,
Datum argV[],
FunctionCallInfo fcinfo,
bool *argIsDone)
{
int i;
bool *nullVect;
List *arg;
nullVect = fcache->nullVect;
i = 0;
foreach(arg, argList)
{
@ -632,16 +631,16 @@ ExecEvalFuncArgs(FunctionCachePtr fcache,
* as arguments but we make an exception in the case of nested dot
* expressions. We have to watch out for this case here.
*/
argV[i] = ExecEvalExpr((Node *) lfirst(arg),
econtext,
&nullVect[i],
argIsDone);
fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
econtext,
&fcinfo->argnull[i],
argIsDone);
if (!(*argIsDone))
{
if (i != 0)
elog(ERROR, "functions can only take sets in their first argument");
fcache->setArg = (char *) argV[0];
fcache->setArg = fcinfo->arg[0];
fcache->hasSetArg = true;
}
i++;
@ -658,40 +657,45 @@ ExecMakeFunctionResult(Node *node,
bool *isNull,
bool *isDone)
{
Datum argV[FUNC_MAX_ARGS];
FunctionCachePtr fcache;
Func *funcNode = NULL;
Oper *operNode = NULL;
bool funcisset = false;
FunctionCallInfoData fcinfo;
FunctionCachePtr fcache;
List *ftlist;
bool funcisset;
Datum result;
bool argDone;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* This is kind of ugly, Func nodes now have targetlists so that we
* know when and what to project out from postquel function results.
* This means we have to pass the func node all the way down instead
* of using only the fcache struct as before. ExecMakeFunctionResult
* becomes a little bit more of a dual personality as a result.
* ExecMakeFunctionResult becomes a little bit more of a dual personality
* as a result.
*/
if (IsA(node, Func))
{
funcNode = (Func *) node;
fcache = funcNode->func_fcache;
fcache = ((Func *) node)->func_fcache;
ftlist = ((Func *) node)->func_tlist;
funcisset = (((Func *) node)->funcid == F_SETEVAL);
}
else
{
operNode = (Oper *) node;
fcache = operNode->op_fcache;
fcache = ((Oper *) node)->op_fcache;
ftlist = NIL;
funcisset = false;
}
fcinfo.flinfo = &fcache->func;
fcinfo.nargs = fcache->nargs;
/*
* arguments is a list of expressions to evaluate before passing to
* the function manager. We collect the results of evaluating the
* expressions into a datum array (argV) and pass this array to
* arrayFmgr()
* the function manager. We collect the results of evaluating the
* expressions into the FunctionCallInfo struct. Note we assume that
* fcache->nargs is the correct length of the arguments list!
*/
if (fcache->nargs != 0)
if (fcache->nargs > 0)
{
bool argDone;
if (fcache->nargs > FUNC_MAX_ARGS)
elog(ERROR, "ExecMakeFunctionResult: too many arguments");
@ -700,21 +704,23 @@ ExecMakeFunctionResult(Node *node,
* returning a set of tuples (i.e. a nested dot expression). We
* don't want to evaluate the arguments again until the function
* is done. hasSetArg will always be false until we eval the args
* for the first time. We should set this in the parser.
* for the first time.
*/
if ((fcache->hasSetArg) && fcache->setArg != NULL)
if (fcache->hasSetArg && fcache->setArg != (Datum) 0)
{
argV[0] = (Datum) fcache->setArg;
fcinfo.arg[0] = fcache->setArg;
argDone = false;
}
else
ExecEvalFuncArgs(fcache, econtext, arguments, argV, &argDone);
ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
if ((fcache->hasSetArg) && (argDone))
if (fcache->hasSetArg && argDone)
{
/* can only get here if input is an empty set. */
if (isDone)
*isDone = true;
return (Datum) NULL;
*isNull = true;
return (Datum) 0;
}
}
@ -731,27 +737,23 @@ ExecMakeFunctionResult(Node *node,
* which defines this set. So replace the existing funcid in the
* funcnode with the set's OID. Also, we want a new fcache which
* points to the right function, so get that, now that we have the
* right OID. Also zero out the argV, since the real set doesn't take
* right OID. Also zero out fcinfo.arg, since the real set doesn't take
* any arguments.
*/
if (((Func *) node)->funcid == F_SETEVAL)
if (funcisset)
{
funcisset = true;
if (fcache->setArg)
{
argV[0] = 0;
((Func *) node)->funcid = (Oid) PointerGetDatum(fcache->setArg);
((Func *) node)->funcid = DatumGetObjectId(fcache->setArg);
}
else
{
((Func *) node)->funcid = (Oid) argV[0];
setFcache(node, argV[0], NIL, econtext);
((Func *) node)->funcid = DatumGetObjectId(fcinfo.arg[0]);
setFcache(node, DatumGetObjectId(fcinfo.arg[0]), NIL, econtext);
fcache = ((Func *) node)->func_fcache;
fcache->setArg = (char *) argV[0];
argV[0] = (Datum) 0;
fcache->setArg = fcinfo.arg[0];
}
fcinfo.arg[0] = (Datum) 0;
}
/*
@ -760,11 +762,6 @@ ExecMakeFunctionResult(Node *node,
*/
if (fcache->language == SQLlanguageId)
{
Datum result;
bool argDone;
Assert(funcNode);
/*--------------------
* This loop handles the situation where we are iterating through
* all results in a nested dot function (whose argument function
@ -777,8 +774,37 @@ ExecMakeFunctionResult(Node *node,
*/
for (;;)
{
result = postquel_function(funcNode, (char **) argV,
isNull, isDone);
/*
* If function is strict, and there are any NULL arguments,
* skip calling the function (at least for this set of args).
*/
bool callit = true;
if (fcinfo.flinfo->fn_strict)
{
int i;
for (i = 0; i < fcinfo.nargs; i++)
{
if (fcinfo.argnull[i])
{
callit = false;
break;
}
}
}
if (callit)
{
result = postquel_function(&fcinfo, fcache, ftlist, isDone);
*isNull = fcinfo.isnull;
}
else
{
result = (Datum) 0;
*isDone = true;
*isNull = true;
}
if (!*isDone)
break; /* got a result from current argument */
@ -786,7 +812,7 @@ ExecMakeFunctionResult(Node *node,
break; /* input not a set, so done */
/* OK, get the next argument... */
ExecEvalFuncArgs(fcache, econtext, arguments, argV, &argDone);
ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone);
if (argDone)
{
@ -795,10 +821,11 @@ ExecMakeFunctionResult(Node *node,
* End of arguments, so reset the setArg flag and say
* "Done"
*/
fcache->setArg = (char *) NULL;
fcache->setArg = (Datum) 0;
fcache->hasSetArg = false;
*isDone = true;
result = (Datum) NULL;
*isNull = true;
result = (Datum) 0;
break;
}
@ -826,21 +853,34 @@ ExecMakeFunctionResult(Node *node,
if (*isDone)
((Func *) node)->func_fcache = NULL;
}
return result;
}
else
{
int i;
/* A non-SQL function cannot return a set, at present. */
if (isDone)
*isDone = true;
for (i = 0; i < fcache->nargs; i++)
if (fcache->nullVect[i] == true)
*isNull = true;
/*
* If function is strict, and there are any NULL arguments,
* skip calling the function and return NULL.
*/
if (fcinfo.flinfo->fn_strict)
{
int i;
return (Datum) fmgr_c(&fcache->func, (FmgrValues *) argV, isNull);
for (i = 0; i < fcinfo.nargs; i++)
{
if (fcinfo.argnull[i])
{
*isNull = true;
return (Datum) 0;
}
}
}
result = FunctionCallInvoke(&fcinfo);
*isNull = fcinfo.isnull;
}
return result;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.55 2000/04/12 17:15:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.56 2000/05/28 17:55:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -52,6 +52,8 @@
#include "executor/execdebug.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
static void ExecGetIndexKeyInfo(Form_pg_index indexTuple, int *numAttsOutP,
AttrNumber **attsOutP, FuncIndexInfoPtr fInfoP);
@ -843,7 +845,7 @@ ExecOpenIndices(Oid resultRelationOid,
*/
if (VARSIZE(&indexStruct->indpred) != 0)
{
predString = fmgr(F_TEXTOUT, &indexStruct->indpred);
predString = textout(&indexStruct->indpred);
predicate = (PredInfo *) stringToNode(predString);
pfree(predString);
}

View File

@ -2,14 +2,13 @@
*
* functions.c
* Routines to handle functions called from the executor
* Putting this stuff in fmgr makes the postmaster a mess....
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.33 2000/04/12 17:15:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.34 2000/05/28 17:55:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -24,7 +23,6 @@
#include "tcop/utility.h"
#include "utils/datum.h"
#undef new
typedef enum
{
@ -39,18 +37,18 @@ typedef struct local_es
ExecStatus status;
} execution_state;
#define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *)NULL)
#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 execution_state *init_execution_state(FunctionCachePtr fcache);
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);
static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
static Datum postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
FunctionCachePtr fcache,
List *func_tlist);
Datum
@ -64,7 +62,6 @@ ProjectAttribute(TupleDesc TD,
Var *attrVar = (Var *) tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = heap_getattr(tup, attrno, TD, isnullP);
if (*isnullP)
return (Datum) NULL;
@ -77,8 +74,7 @@ ProjectAttribute(TupleDesc TD,
}
static execution_state *
init_execution_state(FunctionCachePtr fcache,
char *args[])
init_execution_state(FunctionCachePtr fcache)
{
execution_state *newes;
execution_state *nextes;
@ -196,13 +192,10 @@ postquel_end(execution_state *es)
}
static void
postquel_sub_params(execution_state *es,
int nargs,
char *args[],
bool *nullV)
postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
{
ParamListInfo paramLI;
EState *estate;
ParamListInfo paramLI;
estate = es->estate;
paramLI = estate->es_param_list_info;
@ -211,9 +204,9 @@ postquel_sub_params(execution_state *es,
{
if (paramLI->kind == PARAM_NUM)
{
Assert(paramLI->id <= nargs);
paramLI->value = (Datum) args[(paramLI->id - 1)];
paramLI->isnull = nullV[(paramLI->id - 1)];
Assert(paramLI->id <= fcinfo->nargs);
paramLI->value = fcinfo->arg[paramLI->id - 1];
paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
}
paramLI++;
}
@ -264,10 +257,9 @@ copy_function_result(FunctionCachePtr fcache,
static Datum
postquel_execute(execution_state *es,
FunctionCallInfo fcinfo,
FunctionCachePtr fcache,
List *fTlist,
char **args,
bool *isNull)
List *func_tlist)
{
TupleTableSlot *slot;
Datum value;
@ -278,8 +270,8 @@ postquel_execute(execution_state *es,
* 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 (fcinfo->nargs > 0)
postquel_sub_params(es, fcinfo);
if (es->status == F_EXEC_START)
{
@ -293,7 +285,7 @@ postquel_execute(execution_state *es,
{
postquel_end(es);
es->status = F_EXEC_DONE;
*isNull = true;
fcinfo->isnull = true;
/*
* If this isn't the last command for the function we have to
@ -315,19 +307,20 @@ postquel_execute(execution_state *es,
* logic and code redundancy here.
*/
resSlot = copy_function_result(fcache, slot);
if (fTlist != NIL)
if (func_tlist != NIL)
{
TargetEntry *tle = lfirst(fTlist);
TargetEntry *tle = lfirst(func_tlist);
value = ProjectAttribute(resSlot->ttc_tupleDescriptor,
tle,
resSlot->val,
isNull);
&fcinfo->isnull);
}
else
{
value = (Datum) resSlot;
*isNull = false;
/* XXX is this right? Return whole tuple slot?? */
value = PointerGetDatum(resSlot);
fcinfo->isnull = false;
}
/*
@ -353,11 +346,13 @@ postquel_execute(execution_state *es,
}
Datum
postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
postquel_function(FunctionCallInfo fcinfo,
FunctionCachePtr fcache,
List *func_tlist,
bool *isDone)
{
execution_state *es;
Datum result = 0;
FunctionCachePtr fcache = funcNode->func_fcache;
CommandId savedId;
/*
@ -371,7 +366,7 @@ postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
es = (execution_state *) fcache->func_state;
if (es == NULL)
{
es = init_execution_state(fcache, args);
es = init_execution_state(fcache);
fcache->func_state = (char *) es;
}
@ -388,15 +383,19 @@ postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
while (es != (execution_state *) NULL)
{
result = postquel_execute(es,
fcinfo,
fcache,
funcNode->func_tlist,
args,
isNull);
func_tlist);
if (es->status != F_EXEC_DONE)
break;
es = es->next;
}
/*
* Restore outer command ID.
*/
SetScanCommandId(savedId);
/*
* If we've gone through every command in this function, we are done.
*/
@ -417,17 +416,15 @@ postquel_function(Func *funcNode, char **args, bool *isNull, bool *isDone)
* 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.
* the final command. All others shouldn't be returning anything.
*/
Assert(LAST_POSTQUEL_COMMAND(es));
*isDone = false;
SetScanCommandId(savedId);
*isDone = false;
return result;
}

View File

@ -32,7 +32,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.63 2000/04/12 17:15:09 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.64 2000/05/28 17:55:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -218,8 +218,13 @@ static void
advance_transition_functions(AggStatePerAgg peraggstate,
Datum newVal, bool isNull)
{
Datum args[2];
FunctionCallInfoData fcinfo;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* XXX reconsider isNULL handling here
*/
if (OidIsValid(peraggstate->xfn1_oid) && !isNull)
{
if (peraggstate->noInitValue)
@ -244,28 +249,48 @@ advance_transition_functions(AggStatePerAgg peraggstate,
else
{
/* apply transition function 1 */
args[0] = peraggstate->value1;
args[1] = newVal;
newVal = (Datum) fmgr_c(&peraggstate->xfn1,
(FmgrValues *) args,
&isNull);
if (!peraggstate->transtype1ByVal)
fcinfo.flinfo = &peraggstate->xfn1;
fcinfo.nargs = 2;
fcinfo.arg[0] = peraggstate->value1;
fcinfo.argnull[0] = peraggstate->value1IsNull;
fcinfo.arg[1] = newVal;
fcinfo.argnull[1] = isNull;
if (fcinfo.flinfo->fn_strict &&
(peraggstate->value1IsNull || isNull))
{
/* don't call a strict function with NULL inputs */
newVal = (Datum) 0;
fcinfo.isnull = true;
}
else
newVal = FunctionCallInvoke(&fcinfo);
if (!peraggstate->transtype1ByVal && !peraggstate->value1IsNull)
pfree(peraggstate->value1);
peraggstate->value1 = newVal;
peraggstate->value1IsNull = fcinfo.isnull;
}
}
if (OidIsValid(peraggstate->xfn2_oid))
{
/* apply transition function 2 */
args[0] = peraggstate->value2;
isNull = false; /* value2 cannot be null, currently */
newVal = (Datum) fmgr_c(&peraggstate->xfn2,
(FmgrValues *) args,
&isNull);
if (!peraggstate->transtype2ByVal)
fcinfo.flinfo = &peraggstate->xfn2;
fcinfo.nargs = 1;
fcinfo.arg[0] = peraggstate->value2;
fcinfo.argnull[0] = peraggstate->value2IsNull;
fcinfo.isnull = false; /* must reset after use by xfn1 */
if (fcinfo.flinfo->fn_strict && peraggstate->value2IsNull)
{
/* don't call a strict function with NULL inputs */
newVal = (Datum) 0;
fcinfo.isnull = true;
}
else
newVal = FunctionCallInvoke(&fcinfo);
if (!peraggstate->transtype2ByVal && !peraggstate->value2IsNull)
pfree(peraggstate->value2);
peraggstate->value2 = newVal;
peraggstate->value2IsNull = fcinfo.isnull;
}
}
@ -276,8 +301,10 @@ static void
finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull)
{
Aggref *aggref = peraggstate->aggref;
char *args[2];
Aggref *aggref = peraggstate->aggref;
FunctionCallInfoData fcinfo;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* If it's a DISTINCT aggregate, all we've done so far is to stuff the
@ -337,21 +364,41 @@ finalize_aggregate(AggStatePerAgg peraggstate,
if (OidIsValid(peraggstate->finalfn_oid) &&
!peraggstate->noInitValue)
{
fcinfo.flinfo = &peraggstate->finalfn;
if (peraggstate->finalfn.fn_nargs > 1)
{
args[0] = (char *) peraggstate->value1;
args[1] = (char *) peraggstate->value2;
fcinfo.nargs = 2;
fcinfo.arg[0] = peraggstate->value1;
fcinfo.argnull[0] = peraggstate->value1IsNull;
fcinfo.arg[1] = peraggstate->value2;
fcinfo.argnull[1] = peraggstate->value2IsNull;
}
else if (OidIsValid(peraggstate->xfn1_oid))
args[0] = (char *) peraggstate->value1;
{
fcinfo.nargs = 1;
fcinfo.arg[0] = peraggstate->value1;
fcinfo.argnull[0] = peraggstate->value1IsNull;
}
else if (OidIsValid(peraggstate->xfn2_oid))
args[0] = (char *) peraggstate->value2;
{
fcinfo.nargs = 1;
fcinfo.arg[0] = peraggstate->value2;
fcinfo.argnull[0] = peraggstate->value2IsNull;
}
else
elog(ERROR, "ExecAgg: no valid transition functions??");
*resultIsNull = false;
*resultVal = (Datum) fmgr_c(&peraggstate->finalfn,
(FmgrValues *) args,
resultIsNull);
if (fcinfo.flinfo->fn_strict &&
(fcinfo.argnull[0] || fcinfo.argnull[1]))
{
/* don't call a strict function with NULL inputs */
*resultVal = (Datum) 0;
*resultIsNull = true;
}
else
{
*resultVal = FunctionCallInvoke(&fcinfo);
*resultIsNull = fcinfo.isnull;
}
}
else if (OidIsValid(peraggstate->xfn1_oid))
{