#include "miscadmin.h"
#include "utils/guc.h"
#include "utils/memutils.h"
+#include "utils/rel.h"
#include "utils/syscache.h"
#include "plpython.h"
ReleaseSysCache(tuple);
- PLy_procedure_get(funcoid, is_trigger);
+ /* We can't validate triggers against any particular table ... */
+ PLy_procedure_get(funcoid, InvalidOid, is_trigger);
PG_RETURN_VOID();
}
PG_TRY();
{
+ Oid funcoid = fcinfo->flinfo->fn_oid;
PLyProcedure *proc;
if (CALLED_AS_TRIGGER(fcinfo))
{
+ Relation tgrel = ((TriggerData *) fcinfo->context)->tg_relation;
HeapTuple trv;
- proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
+ proc = PLy_procedure_get(funcoid, RelationGetRelid(tgrel), true);
exec_ctx->curr_proc = proc;
trv = PLy_exec_trigger(fcinfo, proc);
retval = PointerGetDatum(trv);
}
else
{
- proc = PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
+ proc = PLy_procedure_get(funcoid, InvalidOid, false);
exec_ctx->curr_proc = proc;
retval = PLy_exec_function(fcinfo, proc);
}
static HTAB *PLy_procedure_cache = NULL;
-static HTAB *PLy_trigger_cache = NULL;
static PLyProcedure *PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger);
static bool PLy_procedure_argument_valid(PLyTypeInfo *arg);
HASHCTL hash_ctl;
memset(&hash_ctl, 0, sizeof(hash_ctl));
- hash_ctl.keysize = sizeof(Oid);
+ hash_ctl.keysize = sizeof(PLyProcedureKey);
hash_ctl.entrysize = sizeof(PLyProcedureEntry);
- hash_ctl.hash = oid_hash;
+ hash_ctl.hash = tag_hash;
PLy_procedure_cache = hash_create("PL/Python procedures", 32, &hash_ctl,
HASH_ELEM | HASH_FUNCTION);
-
- memset(&hash_ctl, 0, sizeof(hash_ctl));
- hash_ctl.keysize = sizeof(Oid);
- hash_ctl.entrysize = sizeof(PLyProcedureEntry);
- hash_ctl.hash = oid_hash;
- PLy_trigger_cache = hash_create("PL/Python triggers", 32, &hash_ctl,
- HASH_ELEM | HASH_FUNCTION);
}
/*
/*
* PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
- * returns a new PLyProcedure. fcinfo is the call info, tgreloid is the
- * relation OID when calling a trigger, or InvalidOid (zero) for ordinary
- * function calls.
+ * returns a new PLyProcedure.
+ *
+ * fn_oid is the OID of the function requested
+ * fn_rel is InvalidOid or the relation this function triggers on
+ * is_trigger denotes whether the function is a trigger function
+ *
+ * The reason that both fn_rel and is_trigger need to be passed is that when
+ * trigger functions get validated we don't know which relation(s) they'll
+ * be used with, so no sensible fn_rel can be passed.
*/
PLyProcedure *
-PLy_procedure_get(Oid fn_oid, bool is_trigger)
+PLy_procedure_get(Oid fn_oid, Oid fn_rel, bool is_trigger)
{
+ bool use_cache = !(is_trigger && fn_rel == InvalidOid);
HeapTuple procTup;
- PLyProcedureEntry *volatile entry;
- bool found;
+ PLyProcedureKey key;
+ PLyProcedureEntry *volatile entry = NULL;
+ PLyProcedure *volatile proc = NULL;
+ bool found = false;
procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));
if (!HeapTupleIsValid(procTup))
elog(ERROR, "cache lookup failed for function %u", fn_oid);
- /* Look for the function in the corresponding cache */
- if (is_trigger)
- entry = hash_search(PLy_trigger_cache,
- &fn_oid, HASH_ENTER, &found);
- else
- entry = hash_search(PLy_procedure_cache,
- &fn_oid, HASH_ENTER, &found);
+ /*
+ * Look for the function in the cache, unless we don't have the necessary
+ * information (e.g. during validation). In that case we just don't cache
+ * anything.
+ */
+ if (use_cache)
+ {
+ key.fn_oid = fn_oid;
+ key.fn_rel = fn_rel;
+ entry = hash_search(PLy_procedure_cache, &key, HASH_ENTER, &found);
+ proc = entry->proc;
+ }
PG_TRY();
{
if (!found)
{
- /* Haven't found it, create a new cache entry */
- entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
+ /* Haven't found it, create a new procedure */
+ proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
+ if (use_cache)
+ entry->proc = proc;
}
- else if (!PLy_procedure_valid(entry->proc, procTup))
+ else if (!PLy_procedure_valid(proc, procTup))
{
/* Found it, but it's invalid, free and reuse the cache entry */
- PLy_procedure_delete(entry->proc);
- PLy_free(entry->proc);
- entry->proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
+ PLy_procedure_delete(proc);
+ PLy_free(proc);
+ proc = PLy_procedure_create(procTup, fn_oid, is_trigger);
+ entry->proc = proc;
}
/* Found it and it's valid, it's fine to use it */
}
PG_CATCH();
{
/* Do not leave an uninitialised entry in the cache */
- if (is_trigger)
- hash_search(PLy_trigger_cache,
- &fn_oid, HASH_REMOVE, NULL);
- else
- hash_search(PLy_procedure_cache,
- &fn_oid, HASH_REMOVE, NULL);
+ if (use_cache)
+ hash_search(PLy_procedure_cache, &key, HASH_REMOVE, NULL);
PG_RE_THROW();
}
PG_END_TRY();
ReleaseSysCache(procTup);
- return entry->proc;
+ return proc;
}
/*