Coverage Report

Created: 2026-11-09 07:20

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