1 /*------------------------------------------------------------------------- 2 * 3 * parse_param.c 4 * handle parameters in parser 5 * 6 * This code covers two cases that are used within the core backend: 7 * * a fixed list of parameters with known types 8 * * an expandable list of parameters whose types can optionally 9 * be determined from context 10 * In both cases, only explicit $n references (ParamRef nodes) are supported. 11 * 12 * Note that other approaches to parameters are possible using the parser 13 * hooks defined in ParseState. 14 * 15 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group 16 * Portions Copyright (c) 1994, Regents of the University of California 17 * 18 * 19 * IDENTIFICATION 20 * src/backend/parser/parse_param.c 21 * 22 *------------------------------------------------------------------------- 23 */ 24 25 #include "postgres.h" 26 27 #include <limits.h> 28 29 #include "catalog/pg_type.h" 30 #include "nodes/nodeFuncs.h" 31 #include "parser/parse_param.h" 32 #include "utils/builtins.h" 33 #include "utils/lsyscache.h" 34 35 36 typedef struct FixedParamState 37 { 38 Oid *paramTypes; /* array of parameter type OIDs */ 39 int numParams; /* number of array entries */ 40 } FixedParamState; 41 42 /* 43 * In the varparams case, the caller-supplied OID array (if any) can be 44 * re-palloc'd larger at need. A zero array entry means that parameter number 45 * hasn't been seen, while UNKNOWNOID means the parameter has been used but 46 * its type is not yet known. 47 */ 48 typedef struct VarParamState 49 { 50 Oid **paramTypes; /* array of parameter type OIDs */ 51 int *numParams; /* number of array entries */ 52 } VarParamState; 53 54 static Node *fixed_paramref_hook(ParseState *pstate, ParamRef *pref); 55 static Node *variable_paramref_hook(ParseState *pstate, ParamRef *pref); 56 static Node *variable_coerce_param_hook(ParseState *pstate, Param *param, 57 Oid targetTypeId, int32 targetTypeMod, 58 int location); 59 static bool check_parameter_resolution_walker(Node *node, ParseState *pstate); 60 static bool query_contains_extern_params_walker(Node *node, void *context); 61 62 63 /* 64 * Set up to process a query containing references to fixed parameters. 65 */ 66 void 67 parse_fixed_parameters(ParseState *pstate, 68 Oid *paramTypes, int numParams) 69 { 70 FixedParamState *parstate = palloc(sizeof(FixedParamState)); 71 72 parstate->paramTypes = paramTypes; 73 parstate->numParams = numParams; 74 pstate->p_ref_hook_state = (void *) parstate; 75 pstate->p_paramref_hook = fixed_paramref_hook; 76 /* no need to use p_coerce_param_hook */ 77 } 78 79 /* 80 * Set up to process a query containing references to variable parameters. 81 */ 82 void 83 parse_variable_parameters(ParseState *pstate, 84 Oid **paramTypes, int *numParams) 85 { 86 VarParamState *parstate = palloc(sizeof(VarParamState)); 87 88 parstate->paramTypes = paramTypes; 89 parstate->numParams = numParams; 90 pstate->p_ref_hook_state = (void *) parstate; 91 pstate->p_paramref_hook = variable_paramref_hook; 92 pstate->p_coerce_param_hook = variable_coerce_param_hook; 93 } 94 95 /* 96 * Transform a ParamRef using fixed parameter types. 97 */ 98 static Node * 99 fixed_paramref_hook(ParseState *pstate, ParamRef *pref) 100 { 101 FixedParamState *parstate = (FixedParamState *) pstate->p_ref_hook_state; 102 int paramno = pref->number; 103 Param *param; 104 105 /* Check parameter number is valid */ 106 if (paramno <= 0 || paramno > parstate->numParams || 107 !OidIsValid(parstate->paramTypes[paramno - 1])) 108 ereport(ERROR, 109 (errcode(ERRCODE_UNDEFINED_PARAMETER), 110 errmsg("there is no parameter $%d", paramno), 111 parser_errposition(pstate, pref->location))); 112 113 param = makeNode(Param); 114 param->paramkind = PARAM_EXTERN; 115 param->paramid = paramno; 116 param->paramtype = parstate->paramTypes[paramno - 1]; 117 param->paramtypmod = -1; 118 param->paramcollid = get_typcollation(param->paramtype); 119 param->location = pref->location; 120 121 return (Node *) param; 122 } 123 124 /* 125 * Transform a ParamRef using variable parameter types. 126 * 127 * The only difference here is we must enlarge the parameter type array 128 * as needed. 129 */ 130 static Node * 131 variable_paramref_hook(ParseState *pstate, ParamRef *pref) 132 { 133 VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state; 134 int paramno = pref->number; 135 Oid *pptype; 136 Param *param; 137 138 /* Check parameter number is in range */ 139 if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid)) 140 ereport(ERROR, 141 (errcode(ERRCODE_UNDEFINED_PARAMETER), 142 errmsg("there is no parameter $%d", paramno), 143 parser_errposition(pstate, pref->location))); 144 if (paramno > *parstate->numParams) 145 { 146 /* Need to enlarge param array */ 147 if (*parstate->paramTypes) 148 *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes, 149 paramno * sizeof(Oid)); 150 else 151 *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid)); 152 /* Zero out the previously-unreferenced slots */ 153 MemSet(*parstate->paramTypes + *parstate->numParams, 154 0, 155 (paramno - *parstate->numParams) * sizeof(Oid)); 156 *parstate->numParams = paramno; 157 } 158 159 /* Locate param's slot in array */ 160 pptype = &(*parstate->paramTypes)[paramno - 1]; 161 162 /* If not seen before, initialize to UNKNOWN type */ 163 if (*pptype == InvalidOid) 164 *pptype = UNKNOWNOID; 165 166 /* 167 * If the argument is of type void and it's procedure call, interpret it 168 * as unknown. This allows the JDBC driver to not have to distinguish 169 * function and procedure calls. See also another component of this hack 170 * in ParseFuncOrColumn(). 171 */ 172 if (*pptype == VOIDOID && pstate->p_expr_kind == EXPR_KIND_CALL_ARGUMENT) 173 *pptype = UNKNOWNOID; 174 175 param = makeNode(Param); 176 param->paramkind = PARAM_EXTERN; 177 param->paramid = paramno; 178 param->paramtype = *pptype; 179 param->paramtypmod = -1; 180 param->paramcollid = get_typcollation(param->paramtype); 181 param->location = pref->location; 182 183 return (Node *) param; 184 } 185 186 /* 187 * Coerce a Param to a query-requested datatype, in the varparams case. 188 */ 189 static Node * 190 variable_coerce_param_hook(ParseState *pstate, Param *param, 191 Oid targetTypeId, int32 targetTypeMod, 192 int location) 193 { 194 if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID) 195 { 196 /* 197 * Input is a Param of previously undetermined type, and we want to 198 * update our knowledge of the Param's type. 199 */ 200 VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state; 201 Oid *paramTypes = *parstate->paramTypes; 202 int paramno = param->paramid; 203 204 if (paramno <= 0 || /* shouldn't happen, but... */ 205 paramno > *parstate->numParams) 206 ereport(ERROR, 207 (errcode(ERRCODE_UNDEFINED_PARAMETER), 208 errmsg("there is no parameter $%d", paramno), 209 parser_errposition(pstate, param->location))); 210 211 if (paramTypes[paramno - 1] == UNKNOWNOID) 212 { 213 /* We've successfully resolved the type */ 214 paramTypes[paramno - 1] = targetTypeId; 215 } 216 else if (paramTypes[paramno - 1] == targetTypeId) 217 { 218 /* We previously resolved the type, and it matches */ 219 } 220 else 221 { 222 /* Oops */ 223 ereport(ERROR, 224 (errcode(ERRCODE_AMBIGUOUS_PARAMETER), 225 errmsg("inconsistent types deduced for parameter $%d", 226 paramno), 227 errdetail("%s versus %s", 228 format_type_be(paramTypes[paramno - 1]), 229 format_type_be(targetTypeId)), 230 parser_errposition(pstate, param->location))); 231 } 232 233 param->paramtype = targetTypeId; 234 235 /* 236 * Note: it is tempting here to set the Param's paramtypmod to 237 * targetTypeMod, but that is probably unwise because we have no 238 * infrastructure that enforces that the value delivered for a Param 239 * will match any particular typmod. Leaving it -1 ensures that a 240 * run-time length check/coercion will occur if needed. 241 */ 242 param->paramtypmod = -1; 243 244 /* 245 * This module always sets a Param's collation to be the default for 246 * its datatype. If that's not what you want, you should be using the 247 * more general parser substitution hooks. 248 */ 249 param->paramcollid = get_typcollation(param->paramtype); 250 251 /* Use the leftmost of the param's and coercion's locations */ 252 if (location >= 0 && 253 (param->location < 0 || location < param->location)) 254 param->location = location; 255 256 return (Node *) param; 257 } 258 259 /* Else signal to proceed with normal coercion */ 260 return NULL; 261 } 262 263 /* 264 * Check for consistent assignment of variable parameters after completion 265 * of parsing with parse_variable_parameters. 266 * 267 * Note: this code intentionally does not check that all parameter positions 268 * were used, nor that all got non-UNKNOWN types assigned. Caller of parser 269 * should enforce that if it's important. 270 */ 271 void 272 check_variable_parameters(ParseState *pstate, Query *query) 273 { 274 VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state; 275 276 /* If numParams is zero then no Params were generated, so no work */ 277 if (*parstate->numParams > 0) 278 (void) query_tree_walker(query, 279 check_parameter_resolution_walker, 280 (void *) pstate, 0); 281 } 282 283 /* 284 * Traverse a fully-analyzed tree to verify that parameter symbols 285 * match their types. We need this because some Params might still 286 * be UNKNOWN, if there wasn't anything to force their coercion, 287 * and yet other instances seen later might have gotten coerced. 288 */ 289 static bool 290 check_parameter_resolution_walker(Node *node, ParseState *pstate) 291 { 292 if (node == NULL) 293 return false; 294 if (IsA(node, Param)) 295 { 296 Param *param = (Param *) node; 297 298 if (param->paramkind == PARAM_EXTERN) 299 { 300 VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state; 301 int paramno = param->paramid; 302 303 if (paramno <= 0 || /* shouldn't happen, but... */ 304 paramno > *parstate->numParams) 305 ereport(ERROR, 306 (errcode(ERRCODE_UNDEFINED_PARAMETER), 307 errmsg("there is no parameter $%d", paramno), 308 parser_errposition(pstate, param->location))); 309 310 if (param->paramtype != (*parstate->paramTypes)[paramno - 1]) 311 ereport(ERROR, 312 (errcode(ERRCODE_AMBIGUOUS_PARAMETER), 313 errmsg("could not determine data type of parameter $%d", 314 paramno), 315 parser_errposition(pstate, param->location))); 316 } 317 return false; 318 } 319 if (IsA(node, Query)) 320 { 321 /* Recurse into RTE subquery or not-yet-planned sublink subquery */ 322 return query_tree_walker((Query *) node, 323 check_parameter_resolution_walker, 324 (void *) pstate, 0); 325 } 326 return expression_tree_walker(node, check_parameter_resolution_walker, 327 (void *) pstate); 328 } 329 330 /* 331 * Check to see if a fully-parsed query tree contains any PARAM_EXTERN Params. 332 */ 333 bool 334 query_contains_extern_params(Query *query) 335 { 336 return query_tree_walker(query, 337 query_contains_extern_params_walker, 338 NULL, 0); 339 } 340 341 static bool 342 query_contains_extern_params_walker(Node *node, void *context) 343 { 344 if (node == NULL) 345 return false; 346 if (IsA(node, Param)) 347 { 348 Param *param = (Param *) node; 349 350 if (param->paramkind == PARAM_EXTERN) 351 return true; 352 return false; 353 } 354 if (IsA(node, Query)) 355 { 356 /* Recurse into RTE subquery or not-yet-planned sublink subquery */ 357 return query_tree_walker((Query *) node, 358 query_contains_extern_params_walker, 359 context, 0); 360 } 361 return expression_tree_walker(node, query_contains_extern_params_walker, 362 context); 363 } 364