diff options
Diffstat (limited to 'src/backend/nodes/params.c')
-rw-r--r-- | src/backend/nodes/params.c | 422 |
1 files changed, 422 insertions, 0 deletions
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c new file mode 100644 index 0000000..45ebff5 --- /dev/null +++ b/src/backend/nodes/params.c @@ -0,0 +1,422 @@ +/*------------------------------------------------------------------------- + * + * params.c + * Support for finding the values associated with Param nodes. + * + * + * Portions Copyright (c) 1996-2021, 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); +} |