@ -8,22 +8,29 @@
*
*
* IDENTIFICATION
* $ Header : / cvsroot / pgsql / src / backend / executor / functions . c , v 1.37 2000 / 08 / 08 15 : 41 : 22 tgl Exp $
* $ Header : / cvsroot / pgsql / src / backend / executor / functions . c , v 1.38 2000 / 08 / 24 03 : 29 : 03 tgl 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/datum.h"
# include "utils/syscache.h"
/*
* We have an execution_state record for each query in the function .
*/
typedef enum
{
F_EXEC_START , F_EXEC_RUN , F_EXEC_DONE
@ -39,15 +46,40 @@ typedef struct local_es
# 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 */
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 TupleDesc postquel_start ( execution_state * es ) ;
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 , FunctionCallInfo fcinfo ) ;
static Datum postquel_execute ( execution_state * es ,
FunctionCallInfo fcinfo ,
FunctionCachePtr fcache ) ;
SQL FunctionCachePtr fcache ) ;
static Datum
@ -69,21 +101,19 @@ ProjectAttribute(HeapTuple tup,
}
static execution_state *
init_execution_state ( FunctionCachePtr fcache )
init_execution_state ( char * src , Oid * argOidVect , int nargs )
{
execution_state * newes ;
execution_state * nextes ;
execution_state * preves ;
List * queryTree_list ,
* qtl_item ;
int nargs = fcache - > nargs ;
newes = ( execution_state * ) palloc ( sizeof ( execution_state ) ) ;
nextes = newes ;
preves = ( execution_state * ) NULL ;
queryTree_list = pg_parse_and_rewrite ( fcache - > src ,
fcache - > argOidVect , nargs ) ;
queryTree_list = pg_parse_and_rewrite ( src , argOidVect , nargs ) ;
foreach ( qtl_item , queryTree_list )
{
@ -138,6 +168,134 @@ init_execution_state(FunctionCachePtr fcache)
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
*
* 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 ;
}
static TupleDesc
postquel_start ( execution_state * es )
{
@ -208,7 +366,7 @@ postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
}
static TupleTableSlot *
copy_function_result ( FunctionCachePtr fcache ,
copy_function_result ( SQL FunctionCachePtr fcache ,
TupleTableSlot * resultSlot )
{
TupleTableSlot * funcSlot ;
@ -219,10 +377,10 @@ copy_function_result(FunctionCachePtr fcache,
Assert ( ! TupIsNull ( resultSlot ) ) ;
resultTuple = resultSlot - > val ;
funcSlot = ( TupleTableSlot * ) fcache - > funcSlot ;
funcSlot = fcache - > funcSlot ;
if ( funcSlot = = ( TupleTableSlot * ) NULL )
return resultSlot ;
if ( funcSlot = = NULL )
return resultSlot ; /* no need to copy result */
/*
* If first time through , we have to initialize the funcSlot ' s
@ -243,7 +401,7 @@ copy_function_result(FunctionCachePtr fcache,
static Datum
postquel_execute ( execution_state * es ,
FunctionCallInfo fcinfo ,
FunctionCachePtr fcache )
SQL FunctionCachePtr fcache )
{
TupleTableSlot * slot ;
Datum value ;
@ -319,7 +477,7 @@ postquel_execute(execution_state *es,
* If this is a single valued function we have to end the function
* execution now .
*/
if ( ! fcache - > return sS et )
if ( ! fcinfo - > flinfo - > fn_ retset)
{
postquel_end ( es ) ;
es - > status = F_EXEC_DONE ;
@ -338,11 +496,10 @@ postquel_execute(execution_state *es,
}
Datum
postquel_function ( FunctionCallInfo fcinfo ,
FunctionCachePtr fcache ,
bool * isDone )
fmgr_sql ( PG_FUNCTION_ARGS )
{
MemoryContext oldcontext ;
SQLFunctionCachePtr fcache ;
execution_state * es ;
Datum result = 0 ;
CommandId savedId ;
@ -352,7 +509,7 @@ postquel_function(FunctionCallInfo fcinfo,
* parsetrees , plans , etc , will have sufficient lifetime . The
* sub - executor is responsible for deleting per - tuple information .
*/
oldcontext = MemoryContextSwitchTo ( fcache - > fcacheC xt ) ;
oldcontext = MemoryContextSwitchTo ( fcinfo - > flinfo - > fn_mc xt ) ;
/*
* Before we start do anything we must save CurrentScanCommandId to
@ -362,13 +519,21 @@ postquel_function(FunctionCallInfo fcinfo,
savedId = GetScanCommandId ( ) ;
SetScanCommandId ( GetCurrentCommandId ( ) ) ;
es = ( execution_state * ) fcache - > func_state ;
if ( es = = NULL )
/*
* Initialize fcache and execution state if first time through .
*/
fcache = ( SQLFunctionCachePtr ) fcinfo - > flinfo - > fn_extra ;
if ( fcache = = NULL )
{
es = init_execution_state ( fcache ) ;
fcache - > func_state = ( char * ) es ;
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 ;
@ -401,7 +566,7 @@ postquel_function(FunctionCallInfo fcinfo,
/*
* Reset the execution states to start over again
*/
es = ( execution_state * ) fcache - > func_state ;
es = fcache - > func_state ;
while ( es )
{
es - > status = F_EXEC_START ;
@ -411,9 +576,21 @@ postquel_function(FunctionCallInfo fcinfo,
/*
* Let caller know we ' re finished .
*/
* isDone = true ;
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 ;
}
MemoryContextSwitchTo ( oldcontext ) ;
return ( fcache - > returnsSet ) ? ( Datum ) NULL : result ;
return result ;
}
/*
@ -422,7 +599,18 @@ postquel_function(FunctionCallInfo fcinfo,
*/
Assert ( LAST_POSTQUEL_COMMAND ( es ) ) ;
* isDone = false ;
/*
* 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 " ) ;
}
MemoryContextSwitchTo ( oldcontext ) ;