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