*
* parse_param.cpp
* handle parameters in parser
*
* This code covers two cases that are used within the core backend:
* * a fixed list of parameters with known types
* * an expandable list of parameters whose types can optionally
* be determined from context
* In both cases, only explicit $n references (ParamRef nodes) are supported.
*
* Note that other approaches to parameters are possible using the parser
* hooks defined in ParseState.
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/common/backend/parser/parse_param.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include <limits.h>
#include "catalog/pg_type.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_param.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
typedef struct FixedParamState {
Oid* paramTypes;
int numParams;
} FixedParamState;
* In the varparams case, the caller-supplied OID array (if any) can be
* re-palloc'd larger at need. A zero array entry means that parameter number
* hasn't been seen, while UNKNOWNOID means the parameter has been used but
* its type is not yet known.
*/
typedef struct VarParamState {
Oid** paramTypes;
int* numParams;
#ifndef ENABLE_MULTIPLE_NODES
char **paramTypeNames;
#endif
} VarParamState;
static Node* fixed_paramref_hook(ParseState* pstate, ParamRef* pref);
static Node* variable_paramref_hook(ParseState* pstate, ParamRef* pref);
static Node* variable_coerce_param_hook(
ParseState* pstate, Param* param, Oid targetTypeId, int32 targetTypeMod, int location);
static bool check_parameter_resolution_walker(Node* node, ParseState* pstate);
* Set up to process a query containing references to fixed parameters.
*/
void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams)
{
FixedParamState* parstate = (FixedParamState*)palloc(sizeof(FixedParamState));
parstate->paramTypes = paramTypes;
parstate->numParams = numParams;
pstate->p_ref_hook_state = (void*)parstate;
pstate->p_paramref_hook = fixed_paramref_hook;
}
* Set up to process a query containing references to variable parameters.
*/
void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams)
{
VarParamState* parstate = (VarParamState*)palloc(sizeof(VarParamState));
parstate->paramTypes = paramTypes;
parstate->numParams = numParams;
pstate->p_ref_hook_state = (void*)parstate;
pstate->p_paramref_hook = variable_paramref_hook;
pstate->p_coerce_param_hook = variable_coerce_param_hook;
}
* Transform a ParamRef using fixed parameter types.
*/
static Node* fixed_paramref_hook(ParseState* pstate, ParamRef* pref)
{
FixedParamState* parstate = (FixedParamState*)pstate->p_ref_hook_state;
int paramno = pref->number;
Param* param = NULL;
if (paramno <= 0 || paramno > parstate->numParams || !OidIsValid(parstate->paramTypes[paramno - 1])) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, pref->location)));
}
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = parstate->paramTypes[paramno - 1];
param->paramtypmod = -1;
if (OidIsValid(GetCollationConnection()) && IsSupportCharsetType(param->paramtype)) {
param->paramcollid = GetCollationConnection();
} else {
param->paramcollid = get_typcollation(param->paramtype);
}
param->location = pref->location;
param->tableOfIndexTypeList = NULL;
param->is_bind_param = true;
return (Node*)param;
}
* Transform a ParamRef using variable parameter types.
*
* The only difference here is we must enlarge the parameter type array
* as needed.
*/
static Node* variable_paramref_hook(ParseState* pstate, ParamRef* pref)
{
VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state;
uint32 paramno = pref->number;
Oid* pptype = NULL;
Param* param = NULL;
if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%u", paramno),
parser_errposition(pstate, pref->location)));
}
if (paramno > (uint32)*parstate->numParams) {
errno_t rc = EOK;
if (*parstate->paramTypes) {
*parstate->paramTypes = (Oid*)repalloc(*parstate->paramTypes, paramno * sizeof(Oid));
} else {
*parstate->paramTypes = (Oid*)palloc(paramno * sizeof(Oid));
}
rc = memset_s(*parstate->paramTypes + *parstate->numParams,
(paramno - *parstate->numParams) * sizeof(Oid),
0,
(paramno - *parstate->numParams) * sizeof(Oid));
securec_check(rc, "\0", "\0");
*parstate->numParams = paramno;
}
pptype = &(*parstate->paramTypes)[paramno - 1];
if (*pptype == InvalidOid) {
*pptype = UNKNOWNOID;
}
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = *pptype;
param->paramtypmod = -1;
if (OidIsValid(GetCollationConnection()) && IsSupportCharsetType(param->paramtype)) {
param->paramcollid = GetCollationConnection();
} else {
param->paramcollid = get_typcollation(param->paramtype);
}
param->location = pref->location;
param->tableOfIndexTypeList = NULL;
param->is_bind_param = true;
return (Node*)param;
}
* Coerce a Param to a query-requested datatype, in the varparams case.
*/
static Node* variable_coerce_param_hook(
ParseState* pstate, Param* param, Oid targetTypeId, int32 targetTypeMod, int location)
{
if (param->paramkind == PARAM_EXTERN && param->paramtype == UNKNOWNOID) {
* Input is a Param of previously undetermined type, and we want to
* update our knowledge of the Param's type.
*/
VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state;
Oid* paramTypes = *parstate->paramTypes;
int paramno = param->paramid;
if (paramno <= 0 ||
paramno > *parstate->numParams) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
}
if (paramTypes[paramno - 1] == UNKNOWNOID) {
paramTypes[paramno - 1] = targetTypeId;
} else if (paramTypes[paramno - 1] == targetTypeId) {
} else {
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("inconsistent types deduced for parameter $%d", paramno),
errdetail("%s versus %s", format_type_be(paramTypes[paramno - 1]), format_type_be(targetTypeId)),
parser_errposition(pstate, param->location)));
}
param->paramtype = targetTypeId;
* Note: it is tempting here to set the Param's paramtypmod to
* targetTypeMod, but that is probably unwise because we have no
* infrastructure that enforces that the value delivered for a Param
* will match any particular typmod. Leaving it -1 ensures that a
* run-time length check/coercion will occur if needed.
*/
param->paramtypmod = -1;
* This module always sets a Param's collation to be the default for
* its datatype. If that's not what you want, you should be using the
* more general parser substitution hooks.
*/
param->paramcollid = get_typcollation(param->paramtype);
if (location >= 0 && (param->location < 0 || location < param->location)) {
param->location = location;
}
return (Node*)param;
}
return NULL;
}
* Check for consistent assignment of variable parameters after completion
* of parsing with parse_variable_parameters.
*
* Note: this code intentionally does not check that all parameter positions
* were used, nor that all got non-UNKNOWN types assigned. Caller of parser
* should enforce that if it's important.
*/
void check_variable_parameters(ParseState* pstate, Query* query)
{
VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state;
if (*parstate->numParams > 0) {
(void)query_tree_walker(query, (bool (*)())check_parameter_resolution_walker, (void*)pstate, 0);
}
}
* Traverse a fully-analyzed tree to verify that parameter symbols
* match their types. We need this because some Params might still
* be UNKNOWN, if there wasn't anything to force their coercion,
* and yet other instances seen later might have gotten coerced.
*/
static bool check_parameter_resolution_walker(Node* node, ParseState* pstate)
{
if (node == NULL) {
return false;
}
if (IsA(node, Param)) {
Param* param = (Param*)node;
if (param->paramkind == PARAM_EXTERN) {
VarParamState* parstate = (VarParamState*)pstate->p_ref_hook_state;
int paramno = param->paramid;
if (paramno <= 0 ||
paramno > *parstate->numParams) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("there is no parameter $%d", paramno),
parser_errposition(pstate, param->location)));
}
if (param->paramtype != (*parstate->paramTypes)[paramno - 1]) {
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_PARAMETER),
errmsg("could not determine data type of parameter $%d", paramno),
parser_errposition(pstate, param->location)));
}
}
return false;
}
if (IsA(node, Query)) {
return query_tree_walker((Query*)node, (bool (*)())check_parameter_resolution_walker, (void*)pstate, 0);
}
return expression_tree_walker(node, (bool (*)())check_parameter_resolution_walker, (void*)pstate);
}