#include "utils/lsyscache.h"
#include "utils/syscache.h"
+/*
+ * Hooks for function calls
+ */
+PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook = NULL;
+PGDLLIMPORT fmgr_hook_type fmgr_hook = NULL;
/*
* Declaration for old-style function pointer type. This is now used only
finfo->fn_retset = procedureStruct->proretset;
/*
- * If it has prosecdef set, or non-null proconfig, use
- * fmgr_security_definer call handler --- unless we are being called again
- * by fmgr_security_definer.
+ * If it has prosecdef set, non-null proconfig, or if a plugin wants to
+ * hook function entry/exit, use fmgr_security_definer call handler ---
+ * unless we are being called again by fmgr_security_definer.
*
* When using fmgr_security_definer, function stats tracking is always
* disabled at the outer level, and instead we set the flag properly in
*/
if (!ignore_security &&
(procedureStruct->prosecdef ||
- !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig)))
+ !heap_attisnull(procedureTuple, Anum_pg_proc_proconfig) ||
+ FmgrHookIsNeeded(functionId)))
{
finfo->fn_addr = fmgr_security_definer;
finfo->fn_stats = TRACK_FUNC_ALL; /* ie, never track */
FmgrInfo flinfo; /* lookup info for target function */
Oid userid; /* userid to set, or InvalidOid */
ArrayType *proconfig; /* GUC values to set, or NULL */
+ Datum private; /* private usage for plugin modules */
};
/*
- * Function handler for security-definer/proconfig functions. We extract the
- * OID of the actual function and do a fmgr lookup again. Then we fetch the
- * pg_proc row and copy the owner ID and proconfig fields. (All this info
- * is cached for the duration of the current query.) To execute a call,
- * we temporarily replace the flinfo with the cached/looked-up one, while
- * keeping the outer fcinfo (which contains all the actual arguments, etc.)
- * intact. This is not re-entrant, but then the fcinfo itself can't be used
- * re-entrantly anyway.
+ * Function handler for security-definer/proconfig/plugin-hooked functions.
+ * We extract the OID of the actual function and do a fmgr lookup again.
+ * Then we fetch the pg_proc row and copy the owner ID and proconfig fields.
+ * (All this info is cached for the duration of the current query.)
+ * To execute a call, we temporarily replace the flinfo with the cached
+ * and looked-up one, while keeping the outer fcinfo (which contains all
+ * the actual arguments, etc.) intact. This is not re-entrant, but then
+ * the fcinfo itself can't be used re-entrantly anyway.
*/
static Datum
fmgr_security_definer(PG_FUNCTION_ARGS)
GUC_ACTION_SAVE);
}
+ /* function manager hook */
+ if (fmgr_hook)
+ (*fmgr_hook)(FHET_START, &fcache->flinfo, &fcache->private);
+
/*
* We don't need to restore GUC or userid settings on error, because the
* ensuing xact or subxact abort will do that. The PG_TRY block is only
PG_CATCH();
{
fcinfo->flinfo = save_flinfo;
+ if (fmgr_hook)
+ (*fmgr_hook)(FHET_ABORT, &fcache->flinfo, &fcache->private);
PG_RE_THROW();
}
PG_END_TRY();
AtEOXact_GUC(true, save_nestlevel);
if (OidIsValid(fcache->userid))
SetUserIdAndSecContext(save_userid, save_sec_context);
+ if (fmgr_hook)
+ (*fmgr_hook)(FHET_END, &fcache->flinfo, &fcache->private);
return result;
}
extern int AggCheckCallContext(FunctionCallInfo fcinfo,
MemoryContext *aggcontext);
+/*
+ * We allow plugin modules to hook function entry/exit. This is intended
+ * as support for loadable security policy modules, which may want to
+ * perform additional privilege checks on function entry or exit, or to do
+ * other internal bookkeeping. To make this possible, such modules must be
+ * able not only to support normal function entry and exit, but also to trap
+ * the case where we bail out due to an error; and they must also be able to
+ * prevent inlining.
+ */
+typedef enum FmgrHookEventType
+{
+ FHET_START,
+ FHET_END,
+ FHET_ABORT
+} FmgrHookEventType;
+
+typedef bool (*needs_fmgr_hook_type)(Oid fn_oid);
+
+typedef void (*fmgr_hook_type)(FmgrHookEventType event,
+ FmgrInfo *flinfo, Datum *private);
+
+extern PGDLLIMPORT needs_fmgr_hook_type needs_fmgr_hook;
+extern PGDLLIMPORT fmgr_hook_type fmgr_hook;
+
+#define FmgrHookIsNeeded(fn_oid) \
+ (!needs_fmgr_hook ? false : (*needs_fmgr_hook)(fn_oid))
/*
* !!! OLD INTERFACE !!!