Index: src/pl/plpgsql/src/pl_comp.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v retrieving revision 1.58 diff -c -r1.58 pl_comp.c *** src/pl/plpgsql/src/pl_comp.c 5 May 2003 16:46:27 -0000 1.58 --- src/pl/plpgsql/src/pl_comp.c 30 Jun 2003 22:20:15 -0000 *************** *** 79,88 **** PLpgSQL_function *plpgsql_curr_compile; static void plpgsql_compile_error_callback(void *arg); static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod); ! /* * This routine is a crock, and so is everyplace that calls it. The problem --- 79,118 ---- PLpgSQL_function *plpgsql_curr_compile; + /* ---------- + * compiled function hash table related + * ---------- + */ + static HTAB *plpgsql_HashTable = (HTAB *) NULL; + + typedef struct PLpgSQL_func_key + { + Oid funcOid; + Oid argtypes[FUNC_MAX_ARGS]; + } PLpgSQL_func_key; + + typedef struct plpgsql_hashent + { + PLpgSQL_func_key func_key; + PLpgSQL_function *function; + } plpgsql_HashEnt; + #define FUNCS_PER_USER 128 + + + /* ---------- + * static prototypes + * ---------- + */ static void plpgsql_compile_error_callback(void *arg); static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod); ! static PLpgSQL_function *get_function_by_signature(FunctionCallInfo fcinfo, ! HeapTuple procTup); ! static void plpgsql_HashTableInit(void); ! static PLpgSQL_function *plpgsql_HashTableLookup(PLpgSQL_func_key *func_key); ! static void plpgsql_HashTableInsert(PLpgSQL_function *function); ! static void plpgsql_HashTableDelete(PLpgSQL_function *function); ! static PLpgSQL_func_key *function_get_func_key(PLpgSQL_function *function); /* * This routine is a crock, and so is everyplace that calls it. The problem *************** *** 108,114 **** * ---------- */ PLpgSQL_function * ! plpgsql_compile(Oid fn_oid, int functype) { int parse_rc; HeapTuple procTup; --- 138,144 ---- * ---------- */ PLpgSQL_function * ! plpgsql_compile(FunctionCallInfo fcinfo) { int parse_rc; HeapTuple procTup; *************** *** 123,530 **** int i; int arg_varnos[FUNC_MAX_ARGS]; ErrorContextCallback plerrcontext; /* ! * Lookup the pg_proc tuple by Oid */ 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); ! pfree(proc_source); ! ! plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname)); ! plpgsql_error_lineno = 0; /* ! * Setup error traceback support for ereport() */ ! plerrcontext.callback = plpgsql_compile_error_callback; ! plerrcontext.arg = NULL; ! plerrcontext.previous = error_context_stack; ! error_context_stack = &plerrcontext; /* ! * Initialize the compiler */ ! plpgsql_ns_init(); ! plpgsql_ns_push(NULL); ! plpgsql_DumpExecTree = 0; ! ! datums_alloc = 128; ! plpgsql_nDatums = 0; ! plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc); ! datums_last = 0; ! /* ! * Create the new function node ! */ ! function = malloc(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_xmin = HeapTupleHeaderGetXmin(procTup->t_data); ! function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); ! function->fn_functype = functype; ! switch (functype) ! { ! case T_FUNCTION: ! /* ! * Normal function has a defined returntype ! */ ! function->fn_rettype = procStruct->prorettype; ! function->fn_retset = procStruct->proretset; ! /* ! * Lookup the functions return type ! */ ! typeTup = SearchSysCache(TYPEOID, ! ObjectIdGetDatum(procStruct->prorettype), ! 0, 0, 0); ! if (!HeapTupleIsValid(typeTup)) ! elog(ERROR, "cache lookup for return type %u failed", ! procStruct->prorettype); ! typeStruct = (Form_pg_type) GETSTRUCT(typeTup); ! /* Disallow pseudotype result, except VOID or RECORD */ ! if (typeStruct->typtype == 'p') ! { ! if (procStruct->prorettype == VOIDOID || ! procStruct->prorettype == RECORDOID) ! /* okay */ ; ! else if (procStruct->prorettype == TRIGGEROID) ! elog(ERROR, "plpgsql functions cannot return type %s" ! "\n\texcept when used as triggers", ! format_type_be(procStruct->prorettype)); else ! elog(ERROR, "plpgsql functions cannot return type %s", ! format_type_be(procStruct->prorettype)); ! } ! ! if (typeStruct->typrelid != InvalidOid || ! procStruct->prorettype == RECORDOID) ! function->fn_retistuple = true; ! else ! { ! function->fn_retbyval = typeStruct->typbyval; ! function->fn_rettyplen = typeStruct->typlen; ! function->fn_rettypelem = typeStruct->typelem; ! perm_fmgr_info(typeStruct->typinput, &(function->fn_retinput)); ! } ! ReleaseSysCache(typeTup); ! ! /* ! * Create the variables for the procedures parameters ! */ ! for (i = 0; i < procStruct->pronargs; i++) ! { ! char buf[32]; ! snprintf(buf, sizeof(buf), "$%d", i + 1); /* name for variable */ /* ! * Get the parameters type */ typeTup = SearchSysCache(TYPEOID, ! ObjectIdGetDatum(procStruct->proargtypes[i]), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) ! elog(ERROR, "cache lookup for argument type %u failed", ! procStruct->proargtypes[i]); typeStruct = (Form_pg_type) GETSTRUCT(typeTup); ! /* Disallow pseudotype argument */ if (typeStruct->typtype == 'p') ! elog(ERROR, "plpgsql functions cannot take type %s", ! format_type_be(procStruct->proargtypes[i])); ! if (typeStruct->typrelid != InvalidOid) { /* ! * For tuple type parameters, we set up a record of ! * that type */ ! row = plpgsql_build_rowtype(typeStruct->typrelid); ! ! row->refname = strdup(buf); ! plpgsql_adddatum((PLpgSQL_datum *) row); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno, ! row->refname); - arg_varnos[i] = row->rowno; - } - else - { /* ! * Normal parameters get a var node */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup(buf); ! var->lineno = 0; ! var->datatype = build_datatype(typeTup, -1); ! var->isconst = true; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, ! var->refname); ! arg_varnos[i] = var->varno; } ! ReleaseSysCache(typeTup); ! } ! break; ! case T_TRIGGER: ! /* ! * Trigger procedures return type is unknown yet ! */ ! function->fn_rettype = InvalidOid; ! function->fn_retbyval = false; ! function->fn_retistuple = true; ! function->fn_retset = false; ! /* ! * Add the record for referencing NEW ! */ ! rec = malloc(sizeof(PLpgSQL_rec)); ! memset(rec, 0, sizeof(PLpgSQL_rec)); ! rec->dtype = PLPGSQL_DTYPE_REC; ! rec->refname = strdup("new"); ! rec->tup = NULL; ! rec->tupdesc = NULL; ! rec->freetup = false; ! plpgsql_adddatum((PLpgSQL_datum *) rec); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); ! function->new_varno = rec->recno; ! /* ! * Add the record for referencing OLD ! */ ! rec = malloc(sizeof(PLpgSQL_rec)); ! memset(rec, 0, sizeof(PLpgSQL_rec)); ! rec->dtype = PLPGSQL_DTYPE_REC; ! rec->refname = strdup("old"); ! rec->tup = NULL; ! rec->tupdesc = NULL; ! rec->freetup = false; ! plpgsql_adddatum((PLpgSQL_datum *) rec); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); ! function->old_varno = rec->recno; ! /* ! * Add the variable tg_name ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_name"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("name"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_name_varno = var->varno; ! /* ! * Add the variable tg_when ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_when"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_when_varno = var->varno; ! /* ! * Add the variable tg_level ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_level"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_level_varno = var->varno; ! /* ! * Add the variable tg_op ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_op"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_op_varno = var->varno; ! /* ! * Add the variable tg_relid ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_relid"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("oid"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_relid_varno = var->varno; ! /* ! * Add the variable tg_relname ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_relname"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("name"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_relname_varno = var->varno; ! /* ! * Add the variable tg_nargs ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_nargs"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("int4"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_nargs_varno = var->varno; ! break; ! default: ! elog(ERROR, "unknown function type %u in plpgsql_compile()", ! functype); ! break; ! } ! /* ! * Create the magic FOUND variable. ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("found"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("bool"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->found_varno = var->varno; ! /* ! * Forget about the above created variables ! */ ! plpgsql_add_initdatums(NULL); ! /* ! * Now parse the functions text ! */ ! parse_rc = plpgsql_yyparse(); ! if (parse_rc != 0) ! elog(ERROR, "plpgsql: parser returned %d ???", parse_rc); ! plpgsql_scanner_finish(); ! /* ! * If that was successful, complete the functions info. ! */ ! function->fn_nargs = procStruct->pronargs; ! for (i = 0; i < function->fn_nargs; i++) ! function->fn_argvarnos[i] = arg_varnos[i]; ! function->ndatums = plpgsql_nDatums; ! function->datums = malloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums); ! for (i = 0; i < plpgsql_nDatums; i++) ! function->datums[i] = plpgsql_Datums[i]; ! function->action = plpgsql_yylval.program; ReleaseSysCache(procTup); /* ! * Pop the error context stack */ ! error_context_stack = plerrcontext.previous; ! plpgsql_error_funcname = NULL; ! plpgsql_error_lineno = 0; /* * Finally return the compiled function */ if (plpgsql_DumpExecTree) plpgsql_dumptree(function); return function; } --- 153,625 ---- int i; int arg_varnos[FUNC_MAX_ARGS]; ErrorContextCallback plerrcontext; + Oid rettypeid; + Oid funcOid = fcinfo->flinfo->fn_oid; + int functype = CALLED_AS_TRIGGER(fcinfo) ? T_TRIGGER : T_FUNCTION; /* ! * 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); /* ! * Lookup the function in the hashtable based on funcOid and ! * argument type signature. Create the hash table if this ! * is the first time through. */ ! function = get_function_by_signature(fcinfo, procTup); /* ! * if the function wasn't found or was out-of-date, ! * we have to compile it */ ! if (!function) ! { ! /* ! * 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); ! pfree(proc_source); ! plpgsql_error_funcname = pstrdup(NameStr(procStruct->proname)); ! plpgsql_error_lineno = 0; ! /* ! * Setup error traceback support for ereport() ! */ ! plerrcontext.callback = plpgsql_compile_error_callback; ! plerrcontext.arg = NULL; ! plerrcontext.previous = error_context_stack; ! error_context_stack = &plerrcontext; ! /* ! * Initialize the compiler ! */ ! plpgsql_ns_init(); ! plpgsql_ns_push(NULL); ! plpgsql_DumpExecTree = 0; ! ! datums_alloc = 128; ! plpgsql_nDatums = 0; ! plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc); ! datums_last = 0; ! /* ! * Create the new function node ! */ ! function = malloc(sizeof(PLpgSQL_function)); ! memset(function, 0, sizeof(PLpgSQL_function)); ! plpgsql_curr_compile = function; ! ! function->fn_name = strdup(NameStr(procStruct->proname)); ! function->fn_oid = funcOid; ! function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); ! function->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); ! function->fn_functype = functype; ! switch (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. ! */ ! if (procStruct->prorettype == ANYARRAYOID || ! procStruct->prorettype == ANYELEMENTOID) ! { ! rettypeid = get_fn_expr_rettype(fcinfo); ! if (!OidIsValid(rettypeid)) ! elog(ERROR, "could not determine actual return type " \ ! "for polymorphic function %s", ! plpgsql_error_funcname); ! } else ! rettypeid = procStruct->prorettype; ! /* ! * Normal function has a defined returntype ! */ ! function->fn_rettype = rettypeid; ! function->fn_retset = procStruct->proretset; /* ! * Lookup the functions return type */ typeTup = SearchSysCache(TYPEOID, ! ObjectIdGetDatum(rettypeid), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) ! elog(ERROR, "cache lookup for return type %u failed", ! rettypeid); typeStruct = (Form_pg_type) GETSTRUCT(typeTup); ! /* Disallow pseudotype result, except VOID or RECORD */ if (typeStruct->typtype == 'p') ! { ! if (rettypeid == VOIDOID || ! rettypeid == RECORDOID) ! /* okay */ ; ! else if (rettypeid == TRIGGEROID) ! elog(ERROR, "plpgsql functions cannot return type %s" ! "\n\texcept when used as triggers", ! format_type_be(rettypeid)); ! else ! elog(ERROR, "plpgsql functions cannot return type %s", ! format_type_be(rettypeid)); ! } ! ! if (typeStruct->typrelid != InvalidOid || ! rettypeid == RECORDOID) ! function->fn_retistuple = true; ! else ! { ! function->fn_retbyval = typeStruct->typbyval; ! function->fn_rettyplen = typeStruct->typlen; ! function->fn_rettypelem = typeStruct->typelem; ! perm_fmgr_info(typeStruct->typinput, &(function->fn_retinput)); ! } ! ReleaseSysCache(typeTup); ! /* ! * Create the variables for the procedures parameters ! */ ! for (i = 0; i < procStruct->pronargs; i++) { + char buf[32]; + Oid argtypeid = InvalidOid; + + snprintf(buf, sizeof(buf), "$%d", i + 1); /* name for variable */ + /* ! * Check for polymorphic arguments. If found, use the actual ! * parameter type from the caller's FuncExpr node, if we ! * have one. */ ! if (procStruct->proargtypes[i] == ANYARRAYOID || ! procStruct->proargtypes[i] == ANYELEMENTOID) ! { ! argtypeid = get_fn_expr_argtype(fcinfo, i); ! if (!OidIsValid(argtypeid)) ! elog(ERROR, "could not determine actual argument " \ ! "type for polymorphic function %s", ! plpgsql_error_funcname); ! } ! else ! argtypeid = procStruct->proargtypes[i]; ! function->fn_argtypes[i] = argtypeid; /* ! * Get the parameters type */ ! typeTup = SearchSysCache(TYPEOID, ! ObjectIdGetDatum(argtypeid), ! 0, 0, 0); ! if (!HeapTupleIsValid(typeTup)) ! elog(ERROR, "cache lookup for argument type %u failed", ! argtypeid); ! typeStruct = (Form_pg_type) GETSTRUCT(typeTup); ! ! /* Disallow pseudotype argument */ ! if (typeStruct->typtype == 'p') ! elog(ERROR, "plpgsql functions cannot take type %s", ! format_type_be(argtypeid)); ! if (typeStruct->typrelid != InvalidOid) ! { ! /* ! * For tuple type parameters, we set up a record of ! * that type ! */ ! row = plpgsql_build_rowtype(typeStruct->typrelid); ! ! row->refname = strdup(buf); ! ! plpgsql_adddatum((PLpgSQL_datum *) row); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW, row->rowno, ! row->refname); ! ! arg_varnos[i] = row->rowno; ! } ! else ! { ! /* ! * Normal parameters get a var node ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup(buf); ! var->lineno = 0; ! var->datatype = build_datatype(typeTup, -1); ! var->isconst = true; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, ! var->refname); ! arg_varnos[i] = var->varno; ! } ! ReleaseSysCache(typeTup); } ! break; ! case T_TRIGGER: ! /* ! * Trigger procedures return type is unknown yet ! */ ! function->fn_rettype = InvalidOid; ! function->fn_retbyval = false; ! function->fn_retistuple = true; ! function->fn_retset = false; ! /* ! * Add the record for referencing NEW ! */ ! rec = malloc(sizeof(PLpgSQL_rec)); ! memset(rec, 0, sizeof(PLpgSQL_rec)); ! rec->dtype = PLPGSQL_DTYPE_REC; ! rec->refname = strdup("new"); ! rec->tup = NULL; ! rec->tupdesc = NULL; ! rec->freetup = false; ! plpgsql_adddatum((PLpgSQL_datum *) rec); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); ! function->new_varno = rec->recno; ! /* ! * Add the record for referencing OLD ! */ ! rec = malloc(sizeof(PLpgSQL_rec)); ! memset(rec, 0, sizeof(PLpgSQL_rec)); ! rec->dtype = PLPGSQL_DTYPE_REC; ! rec->refname = strdup("old"); ! rec->tup = NULL; ! rec->tupdesc = NULL; ! rec->freetup = false; ! plpgsql_adddatum((PLpgSQL_datum *) rec); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, rec->recno, rec->refname); ! function->old_varno = rec->recno; ! /* ! * Add the variable tg_name ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_name"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("name"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_name_varno = var->varno; ! /* ! * Add the variable tg_when ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_when"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_when_varno = var->varno; ! /* ! * Add the variable tg_level ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_level"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_level_varno = var->varno; ! /* ! * Add the variable tg_op ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_op"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("text"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_op_varno = var->varno; ! /* ! * Add the variable tg_relid ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_relid"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("oid"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_relid_varno = var->varno; ! /* ! * Add the variable tg_relname ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_relname"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("name"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_relname_varno = var->varno; ! /* ! * Add the variable tg_nargs ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("tg_nargs"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("int4"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->tg_nargs_varno = var->varno; ! break; ! default: ! elog(ERROR, "unknown function type %u in plpgsql_compile()", ! functype); ! break; ! } ! /* ! * Create the magic FOUND variable. ! */ ! var = malloc(sizeof(PLpgSQL_var)); ! memset(var, 0, sizeof(PLpgSQL_var)); ! var->dtype = PLPGSQL_DTYPE_VAR; ! var->refname = strdup("found"); ! var->lineno = 0; ! var->datatype = plpgsql_parse_datatype("bool"); ! var->isconst = false; ! var->notnull = false; ! var->default_val = NULL; ! plpgsql_adddatum((PLpgSQL_datum *) var); ! plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, var->varno, var->refname); ! function->found_varno = var->varno; ! /* ! * Forget about the above created variables ! */ ! plpgsql_add_initdatums(NULL); ! /* ! * Now parse the functions text ! */ ! parse_rc = plpgsql_yyparse(); ! if (parse_rc != 0) ! elog(ERROR, "plpgsql: parser returned %d ???", parse_rc); ! plpgsql_scanner_finish(); ! /* ! * If that was successful, complete the functions info. ! */ ! function->fn_nargs = procStruct->pronargs; ! for (i = 0; i < function->fn_nargs; i++) ! function->fn_argvarnos[i] = arg_varnos[i]; ! function->ndatums = plpgsql_nDatums; ! function->datums = malloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums); ! for (i = 0; i < plpgsql_nDatums; i++) ! function->datums[i] = plpgsql_Datums[i]; ! function->action = plpgsql_yylval.program; ! ! /* ! * add it to the hash table ! */ ! plpgsql_HashTableInsert(function); ! ! /* ! * Pop the error context stack ! */ ! error_context_stack = plerrcontext.previous; ! plpgsql_error_funcname = NULL; ! plpgsql_error_lineno = 0; ! } ReleaseSysCache(procTup); /* ! * Save pointer in FmgrInfo to avoid search on subsequent calls */ ! fcinfo->flinfo->fn_extra = (void *) function; /* * Finally return the compiled function */ if (plpgsql_DumpExecTree) plpgsql_dumptree(function); + return function; } *************** *** 1500,1502 **** --- 1595,1767 ---- plpgsql_error_lineno = plpgsql_scanner_lineno(); elog(ERROR, "%s at or near \"%s\"", s, plpgsql_yytext); } + + + /* + * get_function_by_signature + * + * Returns a function keyed by funcOid and argument types, or NULL + * if not found. Also checks to be sure what we found is still + * valid, clearing the hash table entry and returning NULL if it + * is out-of-date. This is needed because CREATE OR REPLACE FUNCTION + * can modify the function's pg_proc entry without changing its OID + * or arguments. + * + * Creates the hashtable if it doesn't already exist. + */ + static PLpgSQL_function * + get_function_by_signature(FunctionCallInfo fcinfo, HeapTuple procTup) + { + PLpgSQL_function *function = NULL; + PLpgSQL_func_key *func_key; + Oid funcOid = fcinfo->flinfo->fn_oid; + Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup); + int i; + bool entry_valid; + + /* + * first see if we've already been here during the current + * SQL function call + */ + function = (PLpgSQL_function *) fcinfo->flinfo->fn_extra; + + if (!function) + { + if (!plpgsql_HashTable) + plpgsql_HashTableInit(); + + func_key = (PLpgSQL_func_key *) palloc0(sizeof(PLpgSQL_func_key)); + func_key->funcOid = funcOid; + + /* get the argument types */ + for (i = 0; i < procStruct->pronargs; i++) + { + Oid argtypeid = InvalidOid; + + /* + * Check for polymorphic arguments. If found, use the actual + * parameter type from the caller's FuncExpr node, if we + * have one. + */ + if (procStruct->proargtypes[i] == ANYARRAYOID || + procStruct->proargtypes[i] == ANYELEMENTOID) + { + argtypeid = get_fn_expr_argtype(fcinfo, i); + if (!OidIsValid(argtypeid)) + elog(ERROR, "could not determine actual argument " \ + "type for polymorphic function %s", + plpgsql_error_funcname); + } + else + argtypeid = procStruct->proargtypes[i]; + + func_key->argtypes[i] = argtypeid; + } + + function = plpgsql_HashTableLookup(func_key); + } + + if (function) + { + /* we have a compiled function, but is it still valid */ + entry_valid = + (function->fn_xmin == HeapTupleHeaderGetXmin(procTup->t_data) && + function->fn_cmin == HeapTupleHeaderGetCmin(procTup->t_data)); + + if (!entry_valid) + { + plpgsql_HashTableDelete(function); + + /* XXX: is it worth worrying about the leaked function struct? */ + function = NULL; + } + } + + return function; + } + + static void + plpgsql_HashTableInit(void) + { + HASHCTL ctl; + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(PLpgSQL_func_key); + ctl.entrysize = sizeof(plpgsql_HashEnt); + ctl.hash = tag_hash; + plpgsql_HashTable = hash_create("PLpgSQL func cache", FUNCS_PER_USER, + &ctl, HASH_ELEM | HASH_FUNCTION); + } + + static PLpgSQL_function * + plpgsql_HashTableLookup(PLpgSQL_func_key *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_HashEnt *hentry; + bool found; + PLpgSQL_func_key *func_key; + + func_key = function_get_func_key(function); + 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; + } + + static void + plpgsql_HashTableDelete(PLpgSQL_function *function) + { + plpgsql_HashEnt *hentry; + PLpgSQL_func_key *func_key; + + func_key = function_get_func_key(function); + hentry = (plpgsql_HashEnt*) hash_search(plpgsql_HashTable, + (void *) func_key, + HASH_REMOVE, + NULL); + + if (hentry == NULL) + elog(WARNING, "trying to delete function that does not exist."); + } + + static PLpgSQL_func_key * + function_get_func_key(PLpgSQL_function *function) + { + PLpgSQL_func_key *func_key; + int i; + + Assert(function != NULL); + + func_key = (PLpgSQL_func_key *) palloc0(sizeof(PLpgSQL_func_key)); + func_key->funcOid = function->fn_oid; + + /* get the argument types */ + for (i = 0; i < function->fn_nargs; i++) + func_key->argtypes[i] = function->fn_argtypes[i]; + + return func_key; + } + Index: src/pl/plpgsql/src/pl_handler.c =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/pl_handler.c,v retrieving revision 1.12 diff -c -r1.12 pl_handler.c *** src/pl/plpgsql/src/pl_handler.c 30 Aug 2002 00:28:41 -0000 1.12 --- src/pl/plpgsql/src/pl_handler.c 30 Jun 2003 21:35:11 -0000 *************** *** 44,59 **** #include "utils/builtins.h" #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 * --- 44,49 ---- *************** *** 67,76 **** Datum plpgsql_call_handler(PG_FUNCTION_ARGS) { - bool isTrigger = CALLED_AS_TRIGGER(fcinfo); - Oid funcOid = fcinfo->flinfo->fn_oid; PLpgSQL_function *func; Datum retval; /* * Connect to SPI manager --- 57,65 ---- Datum plpgsql_call_handler(PG_FUNCTION_ARGS) { PLpgSQL_function *func; Datum retval; + bool isTrigger = CALLED_AS_TRIGGER(fcinfo); /* * Connect to SPI manager *************** *** 78,126 **** 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; ! } /* * Determine if called as function or trigger and call appropriate --- 67,74 ---- if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "plpgsql: cannot connect to SPI manager"); ! /* Find or compile the function */ ! func = plpgsql_compile(fcinfo); /* * Determine if called as function or trigger and call appropriate *************** *** 139,169 **** elog(ERROR, "plpgsql: SPI_finish() failed"); 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; } --- 87,90 ---- Index: src/pl/plpgsql/src/plpgsql.h =================================================================== RCS file: /opt/src/cvs/pgsql-server/src/pl/plpgsql/src/plpgsql.h,v retrieving revision 1.36 diff -c -r1.36 plpgsql.h *** src/pl/plpgsql/src/plpgsql.h 5 May 2003 16:46:28 -0000 1.36 --- src/pl/plpgsql/src/plpgsql.h 30 Jun 2003 21:28:42 -0000 *************** *** 504,509 **** --- 504,510 ---- bool fn_retset; int fn_nargs; + Oid fn_argtypes[FUNC_MAX_ARGS]; int fn_argvarnos[FUNC_MAX_ARGS]; int found_varno; int new_varno; *************** *** 519,526 **** int ndatums; PLpgSQL_datum **datums; PLpgSQL_stmt_block *action; - - struct PLpgSQL_function *next; /* for chaining list of functions */ } PLpgSQL_function; --- 520,525 ---- *************** *** 588,594 **** * Functions in pl_comp.c * ---------- */ ! extern PLpgSQL_function *plpgsql_compile(Oid fn_oid, int functype); extern int plpgsql_parse_word(char *word); extern int plpgsql_parse_dblword(char *word); extern int plpgsql_parse_tripword(char *word); --- 587,593 ---- * Functions in pl_comp.c * ---------- */ ! 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);