summaryrefslogtreecommitdiffstats
path: root/src/backend/nodes/params.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/nodes/params.c')
-rw-r--r--src/backend/nodes/params.c422
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 = &paramLI->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 = &paramLI->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 = &paramLI->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 = &paramLI->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 = &params->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);
+}