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