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 "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
67 2084 : SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
68 : {
69 : StringInfoData buf;
70 :
71 2084 : pq_beginmessage(&buf, PqMsg_FunctionCallResponse);
72 :
73 2084 : if (isnull)
74 : {
75 0 : pq_sendint32(&buf, -1);
76 : }
77 : else
78 : {
79 2084 : if (format == 0)
80 : {
81 : Oid typoutput;
82 : bool typisvarlena;
83 : char *outputstr;
84 :
85 0 : getTypeOutputInfo(rettype, &typoutput, &typisvarlena);
86 0 : outputstr = OidOutputFunctionCall(typoutput, retval);
87 0 : pq_sendcountedtext(&buf, outputstr, strlen(outputstr));
88 0 : pfree(outputstr);
89 : }
90 2084 : else if (format == 1)
91 : {
92 : Oid typsend;
93 : bool typisvarlena;
94 : bytea *outputbytes;
95 :
96 2084 : getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
97 2084 : outputbytes = OidSendFunctionCall(typsend, retval);
98 2084 : pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
99 2084 : pq_sendbytes(&buf, VARDATA(outputbytes),
100 2084 : VARSIZE(outputbytes) - VARHDRSZ);
101 2084 : pfree(outputbytes);
102 : }
103 : else
104 0 : ereport(ERROR,
105 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
106 : errmsg("unsupported format code: %d", format)));
107 : }
108 :
109 2084 : pq_endmessage(&buf);
110 2084 : }
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
119 2084 : 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 139628 : MemSet(fip, 0, sizeof(struct fp_info));
135 2084 : fip->funcid = InvalidOid;
136 :
137 2084 : func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
138 2084 : if (!HeapTupleIsValid(func_htp))
139 0 : ereport(ERROR,
140 : (errcode(ERRCODE_UNDEFINED_FUNCTION),
141 : errmsg("function with OID %u does not exist", func_id)));
142 2084 : pp = (Form_pg_proc) GETSTRUCT(func_htp);
143 :
144 : /* reject pg_proc entries that are unsafe to call via fastpath */
145 2084 : if (pp->prokind != PROKIND_FUNCTION || pp->proretset)
146 0 : 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 2084 : if (pp->pronargs > FUNC_MAX_ARGS)
153 0 : elog(ERROR, "function %s has more than %d arguments",
154 : NameStr(pp->proname), FUNC_MAX_ARGS);
155 :
156 2084 : fip->namespace = pp->pronamespace;
157 2084 : fip->rettype = pp->prorettype;
158 2084 : memcpy(fip->argtypes, pp->proargtypes.values, pp->pronargs * sizeof(Oid));
159 2084 : strlcpy(fip->fname, NameStr(pp->proname), NAMEDATALEN);
160 :
161 2084 : ReleaseSysCache(func_htp);
162 :
163 2084 : fmgr_info(func_id, &fip->flinfo);
164 :
165 : /*
166 : * This must be last!
167 : */
168 2084 : fip->funcid = func_id;
169 2084 : }
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
188 2084 : HandleFunctionRequest(StringInfo msgBuf)
189 : {
190 2084 : 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 2084 : 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 2084 : if (IsAbortedTransactionBlockState())
206 0 : 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 2084 : PushActiveSnapshot(GetTransactionSnapshot());
216 :
217 : /*
218 : * Begin parsing the buffer contents.
219 : */
220 2084 : 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 2084 : fip = &my_fp;
227 2084 : fetch_fp_info(fid, fip);
228 :
229 : /* Log as soon as we have the function OID and name */
230 2084 : if (log_statement == LOGSTMT_ALL)
231 : {
232 1566 : ereport(LOG,
233 : (errmsg("fastpath function call: \"%s\" (OID %u)",
234 : fip->fname, fid)));
235 1566 : 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 2084 : aclresult = object_aclcheck(NamespaceRelationId, fip->namespace, GetUserId(), ACL_USAGE);
243 2084 : if (aclresult != ACLCHECK_OK)
244 0 : aclcheck_error(aclresult, OBJECT_SCHEMA,
245 0 : get_namespace_name(fip->namespace));
246 2084 : InvokeNamespaceSearchHook(fip->namespace, true);
247 :
248 2084 : aclresult = object_aclcheck(ProcedureRelationId, fid, GetUserId(), ACL_EXECUTE);
249 2084 : if (aclresult != ACLCHECK_OK)
250 0 : aclcheck_error(aclresult, OBJECT_FUNCTION,
251 0 : get_func_name(fid));
252 2084 : 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 2084 : InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
262 :
263 2084 : rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
264 :
265 : /* Verify we reached the end of the message where expected. */
266 2084 : pq_getmsgend(msgBuf);
267 :
268 : /*
269 : * If func is strict, must not call it for null args.
270 : */
271 2084 : callit = true;
272 2084 : if (fip->flinfo.fn_strict)
273 : {
274 : int i;
275 :
276 6046 : for (i = 0; i < fcinfo->nargs; i++)
277 : {
278 3962 : if (fcinfo->args[i].isnull)
279 : {
280 0 : callit = false;
281 0 : break;
282 : }
283 : }
284 : }
285 :
286 2084 : if (callit)
287 : {
288 : /* Okay, do it ... */
289 2084 : retval = FunctionCallInvoke(fcinfo);
290 : }
291 : else
292 : {
293 0 : fcinfo->isnull = true;
294 0 : retval = (Datum) 0;
295 : }
296 :
297 : /* ensure we do at least one CHECK_FOR_INTERRUPTS per function call */
298 2084 : CHECK_FOR_INTERRUPTS();
299 :
300 2084 : SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
301 :
302 : /* We no longer need the snapshot */
303 2084 : PopActiveSnapshot();
304 :
305 : /*
306 : * Emit duration logging if appropriate.
307 : */
308 2084 : switch (check_log_duration(msec_str, was_logged))
309 : {
310 0 : case 1:
311 0 : ereport(LOG,
312 : (errmsg("duration: %s ms", msec_str)));
313 0 : break;
314 0 : case 2:
315 0 : ereport(LOG,
316 : (errmsg("duration: %s ms fastpath function call: \"%s\" (OID %u)",
317 : msec_str, fip->fname, fid)));
318 0 : break;
319 : }
320 2084 : }
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
329 2084 : parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
330 : FunctionCallInfo fcinfo)
331 : {
332 : int nargs;
333 : int i;
334 : int numAFormats;
335 2084 : int16 *aformats = NULL;
336 : StringInfoData abuf;
337 :
338 : /* Get the argument format codes */
339 2084 : numAFormats = pq_getmsgint(msgBuf, 2);
340 2084 : if (numAFormats > 0)
341 : {
342 2084 : aformats = (int16 *) palloc(numAFormats * sizeof(int16));
343 4168 : for (i = 0; i < numAFormats; i++)
344 2084 : aformats[i] = pq_getmsgint(msgBuf, 2);
345 : }
346 :
347 2084 : nargs = pq_getmsgint(msgBuf, 2); /* # of arguments */
348 :
349 2084 : if (fip->flinfo.fn_nargs != nargs || nargs > FUNC_MAX_ARGS)
350 0 : 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 2084 : fcinfo->nargs = nargs;
356 :
357 2084 : if (numAFormats > 1 && numAFormats != nargs)
358 0 : ereport(ERROR,
359 : (errcode(ERRCODE_PROTOCOL_VIOLATION),
360 : errmsg("function call message contains %d argument formats but %d arguments",
361 : numAFormats, nargs)));
362 :
363 2084 : initStringInfo(&abuf);
364 :
365 : /*
366 : * Copy supplied arguments into arg vector.
367 : */
368 6046 : for (i = 0; i < nargs; ++i)
369 : {
370 : int argsize;
371 : int16 aformat;
372 :
373 3962 : argsize = pq_getmsgint(msgBuf, 4);
374 3962 : if (argsize == -1)
375 : {
376 0 : fcinfo->args[i].isnull = true;
377 : }
378 : else
379 : {
380 3962 : fcinfo->args[i].isnull = false;
381 3962 : if (argsize < 0)
382 0 : 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 3962 : resetStringInfo(&abuf);
389 3962 : appendBinaryStringInfo(&abuf,
390 3962 : pq_getmsgbytes(msgBuf, argsize),
391 : argsize);
392 : }
393 :
394 3962 : if (numAFormats > 1)
395 0 : aformat = aformats[i];
396 3962 : else if (numAFormats > 0)
397 3962 : aformat = aformats[0];
398 : else
399 0 : aformat = 0; /* default = text */
400 :
401 3962 : if (aformat == 0)
402 : {
403 : Oid typinput;
404 : Oid typioparam;
405 : char *pstring;
406 :
407 0 : 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 0 : if (argsize == -1)
416 0 : pstring = NULL;
417 : else
418 0 : pstring = pg_client_to_server(abuf.data, argsize);
419 :
420 0 : fcinfo->args[i].value = OidInputFunctionCall(typinput, pstring,
421 : typioparam, -1);
422 : /* Free result of encoding conversion, if any */
423 0 : if (pstring && pstring != abuf.data)
424 0 : pfree(pstring);
425 : }
426 3962 : 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 3962 : getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
434 :
435 3962 : if (argsize == -1)
436 0 : bufptr = NULL;
437 : else
438 3962 : bufptr = &abuf;
439 :
440 3962 : fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, bufptr,
441 : typioparam, -1);
442 :
443 : /* Trouble if it didn't eat the whole buffer */
444 3962 : if (argsize != -1 && abuf.cursor != abuf.len)
445 0 : 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 0 : ereport(ERROR,
452 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
453 : errmsg("unsupported format code: %d", aformat)));
454 : }
455 :
456 : /* Return result format code */
457 2084 : return (int16) pq_getmsgint(msgBuf, 2);
458 : }
|