mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-09-29 03:32:22 +02:00
ca3b37487b
Backpatch-through: 9.5
423 lines
11 KiB
C
423 lines
11 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* 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);
|
|
}
|