/*------------------------------------------------------------------------- * * params.c * Support for finding the values associated with Param nodes. * * * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * src/backend/nodes/params.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/xact.h" #include "fmgr.h" #include "mb/stringinfo_mb.h" #include "nodes/params.h" #include "parser/parse_node.h" #include "storage/shmem.h" #include "utils/datum.h" #include "utils/lsyscache.h" #include "utils/memutils.h" static void paramlist_parser_setup(ParseState *pstate, void *arg); static Node *paramlist_param_ref(ParseState *pstate, ParamRef *pref); /* * Allocate and initialize a new ParamListInfo structure. * * To make a new structure for the "dynamic" way (with hooks), pass 0 for * numParams and set numParams manually. * * A default parserSetup function is supplied automatically. Callers may * override it if they choose. (Note that most use-cases for ParamListInfos * will never use the parserSetup function anyway.) */ ParamListInfo makeParamList(int numParams) { ParamListInfo retval; Size size; size = offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData); retval = (ParamListInfo) palloc(size); retval->paramFetch = NULL; retval->paramFetchArg = NULL; retval->paramCompile = NULL; retval->paramCompileArg = NULL; retval->parserSetup = paramlist_parser_setup; retval->parserSetupArg = (void *) retval; retval->paramValuesStr = NULL; retval->numParams = numParams; return retval; } /* * Copy a ParamListInfo structure. * * The result is allocated in CurrentMemoryContext. * * Note: the intent of this function is to make a static, self-contained * set of parameter values. If dynamic parameter hooks are present, we * intentionally do not copy them into the result. Rather, we forcibly * instantiate all available parameter values and copy the datum values. * * paramValuesStr is not copied, either. */ ParamListInfo copyParamList(ParamListInfo from) { ParamListInfo retval; if (from == NULL || from->numParams <= 0) return NULL; retval = makeParamList(from->numParams); for (int i = 0; i < from->numParams; i++) { ParamExternData *oprm; ParamExternData *nprm = &retval->params[i]; ParamExternData prmdata; int16 typLen; bool typByVal; /* give hook a chance in case parameter is dynamic */ if (from->paramFetch != NULL) oprm = from->paramFetch(from, i + 1, false, &prmdata); else oprm = &from->params[i]; /* flat-copy the parameter info */ *nprm = *oprm; /* need datumCopy in case it's a pass-by-reference datatype */ if (nprm->isnull || !OidIsValid(nprm->ptype)) continue; get_typlenbyval(nprm->ptype, &typLen, &typByVal); nprm->value = datumCopy(nprm->value, typByVal, typLen); } return retval; } /* * Set up to parse a query containing references to parameters * sourced from a ParamListInfo. */ static void paramlist_parser_setup(ParseState *pstate, void *arg) { pstate->p_paramref_hook = paramlist_param_ref; /* no need to use p_coerce_param_hook */ pstate->p_ref_hook_state = arg; } /* * Transform a ParamRef using parameter type data from a ParamListInfo. */ static Node * paramlist_param_ref(ParseState *pstate, ParamRef *pref) { ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state; int paramno = pref->number; ParamExternData *prm; ParamExternData prmdata; Param *param; /* check parameter number is valid */ if (paramno <= 0 || paramno > paramLI->numParams) return NULL; /* give hook a chance in case parameter is dynamic */ if (paramLI->paramFetch != NULL) prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata); else prm = ¶mLI->params[paramno - 1]; if (!OidIsValid(prm->ptype)) return NULL; param = makeNode(Param); param->paramkind = PARAM_EXTERN; param->paramid = paramno; param->paramtype = prm->ptype; param->paramtypmod = -1; param->paramcollid = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; } /* * Estimate the amount of space required to serialize a ParamListInfo. */ Size EstimateParamListSpace(ParamListInfo paramLI) { int i; Size sz = sizeof(int); if (paramLI == NULL || paramLI->numParams <= 0) return sz; for (i = 0; i < paramLI->numParams; i++) { ParamExternData *prm; ParamExternData prmdata; Oid typeOid; int16 typLen; bool typByVal; /* give hook a chance in case parameter is dynamic */ if (paramLI->paramFetch != NULL) prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata); else prm = ¶mLI->params[i]; typeOid = prm->ptype; sz = add_size(sz, sizeof(Oid)); /* space for type OID */ sz = add_size(sz, sizeof(uint16)); /* space for pflags */ /* space for datum/isnull */ if (OidIsValid(typeOid)) get_typlenbyval(typeOid, &typLen, &typByVal); else { /* If no type OID, assume by-value, like copyParamList does. */ typLen = sizeof(Datum); typByVal = true; } sz = add_size(sz, datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen)); } return sz; } /* * Serialize a ParamListInfo structure into caller-provided storage. * * We write the number of parameters first, as a 4-byte integer, and then * write details for each parameter in turn. The details for each parameter * consist of a 4-byte type OID, 2 bytes of flags, and then the datum as * serialized by datumSerialize(). The caller is responsible for ensuring * that there is enough storage to store the number of bytes that will be * written; use EstimateParamListSpace to find out how many will be needed. * *start_address is updated to point to the byte immediately following those * written. * * RestoreParamList can be used to recreate a ParamListInfo based on the * serialized representation; this will be a static, self-contained copy * just as copyParamList would create. * * paramValuesStr is not included. */ void SerializeParamList(ParamListInfo paramLI, char **start_address) { int nparams; int i; /* Write number of parameters. */ if (paramLI == NULL || paramLI->numParams <= 0) nparams = 0; else nparams = paramLI->numParams; memcpy(*start_address, &nparams, sizeof(int)); *start_address += sizeof(int); /* Write each parameter in turn. */ for (i = 0; i < nparams; i++) { ParamExternData *prm; ParamExternData prmdata; Oid typeOid; int16 typLen; bool typByVal; /* give hook a chance in case parameter is dynamic */ if (paramLI->paramFetch != NULL) prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata); else prm = ¶mLI->params[i]; typeOid = prm->ptype; /* Write type OID. */ memcpy(*start_address, &typeOid, sizeof(Oid)); *start_address += sizeof(Oid); /* Write flags. */ memcpy(*start_address, &prm->pflags, sizeof(uint16)); *start_address += sizeof(uint16); /* Write datum/isnull. */ if (OidIsValid(typeOid)) get_typlenbyval(typeOid, &typLen, &typByVal); else { /* If no type OID, assume by-value, like copyParamList does. */ typLen = sizeof(Datum); typByVal = true; } datumSerialize(prm->value, prm->isnull, typByVal, typLen, start_address); } } /* * Copy a ParamListInfo structure. * * The result is allocated in CurrentMemoryContext. * * Note: the intent of this function is to make a static, self-contained * set of parameter values. If dynamic parameter hooks are present, we * intentionally do not copy them into the result. Rather, we forcibly * instantiate all available parameter values and copy the datum values. */ ParamListInfo RestoreParamList(char **start_address) { ParamListInfo paramLI; int nparams; memcpy(&nparams, *start_address, sizeof(int)); *start_address += sizeof(int); paramLI = makeParamList(nparams); for (int i = 0; i < nparams; i++) { ParamExternData *prm = ¶mLI->params[i]; /* Read type OID. */ memcpy(&prm->ptype, *start_address, sizeof(Oid)); *start_address += sizeof(Oid); /* Read flags. */ memcpy(&prm->pflags, *start_address, sizeof(uint16)); *start_address += sizeof(uint16); /* Read datum/isnull. */ prm->value = datumRestore(start_address, &prm->isnull); } return paramLI; } /* * BuildParamLogString * Return a string that represents the parameter list, for logging. * * If caller already knows textual representations for some parameters, it can * pass an array of exactly params->numParams values as knownTextValues, which * can contain NULLs for any unknown individual values. NULL can be given if * no parameters are known. * * If maxlen is >= 0, that's the maximum number of bytes of any one * parameter value to be printed; an ellipsis is added if the string is * longer. (Added quotes are not considered in this calculation.) */ char * BuildParamLogString(ParamListInfo params, char **knownTextValues, int maxlen) { MemoryContext tmpCxt, oldCxt; StringInfoData buf; /* * NB: think not of returning params->paramValuesStr! It may have been * generated with a different maxlen, and so be unsuitable. Besides that, * this is the function used to create that string. */ /* * No work if the param fetch hook is in use. Also, it's not possible to * do this in an aborted transaction. (It might be possible to improve on * this last point when some knownTextValues exist, but it seems tricky.) */ if (params->paramFetch != NULL || IsAbortedTransactionBlockState()) return NULL; /* Initialize the output stringinfo, in caller's memory context */ initStringInfo(&buf); /* Use a temporary context to call output functions, just in case */ tmpCxt = AllocSetContextCreate(CurrentMemoryContext, "BuildParamLogString", ALLOCSET_DEFAULT_SIZES); oldCxt = MemoryContextSwitchTo(tmpCxt); for (int paramno = 0; paramno < params->numParams; paramno++) { ParamExternData *param = ¶ms->params[paramno]; appendStringInfo(&buf, "%s$%d = ", paramno > 0 ? ", " : "", paramno + 1); if (param->isnull || !OidIsValid(param->ptype)) appendStringInfoString(&buf, "NULL"); else { if (knownTextValues != NULL && knownTextValues[paramno] != NULL) appendStringInfoStringQuoted(&buf, knownTextValues[paramno], maxlen); else { Oid typoutput; bool typisvarlena; char *pstring; getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena); pstring = OidOutputFunctionCall(typoutput, param->value); appendStringInfoStringQuoted(&buf, pstring, maxlen); } } } MemoryContextSwitchTo(oldCxt); MemoryContextDelete(tmpCxt); return buf.data; } /* * ParamsErrorCallback - callback for printing parameters in error context * * Note that this is a no-op unless BuildParamLogString has been called * beforehand. */ void ParamsErrorCallback(void *arg) { ParamsErrorCbData *data = (ParamsErrorCbData *) arg; if (data == NULL || data->params == NULL || data->params->paramValuesStr == NULL) return; if (data->portalName && data->portalName[0] != '\0') errcontext("portal \"%s\" with parameters: %s", data->portalName, data->params->paramValuesStr); else errcontext("unnamed portal with parameters: %s", data->params->paramValuesStr); }