/* * 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; /* Check parameter number is valid */ 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; param->paramcollid = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; }
/* * sql_fn_param_ref parser callback for ParamRefs ($n symbols) */ static Node * sql_fn_param_ref(ParseState *pstate, ParamRef *pref) { SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state; int paramno = pref->number; Param *param; /* Check parameter number is valid */ if (paramno <= 0 || paramno > pinfo->nargs) return NULL; /* unknown parameter number */ param = makeNode(Param); param->paramkind = PARAM_EXTERN; param->paramid = paramno; param->paramtype = pinfo->argtypes[paramno - 1]; param->paramtypmod = -1; param->paramcollid = get_typcollation(param->paramtype); param->location = pref->location; /* * If we have a function input collation, allow it to override the * type-derived collation for parameter symbols. (XXX perhaps this should * not happen if the type collation is not default?) */ if (OidIsValid(pinfo->collation) && OidIsValid(param->paramcollid)) param->paramcollid = pinfo->collation; 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; int paramno = pref->number; Oid *pptype; Param *param; /* Check parameter number is in range */ if (paramno <= 0 || paramno > INT_MAX / sizeof(Oid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PARAMETER), errmsg("there is no parameter $%d", paramno), parser_errposition(pstate, pref->location))); if (paramno > *parstate->numParams) { /* Need to enlarge param array */ if (*parstate->paramTypes) *parstate->paramTypes = (Oid *) repalloc(*parstate->paramTypes, paramno * sizeof(Oid)); else *parstate->paramTypes = (Oid *) palloc(paramno * sizeof(Oid)); /* Zero out the previously-unreferenced slots */ MemSet(*parstate->paramTypes + *parstate->numParams, 0, (paramno - *parstate->numParams) * sizeof(Oid)); *parstate->numParams = paramno; } /* Locate param's slot in array */ pptype = &(*parstate->paramTypes)[paramno - 1]; /* If not seen before, initialize to UNKNOWN type */ if (*pptype == InvalidOid) *pptype = UNKNOWNOID; param = makeNode(Param); param->paramkind = PARAM_EXTERN; param->paramid = paramno; param->paramtype = *pptype; param->paramtypmod = -1; param->paramcollid = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; }
/* * makeConst - * creates a Const node */ Const * makeConst(Oid consttype, int32 consttypmod, int constlen, Datum constvalue, bool constisnull, bool constbyval) { Const *cnst = makeNode(Const); cnst->consttype = consttype; cnst->consttypmod = consttypmod; cnst->constcollid = get_typcollation(consttype); cnst->constlen = constlen; cnst->constvalue = constvalue; cnst->constisnull = constisnull; cnst->constbyval = constbyval; cnst->location = -1; /* "unknown" */ return cnst; }
/* * 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 || /* shouldn't happen, but... */ 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) { /* We've successfully resolved the type */ paramTypes[paramno - 1] = targetTypeId; } else if (paramTypes[paramno - 1] == targetTypeId) { /* We previously resolved the type, and it matches */ } else { /* Ooops */ 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); /* Use the leftmost of the param's and coercion's locations */ if (location >= 0 && (param->location < 0 || location < param->location)) param->location = location; return (Node *) param; } /* Else signal to proceed with normal coercion */ return NULL; }
/* * Given the result tuple descriptor for a function with OUT parameters, * replace any polymorphic columns (ANYELEMENT etc) with correct data types * deduced from the input arguments. Returns TRUE if able to deduce all types, * FALSE if not. */ static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, Node *call_expr) { int natts = tupdesc->natts; int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; bool have_anynonarray = false; bool have_anyenum = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; Oid anycollation; int i; /* See if there are any polymorphic outputs; quick out if not */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: have_anyelement_result = true; break; case ANYARRAYOID: have_anyarray_result = true; break; case ANYNONARRAYOID: have_anyelement_result = true; have_anynonarray = true; break; case ANYENUMOID: have_anyelement_result = true; have_anyenum = true; break; default: break; } } if (!have_anyelement_result && !have_anyarray_result) return true; /* * Otherwise, extract actual datatype(s) from input arguments. (We assume * the parser already validated consistency of the arguments.) */ if (!call_expr) return false; /* no hope */ for (i = 0; i < nargs; i++) { switch (declared_args->values[i]) { case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: if (!OidIsValid(anyelement_type)) anyelement_type = get_call_expr_argtype(call_expr, i); break; case ANYARRAYOID: if (!OidIsValid(anyarray_type)) anyarray_type = get_call_expr_argtype(call_expr, i); break; default: break; } } /* If nothing found, parser messed up */ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type)) return false; /* If needed, deduce one polymorphic type from the other */ if (have_anyelement_result && !OidIsValid(anyelement_type)) anyelement_type = resolve_generic_type(ANYELEMENTOID, anyarray_type, ANYARRAYOID); if (have_anyarray_result && !OidIsValid(anyarray_type)) anyarray_type = resolve_generic_type(ANYARRAYOID, anyelement_type, ANYELEMENTOID); /* Enforce ANYNONARRAY if needed */ if (have_anynonarray && type_is_array(anyelement_type)) return false; /* Enforce ANYENUM if needed */ if (have_anyenum && !type_is_enum(anyelement_type)) return false; /* * Identify the collation to use for polymorphic OUT parameters. (It'll * necessarily be the same for both anyelement and anyarray.) */ anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type); if (OidIsValid(anycollation)) { /* * The types are collatable, so consider whether to use a nondefault * collation. We do so if we can identify the input collation used * for the function. */ Oid inputcollation = exprInputCollation(call_expr); if (OidIsValid(inputcollation)) anycollation = inputcollation; } /* And finally replace the tuple column types as needed */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyelement_type, -1, 0); TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); break; case ANYARRAYOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyarray_type, -1, 0); TupleDescInitEntryCollation(tupdesc, i + 1, anycollation); break; default: break; } } return true; }