1 /*-------------------------------------------------------------------------
2  *
3  * fastpath.c
4  *	  routines to handle function requests from the frontend
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/backend/tcop/fastpath.c
12  *
13  * NOTES
14  *	  This cruft is the server side of PQfn.
15  *
16  *-------------------------------------------------------------------------
17  */
18 #include "postgres.h"
19 
20 #include "access/htup_details.h"
21 #include "access/xact.h"
22 #include "catalog/objectaccess.h"
23 #include "catalog/pg_proc.h"
24 #include "libpq/libpq.h"
25 #include "libpq/pqformat.h"
26 #include "mb/pg_wchar.h"
27 #include "miscadmin.h"
28 #include "port/pg_bswap.h"
29 #include "tcop/fastpath.h"
30 #include "tcop/tcopprot.h"
31 #include "utils/acl.h"
32 #include "utils/lsyscache.h"
33 #include "utils/snapmgr.h"
34 #include "utils/syscache.h"
35 
36 
37 /*
38  * Formerly, this code attempted to cache the function and type info
39  * looked up by fetch_fp_info, but only for the duration of a single
40  * transaction command (since in theory the info could change between
41  * commands).  This was utterly useless, because postgres.c executes
42  * each fastpath call as a separate transaction command, and so the
43  * cached data could never actually have been reused.  If it had worked
44  * as intended, it would have had problems anyway with dangling references
45  * in the FmgrInfo struct.  So, forget about caching and just repeat the
46  * syscache fetches on each usage.  They're not *that* expensive.
47  */
48 struct fp_info
49 {
50 	Oid			funcid;
51 	FmgrInfo	flinfo;			/* function lookup info for funcid */
52 	Oid			namespace;		/* other stuff from pg_proc */
53 	Oid			rettype;
54 	Oid			argtypes[FUNC_MAX_ARGS];
55 	char		fname[NAMEDATALEN]; /* function name for logging */
56 };
57 
58 
59 static int16 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
60 								   FunctionCallInfo fcinfo);
61 
62 /* ----------------
63  *		SendFunctionResult
64  * ----------------
65  */
66 static void
SendFunctionResult(Datum retval,bool isnull,Oid rettype,int16 format)67 SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
68 {
69 	StringInfoData buf;
70 
71 	pq_beginmessage(&buf, 'V');
72 
73 	if (isnull)
74 	{
75 		pq_sendint32(&buf, -1);
76 	}
77 	else
78 	{
79 		if (format == 0)
80 		{
81 			Oid			typoutput;
82 			bool		typisvarlena;
83 			char	   *outputstr;
84 
85 			getTypeOutputInfo(rettype, &typoutput, &typisvarlena);
86 			outputstr = OidOutputFunctionCall(typoutput, retval);
87 			pq_sendcountedtext(&buf, outputstr, strlen(outputstr), false);
88 			pfree(outputstr);
89 		}
90 		else if (format == 1)
91 		{
92 			Oid			typsend;
93 			bool		typisvarlena;
94 			bytea	   *outputbytes;
95 
96 			getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
97 			outputbytes = OidSendFunctionCall(typsend, retval);
98 			pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
99 			pq_sendbytes(&buf, VARDATA(outputbytes),
100 						 VARSIZE(outputbytes) - VARHDRSZ);
101 			pfree(outputbytes);
102 		}
103 		else
104 			ereport(ERROR,
105 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
106 					 errmsg("unsupported format code: %d", format)));
107 	}
108 
109 	pq_endmessage(&buf);
110 }
111 
112 /*
113  * fetch_fp_info
114  *
115  * Performs catalog lookups to load a struct fp_info 'fip' for the
116  * function 'func_id'.
117  */
118 static void
fetch_fp_info(Oid func_id,struct fp_info * fip)119 fetch_fp_info(Oid func_id, struct fp_info *fip)
120 {
121 	HeapTuple	func_htp;
122 	Form_pg_proc pp;
123 
124 	Assert(fip != NULL);
125 
126 	/*
127 	 * Since the validity of this structure is determined by whether the
128 	 * funcid is OK, we clear the funcid here.  It must not be set to the
129 	 * correct value until we are about to return with a good struct fp_info,
130 	 * since we can be interrupted (i.e., with an ereport(ERROR, ...)) at any
131 	 * time.  [No longer really an issue since we don't save the struct
132 	 * fp_info across transactions anymore, but keep it anyway.]
133 	 */
134 	MemSet(fip, 0, sizeof(struct fp_info));
135 	fip->funcid = InvalidOid;
136 
137 	func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
138 	if (!HeapTupleIsValid(func_htp))
139 		ereport(ERROR,
140 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
141 				 errmsg("function with OID %u does not exist", func_id)));
142 	pp = (Form_pg_proc) GETSTRUCT(func_htp);
143 
144 	/* reject pg_proc entries that are unsafe to call via fastpath */
145 	if (pp->prokind != PROKIND_FUNCTION || pp->proretset)
146 		ereport(ERROR,
147 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
148 				 errmsg("cannot call function \"%s\" via fastpath interface",
149 						NameStr(pp->proname))));
150 
151 	/* watch out for catalog entries with more than FUNC_MAX_ARGS args */
152 	if (pp->pronargs > FUNC_MAX_ARGS)
153 		elog(ERROR, "function %s has more than %d arguments",
154 			 NameStr(pp->proname), FUNC_MAX_ARGS);
155 
156 	fip->namespace = pp->pronamespace;
157 	fip->rettype = pp->prorettype;
158 	memcpy(fip->argtypes, pp->proargtypes.values, pp->pronargs * sizeof(Oid));
159 	strlcpy(fip->fname, NameStr(pp->proname), NAMEDATALEN);
160 
161 	ReleaseSysCache(func_htp);
162 
163 	fmgr_info(func_id, &fip->flinfo);
164 
165 	/*
166 	 * This must be last!
167 	 */
168 	fip->funcid = func_id;
169 }
170 
171 
172 /*
173  * HandleFunctionRequest
174  *
175  * Server side of PQfn (fastpath function calls from the frontend).
176  * This corresponds to the libpq protocol symbol "F".
177  *
178  * INPUT:
179  *		postgres.c has already read the message body and will pass it in
180  *		msgBuf.
181  *
182  * Note: palloc()s done here and in the called function do not need to be
183  * cleaned up explicitly.  We are called from PostgresMain() in the
184  * MessageContext memory context, which will be automatically reset when
185  * control returns to PostgresMain.
186  */
187 void
HandleFunctionRequest(StringInfo msgBuf)188 HandleFunctionRequest(StringInfo msgBuf)
189 {
190 	LOCAL_FCINFO(fcinfo, FUNC_MAX_ARGS);
191 	Oid			fid;
192 	AclResult	aclresult;
193 	int16		rformat;
194 	Datum		retval;
195 	struct fp_info my_fp;
196 	struct fp_info *fip;
197 	bool		callit;
198 	bool		was_logged = false;
199 	char		msec_str[32];
200 
201 	/*
202 	 * We only accept COMMIT/ABORT if we are in an aborted transaction, and
203 	 * COMMIT/ABORT cannot be executed through the fastpath interface.
204 	 */
205 	if (IsAbortedTransactionBlockState())
206 		ereport(ERROR,
207 				(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
208 				 errmsg("current transaction is aborted, "
209 						"commands ignored until end of transaction block")));
210 
211 	/*
212 	 * Now that we know we are in a valid transaction, set snapshot in case
213 	 * needed by function itself or one of the datatype I/O routines.
214 	 */
215 	PushActiveSnapshot(GetTransactionSnapshot());
216 
217 	/*
218 	 * Begin parsing the buffer contents.
219 	 */
220 	fid = (Oid) pq_getmsgint(msgBuf, 4);	/* function oid */
221 
222 	/*
223 	 * There used to be a lame attempt at caching lookup info here. Now we
224 	 * just do the lookups on every call.
225 	 */
226 	fip = &my_fp;
227 	fetch_fp_info(fid, fip);
228 
229 	/* Log as soon as we have the function OID and name */
230 	if (log_statement == LOGSTMT_ALL)
231 	{
232 		ereport(LOG,
233 				(errmsg("fastpath function call: \"%s\" (OID %u)",
234 						fip->fname, fid)));
235 		was_logged = true;
236 	}
237 
238 	/*
239 	 * Check permission to access and call function.  Since we didn't go
240 	 * through a normal name lookup, we need to check schema usage too.
241 	 */
242 	aclresult = pg_namespace_aclcheck(fip->namespace, GetUserId(), ACL_USAGE);
243 	if (aclresult != ACLCHECK_OK)
244 		aclcheck_error(aclresult, OBJECT_SCHEMA,
245 					   get_namespace_name(fip->namespace));
246 	InvokeNamespaceSearchHook(fip->namespace, true);
247 
248 	aclresult = pg_proc_aclcheck(fid, GetUserId(), ACL_EXECUTE);
249 	if (aclresult != ACLCHECK_OK)
250 		aclcheck_error(aclresult, OBJECT_FUNCTION,
251 					   get_func_name(fid));
252 	InvokeFunctionExecuteHook(fid);
253 
254 	/*
255 	 * Prepare function call info block and insert arguments.
256 	 *
257 	 * Note: for now we pass collation = InvalidOid, so collation-sensitive
258 	 * functions can't be called this way.  Perhaps we should pass
259 	 * DEFAULT_COLLATION_OID, instead?
260 	 */
261 	InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
262 
263 	rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
264 
265 	/* Verify we reached the end of the message where expected. */
266 	pq_getmsgend(msgBuf);
267 
268 	/*
269 	 * If func is strict, must not call it for null args.
270 	 */
271 	callit = true;
272 	if (fip->flinfo.fn_strict)
273 	{
274 		int			i;
275 
276 		for (i = 0; i < fcinfo->nargs; i++)
277 		{
278 			if (fcinfo->args[i].isnull)
279 			{
280 				callit = false;
281 				break;
282 			}
283 		}
284 	}
285 
286 	if (callit)
287 	{
288 		/* Okay, do it ... */
289 		retval = FunctionCallInvoke(fcinfo);
290 	}
291 	else
292 	{
293 		fcinfo->isnull = true;
294 		retval = (Datum) 0;
295 	}
296 
297 	/* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
298 	CHECK_FOR_INTERRUPTS();
299 
300 	SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
301 
302 	/* We no longer need the snapshot */
303 	PopActiveSnapshot();
304 
305 	/*
306 	 * Emit duration logging if appropriate.
307 	 */
308 	switch (check_log_duration(msec_str, was_logged))
309 	{
310 		case 1:
311 			ereport(LOG,
312 					(errmsg("duration: %s ms", msec_str)));
313 			break;
314 		case 2:
315 			ereport(LOG,
316 					(errmsg("duration: %s ms  fastpath function call: \"%s\" (OID %u)",
317 							msec_str, fip->fname, fid)));
318 			break;
319 	}
320 }
321 
322 /*
323  * Parse function arguments in a 3.0 protocol message
324  *
325  * Argument values are loaded into *fcinfo, and the desired result format
326  * is returned.
327  */
328 static int16
parse_fcall_arguments(StringInfo msgBuf,struct fp_info * fip,FunctionCallInfo fcinfo)329 parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
330 					  FunctionCallInfo fcinfo)
331 {
332 	int			nargs;
333 	int			i;
334 	int			numAFormats;
335 	int16	   *aformats = NULL;
336 	StringInfoData abuf;
337 
338 	/* Get the argument format codes */
339 	numAFormats = pq_getmsgint(msgBuf, 2);
340 	if (numAFormats > 0)
341 	{
342 		aformats = (int16 *) palloc(numAFormats * sizeof(int16));
343 		for (i = 0; i < numAFormats; i++)
344 			aformats[i] = pq_getmsgint(msgBuf, 2);
345 	}
346 
347 	nargs = pq_getmsgint(msgBuf, 2);	/* # of arguments */
348 
349 	if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
350 		ereport(ERROR,
351 				(errcode(ERRCODE_PROTOCOL_VIOLATION),
352 				 errmsg("function call message contains %d arguments but function requires %d",
353 						nargs, fip->flinfo.fn_nargs)));
354 
355 	fcinfo->nargs = nargs;
356 
357 	if (numAFormats > 1 && numAFormats != nargs)
358 		ereport(ERROR,
359 				(errcode(ERRCODE_PROTOCOL_VIOLATION),
360 				 errmsg("function call message contains %d argument formats but %d arguments",
361 						numAFormats, nargs)));
362 
363 	initStringInfo(&abuf);
364 
365 	/*
366 	 * Copy supplied arguments into arg vector.
367 	 */
368 	for (i = 0; i < nargs; ++i)
369 	{
370 		int			argsize;
371 		int16		aformat;
372 
373 		argsize = pq_getmsgint(msgBuf, 4);
374 		if (argsize == -1)
375 		{
376 			fcinfo->args[i].isnull = true;
377 		}
378 		else
379 		{
380 			fcinfo->args[i].isnull = false;
381 			if (argsize < 0)
382 				ereport(ERROR,
383 						(errcode(ERRCODE_PROTOCOL_VIOLATION),
384 						 errmsg("invalid argument size %d in function call message",
385 								argsize)));
386 
387 			/* Reset abuf to empty, and insert raw data into it */
388 			resetStringInfo(&abuf);
389 			appendBinaryStringInfo(&abuf,
390 								   pq_getmsgbytes(msgBuf, argsize),
391 								   argsize);
392 		}
393 
394 		if (numAFormats > 1)
395 			aformat = aformats[i];
396 		else if (numAFormats > 0)
397 			aformat = aformats[0];
398 		else
399 			aformat = 0;		/* default = text */
400 
401 		if (aformat == 0)
402 		{
403 			Oid			typinput;
404 			Oid			typioparam;
405 			char	   *pstring;
406 
407 			getTypeInputInfo(fip->argtypes[i], &typinput, &typioparam);
408 
409 			/*
410 			 * Since stringinfo.c keeps a trailing null in place even for
411 			 * binary data, the contents of abuf are a valid C string.  We
412 			 * have to do encoding conversion before calling the typinput
413 			 * routine, though.
414 			 */
415 			if (argsize == -1)
416 				pstring = NULL;
417 			else
418 				pstring = pg_client_to_server(abuf.data, argsize);
419 
420 			fcinfo->args[i].value = OidInputFunctionCall(typinput, pstring,
421 														 typioparam, -1);
422 			/* Free result of encoding conversion, if any */
423 			if (pstring && pstring != abuf.data)
424 				pfree(pstring);
425 		}
426 		else if (aformat == 1)
427 		{
428 			Oid			typreceive;
429 			Oid			typioparam;
430 			StringInfo	bufptr;
431 
432 			/* Call the argument type's binary input converter */
433 			getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
434 
435 			if (argsize == -1)
436 				bufptr = NULL;
437 			else
438 				bufptr = &abuf;
439 
440 			fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, bufptr,
441 														   typioparam, -1);
442 
443 			/* Trouble if it didn't eat the whole buffer */
444 			if (argsize != -1 && abuf.cursor != abuf.len)
445 				ereport(ERROR,
446 						(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
447 						 errmsg("incorrect binary data format in function argument %d",
448 								i + 1)));
449 		}
450 		else
451 			ereport(ERROR,
452 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
453 					 errmsg("unsupported format code: %d", aformat)));
454 	}
455 
456 	/* Return result format code */
457 	return (int16) pq_getmsgint(msgBuf, 2);
458 }
459