LCOV - code coverage report
Current view: top level - src/backend/nodes - params.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 119 131 90.8 %
Date: 2025-12-04 17:18:24 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * params.c
       4             :  *    Support for finding the values associated with Param nodes.
       5             :  *
       6             :  *
       7             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/nodes/params.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : 
      16             : #include "postgres.h"
      17             : 
      18             : #include "access/xact.h"
      19             : #include "fmgr.h"
      20             : #include "mb/stringinfo_mb.h"
      21             : #include "nodes/params.h"
      22             : #include "parser/parse_node.h"
      23             : #include "storage/shmem.h"
      24             : #include "utils/datum.h"
      25             : #include "utils/lsyscache.h"
      26             : #include "utils/memutils.h"
      27             : 
      28             : 
      29             : static void paramlist_parser_setup(ParseState *pstate, void *arg);
      30             : static Node *paramlist_param_ref(ParseState *pstate, ParamRef *pref);
      31             : 
      32             : 
      33             : /*
      34             :  * Allocate and initialize a new ParamListInfo structure.
      35             :  *
      36             :  * To make a new structure for the "dynamic" way (with hooks), pass 0 for
      37             :  * numParams and set numParams manually.
      38             :  *
      39             :  * A default parserSetup function is supplied automatically.  Callers may
      40             :  * override it if they choose.  (Note that most use-cases for ParamListInfos
      41             :  * will never use the parserSetup function anyway.)
      42             :  */
      43             : ParamListInfo
      44      164168 : makeParamList(int numParams)
      45             : {
      46             :     ParamListInfo retval;
      47             :     Size        size;
      48             : 
      49      164168 :     size = offsetof(ParamListInfoData, params) +
      50             :         numParams * sizeof(ParamExternData);
      51             : 
      52      164168 :     retval = (ParamListInfo) palloc(size);
      53      164168 :     retval->paramFetch = NULL;
      54      164168 :     retval->paramFetchArg = NULL;
      55      164168 :     retval->paramCompile = NULL;
      56      164168 :     retval->paramCompileArg = NULL;
      57      164168 :     retval->parserSetup = paramlist_parser_setup;
      58      164168 :     retval->parserSetupArg = retval;
      59      164168 :     retval->paramValuesStr = NULL;
      60      164168 :     retval->numParams = numParams;
      61             : 
      62      164168 :     return retval;
      63             : }
      64             : 
      65             : /*
      66             :  * Copy a ParamListInfo structure.
      67             :  *
      68             :  * The result is allocated in CurrentMemoryContext.
      69             :  *
      70             :  * Note: the intent of this function is to make a static, self-contained
      71             :  * set of parameter values.  If dynamic parameter hooks are present, we
      72             :  * intentionally do not copy them into the result.  Rather, we forcibly
      73             :  * instantiate all available parameter values and copy the datum values.
      74             :  *
      75             :  * paramValuesStr is not copied, either.
      76             :  */
      77             : ParamListInfo
      78        5168 : copyParamList(ParamListInfo from)
      79             : {
      80             :     ParamListInfo retval;
      81             : 
      82        5168 :     if (from == NULL || from->numParams <= 0)
      83        3742 :         return NULL;
      84             : 
      85        1426 :     retval = makeParamList(from->numParams);
      86             : 
      87        6752 :     for (int i = 0; i < from->numParams; i++)
      88             :     {
      89             :         ParamExternData *oprm;
      90        5326 :         ParamExternData *nprm = &retval->params[i];
      91             :         ParamExternData prmdata;
      92             :         int16       typLen;
      93             :         bool        typByVal;
      94             : 
      95             :         /* give hook a chance in case parameter is dynamic */
      96        5326 :         if (from->paramFetch != NULL)
      97        4154 :             oprm = from->paramFetch(from, i + 1, false, &prmdata);
      98             :         else
      99        1172 :             oprm = &from->params[i];
     100             : 
     101             :         /* flat-copy the parameter info */
     102        5326 :         *nprm = *oprm;
     103             : 
     104             :         /* need datumCopy in case it's a pass-by-reference datatype */
     105        5326 :         if (nprm->isnull || !OidIsValid(nprm->ptype))
     106        3434 :             continue;
     107        1892 :         get_typlenbyval(nprm->ptype, &typLen, &typByVal);
     108        1892 :         nprm->value = datumCopy(nprm->value, typByVal, typLen);
     109             :     }
     110             : 
     111        1426 :     return retval;
     112             : }
     113             : 
     114             : 
     115             : /*
     116             :  * Set up to parse a query containing references to parameters
     117             :  * sourced from a ParamListInfo.
     118             :  */
     119             : static void
     120         582 : paramlist_parser_setup(ParseState *pstate, void *arg)
     121             : {
     122         582 :     pstate->p_paramref_hook = paramlist_param_ref;
     123             :     /* no need to use p_coerce_param_hook */
     124         582 :     pstate->p_ref_hook_state = arg;
     125         582 : }
     126             : 
     127             : /*
     128             :  * Transform a ParamRef using parameter type data from a ParamListInfo.
     129             :  */
     130             : static Node *
     131        1152 : paramlist_param_ref(ParseState *pstate, ParamRef *pref)
     132             : {
     133        1152 :     ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state;
     134        1152 :     int         paramno = pref->number;
     135             :     ParamExternData *prm;
     136             :     ParamExternData prmdata;
     137             :     Param      *param;
     138             : 
     139             :     /* check parameter number is valid */
     140        1152 :     if (paramno <= 0 || paramno > paramLI->numParams)
     141           0 :         return NULL;
     142             : 
     143             :     /* give hook a chance in case parameter is dynamic */
     144        1152 :     if (paramLI->paramFetch != NULL)
     145           0 :         prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
     146             :     else
     147        1152 :         prm = &paramLI->params[paramno - 1];
     148             : 
     149        1152 :     if (!OidIsValid(prm->ptype))
     150           0 :         return NULL;
     151             : 
     152        1152 :     param = makeNode(Param);
     153        1152 :     param->paramkind = PARAM_EXTERN;
     154        1152 :     param->paramid = paramno;
     155        1152 :     param->paramtype = prm->ptype;
     156        1152 :     param->paramtypmod = -1;
     157        1152 :     param->paramcollid = get_typcollation(param->paramtype);
     158        1152 :     param->location = pref->location;
     159             : 
     160        1152 :     return (Node *) param;
     161             : }
     162             : 
     163             : /*
     164             :  * Estimate the amount of space required to serialize a ParamListInfo.
     165             :  */
     166             : Size
     167         754 : EstimateParamListSpace(ParamListInfo paramLI)
     168             : {
     169         754 :     Size        sz = sizeof(int);
     170             : 
     171         754 :     if (paramLI == NULL || paramLI->numParams <= 0)
     172         718 :         return sz;
     173             : 
     174         120 :     for (int i = 0; i < paramLI->numParams; i++)
     175             :     {
     176             :         ParamExternData *prm;
     177             :         ParamExternData prmdata;
     178             :         Oid         typeOid;
     179             :         int16       typLen;
     180             :         bool        typByVal;
     181             : 
     182             :         /* give hook a chance in case parameter is dynamic */
     183          84 :         if (paramLI->paramFetch != NULL)
     184           0 :             prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
     185             :         else
     186          84 :             prm = &paramLI->params[i];
     187             : 
     188          84 :         typeOid = prm->ptype;
     189             : 
     190          84 :         sz = add_size(sz, sizeof(Oid)); /* space for type OID */
     191          84 :         sz = add_size(sz, sizeof(uint16));  /* space for pflags */
     192             : 
     193             :         /* space for datum/isnull */
     194          84 :         if (OidIsValid(typeOid))
     195          84 :             get_typlenbyval(typeOid, &typLen, &typByVal);
     196             :         else
     197             :         {
     198             :             /* If no type OID, assume by-value, like copyParamList does. */
     199           0 :             typLen = sizeof(Datum);
     200           0 :             typByVal = true;
     201             :         }
     202          84 :         sz = add_size(sz,
     203          84 :                       datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
     204             :     }
     205             : 
     206          36 :     return sz;
     207             : }
     208             : 
     209             : /*
     210             :  * Serialize a ParamListInfo structure into caller-provided storage.
     211             :  *
     212             :  * We write the number of parameters first, as a 4-byte integer, and then
     213             :  * write details for each parameter in turn.  The details for each parameter
     214             :  * consist of a 4-byte type OID, 2 bytes of flags, and then the datum as
     215             :  * serialized by datumSerialize().  The caller is responsible for ensuring
     216             :  * that there is enough storage to store the number of bytes that will be
     217             :  * written; use EstimateParamListSpace to find out how many will be needed.
     218             :  * *start_address is updated to point to the byte immediately following those
     219             :  * written.
     220             :  *
     221             :  * RestoreParamList can be used to recreate a ParamListInfo based on the
     222             :  * serialized representation; this will be a static, self-contained copy
     223             :  * just as copyParamList would create.
     224             :  *
     225             :  * paramValuesStr is not included.
     226             :  */
     227             : void
     228         754 : SerializeParamList(ParamListInfo paramLI, char **start_address)
     229             : {
     230             :     int         nparams;
     231             : 
     232             :     /* Write number of parameters. */
     233         754 :     if (paramLI == NULL || paramLI->numParams <= 0)
     234         718 :         nparams = 0;
     235             :     else
     236          36 :         nparams = paramLI->numParams;
     237         754 :     memcpy(*start_address, &nparams, sizeof(int));
     238         754 :     *start_address += sizeof(int);
     239             : 
     240             :     /* Write each parameter in turn. */
     241         838 :     for (int i = 0; i < nparams; i++)
     242             :     {
     243             :         ParamExternData *prm;
     244             :         ParamExternData prmdata;
     245             :         Oid         typeOid;
     246             :         int16       typLen;
     247             :         bool        typByVal;
     248             : 
     249             :         /* give hook a chance in case parameter is dynamic */
     250          84 :         if (paramLI->paramFetch != NULL)
     251           0 :             prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
     252             :         else
     253          84 :             prm = &paramLI->params[i];
     254             : 
     255          84 :         typeOid = prm->ptype;
     256             : 
     257             :         /* Write type OID. */
     258          84 :         memcpy(*start_address, &typeOid, sizeof(Oid));
     259          84 :         *start_address += sizeof(Oid);
     260             : 
     261             :         /* Write flags. */
     262          84 :         memcpy(*start_address, &prm->pflags, sizeof(uint16));
     263          84 :         *start_address += sizeof(uint16);
     264             : 
     265             :         /* Write datum/isnull. */
     266          84 :         if (OidIsValid(typeOid))
     267          84 :             get_typlenbyval(typeOid, &typLen, &typByVal);
     268             :         else
     269             :         {
     270             :             /* If no type OID, assume by-value, like copyParamList does. */
     271           0 :             typLen = sizeof(Datum);
     272           0 :             typByVal = true;
     273             :         }
     274          84 :         datumSerialize(prm->value, prm->isnull, typByVal, typLen,
     275             :                        start_address);
     276             :     }
     277         754 : }
     278             : 
     279             : /*
     280             :  * Copy a ParamListInfo structure.
     281             :  *
     282             :  * The result is allocated in CurrentMemoryContext.
     283             :  *
     284             :  * Note: the intent of this function is to make a static, self-contained
     285             :  * set of parameter values.  If dynamic parameter hooks are present, we
     286             :  * intentionally do not copy them into the result.  Rather, we forcibly
     287             :  * instantiate all available parameter values and copy the datum values.
     288             :  */
     289             : ParamListInfo
     290        2662 : RestoreParamList(char **start_address)
     291             : {
     292             :     ParamListInfo paramLI;
     293             :     int         nparams;
     294             : 
     295        2662 :     memcpy(&nparams, *start_address, sizeof(int));
     296        2662 :     *start_address += sizeof(int);
     297             : 
     298        2662 :     paramLI = makeParamList(nparams);
     299             : 
     300        2854 :     for (int i = 0; i < nparams; i++)
     301             :     {
     302         192 :         ParamExternData *prm = &paramLI->params[i];
     303             : 
     304             :         /* Read type OID. */
     305         192 :         memcpy(&prm->ptype, *start_address, sizeof(Oid));
     306         192 :         *start_address += sizeof(Oid);
     307             : 
     308             :         /* Read flags. */
     309         192 :         memcpy(&prm->pflags, *start_address, sizeof(uint16));
     310         192 :         *start_address += sizeof(uint16);
     311             : 
     312             :         /* Read datum/isnull. */
     313         192 :         prm->value = datumRestore(start_address, &prm->isnull);
     314             :     }
     315             : 
     316        2662 :     return paramLI;
     317             : }
     318             : 
     319             : /*
     320             :  * BuildParamLogString
     321             :  *      Return a string that represents the parameter list, for logging.
     322             :  *
     323             :  * If caller already knows textual representations for some parameters, it can
     324             :  * pass an array of exactly params->numParams values as knownTextValues, which
     325             :  * can contain NULLs for any unknown individual values.  NULL can be given if
     326             :  * no parameters are known.
     327             :  *
     328             :  * If maxlen is >= 0, that's the maximum number of bytes of any one
     329             :  * parameter value to be printed; an ellipsis is added if the string is
     330             :  * longer.  (Added quotes are not considered in this calculation.)
     331             :  */
     332             : char *
     333        4776 : BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen)
     334             : {
     335             :     MemoryContext tmpCxt,
     336             :                 oldCxt;
     337             :     StringInfoData buf;
     338             : 
     339             :     /*
     340             :      * NB: think not of returning params->paramValuesStr!  It may have been
     341             :      * generated with a different maxlen, and so be unsuitable.  Besides that,
     342             :      * this is the function used to create that string.
     343             :      */
     344             : 
     345             :     /*
     346             :      * No work if the param fetch hook is in use.  Also, it's not possible to
     347             :      * do this in an aborted transaction.  (It might be possible to improve on
     348             :      * this last point when some knownTextValues exist, but it seems tricky.)
     349             :      */
     350        9552 :     if (params->paramFetch != NULL ||
     351        4776 :         IsAbortedTransactionBlockState())
     352           0 :         return NULL;
     353             : 
     354             :     /* Initialize the output stringinfo, in caller's memory context */
     355        4776 :     initStringInfo(&buf);
     356             : 
     357             :     /* Use a temporary context to call output functions, just in case */
     358        4776 :     tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
     359             :                                    "BuildParamLogString",
     360             :                                    ALLOCSET_DEFAULT_SIZES);
     361        4776 :     oldCxt = MemoryContextSwitchTo(tmpCxt);
     362             : 
     363       13002 :     for (int paramno = 0; paramno < params->numParams; paramno++)
     364             :     {
     365        8226 :         ParamExternData *param = &params->params[paramno];
     366             : 
     367        8226 :         appendStringInfo(&buf,
     368             :                          "%s$%d = ",
     369             :                          paramno > 0 ? ", " : "",
     370             :                          paramno + 1);
     371             : 
     372        8226 :         if (param->isnull || !OidIsValid(param->ptype))
     373          10 :             appendStringInfoString(&buf, "NULL");
     374             :         else
     375             :         {
     376        8216 :             if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
     377          12 :                 appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
     378             :                                              maxlen);
     379             :             else
     380             :             {
     381             :                 Oid         typoutput;
     382             :                 bool        typisvarlena;
     383             :                 char       *pstring;
     384             : 
     385        8204 :                 getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
     386        8204 :                 pstring = OidOutputFunctionCall(typoutput, param->value);
     387        8204 :                 appendStringInfoStringQuoted(&buf, pstring, maxlen);
     388             :             }
     389             :         }
     390             :     }
     391             : 
     392        4776 :     MemoryContextSwitchTo(oldCxt);
     393        4776 :     MemoryContextDelete(tmpCxt);
     394             : 
     395        4776 :     return buf.data;
     396             : }
     397             : 
     398             : /*
     399             :  * ParamsErrorCallback - callback for printing parameters in error context
     400             :  *
     401             :  * Note that this is a no-op unless BuildParamLogString has been called
     402             :  * beforehand.
     403             :  */
     404             : void
     405         122 : ParamsErrorCallback(void *arg)
     406             : {
     407         122 :     ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
     408             : 
     409         122 :     if (data == NULL ||
     410         122 :         data->params == NULL ||
     411          60 :         data->params->paramValuesStr == NULL)
     412         114 :         return;
     413             : 
     414           8 :     if (data->portalName && data->portalName[0] != '\0')
     415           0 :         errcontext("portal \"%s\" with parameters: %s",
     416           0 :                    data->portalName, data->params->paramValuesStr);
     417             :     else
     418           8 :         errcontext("unnamed portal with parameters: %s",
     419           8 :                    data->params->paramValuesStr);
     420             : }

Generated by: LCOV version 1.16