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