Support polymorphic functions in plpgsql. Along the way, replace
linked-list search of function cache with hash-table lookup. By Joe Conway.
This commit is contained in:
parent
cc3002313f
commit
b837c99210
@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.58 2003/05/05 16:46:27 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.59 2003/07/01 21:47:09 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -79,10 +79,38 @@ int plpgsql_DumpExecTree = 0;
|
||||
|
||||
PLpgSQL_function *plpgsql_curr_compile;
|
||||
|
||||
/* ----------
|
||||
* Hash table for compiled functions
|
||||
* ----------
|
||||
*/
|
||||
static HTAB *plpgsql_HashTable = (HTAB *) NULL;
|
||||
|
||||
typedef struct plpgsql_hashent
|
||||
{
|
||||
PLpgSQL_func_hashkey key;
|
||||
PLpgSQL_function *function;
|
||||
} plpgsql_HashEnt;
|
||||
|
||||
#define FUNCS_PER_USER 128 /* initial table size */
|
||||
|
||||
|
||||
/* ----------
|
||||
* static prototypes
|
||||
* ----------
|
||||
*/
|
||||
static PLpgSQL_function *do_compile(FunctionCallInfo fcinfo,
|
||||
HeapTuple procTup,
|
||||
PLpgSQL_func_hashkey *hashkey);
|
||||
static void plpgsql_compile_error_callback(void *arg);
|
||||
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
|
||||
|
||||
static void compute_function_hashkey(FmgrInfo *flinfo,
|
||||
Form_pg_proc procStruct,
|
||||
PLpgSQL_func_hashkey *hashkey);
|
||||
static void plpgsql_HashTableInit(void);
|
||||
static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key);
|
||||
static void plpgsql_HashTableInsert(PLpgSQL_function *function,
|
||||
PLpgSQL_func_hashkey *func_key);
|
||||
static void plpgsql_HashTableDelete(PLpgSQL_function *function);
|
||||
|
||||
/*
|
||||
* This routine is a crock, and so is everyplace that calls it. The problem
|
||||
@ -103,42 +131,127 @@ perm_fmgr_info(Oid functionId, FmgrInfo *finfo)
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_compile Given a pg_proc's oid, make
|
||||
* an execution tree for it.
|
||||
* plpgsql_compile Make an execution tree for a PL/pgSQL function.
|
||||
*
|
||||
* Note: it's important for this to fall through quickly if the function
|
||||
* has already been compiled.
|
||||
* ----------
|
||||
*/
|
||||
PLpgSQL_function *
|
||||
plpgsql_compile(Oid fn_oid, int functype)
|
||||
plpgsql_compile(FunctionCallInfo fcinfo)
|
||||
{
|
||||
int parse_rc;
|
||||
Oid funcOid = fcinfo->flinfo->fn_oid;
|
||||
HeapTuple procTup;
|
||||
Form_pg_proc procStruct;
|
||||
PLpgSQL_function *function;
|
||||
PLpgSQL_func_hashkey hashkey;
|
||||
bool hashkey_valid = false;
|
||||
|
||||
/*
|
||||
* Lookup the pg_proc tuple by Oid; we'll need it in any case
|
||||
*/
|
||||
procTup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcOid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(procTup))
|
||||
elog(ERROR, "plpgsql: cache lookup for proc %u failed", funcOid);
|
||||
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||
|
||||
/*
|
||||
* See if there's already a cache entry for the current FmgrInfo.
|
||||
* If not, try to find one in the hash table.
|
||||
*/
|
||||
function = (PLpgSQL_function *) fcinfo->flinfo->fn_extra;
|
||||
|
||||
if (!function)
|
||||
{
|
||||
/* First time through in this backend? If so, init hashtable */
|
||||
if (!plpgsql_HashTable)
|
||||
plpgsql_HashTableInit();
|
||||
|
||||
/* Compute hashkey using function signature and actual arg types */
|
||||
compute_function_hashkey(fcinfo->flinfo, procStruct, &hashkey);
|
||||
hashkey_valid = true;
|
||||
|
||||
/* And do the lookup */
|
||||
function = plpgsql_HashTableLookup(&hashkey);
|
||||
}
|
||||
|
||||
if (function)
|
||||
{
|
||||
/* We have a compiled function, but is it still valid? */
|
||||
if (!(function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
|
||||
function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)))
|
||||
{
|
||||
/*
|
||||
* Nope, drop the hashtable entry. XXX someday, free all the
|
||||
* subsidiary storage as well.
|
||||
*/
|
||||
plpgsql_HashTableDelete(function);
|
||||
|
||||
function = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the function wasn't found or was out-of-date, we have to compile it
|
||||
*/
|
||||
if (!function)
|
||||
{
|
||||
/*
|
||||
* Calculate hashkey if we didn't already; we'll need it to store
|
||||
* the completed function.
|
||||
*/
|
||||
if (!hashkey_valid)
|
||||
compute_function_hashkey(fcinfo->flinfo, procStruct, &hashkey);
|
||||
|
||||
/*
|
||||
* Do the hard part.
|
||||
*/
|
||||
function = do_compile(fcinfo, procTup, &hashkey);
|
||||
}
|
||||
|
||||
ReleaseSysCache(procTup);
|
||||
|
||||
/*
|
||||
* Save pointer in FmgrInfo to avoid search on subsequent calls
|
||||
*/
|
||||
fcinfo->flinfo->fn_extra = (void *) function;
|
||||
|
||||
/*
|
||||
* Finally return the compiled function
|
||||
*/
|
||||
return function;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the slow part of plpgsql_compile().
|
||||
*/
|
||||
static PLpgSQL_function *
|
||||
do_compile(FunctionCallInfo fcinfo,
|
||||
HeapTuple procTup,
|
||||
PLpgSQL_func_hashkey *hashkey)
|
||||
{
|
||||
Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||
int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION;
|
||||
PLpgSQL_function *function;
|
||||
char *proc_source;
|
||||
HeapTuple typeTup;
|
||||
Form_pg_type typeStruct;
|
||||
char *proc_source;
|
||||
PLpgSQL_function *function;
|
||||
PLpgSQL_var *var;
|
||||
PLpgSQL_row *row;
|
||||
PLpgSQL_rec *rec;
|
||||
int i;
|
||||
int arg_varnos[FUNC_MAX_ARGS];
|
||||
ErrorContextCallback plerrcontext;
|
||||
int parse_rc;
|
||||
Oid rettypeid;
|
||||
|
||||
/*
|
||||
* Lookup the pg_proc tuple by Oid
|
||||
* Setup the scanner input and error info. We assume that this
|
||||
* function cannot be invoked recursively, so there's no need to save
|
||||
* and restore the static variables used here.
|
||||
*/
|
||||
procTup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(fn_oid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(procTup))
|
||||
elog(ERROR, "plpgsql: cache lookup for proc %u failed", fn_oid);
|
||||
|
||||
/*
|
||||
* Setup the scanner input and error info. We assume that this function
|
||||
* cannot be invoked recursively, so there's no need to save and restore
|
||||
* the static variables used here.
|
||||
*/
|
||||
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
|
||||
proc_source = DatumGetCString(DirectFunctionCall1(textout,
|
||||
PointerGetDatum(&procStruct->prosrc)));
|
||||
plpgsql_scanner_init(proc_source, functype);
|
||||
@ -171,11 +284,11 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
* Create the new function node
|
||||
*/
|
||||
function = malloc(sizeof(PLpgSQL_function));
|
||||
memset(function, 0, sizeof(PLpgSQL_function));
|
||||
MemSet(function, 0, sizeof(PLpgSQL_function));
|
||||
plpgsql_curr_compile = function;
|
||||
|
||||
function->fn_name = strdup(NameStr(procStruct->proname));
|
||||
function->fn_oid = fn_oid;
|
||||
function->fn_oid = fcinfo->flinfo->fn_oid;
|
||||
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
|
||||
function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data);
|
||||
function->fn_functype = functype;
|
||||
@ -184,40 +297,56 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
{
|
||||
case T_FUNCTION:
|
||||
|
||||
/*
|
||||
* Check for a polymorphic returntype. If found, use the actual
|
||||
* returntype type from the caller's FuncExpr node, if we
|
||||
* have one.
|
||||
*/
|
||||
rettypeid = procStruct->prorettype;
|
||||
if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
|
||||
{
|
||||
rettypeid = get_fn_expr_rettype(fcinfo->flinfo);
|
||||
if (!OidIsValid(rettypeid))
|
||||
elog(ERROR, "could not determine actual return type "
|
||||
"for polymorphic function %s",
|
||||
plpgsql_error_funcname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal function has a defined returntype
|
||||
*/
|
||||
function->fn_rettype = procStruct->prorettype;
|
||||
function->fn_rettype = rettypeid;
|
||||
function->fn_retset = procStruct->proretset;
|
||||
|
||||
/*
|
||||
* Lookup the functions return type
|
||||
*/
|
||||
typeTup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(procStruct->prorettype),
|
||||
ObjectIdGetDatum(rettypeid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
elog(ERROR, "cache lookup for return type %u failed",
|
||||
procStruct->prorettype);
|
||||
rettypeid);
|
||||
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
|
||||
/* Disallow pseudotype result, except VOID or RECORD */
|
||||
/* (note we already replaced ANYARRAY/ANYELEMENT) */
|
||||
if (typeStruct->typtype == 'p')
|
||||
{
|
||||
if (procStruct->prorettype == VOIDOID ||
|
||||
procStruct->prorettype == RECORDOID)
|
||||
if (rettypeid == VOIDOID ||
|
||||
rettypeid == RECORDOID)
|
||||
/* okay */ ;
|
||||
else if (procStruct->prorettype == TRIGGEROID)
|
||||
else if (rettypeid == TRIGGEROID)
|
||||
elog(ERROR, "plpgsql functions cannot return type %s"
|
||||
"\n\texcept when used as triggers",
|
||||
format_type_be(procStruct->prorettype));
|
||||
format_type_be(rettypeid));
|
||||
else
|
||||
elog(ERROR, "plpgsql functions cannot return type %s",
|
||||
format_type_be(procStruct->prorettype));
|
||||
format_type_be(rettypeid));
|
||||
}
|
||||
|
||||
if (typeStruct->typrelid != InvalidOid ||
|
||||
procStruct->prorettype == RECORDOID)
|
||||
rettypeid == RECORDOID)
|
||||
function->fn_retistuple = true;
|
||||
else
|
||||
{
|
||||
@ -229,29 +358,39 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
ReleaseSysCache(typeTup);
|
||||
|
||||
/*
|
||||
* Create the variables for the procedures parameters
|
||||
* Create the variables for the procedure's parameters
|
||||
*/
|
||||
for (i = 0; i < procStruct->pronargs; i++)
|
||||
{
|
||||
char buf[32];
|
||||
Oid argtypeid;
|
||||
|
||||
snprintf(buf, sizeof(buf), "$%d", i + 1); /* name for variable */
|
||||
/* name for variable */
|
||||
snprintf(buf, sizeof(buf), "$%d", i + 1);
|
||||
|
||||
/*
|
||||
* Since we already did the replacement of polymorphic
|
||||
* argument types by actual argument types while computing
|
||||
* the hashkey, we can just use those results.
|
||||
*/
|
||||
argtypeid = hashkey->argtypes[i];
|
||||
|
||||
/*
|
||||
* Get the parameters type
|
||||
*/
|
||||
typeTup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(procStruct->proargtypes[i]),
|
||||
ObjectIdGetDatum(argtypeid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
elog(ERROR, "cache lookup for argument type %u failed",
|
||||
procStruct->proargtypes[i]);
|
||||
argtypeid);
|
||||
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
|
||||
/* Disallow pseudotype argument */
|
||||
/* (note we already replaced ANYARRAY/ANYELEMENT) */
|
||||
if (typeStruct->typtype == 'p')
|
||||
elog(ERROR, "plpgsql functions cannot take type %s",
|
||||
format_type_be(procStruct->proargtypes[i]));
|
||||
format_type_be(argtypeid));
|
||||
|
||||
if (typeStruct->typrelid != InvalidOid)
|
||||
{
|
||||
@ -511,7 +650,14 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
function->datums[i] = plpgsql_Datums[i];
|
||||
function->action = plpgsql_yylval.program;
|
||||
|
||||
ReleaseSysCache(procTup);
|
||||
/* Debug dump for completed functions */
|
||||
if (plpgsql_DumpExecTree)
|
||||
plpgsql_dumptree(function);
|
||||
|
||||
/*
|
||||
* add it to the hash table
|
||||
*/
|
||||
plpgsql_HashTableInsert(function, hashkey);
|
||||
|
||||
/*
|
||||
* Pop the error context stack
|
||||
@ -520,11 +666,6 @@ plpgsql_compile(Oid fn_oid, int functype)
|
||||
plpgsql_error_funcname = NULL;
|
||||
plpgsql_error_lineno = 0;
|
||||
|
||||
/*
|
||||
* Finally return the compiled function
|
||||
*/
|
||||
if (plpgsql_DumpExecTree)
|
||||
plpgsql_dumptree(function);
|
||||
return function;
|
||||
}
|
||||
|
||||
@ -1500,3 +1641,112 @@ plpgsql_yyerror(const char *s)
|
||||
plpgsql_error_lineno = plpgsql_scanner_lineno();
|
||||
elog(ERROR, "%s at or near \"%s\"", s, plpgsql_yytext);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compute the hashkey for a given function invocation
|
||||
*
|
||||
* The hashkey is returned into the caller-provided storage at *hashkey.
|
||||
*/
|
||||
static void
|
||||
compute_function_hashkey(FmgrInfo *flinfo,
|
||||
Form_pg_proc procStruct,
|
||||
PLpgSQL_func_hashkey *hashkey)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Make sure any unused bytes of the struct are zero */
|
||||
MemSet(hashkey, 0, sizeof(PLpgSQL_func_hashkey));
|
||||
|
||||
hashkey->funcOid = flinfo->fn_oid;
|
||||
|
||||
/* get the argument types */
|
||||
for (i = 0; i < procStruct->pronargs; i++)
|
||||
{
|
||||
Oid argtypeid = procStruct->proargtypes[i];
|
||||
|
||||
/*
|
||||
* Check for polymorphic arguments. If found, use the actual
|
||||
* parameter type from the caller's FuncExpr node, if we
|
||||
* have one.
|
||||
*
|
||||
* We can support arguments of type ANY the same way as normal
|
||||
* polymorphic arguments.
|
||||
*/
|
||||
if (argtypeid == ANYARRAYOID || argtypeid == ANYELEMENTOID ||
|
||||
argtypeid == ANYOID)
|
||||
{
|
||||
argtypeid = get_fn_expr_argtype(flinfo, i);
|
||||
if (!OidIsValid(argtypeid))
|
||||
elog(ERROR, "could not determine actual argument "
|
||||
"type for polymorphic function %s",
|
||||
NameStr(procStruct->proname));
|
||||
}
|
||||
|
||||
hashkey->argtypes[i] = argtypeid;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
plpgsql_HashTableInit(void)
|
||||
{
|
||||
HASHCTL ctl;
|
||||
|
||||
memset(&ctl, 0, sizeof(ctl));
|
||||
ctl.keysize = sizeof(PLpgSQL_func_hashkey);
|
||||
ctl.entrysize = sizeof(plpgsql_HashEnt);
|
||||
ctl.hash = tag_hash;
|
||||
plpgsql_HashTable = hash_create("PLpgSQL function cache",
|
||||
FUNCS_PER_USER,
|
||||
&ctl,
|
||||
HASH_ELEM | HASH_FUNCTION);
|
||||
}
|
||||
|
||||
static PLpgSQL_function *
|
||||
plpgsql_HashTableLookup(PLpgSQL_func_hashkey *func_key)
|
||||
{
|
||||
plpgsql_HashEnt *hentry;
|
||||
|
||||
hentry = (plpgsql_HashEnt*) hash_search(plpgsql_HashTable,
|
||||
(void *) func_key,
|
||||
HASH_FIND,
|
||||
NULL);
|
||||
if (hentry)
|
||||
return hentry->function;
|
||||
else
|
||||
return (PLpgSQL_function *) NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
plpgsql_HashTableInsert(PLpgSQL_function *function,
|
||||
PLpgSQL_func_hashkey *func_key)
|
||||
{
|
||||
plpgsql_HashEnt *hentry;
|
||||
bool found;
|
||||
|
||||
hentry = (plpgsql_HashEnt*) hash_search(plpgsql_HashTable,
|
||||
(void *) func_key,
|
||||
HASH_ENTER,
|
||||
&found);
|
||||
if (hentry == NULL)
|
||||
elog(ERROR, "out of memory in plpgsql_HashTable");
|
||||
if (found)
|
||||
elog(WARNING, "trying to insert a function that exists");
|
||||
|
||||
hentry->function = function;
|
||||
/* prepare back link from function to hashtable key */
|
||||
function->fn_hashkey = &hentry->key;
|
||||
}
|
||||
|
||||
static void
|
||||
plpgsql_HashTableDelete(PLpgSQL_function *function)
|
||||
{
|
||||
plpgsql_HashEnt *hentry;
|
||||
|
||||
hentry = (plpgsql_HashEnt*) hash_search(plpgsql_HashTable,
|
||||
(void *) function->fn_hashkey,
|
||||
HASH_REMOVE,
|
||||
NULL);
|
||||
if (hentry == NULL)
|
||||
elog(WARNING, "trying to delete function that does not exist");
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.12 2002/08/30 00:28:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.13 2003/07/01 21:47:09 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -45,15 +45,6 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*
|
||||
* Head of list of already-compiled functions
|
||||
*/
|
||||
static PLpgSQL_function *compiled_functions = NULL;
|
||||
|
||||
|
||||
static bool func_up_to_date(PLpgSQL_function * func);
|
||||
|
||||
|
||||
/* ----------
|
||||
* plpgsql_call_handler
|
||||
*
|
||||
@ -67,8 +58,6 @@ PG_FUNCTION_INFO_V1(plpgsql_call_handler);
|
||||
Datum
|
||||
plpgsql_call_handler(PG_FUNCTION_ARGS)
|
||||
{
|
||||
bool isTrigger = CALLED_AS_TRIGGER(fcinfo);
|
||||
Oid funcOid = fcinfo->flinfo->fn_oid;
|
||||
PLpgSQL_function *func;
|
||||
Datum retval;
|
||||
|
||||
@ -78,55 +67,14 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
||||
if (SPI_connect() != SPI_OK_CONNECT)
|
||||
elog(ERROR, "plpgsql: cannot connect to SPI manager");
|
||||
|
||||
/*
|
||||
* Check if we already compiled this function and saved the pointer
|
||||
* (ie, current FmgrInfo has been used before)
|
||||
*/
|
||||
func = (PLpgSQL_function *) fcinfo->flinfo->fn_extra;
|
||||
if (func != NULL)
|
||||
{
|
||||
Assert(func->fn_oid == funcOid);
|
||||
|
||||
/*
|
||||
* But is the function still up to date?
|
||||
*/
|
||||
if (!func_up_to_date(func))
|
||||
func = NULL;
|
||||
}
|
||||
|
||||
if (func == NULL)
|
||||
{
|
||||
/*
|
||||
* Check if we already compiled this function for another caller
|
||||
*/
|
||||
for (func = compiled_functions; func != NULL; func = func->next)
|
||||
{
|
||||
if (funcOid == func->fn_oid && func_up_to_date(func))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If not, do so and add it to the compiled ones
|
||||
*/
|
||||
if (func == NULL)
|
||||
{
|
||||
func = plpgsql_compile(funcOid,
|
||||
isTrigger ? T_TRIGGER : T_FUNCTION);
|
||||
func->next = compiled_functions;
|
||||
compiled_functions = func;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save pointer in FmgrInfo to avoid search on subsequent calls
|
||||
*/
|
||||
fcinfo->flinfo->fn_extra = (void *) func;
|
||||
}
|
||||
/* Find or compile the function */
|
||||
func = plpgsql_compile(fcinfo);
|
||||
|
||||
/*
|
||||
* Determine if called as function or trigger and call appropriate
|
||||
* subhandler
|
||||
*/
|
||||
if (isTrigger)
|
||||
if (CALLED_AS_TRIGGER(fcinfo))
|
||||
retval = PointerGetDatum(plpgsql_exec_trigger(func,
|
||||
(TriggerData *) fcinfo->context));
|
||||
else
|
||||
@ -140,30 +88,3 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Check to see if a compiled function is still up-to-date. This
|
||||
* is needed because CREATE OR REPLACE FUNCTION can modify the
|
||||
* function's pg_proc entry without changing its OID.
|
||||
*/
|
||||
static bool
|
||||
func_up_to_date(PLpgSQL_function * func)
|
||||
{
|
||||
HeapTuple procTup;
|
||||
bool result;
|
||||
|
||||
procTup = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(func->fn_oid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(procTup))
|
||||
elog(ERROR, "plpgsql: cache lookup for proc %u failed",
|
||||
func->fn_oid);
|
||||
|
||||
result = (func->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) &&
|
||||
func->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data));
|
||||
|
||||
ReleaseSysCache(procTup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* procedural language
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.36 2003/05/05 16:46:28 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.37 2003/07/01 21:47:09 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -487,6 +487,18 @@ typedef struct
|
||||
} PLpgSQL_stmt_dynexecute;
|
||||
|
||||
|
||||
typedef struct PLpgSQL_func_hashkey
|
||||
{ /* Hash lookup key for functions */
|
||||
Oid funcOid;
|
||||
/*
|
||||
* We include actual argument types in the hash key to support
|
||||
* polymorphic PLpgSQL functions. Be careful that extra positions
|
||||
* are zeroed!
|
||||
*/
|
||||
Oid argtypes[FUNC_MAX_ARGS];
|
||||
} PLpgSQL_func_hashkey;
|
||||
|
||||
|
||||
typedef struct PLpgSQL_function
|
||||
{ /* Complete compiled function */
|
||||
char *fn_name;
|
||||
@ -494,6 +506,7 @@ typedef struct PLpgSQL_function
|
||||
TransactionId fn_xmin;
|
||||
CommandId fn_cmin;
|
||||
int fn_functype;
|
||||
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
|
||||
|
||||
Oid fn_rettype;
|
||||
int fn_rettyplen;
|
||||
@ -519,8 +532,6 @@ typedef struct PLpgSQL_function
|
||||
int ndatums;
|
||||
PLpgSQL_datum **datums;
|
||||
PLpgSQL_stmt_block *action;
|
||||
|
||||
struct PLpgSQL_function *next; /* for chaining list of functions */
|
||||
} PLpgSQL_function;
|
||||
|
||||
|
||||
@ -588,7 +599,7 @@ extern PLpgSQL_function *plpgsql_curr_compile;
|
||||
* Functions in pl_comp.c
|
||||
* ----------
|
||||
*/
|
||||
extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype);
|
||||
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo);
|
||||
extern int plpgsql_parse_word(char *word);
|
||||
extern int plpgsql_parse_dblword(char *word);
|
||||
extern int plpgsql_parse_tripword(char *word);
|
||||
|
Loading…
x
Reference in New Issue
Block a user