Coverage Report

Created: 2025-08-12 06:43

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