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