Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * params.c
4 : * Support for finding the values associated with Param nodes.
5 : *
6 : *
7 : * Portions Copyright (c) 1996-2023, 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
1487 peter 44 CBC 63508 : makeParamList(int numParams)
45 : {
46 : ParamListInfo retval;
47 : Size size;
48 :
49 63508 : size = offsetof(ParamListInfoData, params) +
50 : numParams * sizeof(ParamExternData);
51 :
52 63508 : retval = (ParamListInfo) palloc(size);
53 63508 : retval->paramFetch = NULL;
54 63508 : retval->paramFetchArg = NULL;
55 63508 : retval->paramCompile = NULL;
56 63508 : retval->paramCompileArg = NULL;
1031 tgl 57 63508 : retval->parserSetup = paramlist_parser_setup;
58 63508 : retval->parserSetupArg = (void *) retval;
1215 alvherre 59 63508 : retval->paramValuesStr = NULL;
1487 peter 60 63508 : retval->numParams = numParams;
61 :
62 63508 : 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
6824 tgl 78 1671 : copyParamList(ParamListInfo from)
79 : {
80 : ParamListInfo retval;
81 :
6196 82 1671 : if (from == NULL || from->numParams <= 0)
6824 83 966 : return NULL;
84 :
1487 peter 85 705 : retval = makeParamList(from->numParams);
86 :
87 3454 : for (int i = 0; i < from->numParams; i++)
88 : {
89 : ParamExternData *oprm;
4904 tgl 90 2749 : 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 */
1935 96 2749 : if (from->paramFetch != NULL)
97 2179 : oprm = from->paramFetch(from, i + 1, false, &prmdata);
98 : else
99 570 : oprm = &from->params[i];
100 :
101 : /* flat-copy the parameter info */
4904 102 2749 : *nprm = *oprm;
103 :
104 : /* need datumCopy in case it's a pass-by-reference datatype */
105 2749 : if (nprm->isnull || !OidIsValid(nprm->ptype))
6196 106 1812 : continue;
4904 107 937 : get_typlenbyval(nprm->ptype, &typLen, &typByVal);
108 937 : nprm->value = datumCopy(nprm->value, typByVal, typLen);
109 : }
110 :
6824 111 705 : 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
1031 120 291 : paramlist_parser_setup(ParseState *pstate, void *arg)
121 : {
122 291 : pstate->p_paramref_hook = paramlist_param_ref;
123 : /* no need to use p_coerce_param_hook */
124 291 : pstate->p_ref_hook_state = arg;
125 291 : }
126 :
127 : /*
128 : * Transform a ParamRef using parameter type data from a ParamListInfo.
129 : */
130 : static Node *
131 576 : paramlist_param_ref(ParseState *pstate, ParamRef *pref)
132 : {
133 576 : ParamListInfo paramLI = (ParamListInfo) pstate->p_ref_hook_state;
134 576 : int paramno = pref->number;
135 : ParamExternData *prm;
136 : ParamExternData prmdata;
137 : Param *param;
138 :
139 : /* check parameter number is valid */
140 576 : if (paramno <= 0 || paramno > paramLI->numParams)
1031 tgl 141 UBC 0 : return NULL;
142 :
143 : /* give hook a chance in case parameter is dynamic */
1031 tgl 144 CBC 576 : if (paramLI->paramFetch != NULL)
1031 tgl 145 UBC 0 : prm = paramLI->paramFetch(paramLI, paramno, false, &prmdata);
146 : else
1031 tgl 147 CBC 576 : prm = ¶mLI->params[paramno - 1];
148 :
149 576 : if (!OidIsValid(prm->ptype))
1031 tgl 150 UBC 0 : return NULL;
151 :
1031 tgl 152 CBC 576 : param = makeNode(Param);
153 576 : param->paramkind = PARAM_EXTERN;
154 576 : param->paramid = paramno;
155 576 : param->paramtype = prm->ptype;
156 576 : param->paramtypmod = -1;
157 576 : param->paramcollid = get_typcollation(param->paramtype);
158 576 : param->location = pref->location;
159 :
160 576 : return (Node *) param;
161 : }
162 :
163 : /*
164 : * Estimate the amount of space required to serialize a ParamListInfo.
165 : */
166 : Size
2750 rhaas 167 323 : EstimateParamListSpace(ParamListInfo paramLI)
168 : {
169 : int i;
2495 170 323 : Size sz = sizeof(int);
171 :
2750 172 323 : if (paramLI == NULL || paramLI->numParams <= 0)
173 305 : return sz;
174 :
175 60 : 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 */
1935 tgl 184 42 : if (paramLI->paramFetch != NULL)
1935 tgl 185 UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
186 : else
1935 tgl 187 CBC 42 : prm = ¶mLI->params[i];
188 :
189 42 : typeOid = prm->ptype;
190 :
2495 rhaas 191 42 : sz = add_size(sz, sizeof(Oid)); /* space for type OID */
2118 tgl 192 42 : sz = add_size(sz, sizeof(uint16)); /* space for pflags */
193 :
194 : /* space for datum/isnull */
2715 rhaas 195 42 : if (OidIsValid(typeOid))
196 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
197 : else
198 : {
199 : /* If no type OID, assume by-value, like copyParamList does. */
2750 rhaas 200 UBC 0 : typLen = sizeof(Datum);
201 0 : typByVal = true;
202 : }
2750 rhaas 203 CBC 42 : sz = add_size(sz,
2118 tgl 204 42 : datumEstimateSpace(prm->value, prm->isnull, typByVal, typLen));
205 : }
206 :
2750 rhaas 207 18 : 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
229 323 : SerializeParamList(ParamListInfo paramLI, char **start_address)
230 : {
231 : int nparams;
232 : int i;
233 :
234 : /* Write number of parameters. */
235 323 : if (paramLI == NULL || paramLI->numParams <= 0)
236 305 : nparams = 0;
237 : else
238 18 : nparams = paramLI->numParams;
239 323 : memcpy(*start_address, &nparams, sizeof(int));
240 323 : *start_address += sizeof(int);
241 :
242 : /* Write each parameter in turn. */
243 365 : 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 */
1935 tgl 252 42 : if (paramLI->paramFetch != NULL)
1935 tgl 253 UBC 0 : prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
254 : else
1935 tgl 255 CBC 42 : prm = ¶mLI->params[i];
256 :
257 42 : typeOid = prm->ptype;
258 :
259 : /* Write type OID. */
2715 rhaas 260 42 : memcpy(*start_address, &typeOid, sizeof(Oid));
2750 261 42 : *start_address += sizeof(Oid);
262 :
263 : /* Write flags. */
264 42 : memcpy(*start_address, &prm->pflags, sizeof(uint16));
265 42 : *start_address += sizeof(uint16);
266 :
267 : /* Write datum/isnull. */
2715 268 42 : if (OidIsValid(typeOid))
269 42 : get_typlenbyval(typeOid, &typLen, &typByVal);
270 : else
271 : {
272 : /* If no type OID, assume by-value, like copyParamList does. */
2750 rhaas 273 UBC 0 : typLen = sizeof(Datum);
274 0 : typByVal = true;
275 : }
2750 rhaas 276 CBC 42 : datumSerialize(prm->value, prm->isnull, typByVal, typLen,
277 : start_address);
278 : }
279 323 : }
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
292 1210 : RestoreParamList(char **start_address)
293 : {
294 : ParamListInfo paramLI;
295 : int nparams;
296 :
297 1210 : memcpy(&nparams, *start_address, sizeof(int));
298 1210 : *start_address += sizeof(int);
299 :
1487 peter 300 1210 : paramLI = makeParamList(nparams);
301 :
302 1306 : for (int i = 0; i < nparams; i++)
303 : {
2750 rhaas 304 96 : ParamExternData *prm = ¶mLI->params[i];
305 :
306 : /* Read type OID. */
307 96 : memcpy(&prm->ptype, *start_address, sizeof(Oid));
308 96 : *start_address += sizeof(Oid);
309 :
310 : /* Read flags. */
311 96 : memcpy(&prm->pflags, *start_address, sizeof(uint16));
312 96 : *start_address += sizeof(uint16);
313 :
314 : /* Read datum/isnull. */
315 96 : prm->value = datumRestore(start_address, &prm->isnull);
316 : }
317 :
318 1210 : 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 *
1215 alvherre 335 2288 : 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 4576 : if (params->paramFetch != NULL ||
353 2288 : IsAbortedTransactionBlockState())
1215 alvherre 354 UBC 0 : return NULL;
355 :
356 : /* Initialize the output stringinfo, in caller's memory context */
1215 alvherre 357 CBC 2288 : initStringInfo(&buf);
358 :
359 : /* Use a temporary context to call output functions, just in case */
360 2288 : tmpCxt = AllocSetContextCreate(CurrentMemoryContext,
361 : "BuildParamLogString",
362 : ALLOCSET_DEFAULT_SIZES);
363 2288 : oldCxt = MemoryContextSwitchTo(tmpCxt);
364 :
365 6262 : for (int paramno = 0; paramno < params->numParams; paramno++)
366 : {
367 3974 : ParamExternData *param = ¶ms->params[paramno];
368 :
369 3974 : appendStringInfo(&buf,
370 : "%s$%d = ",
371 : paramno > 0 ? ", " : "",
372 : paramno + 1);
373 :
374 3974 : if (param->isnull || !OidIsValid(param->ptype))
375 5 : appendStringInfoString(&buf, "NULL");
376 : else
377 : {
378 3969 : if (knownTextValues != NULL && knownTextValues[paramno] != NULL)
379 6 : appendStringInfoStringQuoted(&buf, knownTextValues[paramno],
380 : maxlen);
381 : else
382 : {
383 : Oid typoutput;
384 : bool typisvarlena;
385 : char *pstring;
386 :
387 3963 : getTypeOutputInfo(param->ptype, &typoutput, &typisvarlena);
388 3963 : pstring = OidOutputFunctionCall(typoutput, param->value);
389 3963 : appendStringInfoStringQuoted(&buf, pstring, maxlen);
390 : }
391 : }
392 : }
393 :
394 2288 : MemoryContextSwitchTo(oldCxt);
395 2288 : MemoryContextDelete(tmpCxt);
396 :
397 2288 : 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
407 20 : ParamsErrorCallback(void *arg)
408 : {
409 20 : ParamsErrorCbData *data = (ParamsErrorCbData *) arg;
410 :
411 20 : if (data == NULL ||
412 20 : data->params == NULL ||
413 12 : data->params->paramValuesStr == NULL)
414 16 : return;
415 :
416 4 : if (data->portalName && data->portalName[0] != '\0')
937 peter 417 UBC 0 : errcontext("portal \"%s\" with parameters: %s",
1215 alvherre 418 0 : data->portalName, data->params->paramValuesStr);
419 : else
937 peter 420 CBC 4 : errcontext("unnamed portal with parameters: %s",
1215 alvherre 421 4 : data->params->paramValuesStr);
422 : }
|