1 /*-------------------------------------------------------------------------
2 *
3 * plsample.c
4 * Handler for the PL/Sample procedural language
5 *
6 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
8 *
9 *
10 * IDENTIFICATION
11 * src/test/modules/plsample/plsample.c
12 *
13 *-------------------------------------------------------------------------
14 */
15
16 #include "postgres.h"
17
18 #include "catalog/pg_proc.h"
19 #include "catalog/pg_type.h"
20 #include "commands/event_trigger.h"
21 #include "commands/trigger.h"
22 #include "funcapi.h"
23 #include "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/syscache.h"
26
27 PG_MODULE_MAGIC;
28
29 PG_FUNCTION_INFO_V1(plsample_call_handler);
30
31 static Datum plsample_func_handler(PG_FUNCTION_ARGS);
32
33 /*
34 * Handle function, procedure, and trigger calls.
35 */
36 Datum
plsample_call_handler(PG_FUNCTION_ARGS)37 plsample_call_handler(PG_FUNCTION_ARGS)
38 {
39 Datum retval = (Datum) 0;
40
41 PG_TRY();
42 {
43 /*
44 * Determine if called as function or trigger and call appropriate
45 * subhandler.
46 */
47 if (CALLED_AS_TRIGGER(fcinfo))
48 {
49 /*
50 * This function has been called as a trigger function, where
51 * (TriggerData *) fcinfo->context includes the information of the
52 * context.
53 */
54 }
55 else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
56 {
57 /*
58 * This function is called as an event trigger function, where
59 * (EventTriggerData *) fcinfo->context includes the information
60 * of the context.
61 */
62 }
63 else
64 {
65 /* Regular function handler */
66 retval = plsample_func_handler(fcinfo);
67 }
68 }
69 PG_FINALLY();
70 {
71 }
72 PG_END_TRY();
73
74 return retval;
75 }
76
77 /*
78 * plsample_func_handler
79 *
80 * Function called by the call handler for function execution.
81 */
82 static Datum
plsample_func_handler(PG_FUNCTION_ARGS)83 plsample_func_handler(PG_FUNCTION_ARGS)
84 {
85 HeapTuple pl_tuple;
86 Datum ret;
87 char *source;
88 bool isnull;
89 FmgrInfo *arg_out_func;
90 Form_pg_type type_struct;
91 HeapTuple type_tuple;
92 Form_pg_proc pl_struct;
93 volatile MemoryContext proc_cxt = NULL;
94 Oid *argtypes;
95 char **argnames;
96 char *argmodes;
97 char *proname;
98 Form_pg_type pg_type_entry;
99 Oid result_typioparam;
100 Oid prorettype;
101 FmgrInfo result_in_func;
102 int numargs;
103
104 /* Fetch the source text of the function. */
105 pl_tuple = SearchSysCache(PROCOID,
106 ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0);
107 if (!HeapTupleIsValid(pl_tuple))
108 elog(ERROR, "cache lookup failed for function %u",
109 fcinfo->flinfo->fn_oid);
110
111 /*
112 * Extract and print the source text of the function. This can be used as
113 * a base for the function validation and execution.
114 */
115 pl_struct = (Form_pg_proc) GETSTRUCT(pl_tuple);
116 proname = pstrdup(NameStr(pl_struct->proname));
117 ret = SysCacheGetAttr(PROCOID, pl_tuple, Anum_pg_proc_prosrc, &isnull);
118 if (isnull)
119 elog(ERROR, "could not find source text of function \"%s\"",
120 proname);
121 source = DatumGetCString(DirectFunctionCall1(textout, ret));
122 ereport(NOTICE,
123 (errmsg("source text of function \"%s\": %s",
124 proname, source)));
125
126 /*
127 * Allocate a context that will hold all the Postgres data for the
128 * procedure.
129 */
130 proc_cxt = AllocSetContextCreate(TopMemoryContext,
131 "PL/Sample function",
132 ALLOCSET_SMALL_SIZES);
133
134 arg_out_func = (FmgrInfo *) palloc0(fcinfo->nargs * sizeof(FmgrInfo));
135 numargs = get_func_arg_info(pl_tuple, &argtypes, &argnames, &argmodes);
136
137 /*
138 * Iterate through all of the function arguments, printing each input
139 * value.
140 */
141 for (int i = 0; i < numargs; i++)
142 {
143 Oid argtype = pl_struct->proargtypes.values[i];
144 char *value;
145
146 type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(argtype));
147 if (!HeapTupleIsValid(type_tuple))
148 elog(ERROR, "cache lookup failed for type %u", argtype);
149
150 type_struct = (Form_pg_type) GETSTRUCT(type_tuple);
151 fmgr_info_cxt(type_struct->typoutput, &(arg_out_func[i]), proc_cxt);
152 ReleaseSysCache(type_tuple);
153
154 value = OutputFunctionCall(&arg_out_func[i], fcinfo->args[i].value);
155 ereport(NOTICE,
156 (errmsg("argument: %d; name: %s; value: %s",
157 i, argnames[i], value)));
158 }
159
160 /* Type of the result */
161 prorettype = pl_struct->prorettype;
162 ReleaseSysCache(pl_tuple);
163
164 /*
165 * Get the required information for input conversion of the return value.
166 *
167 * If the function uses VOID as result, it is better to return NULL.
168 * Anyway, let's be honest. This is just a template, so there is not much
169 * we can do here. This returns NULL except if the result type is text,
170 * where the result is the source text of the function.
171 */
172 if (prorettype != TEXTOID)
173 PG_RETURN_NULL();
174
175 type_tuple = SearchSysCache1(TYPEOID,
176 ObjectIdGetDatum(prorettype));
177 if (!HeapTupleIsValid(type_tuple))
178 elog(ERROR, "cache lookup failed for type %u", prorettype);
179 pg_type_entry = (Form_pg_type) GETSTRUCT(type_tuple);
180 result_typioparam = getTypeIOParam(type_tuple);
181
182 fmgr_info_cxt(pg_type_entry->typinput, &result_in_func, proc_cxt);
183 ReleaseSysCache(type_tuple);
184
185 ret = InputFunctionCall(&result_in_func, source, result_typioparam, -1);
186 PG_RETURN_DATUM(ret);
187 }
188