summaryrefslogtreecommitdiffstats
path: root/src/test/modules/plsample/plsample.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/modules/plsample/plsample.c')
-rw-r--r--src/test/modules/plsample/plsample.c187
1 files changed, 187 insertions, 0 deletions
diff --git a/src/test/modules/plsample/plsample.c b/src/test/modules/plsample/plsample.c
new file mode 100644
index 0000000..6fc33c7
--- /dev/null
+++ b/src/test/modules/plsample/plsample.c
@@ -0,0 +1,187 @@
+/*-------------------------------------------------------------------------
+ *
+ * plsample.c
+ * Handler for the PL/Sample procedural language
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/test/modules/plsample/plsample.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/event_trigger.h"
+#include "commands/trigger.h"
+#include "funcapi.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(plsample_call_handler);
+
+static Datum plsample_func_handler(PG_FUNCTION_ARGS);
+
+/*
+ * Handle function, procedure, and trigger calls.
+ */
+Datum
+plsample_call_handler(PG_FUNCTION_ARGS)
+{
+ Datum retval = (Datum) 0;
+
+ PG_TRY();
+ {
+ /*
+ * Determine if called as function or trigger and call appropriate
+ * subhandler.
+ */
+ if (CALLED_AS_TRIGGER(fcinfo))
+ {
+ /*
+ * This function has been called as a trigger function, where
+ * (TriggerData *) fcinfo->context includes the information of the
+ * context.
+ */
+ }
+ else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
+ {
+ /*
+ * This function is called as an event trigger function, where
+ * (EventTriggerData *) fcinfo->context includes the information
+ * of the context.
+ */
+ }
+ else
+ {
+ /* Regular function handler */
+ retval = plsample_func_handler(fcinfo);
+ }
+ }
+ PG_FINALLY();
+ {
+ }
+ PG_END_TRY();
+
+ return retval;
+}
+
+/*
+ * plsample_func_handler
+ *
+ * Function called by the call handler for function execution.
+ */
+static Datum
+plsample_func_handler(PG_FUNCTION_ARGS)
+{
+ HeapTuple pl_tuple;
+ Datum ret;
+ char *source;
+ bool isnull;
+ FmgrInfo *arg_out_func;
+ Form_pg_type type_struct;
+ HeapTuple type_tuple;
+ Form_pg_proc pl_struct;
+ volatile MemoryContext proc_cxt = NULL;
+ Oid *argtypes;
+ char **argnames;
+ char *argmodes;
+ char *proname;
+ Form_pg_type pg_type_entry;
+ Oid result_typioparam;
+ Oid prorettype;
+ FmgrInfo result_in_func;
+ int numargs;
+
+ /* Fetch the source text of the function. */
+ pl_tuple = SearchSysCache(PROCOID,
+ ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
+ if (!HeapTupleIsValid(pl_tuple))
+ elog(ERROR, "cache lookup failed for function %u",
+ fcinfo->flinfo->fn_oid);
+
+ /*
+ * Extract and print the source text of the function. This can be used as
+ * a base for the function validation and execution.
+ */
+ pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
+ proname = pstrdup(NameStr(pl_struct->proname));
+ ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
+ if (isnull)
+ elog(ERROR, "could not find source text of function \"%s\"",
+ proname);
+ source = DatumGetCString(DirectFunctionCall1(textout, ret));
+ ereport(NOTICE,
+ (errmsg("source text of function \"%s\": %s",
+ proname, source)));
+
+ /*
+ * Allocate a context that will hold all the Postgres data for the
+ * procedure.
+ */
+ proc_cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/Sample function",
+ ALLOCSET_SMALL_SIZES);
+
+ arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
+ numargs = get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
+
+ /*
+ * Iterate through all of the function arguments, printing each input
+ * value.
+ */
+ for (int i = 0; i < numargs; i++)
+ {
+ Oid argtype = pl_struct->proargtypes.values[i];
+ char *value;
+
+ type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
+ if (!HeapTupleIsValid(type_tuple))
+ elog(ERROR, "cache lookup failed for type %u", argtype);
+
+ type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
+ fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
+ ReleaseSysCache(type_tuple);
+
+ value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
+ ereport(NOTICE,
+ (errmsg("argument: %d; name: %s; value: %s",
+ i, argnames[i], value)));
+ }
+
+ /* Type of the result */
+ prorettype = pl_struct->prorettype;
+ ReleaseSysCache(pl_tuple);
+
+ /*
+ * Get the required information for input conversion of the return value.
+ *
+ * If the function uses VOID as result, it is better to return NULL.
+ * Anyway, let's be honest. This is just a template, so there is not much
+ * we can do here. This returns NULL except if the result type is text,
+ * where the result is the source text of the function.
+ */
+ if (prorettype != TEXTOID)
+ PG_RETURN_NULL();
+
+ type_tuple = SearchSysCache1(TYPEOID,
+ ObjectIdGetDatum(prorettype));
+ if (!HeapTupleIsValid(type_tuple))
+ elog(ERROR, "cache lookup failed for type %u", prorettype);
+ pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
+ result_typioparam = getTypeIOParam(type_tuple);
+
+ fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
+ ReleaseSysCache(type_tuple);
+
+ ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
+ PG_RETURN_DATUM(ret);
+}