Age Owner Branch data TLA 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
8511 tgl@sss.pgh.pa.us 69 :CBC 1343 : SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
70 : : {
71 : : StringInfoData buf;
72 : :
1101 nathan@postgresql.or 73 : 1343 : pq_beginmessage(&buf, PqMsg_FunctionCallResponse);
74 : :
8511 tgl@sss.pgh.pa.us 75 [ - + ]: 1343 : if (isnull)
76 : : {
2002 heikki.linnakangas@i 77 :UBC 0 : pq_sendint32(&buf, -1);
78 : : }
79 : : else
80 : : {
8511 tgl@sss.pgh.pa.us 81 [ - + ]:CBC 1343 : if (format == 0)
82 : : {
83 : : Oid typoutput;
84 : : bool typisvarlena;
85 : : char *outputstr;
86 : :
7788 tgl@sss.pgh.pa.us 87 :UBC 0 : getTypeOutputInfo(rettype, &typoutput, &typisvarlena);
7450 88 : 0 : outputstr = OidOutputFunctionCall(typoutput, retval);
906 heikki.linnakangas@i 89 : 0 : pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
8511 tgl@sss.pgh.pa.us 90 : 0 : pfree(outputstr);
91 : : }
8511 tgl@sss.pgh.pa.us 92 [ + - ]:CBC 1343 : else if (format == 1)
93 : : {
94 : : Oid typsend;
95 : : bool typisvarlena;
96 : : bytea *outputbytes;
97 : :
7788 98 : 1343 : getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
7450 99 : 1343 : outputbytes = OidSendFunctionCall(typsend, retval);
3242 andres@anarazel.de 100 : 1343 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
8511 tgl@sss.pgh.pa.us 101 : 1343 : pq_sendbytes(&buf, VARDATA(outputbytes),
102 : 1343 : VARSIZE(outputbytes) - VARHDRSZ);
103 : 1343 : pfree(outputbytes);
104 : : }
105 : : else
8437 tgl@sss.pgh.pa.us 106 [ # # ]:UBC 0 : ereport(ERROR,
107 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
108 : : errmsg("unsupported format code: %d", format)));
109 : : }
110 : :
9986 tgl@sss.pgh.pa.us 111 :CBC 1343 : pq_endmessage(&buf);
11006 scrappy@hub.org 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
3354 tgl@sss.pgh.pa.us 121 : 1343 : fetch_fp_info(Oid func_id, struct fp_info *fip)
122 : : {
123 : : HeapTuple func_htp;
124 : : Form_pg_proc pp;
125 : :
8268 neilc@samurai.com 126 [ - + ]: 1343 : 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 : : */
7821 tgl@sss.pgh.pa.us 136 [ + - + - : 89981 : MemSet(fip, 0, sizeof(struct fp_info));
+ - + - +
+ ]
10581 bruce@momjian.us 137 : 1343 : fip->funcid = InvalidOid;
138 : :
6038 rhaas@postgresql.org 139 : 1343 : func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
10581 bruce@momjian.us 140 [ - + ]: 1343 : if (!HeapTupleIsValid(func_htp))
8437 tgl@sss.pgh.pa.us 141 [ # # ]:UBC 0 : ereport(ERROR,
142 : : (errcode(ERRCODE_UNDEFINED_FUNCTION),
143 : : errmsg("function with OID %u does not exist", func_id)));
10581 bruce@momjian.us 144 :CBC 1343 : pp = (Form_pg_proc) GETSTRUCT(func_htp);
145 : :
146 : : /* reject pg_proc entries that are unsafe to call via fastpath */
1945 tgl@sss.pgh.pa.us 147 [ + - - + ]: 1343 : if (pp->prokind != PROKIND_FUNCTION || pp->proretset)
1945 tgl@sss.pgh.pa.us 148 [ # # ]:UBC 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 */
7821 tgl@sss.pgh.pa.us 154 [ - + ]:CBC 1343 : if (pp->pronargs > FUNC_MAX_ARGS)
7821 tgl@sss.pgh.pa.us 155 [ # # ]:UBC 0 : elog(ERROR, "function %s has more than %d arguments",
156 : : NameStr(pp->proname), FUNC_MAX_ARGS);
157 : :
8511 tgl@sss.pgh.pa.us 158 :CBC 1343 : fip->namespace = pp->pronamespace;
159 : 1343 : fip->rettype = pp->prorettype;
7821 160 : 1343 : memcpy(fip->argtypes, pp->proargtypes.values, pp->pronargs * sizeof(Oid));
7252 161 : 1343 : strlcpy(fip->fname, NameStr(pp->proname), NAMEDATALEN);
162 : :
9415 163 : 1343 : ReleaseSysCache(func_htp);
164 : :
1945 165 : 1343 : fmgr_info(func_id, &fip->flinfo);
166 : :
167 : : /*
168 : : * This must be last!
169 : : */
10581 bruce@momjian.us 170 : 1343 : fip->funcid = func_id;
11006 scrappy@hub.org 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
8531 tgl@sss.pgh.pa.us 190 : 1343 : HandleFunctionRequest(StringInfo msgBuf)
191 : : {
2770 andres@anarazel.de 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;
7293 tgl@sss.pgh.pa.us 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 : : */
8531 207 [ - + ]: 1343 : if (IsAbortedTransactionBlockState())
8437 tgl@sss.pgh.pa.us 208 [ # # ]:UBC 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 : : */
6681 alvherre@alvh.no-ip. 217 :CBC 1343 : PushActiveSnapshot(GetTransactionSnapshot());
218 : :
219 : : /*
220 : : * Begin parsing the buffer contents.
221 : : */
3354 tgl@sss.pgh.pa.us 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 : : */
9218 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 */
7252 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 : : */
1383 peter@eisentraut.org 244 : 1343 : aclresult = object_aclcheck(NamespaceRelationId, fip->namespace, GetUserId(), ACL_USAGE);
8511 tgl@sss.pgh.pa.us 245 [ - + ]: 1343 : if (aclresult != ACLCHECK_OK)
3190 peter_e@gmx.net 246 :UBC 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
8427 tgl@sss.pgh.pa.us 247 : 0 : get_namespace_name(fip->namespace));
4892 rhaas@postgresql.org 248 [ - + ]:CBC 1343 : InvokeNamespaceSearchHook(fip->namespace, true);
249 : :
1383 peter@eisentraut.org 250 : 1343 : aclresult = object_aclcheck(ProcedureRelationId, fid, GetUserId(), ACL_EXECUTE);
8531 tgl@sss.pgh.pa.us 251 [ - + ]: 1343 : if (aclresult != ACLCHECK_OK)
3190 peter_e@gmx.net 252 :UBC 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
8427 tgl@sss.pgh.pa.us 253 : 0 : get_func_name(fid));
4885 rhaas@postgresql.org 254 [ - + ]:CBC 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 : : */
2770 andres@anarazel.de 263 : 1343 : InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
264 : :
2002 heikki.linnakangas@i 265 : 1343 : rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
266 : :
267 : : /* Verify we reached the end of the message where expected. */
8512 tgl@sss.pgh.pa.us 268 : 1343 : pq_getmsgend(msgBuf);
269 : :
270 : : /*
271 : : * If func is strict, must not call it for null args.
272 : : */
8511 273 : 1343 : callit = true;
274 [ + - ]: 1343 : if (fip->flinfo.fn_strict)
275 : : {
276 : : int i;
277 : :
2770 andres@anarazel.de 278 [ + + ]: 3906 : for (i = 0; i < fcinfo->nargs; i++)
279 : : {
280 [ - + ]: 2563 : if (fcinfo->args[i].isnull)
281 : : {
8511 tgl@sss.pgh.pa.us 282 :UBC 0 : callit = false;
283 : 0 : break;
284 : : }
285 : : }
286 : : }
287 : :
8511 tgl@sss.pgh.pa.us 288 [ + - ]:CBC 1343 : if (callit)
289 : : {
290 : : /* Okay, do it ... */
2770 andres@anarazel.de 291 : 1343 : retval = FunctionCallInvoke(fcinfo);
292 : : }
293 : : else
294 : : {
2770 andres@anarazel.de 295 :UBC 0 : fcinfo->isnull = true;
8511 tgl@sss.pgh.pa.us 296 : 0 : retval = (Datum) 0;
297 : : }
298 : :
299 : : /* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
7379 tgl@sss.pgh.pa.us 300 [ - + ]:CBC 1343 : CHECK_FOR_INTERRUPTS();
301 : :
2770 andres@anarazel.de 302 : 1343 : SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
303 : :
304 : : /* We no longer need the snapshot */
6681 alvherre@alvh.no-ip. 305 : 1343 : PopActiveSnapshot();
306 : :
307 : : /*
308 : : * Emit duration logging if appropriate.
309 : : */
7293 tgl@sss.pgh.pa.us 310 [ - - + ]: 1343 : switch (check_log_duration(msec_str, was_logged))
311 : : {
7293 tgl@sss.pgh.pa.us 312 :UBC 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 : : }
8512 tgl@sss.pgh.pa.us 322 :CBC 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
3354 331 : 1343 : parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
332 : : FunctionCallInfo fcinfo)
333 : : {
334 : : int nargs;
335 : : int i;
336 : : int numAFormats;
8512 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 : : {
10 michael@paquier.xyz 344 :GNC 1343 : aformats = palloc_array(int16, numAFormats);
8512 tgl@sss.pgh.pa.us 345 [ + + ]:CBC 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 : :
9587 351 [ + - - + ]: 1343 : if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
8437 tgl@sss.pgh.pa.us 352 [ # # ]:UBC 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 : :
8512 tgl@sss.pgh.pa.us 357 :CBC 1343 : fcinfo->nargs = nargs;
358 : :
8511 359 [ - + - - ]: 1343 : if (numAFormats > 1 && numAFormats != nargs)
8437 tgl@sss.pgh.pa.us 360 [ # # ]:UBC 0 : ereport(ERROR,
361 : : (errcode(ERRCODE_PROTOCOL_VIOLATION),
362 : : errmsg("function call message contains %d argument formats but %d arguments",
363 : : numAFormats, nargs)));
364 : :
8511 tgl@sss.pgh.pa.us 365 :CBC 1343 : initStringInfo(&abuf);
366 : :
367 : : /*
368 : : * Copy supplied arguments into arg vector.
369 : : */
9587 370 [ + + ]: 3906 : for (i = 0; i < nargs; ++i)
371 : : {
372 : : int argsize;
373 : : int16 aformat;
374 : :
8531 375 : 2563 : argsize = pq_getmsgint(msgBuf, 4);
8511 376 [ - + ]: 2563 : if (argsize == -1)
377 : : {
2770 andres@anarazel.de 378 :UBC 0 : fcinfo->args[i].isnull = true;
379 : : }
380 : : else
381 : : {
2770 andres@anarazel.de 382 :CBC 2563 : fcinfo->args[i].isnull = false;
7450 tgl@sss.pgh.pa.us 383 [ - + ]: 2563 : if (argsize < 0)
7450 tgl@sss.pgh.pa.us 384 [ # # ]:UBC 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 */
7117 neilc@samurai.com 390 :CBC 2563 : resetStringInfo(&abuf);
7450 tgl@sss.pgh.pa.us 391 : 2563 : appendBinaryStringInfo(&abuf,
392 : 2563 : pq_getmsgbytes(msgBuf, argsize),
393 : : argsize);
394 : : }
395 : :
8511 396 [ - + ]: 2563 : if (numAFormats > 1)
8511 tgl@sss.pgh.pa.us 397 :UBC 0 : aformat = aformats[i];
8511 tgl@sss.pgh.pa.us 398 [ + - ]:CBC 2563 : else if (numAFormats > 0)
399 : 2563 : aformat = aformats[0];
400 : : else
8511 tgl@sss.pgh.pa.us 401 :UBC 0 : aformat = 0; /* default = text */
402 : :
8511 tgl@sss.pgh.pa.us 403 [ - + ]:CBC 2563 : if (aformat == 0)
404 : : {
405 : : Oid typinput;
406 : : Oid typioparam;
407 : : char *pstring;
408 : :
8117 tgl@sss.pgh.pa.us 409 :UBC 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 : : */
7450 417 [ # # ]: 0 : if (argsize == -1)
418 : 0 : pstring = NULL;
419 : : else
420 : 0 : pstring = pg_client_to_server(abuf.data, argsize);
421 : :
2770 andres@anarazel.de 422 : 0 : fcinfo->args[i].value = OidInputFunctionCall(typinput, pstring,
423 : : typioparam, -1);
424 : : /* Free result of encoding conversion, if any */
7450 tgl@sss.pgh.pa.us 425 [ # # # # ]: 0 : if (pstring && pstring != abuf.data)
8511 426 : 0 : pfree(pstring);
427 : : }
8511 tgl@sss.pgh.pa.us 428 [ + - ]:CBC 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 */
8117 435 : 2563 : getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
436 : :
7450 437 [ - + ]: 2563 : if (argsize == -1)
7450 tgl@sss.pgh.pa.us 438 :UBC 0 : bufptr = NULL;
439 : : else
7450 tgl@sss.pgh.pa.us 440 :CBC 2563 : bufptr = &abuf;
441 : :
2770 andres@anarazel.de 442 : 2563 : fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, bufptr,
443 : : typioparam, -1);
444 : :
445 : : /* Trouble if it didn't eat the whole buffer */
7450 tgl@sss.pgh.pa.us 446 [ + - - + ]: 2563 : if (argsize != -1 && abuf.cursor != abuf.len)
8437 tgl@sss.pgh.pa.us 447 [ # # ]:UBC 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 */
8511 tgl@sss.pgh.pa.us 459 :CBC 1343 : return (int16) pq_getmsgint(msgBuf, 2);
460 : : }
|