LCOV - code coverage report
Current view: top level - src/backend/tcop - fastpath.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 69.5 % 131 91
Test Date: 2026-03-14 18:14:56 Functions: 100.0 % 4 4
Legend: Lines:     hit not hit

            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.
      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         1070 : SendFunctionResult(Datum retval, bool isnull, Oid rettype, int16 format)
      68              : {
      69              :     StringInfoData buf;
      70              : 
      71         1070 :     pq_beginmessage(&buf, PqMsg_FunctionCallResponse);
      72              : 
      73         1070 :     if (isnull)
      74              :     {
      75            0 :         pq_sendint32(&buf, -1);
      76              :     }
      77              :     else
      78              :     {
      79         1070 :         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         1070 :         else if (format == 1)
      91              :         {
      92              :             Oid         typsend;
      93              :             bool        typisvarlena;
      94              :             bytea      *outputbytes;
      95              : 
      96         1070 :             getTypeBinaryOutputInfo(rettype, &typsend, &typisvarlena);
      97         1070 :             outputbytes = OidSendFunctionCall(typsend, retval);
      98         1070 :             pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
      99         1070 :             pq_sendbytes(&buf, VARDATA(outputbytes),
     100         1070 :                          VARSIZE(outputbytes) - VARHDRSZ);
     101         1070 :             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         1070 :     pq_endmessage(&buf);
     110         1070 : }
     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         1070 : 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        71690 :     MemSet(fip, 0, sizeof(struct fp_info));
     135         1070 :     fip->funcid = InvalidOid;
     136              : 
     137         1070 :     func_htp = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
     138         1070 :     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         1070 :     pp = (Form_pg_proc) GETSTRUCT(func_htp);
     143              : 
     144              :     /* reject pg_proc entries that are unsafe to call via fastpath */
     145         1070 :     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         1070 :     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         1070 :     fip->namespace = pp->pronamespace;
     157         1070 :     fip->rettype = pp->prorettype;
     158         1070 :     memcpy(fip->argtypes, pp->proargtypes.values, pp->pronargs * sizeof(Oid));
     159         1070 :     strlcpy(fip->fname, NameStr(pp->proname), NAMEDATALEN);
     160              : 
     161         1070 :     ReleaseSysCache(func_htp);
     162              : 
     163         1070 :     fmgr_info(func_id, &fip->flinfo);
     164              : 
     165              :     /*
     166              :      * This must be last!
     167              :      */
     168         1070 :     fip->funcid = func_id;
     169         1070 : }
     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         1070 : HandleFunctionRequest(StringInfo msgBuf)
     189              : {
     190         1070 :     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         1070 :     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         1070 :     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         1070 :     PushActiveSnapshot(GetTransactionSnapshot());
     216              : 
     217              :     /*
     218              :      * Begin parsing the buffer contents.
     219              :      */
     220         1070 :     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         1070 :     fip = &my_fp;
     227         1070 :     fetch_fp_info(fid, fip);
     228              : 
     229              :     /* Log as soon as we have the function OID and name */
     230         1070 :     if (log_statement == LOGSTMT_ALL)
     231              :     {
     232          530 :         ereport(LOG,
     233              :                 (errmsg("fastpath function call: \"%s\" (OID %u)",
     234              :                         fip->fname, fid)));
     235          530 :         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         1070 :     aclresult = object_aclcheck(NamespaceRelationId, fip->namespace, GetUserId(), ACL_USAGE);
     243         1070 :     if (aclresult != ACLCHECK_OK)
     244            0 :         aclcheck_error(aclresult, OBJECT_SCHEMA,
     245            0 :                        get_namespace_name(fip->namespace));
     246         1070 :     InvokeNamespaceSearchHook(fip->namespace, true);
     247              : 
     248         1070 :     aclresult = object_aclcheck(ProcedureRelationId, fid, GetUserId(), ACL_EXECUTE);
     249         1070 :     if (aclresult != ACLCHECK_OK)
     250            0 :         aclcheck_error(aclresult, OBJECT_FUNCTION,
     251            0 :                        get_func_name(fid));
     252         1070 :     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         1070 :     InitFunctionCallInfoData(*fcinfo, &fip->flinfo, 0, InvalidOid, NULL, NULL);
     262              : 
     263         1070 :     rformat = parse_fcall_arguments(msgBuf, fip, fcinfo);
     264              : 
     265              :     /* Verify we reached the end of the message where expected. */
     266         1070 :     pq_getmsgend(msgBuf);
     267              : 
     268              :     /*
     269              :      * If func is strict, must not call it for null args.
     270              :      */
     271         1070 :     callit = true;
     272         1070 :     if (fip->flinfo.fn_strict)
     273              :     {
     274              :         int         i;
     275              : 
     276         3100 :         for (i = 0; i < fcinfo->nargs; i++)
     277              :         {
     278         2030 :             if (fcinfo->args[i].isnull)
     279              :             {
     280            0 :                 callit = false;
     281            0 :                 break;
     282              :             }
     283              :         }
     284              :     }
     285              : 
     286         1070 :     if (callit)
     287              :     {
     288              :         /* Okay, do it ... */
     289         1070 :         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         1070 :     CHECK_FOR_INTERRUPTS();
     299              : 
     300         1070 :     SendFunctionResult(retval, fcinfo->isnull, fip->rettype, rformat);
     301              : 
     302              :     /* We no longer need the snapshot */
     303         1070 :     PopActiveSnapshot();
     304              : 
     305              :     /*
     306              :      * Emit duration logging if appropriate.
     307              :      */
     308         1070 :     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         1070 : }
     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         1070 : parse_fcall_arguments(StringInfo msgBuf, struct fp_info *fip,
     330              :                       FunctionCallInfo fcinfo)
     331              : {
     332              :     int         nargs;
     333              :     int         i;
     334              :     int         numAFormats;
     335         1070 :     int16      *aformats = NULL;
     336              :     StringInfoData abuf;
     337              : 
     338              :     /* Get the argument format codes */
     339         1070 :     numAFormats = pq_getmsgint(msgBuf, 2);
     340         1070 :     if (numAFormats > 0)
     341              :     {
     342         1070 :         aformats = (int16 *) palloc(numAFormats * sizeof(int16));
     343         2140 :         for (i = 0; i < numAFormats; i++)
     344         1070 :             aformats[i] = pq_getmsgint(msgBuf, 2);
     345              :     }
     346              : 
     347         1070 :     nargs = pq_getmsgint(msgBuf, 2);    /* # of arguments */
     348              : 
     349         1070 :     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         1070 :     fcinfo->nargs = nargs;
     356              : 
     357         1070 :     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         1070 :     initStringInfo(&abuf);
     364              : 
     365              :     /*
     366              :      * Copy supplied arguments into arg vector.
     367              :      */
     368         3100 :     for (i = 0; i < nargs; ++i)
     369              :     {
     370              :         int         argsize;
     371              :         int16       aformat;
     372              : 
     373         2030 :         argsize = pq_getmsgint(msgBuf, 4);
     374         2030 :         if (argsize == -1)
     375              :         {
     376            0 :             fcinfo->args[i].isnull = true;
     377              :         }
     378              :         else
     379              :         {
     380         2030 :             fcinfo->args[i].isnull = false;
     381         2030 :             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         2030 :             resetStringInfo(&abuf);
     389         2030 :             appendBinaryStringInfo(&abuf,
     390         2030 :                                    pq_getmsgbytes(msgBuf, argsize),
     391              :                                    argsize);
     392              :         }
     393              : 
     394         2030 :         if (numAFormats > 1)
     395            0 :             aformat = aformats[i];
     396         2030 :         else if (numAFormats > 0)
     397         2030 :             aformat = aformats[0];
     398              :         else
     399            0 :             aformat = 0;        /* default = text */
     400              : 
     401         2030 :         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         2030 :         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         2030 :             getTypeBinaryInputInfo(fip->argtypes[i], &typreceive, &typioparam);
     434              : 
     435         2030 :             if (argsize == -1)
     436            0 :                 bufptr = NULL;
     437              :             else
     438         2030 :                 bufptr = &abuf;
     439              : 
     440         2030 :             fcinfo->args[i].value = OidReceiveFunctionCall(typreceive, bufptr,
     441              :                                                            typioparam, -1);
     442              : 
     443              :             /* Trouble if it didn't eat the whole buffer */
     444         2030 :             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         1070 :     return (int16) pq_getmsgint(msgBuf, 2);
     458              : }
        

Generated by: LCOV version 2.0-1