LCOV - differential code coverage report
Current view: top level - src/backend/tcop - fastpath.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 69.7 % 132 92 12 16 12 3 63 2 24 25 56
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 4 4 4 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a