#include "parser/parse_node.h"
Go to the source code of this file.
Typedefs | |
typedef char | TYPCATEGORY |
typedef enum CoercionPathType | CoercionPathType |
Enumerations | |
enum | CoercionPathType { COERCION_PATH_NONE, COERCION_PATH_FUNC, COERCION_PATH_RELABELTYPE, COERCION_PATH_ARRAYCOERCE, COERCION_PATH_COERCEVIAIO } |
Functions | |
bool | IsBinaryCoercible (Oid srctype, Oid targettype) |
bool | IsPreferredType (TYPCATEGORY category, Oid type) |
TYPCATEGORY | TypeCategory (Oid type) |
Node * | coerce_to_target_type (ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location) |
bool | can_coerce_type (int nargs, Oid *input_typeids, Oid *target_typeids, CoercionContext ccontext) |
Node * | coerce_type (ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod, CoercionContext ccontext, CoercionForm cformat, int location) |
Node * | coerce_to_domain (Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionForm cformat, int location, bool hideInputCoercion, bool lengthCoercionDone) |
Node * | coerce_to_boolean (ParseState *pstate, Node *node, const char *constructName) |
Node * | coerce_to_specific_type (ParseState *pstate, Node *node, Oid targetTypeId, const char *constructName) |
int | parser_coercion_errposition (ParseState *pstate, int coerce_location, Node *input_expr) |
Oid | select_common_type (ParseState *pstate, List *exprs, const char *context, Node **which_expr) |
Node * | coerce_to_common_type (ParseState *pstate, Node *node, Oid targetTypeId, const char *context) |
bool | check_generic_type_consistency (Oid *actual_arg_types, Oid *declared_arg_types, int nargs) |
Oid | enforce_generic_type_consistency (Oid *actual_arg_types, Oid *declared_arg_types, int nargs, Oid rettype, bool allow_poly) |
Oid | resolve_generic_type (Oid declared_type, Oid context_actual_type, Oid context_declared_type) |
CoercionPathType | find_coercion_pathway (Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid) |
CoercionPathType | find_typmod_coercion_function (Oid typeId, Oid *funcid) |
typedef enum CoercionPathType CoercionPathType |
typedef char TYPCATEGORY |
Definition at line 21 of file parse_coerce.h.
enum CoercionPathType |
COERCION_PATH_NONE | |
COERCION_PATH_FUNC | |
COERCION_PATH_RELABELTYPE | |
COERCION_PATH_ARRAYCOERCE | |
COERCION_PATH_COERCEVIAIO |
Definition at line 24 of file parse_coerce.h.
{ COERCION_PATH_NONE, /* failed to find any coercion pathway */ COERCION_PATH_FUNC, /* apply the specified coercion function */ COERCION_PATH_RELABELTYPE, /* binary-compatible cast, no function */ COERCION_PATH_ARRAYCOERCE, /* need an ArrayCoerceExpr node */ COERCION_PATH_COERCEVIAIO /* need a CoerceViaIO node */ } CoercionPathType;
bool can_coerce_type | ( | int | nargs, | |
Oid * | input_typeids, | |||
Oid * | target_typeids, | |||
CoercionContext | ccontext | |||
) |
Definition at line 489 of file parse_coerce.c.
References ANYOID, check_generic_type_consistency(), COERCION_PATH_NONE, find_coercion_pathway(), i, is_complex_array(), ISCOMPLEX, IsPolymorphicType, RECORDARRAYOID, RECORDOID, typeInheritsFrom(), typeIsOfTypedTable(), and UNKNOWNOID.
Referenced by ATAddForeignKeyConstraint(), coerce_to_common_type(), coerce_to_target_type(), func_match_argtypes(), func_select_candidate(), and select_common_type().
{ bool have_generics = false; int i; /* run through argument list... */ for (i = 0; i < nargs; i++) { Oid inputTypeId = input_typeids[i]; Oid targetTypeId = target_typeids[i]; CoercionPathType pathtype; Oid funcId; /* no problem if same type */ if (inputTypeId == targetTypeId) continue; /* accept if target is ANY */ if (targetTypeId == ANYOID) continue; /* accept if target is polymorphic, for now */ if (IsPolymorphicType(targetTypeId)) { have_generics = true; /* do more checking later */ continue; } /* * If input is an untyped string constant, assume we can convert it to * anything. */ if (inputTypeId == UNKNOWNOID) continue; /* * If pg_cast shows that we can coerce, accept. This test now covers * both binary-compatible and coercion-function cases. */ pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) continue; /* * If input is RECORD and target is a composite type, assume we can * coerce (may need tighter checking here) */ if (inputTypeId == RECORDOID && ISCOMPLEX(targetTypeId)) continue; /* * If input is a composite type and target is RECORD, accept */ if (targetTypeId == RECORDOID && ISCOMPLEX(inputTypeId)) continue; #ifdef NOT_USED /* not implemented yet */ /* * If input is record[] and target is a composite array type, assume * we can coerce (may need tighter checking here) */ if (inputTypeId == RECORDARRAYOID && is_complex_array(targetTypeId)) continue; #endif /* * If input is a composite array type and target is record[], accept */ if (targetTypeId == RECORDARRAYOID && is_complex_array(inputTypeId)) continue; /* * If input is a class type that inherits from target, accept */ if (typeInheritsFrom(inputTypeId, targetTypeId) || typeIsOfTypedTable(inputTypeId, targetTypeId)) continue; /* * Else, cannot coerce at this argument position */ return false; } /* If we found any generic argument types, cross-check them */ if (have_generics) { if (!check_generic_type_consistency(input_typeids, target_typeids, nargs)) return false; } return true; }
Definition at line 1346 of file parse_coerce.c.
References ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYRANGEOID, get_element_type(), get_range_subtype(), getBaseType(), OidIsValid, type_is_array_domain, type_is_enum(), and UNKNOWNOID.
Referenced by can_coerce_type().
{ int j; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid array_typelem; Oid range_typeid = InvalidOid; Oid range_typelem; bool have_anyelement = false; bool have_anynonarray = false; bool have_anyenum = false; /* * Loop through the arguments to see if we have any that are polymorphic. * If so, require the actual types to be consistent. */ for (j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; if (decl_type == ANYELEMENTOID || decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) { have_anyelement = true; if (decl_type == ANYNONARRAYOID) have_anynonarray = true; else if (decl_type == ANYENUMOID) have_anyenum = true; if (actual_type == UNKNOWNOID) continue; if (OidIsValid(elem_typeid) && actual_type != elem_typeid) return false; elem_typeid = actual_type; } else if (decl_type == ANYARRAYOID) { if (actual_type == UNKNOWNOID) continue; actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(array_typeid) && actual_type != array_typeid) return false; array_typeid = actual_type; } else if (decl_type == ANYRANGEOID) { if (actual_type == UNKNOWNOID) continue; actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(range_typeid) && actual_type != range_typeid) return false; range_typeid = actual_type; } } /* Get the element type based on the array type, if we have one */ if (OidIsValid(array_typeid)) { if (array_typeid == ANYARRAYOID) { /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ if (have_anyelement) return false; return true; } array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem)) return false; /* should be an array, but isn't */ if (!OidIsValid(elem_typeid)) { /* * if we don't have an element type yet, use the one we just got */ elem_typeid = array_typelem; } else if (array_typelem != elem_typeid) { /* otherwise, they better match */ return false; } } /* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid)) { range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem)) return false; /* should be a range, but isn't */ if (!OidIsValid(elem_typeid)) { /* * if we don't have an element type yet, use the one we just got */ elem_typeid = range_typelem; } else if (range_typelem != elem_typeid) { /* otherwise, they better match */ return false; } } if (have_anynonarray) { /* require the element type to not be an array or domain over array */ if (type_is_array_domain(elem_typeid)) return false; } if (have_anyenum) { /* require the element type to be an enum */ if (!type_is_enum(elem_typeid)) return false; } /* Looks valid */ return true; }
Node* coerce_to_boolean | ( | ParseState * | pstate, | |
Node * | node, | |||
const char * | constructName | |||
) |
Definition at line 1024 of file parse_coerce.c.
References BOOLOID, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ereport, errcode(), errmsg(), ERROR, expression_returns_set(), exprLocation(), exprType(), format_type_be(), NULL, and parser_errposition().
Referenced by cookConstraint(), domainAddConstraint(), transformAExprAnd(), transformAExprIn(), transformAExprNot(), transformAExprOr(), transformBooleanTest(), transformCaseExpr(), transformJoinUsingClause(), transformWhereClause(), and transformXmlExpr().
{ Oid inputTypeId = exprType(node); if (inputTypeId != BOOLOID) { Node *newnode; newnode = coerce_to_target_type(pstate, node, inputTypeId, BOOLOID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (newnode == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg WHERE */ errmsg("argument of %s must be type boolean, not type %s", constructName, format_type_be(inputTypeId)), parser_errposition(pstate, exprLocation(node)))); node = newnode; } if (expression_returns_set(node)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: %s is name of a SQL construct, eg WHERE */ errmsg("argument of %s must not return a set", constructName), parser_errposition(pstate, exprLocation(node)))); return node; }
Node* coerce_to_common_type | ( | ParseState * | pstate, | |
Node * | node, | |||
Oid | targetTypeId, | |||
const char * | context | |||
) |
Definition at line 1275 of file parse_coerce.c.
References can_coerce_type(), COERCE_IMPLICIT_CAST, coerce_type(), COERCION_IMPLICIT, ereport, errcode(), errmsg(), ERROR, exprLocation(), exprType(), format_type_be(), and parser_errposition().
Referenced by generate_setop_tlist(), transformAExprIn(), transformArrayExpr(), transformCaseExpr(), transformCoalesceExpr(), transformMinMaxExpr(), transformSetOperationTree(), and transformValuesClause().
{ Oid inputTypeId = exprType(node); if (inputTypeId == targetTypeId) return node; /* no work */ if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT)) node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1, COERCION_IMPLICIT, COERCE_IMPLICIT_CAST, -1); else ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), /* translator: first %s is name of a SQL construct, eg CASE */ errmsg("%s could not convert type %s to %s", context, format_type_be(inputTypeId), format_type_be(targetTypeId)), parser_errposition(pstate, exprLocation(node)))); return node; }
Node* coerce_to_domain | ( | Node * | arg, | |
Oid | baseTypeId, | |||
int32 | baseTypeMod, | |||
Oid | typeId, | |||
CoercionForm | cformat, | |||
int | location, | |||
bool | hideInputCoercion, | |||
bool | lengthCoercionDone | |||
) |
Definition at line 610 of file parse_coerce.c.
References CoerceToDomain::arg, COERCE_IMPLICIT_CAST, coerce_type_typmod(), CoerceToDomain::coercionformat, getBaseTypeAndTypmod(), hide_coercion_node(), InvalidOid, CoerceToDomain::location, makeNode, CoerceToDomain::resulttype, and CoerceToDomain::resulttypmod.
Referenced by coerce_type(), expand_targetlist(), ReplaceVarsFromTargetList_callback(), rewriteTargetListIU(), and rewriteValuesRTE().
{ CoerceToDomain *result; /* Get the base type if it hasn't been supplied */ if (baseTypeId == InvalidOid) baseTypeId = getBaseTypeAndTypmod(typeId, &baseTypeMod); /* If it isn't a domain, return the node as it was passed in */ if (baseTypeId == typeId) return arg; /* Suppress display of nested coercion steps */ if (hideInputCoercion) hide_coercion_node(arg); /* * If the domain applies a typmod to its base type, build the appropriate * coercion step. Mark it implicit for display purposes, because we don't * want it shown separately by ruleutils.c; but the isExplicit flag passed * to the conversion function depends on the manner in which the domain * coercion is invoked, so that the semantics of implicit and explicit * coercion differ. (Is that really the behavior we want?) * * NOTE: because we apply this as part of the fixed expression structure, * ALTER DOMAIN cannot alter the typtypmod. But it's unclear that that * would be safe to do anyway, without lots of knowledge about what the * base type thinks the typmod means. */ if (!lengthCoercionDone) { if (baseTypeMod >= 0) arg = coerce_type_typmod(arg, baseTypeId, baseTypeMod, COERCE_IMPLICIT_CAST, location, (cformat != COERCE_IMPLICIT_CAST), false); } /* * Now build the domain coercion node. This represents run-time checking * of any constraints currently attached to the domain. This also ensures * that the expression is properly labeled as to result type. */ result = makeNode(CoerceToDomain); result->arg = (Expr *) arg; result->resulttype = typeId; result->resulttypmod = -1; /* currently, always -1 for domains */ /* resultcollid will be set by parse_collate.c */ result->coercionformat = cformat; result->location = location; return (Node *) result; }
Node* coerce_to_specific_type | ( | ParseState * | pstate, | |
Node * | node, | |||
Oid | targetTypeId, | |||
const char * | constructName | |||
) |
Definition at line 1070 of file parse_coerce.c.
References COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ereport, errcode(), errmsg(), ERROR, expression_returns_set(), exprLocation(), exprType(), format_type_be(), NULL, and parser_errposition().
Referenced by examine_parameter_list(), transformFrameOffset(), transformLimitClause(), transformXmlExpr(), and transformXmlSerialize().
{ Oid inputTypeId = exprType(node); if (inputTypeId != targetTypeId) { Node *newnode; newnode = coerce_to_target_type(pstate, node, inputTypeId, targetTypeId, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST, -1); if (newnode == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: first %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must be type %s, not type %s", constructName, format_type_be(targetTypeId), format_type_be(inputTypeId)), parser_errposition(pstate, exprLocation(node)))); node = newnode; } if (expression_returns_set(node)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /* translator: %s is name of a SQL construct, eg LIMIT */ errmsg("argument of %s must not return a set", constructName), parser_errposition(pstate, exprLocation(node)))); return node; }
Node* coerce_to_target_type | ( | ParseState * | pstate, | |
Node * | expr, | |||
Oid | exprtype, | |||
Oid | targettype, | |||
int32 | targettypmod, | |||
CoercionContext | ccontext, | |||
CoercionForm | cformat, | |||
int | location | |||
) |
Definition at line 76 of file parse_coerce.c.
References CollateExpr::arg, arg, can_coerce_type(), COERCE_IMPLICIT_CAST, coerce_type(), coerce_type_typmod(), CollateExpr::collOid, IsA, CollateExpr::location, and makeNode.
Referenced by ATExecAddColumn(), ATExecAlterColumnType(), ATPrepAlterColumnType(), build_column_default(), coerce_record_to_complex(), coerce_to_boolean(), coerce_to_specific_type(), cookDefault(), EvaluateParams(), transformArrayExpr(), transformArraySubscripts(), transformAssignedExpr(), transformAssignmentIndirection(), transformAssignmentSubscripts(), transformTypeCast(), and transformXmlSerialize().
{ Node *result; Node *origexpr; if (!can_coerce_type(1, &exprtype, &targettype, ccontext)) return NULL; /* * If the input has a CollateExpr at the top, strip it off, perform the * coercion, and put a new one back on. This is annoying since it * duplicates logic in coerce_type, but if we don't do this then it's too * hard to tell whether coerce_type actually changed anything, and we * *must* know that to avoid possibly calling hide_coercion_node on * something that wasn't generated by coerce_type. Note that if there are * multiple stacked CollateExprs, we just discard all but the topmost. */ origexpr = expr; while (expr && IsA(expr, CollateExpr)) expr = (Node *) ((CollateExpr *) expr)->arg; result = coerce_type(pstate, expr, exprtype, targettype, targettypmod, ccontext, cformat, location); /* * If the target is a fixed-length type, it may need a length coercion as * well as a type coercion. If we find ourselves adding both, force the * inner coercion node to implicit display form. */ result = coerce_type_typmod(result, targettype, targettypmod, cformat, location, (cformat != COERCE_IMPLICIT_CAST), (result != expr && !IsA(result, Const))); if (expr != origexpr) { /* Reinstall top CollateExpr */ CollateExpr *coll = (CollateExpr *) origexpr; CollateExpr *newcoll = makeNode(CollateExpr); newcoll->arg = (Expr *) result; newcoll->collOid = coll->collOid; newcoll->location = coll->location; result = (Node *) newcoll; } return result; }
Node* coerce_type | ( | ParseState * | pstate, | |
Node * | node, | |||
Oid | inputTypeId, | |||
Oid | targetTypeId, | |||
int32 | targetTypeMod, | |||
CoercionContext | ccontext, | |||
CoercionForm | cformat, | |||
int | location | |||
) |
Definition at line 155 of file parse_coerce.c.
References ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYOID, ANYRANGEOID, ConvertRowtypeExpr::arg, CollateExpr::arg, build_coercion_expression(), cancel_parser_errposition_callback(), COERCE_IMPLICIT_CAST, coerce_record_to_complex(), coerce_to_domain(), coerce_type(), COERCION_PATH_NONE, COERCION_PATH_RELABELTYPE, CollateExpr::collOid, Const::constbyval, Const::constcollid, Const::constisnull, Const::constlen, Const::consttype, Const::consttypmod, Const::constvalue, ConvertRowtypeExpr::convertformat, DatumGetCString, elog, ERROR, exprIsLengthCoercion(), find_coercion_pathway(), format_type_be(), getBaseType(), getBaseTypeAndTypmod(), INTERVALOID, InvalidOid, is_complex_array(), IsA, ISCOMPLEX, ConvertRowtypeExpr::location, CollateExpr::location, Const::location, RelabelType::location, makeNode, makeRelabelType(), NULL, ParseState::p_coerce_param_hook, RECORDARRAYOID, RECORDOID, ReleaseSysCache(), ConvertRowtypeExpr::resulttype, setup_parser_errposition_callback(), stringTypeDatum(), typeByVal(), typeidType(), typeInheritsFrom(), typeIsOfTypedTable(), typeLen(), typeTypeCollation(), and UNKNOWNOID.
Referenced by addTargetToGroupList(), addTargetToSortList(), buildMergedJoinVar(), coerce_to_common_type(), coerce_to_target_type(), coerce_type(), make_fn_arguments(), and ParseFuncOrColumn().
{ Node *result; CoercionPathType pathtype; Oid funcId; if (targetTypeId == inputTypeId || node == NULL) { /* no conversion needed */ return node; } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || targetTypeId == ANYNONARRAYOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. * * Note: by returning the unmodified node here, we are saying that * it's OK to treat an UNKNOWN constant as a valid input for a * function accepting ANY, ANYELEMENT, or ANYNONARRAY. This should be * all right, since an UNKNOWN value is still a perfectly valid Datum. * * NB: we do NOT want a RelabelType here: the exposed type of the * function argument must be its actual type, not the polymorphic * pseudotype. */ return node; } if (targetTypeId == ANYARRAYOID || targetTypeId == ANYENUMOID || targetTypeId == ANYRANGEOID) { /* * Assume can_coerce_type verified that implicit coercion is okay. * * These cases are unlike the ones above because the exposed type of * the argument must be an actual array, enum, or range type. In * particular the argument must *not* be an UNKNOWN constant. If it * is, we just fall through; below, we'll call anyarray_in, * anyenum_in, or anyrange_in, which will produce an error. Also, if * what we have is a domain over array, enum, or range, we have to * relabel it to its base type. * * Note: currently, we can't actually see a domain-over-enum here, * since the other functions in this file will not match such a * parameter to ANYENUM. But that should get changed eventually. */ if (inputTypeId != UNKNOWNOID) { Oid baseTypeId = getBaseType(inputTypeId); if (baseTypeId != inputTypeId) { RelabelType *r = makeRelabelType((Expr *) node, baseTypeId, -1, InvalidOid, cformat); r->location = location; return (Node *) r; } /* Not a domain type, so return it as-is */ return node; } } if (inputTypeId == UNKNOWNOID && IsA(node, Const)) { /* * Input is a string constant with previously undetermined type. Apply * the target type's typinput function to it to produce a constant of * the target type. * * NOTE: this case cannot be folded together with the other * constant-input case, since the typinput function does not * necessarily behave the same as a type conversion function. For * example, int4's typinput function will reject "1.2", whereas * float-to-int type conversion will round to integer. * * XXX if the typinput function is not immutable, we really ought to * postpone evaluation of the function call until runtime. But there * is no way to represent a typinput function call as an expression * tree, because C-string values are not Datums. (XXX This *is* * possible as of 7.3, do we want to do it?) */ Const *con = (Const *) node; Const *newcon = makeNode(Const); Oid baseTypeId; int32 baseTypeMod; int32 inputTypeMod; Type targetType; ParseCallbackState pcbstate; /* * If the target type is a domain, we want to call its base type's * input routine, not domain_in(). This is to avoid premature failure * when the domain applies a typmod: existing input routines follow * implicit-coercion semantics for length checks, which is not always * what we want here. The needed check will be applied properly * inside coerce_to_domain(). */ baseTypeMod = targetTypeMod; baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); /* * For most types we pass typmod -1 to the input routine, because * existing input routines follow implicit-coercion semantics for * length checks, which is not always what we want here. Any length * constraint will be applied later by our caller. An exception * however is the INTERVAL type, for which we *must* pass the typmod * or it won't be able to obey the bizarre SQL-spec input rules. (Ugly * as sin, but so is this part of the spec...) */ if (baseTypeId == INTERVALOID) inputTypeMod = baseTypeMod; else inputTypeMod = -1; targetType = typeidType(baseTypeId); newcon->consttype = baseTypeId; newcon->consttypmod = inputTypeMod; newcon->constcollid = typeTypeCollation(targetType); newcon->constlen = typeLen(targetType); newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; /* * We use the original literal's location regardless of the position * of the coercion. This is a change from pre-9.2 behavior, meant to * simplify life for pg_stat_statements. */ newcon->location = con->location; /* * Set up to point at the constant's text if the input routine throws * an error. */ setup_parser_errposition_callback(&pcbstate, pstate, con->location); /* * We assume here that UNKNOWN's internal representation is the same * as CSTRING. */ if (!con->constisnull) newcon->constvalue = stringTypeDatum(targetType, DatumGetCString(con->constvalue), inputTypeMod); else newcon->constvalue = stringTypeDatum(targetType, NULL, inputTypeMod); cancel_parser_errposition_callback(&pcbstate); result = (Node *) newcon; /* If target is a domain, apply constraints. */ if (baseTypeId != targetTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, cformat, location, false, false); ReleaseSysCache(targetType); return result; } if (IsA(node, Param) && pstate != NULL && pstate->p_coerce_param_hook != NULL) { /* * Allow the CoerceParamHook to decide what happens. It can return a * transformed node (very possibly the same Param node), or return * NULL to indicate we should proceed with normal coercion. */ result = (*pstate->p_coerce_param_hook) (pstate, (Param *) node, targetTypeId, targetTypeMod, location); if (result) return result; } if (IsA(node, CollateExpr)) { /* * If we have a COLLATE clause, we have to push the coercion * underneath the COLLATE. This is really ugly, but there is little * choice because the above hacks on Consts and Params wouldn't happen * otherwise. This kluge has consequences in coerce_to_target_type. */ CollateExpr *coll = (CollateExpr *) node; CollateExpr *newcoll = makeNode(CollateExpr); newcoll->arg = (Expr *) coerce_type(pstate, (Node *) coll->arg, inputTypeId, targetTypeId, targetTypeMod, ccontext, cformat, location); newcoll->collOid = coll->collOid; newcoll->location = coll->location; return (Node *) newcoll; } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) { if (pathtype != COERCION_PATH_RELABELTYPE) { /* * Generate an expression tree representing run-time application * of the conversion function. If we are dealing with a domain * target type, the conversion function will yield the base type, * and we need to extract the correct typmod to use from the * domain's typtypmod. */ Oid baseTypeId; int32 baseTypeMod; baseTypeMod = targetTypeMod; baseTypeId = getBaseTypeAndTypmod(targetTypeId, &baseTypeMod); result = build_coercion_expression(node, pathtype, funcId, baseTypeId, baseTypeMod, cformat, location, (cformat != COERCE_IMPLICIT_CAST)); /* * If domain, coerce to the domain type and relabel with domain * type ID. We can skip the internal length-coercion step if the * selected coercion function was a type-and-length coercion. */ if (targetTypeId != baseTypeId) result = coerce_to_domain(result, baseTypeId, baseTypeMod, targetTypeId, cformat, location, true, exprIsLengthCoercion(result, NULL)); } else { /* * We don't need to do a physical conversion, but we do need to * attach a RelabelType node so that the expression will be seen * to have the intended type when inspected by higher-level code. * * Also, domains may have value restrictions beyond the base type * that must be accounted for. If the destination is a domain * then we won't need a RelabelType node. */ result = coerce_to_domain(node, InvalidOid, -1, targetTypeId, cformat, location, false, false); if (result == node) { /* * XXX could we label result with exprTypmod(node) instead of * default -1 typmod, to save a possible length-coercion * later? Would work if both types have same interpretation of * typmod, which is likely but not certain. */ RelabelType *r = makeRelabelType((Expr *) result, targetTypeId, -1, InvalidOid, cformat); r->location = location; result = (Node *) r; } } return result; } if (inputTypeId == RECORDOID && ISCOMPLEX(targetTypeId)) { /* Coerce a RECORD to a specific complex type */ return coerce_record_to_complex(pstate, node, targetTypeId, ccontext, cformat, location); } if (targetTypeId == RECORDOID && ISCOMPLEX(inputTypeId)) { /* Coerce a specific complex type to RECORD */ /* NB: we do NOT want a RelabelType here */ return node; } #ifdef NOT_USED if (inputTypeId == RECORDARRAYOID && is_complex_array(targetTypeId)) { /* Coerce record[] to a specific complex array type */ /* not implemented yet ... */ } #endif if (targetTypeId == RECORDARRAYOID && is_complex_array(inputTypeId)) { /* Coerce a specific complex array type to record[] */ /* NB: we do NOT want a RelabelType here */ return node; } if (typeInheritsFrom(inputTypeId, targetTypeId) || typeIsOfTypedTable(inputTypeId, targetTypeId)) { /* * Input class type is a subclass of target, so generate an * appropriate runtime conversion (removing unneeded columns and * possibly rearranging the ones that are wanted). */ ConvertRowtypeExpr *r = makeNode(ConvertRowtypeExpr); r->arg = (Expr *) node; r->resulttype = targetTypeId; r->convertformat = cformat; r->location = location; return (Node *) r; } /* If we get here, caller blew it */ elog(ERROR, "failed to find conversion function from %s to %s", format_type_be(inputTypeId), format_type_be(targetTypeId)); return NULL; /* keep compiler quiet */ }
Oid enforce_generic_type_consistency | ( | Oid * | actual_arg_types, | |
Oid * | declared_arg_types, | |||
int | nargs, | |||
Oid | rettype, | |||
bool | allow_poly | |||
) |
Definition at line 1543 of file parse_coerce.c.
References ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYRANGEOID, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_be(), get_array_type(), get_element_type(), get_range_subtype(), getBaseType(), OidIsValid, type_is_array_domain, type_is_enum(), and UNKNOWNOID.
Referenced by count_agg_clauses_walker(), ExecInitAgg(), initialize_peragg(), lookup_agg_function(), make_op(), make_scalar_array_op(), ParseFuncOrColumn(), and recheck_cast_function_args().
{ int j; bool have_generics = false; bool have_unknowns = false; Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid range_typeid = InvalidOid; Oid array_typelem; Oid range_typelem; bool have_anyelement = (rettype == ANYELEMENTOID || rettype == ANYNONARRAYOID || rettype == ANYENUMOID); bool have_anynonarray = (rettype == ANYNONARRAYOID); bool have_anyenum = (rettype == ANYENUMOID); /* * Loop through the arguments to see if we have any that are polymorphic. * If so, require the actual types to be consistent. */ for (j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; if (decl_type == ANYELEMENTOID || decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) { have_generics = have_anyelement = true; if (decl_type == ANYNONARRAYOID) have_anynonarray = true; else if (decl_type == ANYENUMOID) have_anyenum = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) continue; /* no new information here */ if (OidIsValid(elem_typeid) && actual_type != elem_typeid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("arguments declared \"anyelement\" are not all alike"), errdetail("%s versus %s", format_type_be(elem_typeid), format_type_be(actual_type)))); elem_typeid = actual_type; } else if (decl_type == ANYARRAYOID) { have_generics = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) continue; /* no new information here */ actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(array_typeid) && actual_type != array_typeid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("arguments declared \"anyarray\" are not all alike"), errdetail("%s versus %s", format_type_be(array_typeid), format_type_be(actual_type)))); array_typeid = actual_type; } else if (decl_type == ANYRANGEOID) { have_generics = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; continue; } if (allow_poly && decl_type == actual_type) continue; /* no new information here */ actual_type = getBaseType(actual_type); /* flatten domains */ if (OidIsValid(range_typeid) && actual_type != range_typeid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("arguments declared \"anyrange\" are not all alike"), errdetail("%s versus %s", format_type_be(range_typeid), format_type_be(actual_type)))); range_typeid = actual_type; } } /* * Fast Track: if none of the arguments are polymorphic, return the * unmodified rettype. We assume it can't be polymorphic either. */ if (!have_generics) return rettype; /* Get the element type based on the array type, if we have one */ if (OidIsValid(array_typeid)) { if (array_typeid == ANYARRAYOID && !have_anyelement) { /* Special case for ANYARRAY input: okay iff no ANYELEMENT */ array_typelem = ANYELEMENTOID; } else { array_typelem = get_element_type(array_typeid); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not an array but type %s", format_type_be(array_typeid)))); } if (!OidIsValid(elem_typeid)) { /* * if we don't have an element type yet, use the one we just got */ elem_typeid = array_typelem; } else if (array_typelem != elem_typeid) { /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not consistent with argument declared \"anyelement\""), errdetail("%s versus %s", format_type_be(array_typeid), format_type_be(elem_typeid)))); } } /* Get the element type based on the range type, if we have one */ if (OidIsValid(range_typeid)) { if (range_typeid == ANYRANGEOID && !have_anyelement) { /* Special case for ANYRANGE input: okay iff no ANYELEMENT */ range_typelem = ANYELEMENTOID; } else { range_typelem = get_range_subtype(range_typeid); if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyrange\" is not a range type but type %s", format_type_be(range_typeid)))); } if (!OidIsValid(elem_typeid)) { /* * if we don't have an element type yet, use the one we just got */ elem_typeid = range_typelem; } else if (range_typelem != elem_typeid) { /* otherwise, they better match */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyrange\" is not consistent with argument declared \"anyelement\""), errdetail("%s versus %s", format_type_be(range_typeid), format_type_be(elem_typeid)))); } } if (!OidIsValid(elem_typeid)) { if (allow_poly) { elem_typeid = ANYELEMENTOID; array_typeid = ANYARRAYOID; range_typeid = ANYRANGEOID; } else { /* Only way to get here is if all the generic args are UNKNOWN */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("could not determine polymorphic type because input has type \"unknown\""))); } } if (have_anynonarray && elem_typeid != ANYELEMENTOID) { /* require the element type to not be an array or domain over array */ if (type_is_array_domain(elem_typeid)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type matched to anynonarray is an array type: %s", format_type_be(elem_typeid)))); } if (have_anyenum && elem_typeid != ANYELEMENTOID) { /* require the element type to be an enum */ if (!type_is_enum(elem_typeid)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("type matched to anyenum is not an enum type: %s", format_type_be(elem_typeid)))); } /* * If we had any unknown inputs, re-scan to assign correct types */ if (have_unknowns) { for (j = 0; j < nargs; j++) { Oid decl_type = declared_arg_types[j]; Oid actual_type = actual_arg_types[j]; if (actual_type != UNKNOWNOID) continue; if (decl_type == ANYELEMENTOID || decl_type == ANYNONARRAYOID || decl_type == ANYENUMOID) declared_arg_types[j] = elem_typeid; else if (decl_type == ANYARRAYOID) { if (!OidIsValid(array_typeid)) { array_typeid = get_array_type(elem_typeid); if (!OidIsValid(array_typeid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(elem_typeid)))); } declared_arg_types[j] = array_typeid; } else if (decl_type == ANYRANGEOID) { if (!OidIsValid(range_typeid)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find range type for data type %s", format_type_be(elem_typeid)))); } declared_arg_types[j] = range_typeid; } } } /* if we return ANYARRAY use the appropriate argument type */ if (rettype == ANYARRAYOID) { if (!OidIsValid(array_typeid)) { array_typeid = get_array_type(elem_typeid); if (!OidIsValid(array_typeid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(elem_typeid)))); } return array_typeid; } /* if we return ANYRANGE use the appropriate argument type */ if (rettype == ANYRANGEOID) { if (!OidIsValid(range_typeid)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find range type for data type %s", format_type_be(elem_typeid)))); } return range_typeid; } /* if we return ANYELEMENT use the appropriate argument type */ if (rettype == ANYELEMENTOID || rettype == ANYNONARRAYOID || rettype == ANYENUMOID) return elem_typeid; /* we don't return a generic type; send back the original return type */ return rettype; }
CoercionPathType find_coercion_pathway | ( | Oid | targetTypeId, | |
Oid | sourceTypeId, | |||
CoercionContext | ccontext, | |||
Oid * | funcid | |||
) |
Definition at line 2095 of file parse_coerce.c.
References CASTSOURCETARGET, COERCION_ASSIGNMENT, COERCION_CODE_ASSIGNMENT, COERCION_CODE_EXPLICIT, COERCION_CODE_IMPLICIT, COERCION_EXPLICIT, COERCION_METHOD_BINARY, COERCION_METHOD_FUNCTION, COERCION_METHOD_INOUT, COERCION_PATH_ARRAYCOERCE, COERCION_PATH_COERCEVIAIO, COERCION_PATH_NONE, elog, ERROR, find_coercion_pathway(), get_base_element_type(), get_element_type(), getBaseType(), GETSTRUCT, HeapTupleIsValid, INT2VECTOROID, InvalidOid, ObjectIdGetDatum, OidIsValid, OIDVECTOROID, ReleaseSysCache(), SearchSysCache2, TYPCATEGORY_STRING, and TypeCategory().
Referenced by can_coerce_type(), coerce_type(), find_coercion_pathway(), findFkeyCast(), func_get_detail(), and ri_HashCompareOp().
{ CoercionPathType result = COERCION_PATH_NONE; HeapTuple tuple; *funcid = InvalidOid; /* Perhaps the types are domains; if so, look at their base types */ if (OidIsValid(sourceTypeId)) sourceTypeId = getBaseType(sourceTypeId); if (OidIsValid(targetTypeId)) targetTypeId = getBaseType(targetTypeId); /* Domains are always coercible to and from their base type */ if (sourceTypeId == targetTypeId) return COERCION_PATH_RELABELTYPE; /* Look in pg_cast */ tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(sourceTypeId), ObjectIdGetDatum(targetTypeId)); if (HeapTupleIsValid(tuple)) { Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); CoercionContext castcontext; /* convert char value for castcontext to CoercionContext enum */ switch (castForm->castcontext) { case COERCION_CODE_IMPLICIT: castcontext = COERCION_IMPLICIT; break; case COERCION_CODE_ASSIGNMENT: castcontext = COERCION_ASSIGNMENT; break; case COERCION_CODE_EXPLICIT: castcontext = COERCION_EXPLICIT; break; default: elog(ERROR, "unrecognized castcontext: %d", (int) castForm->castcontext); castcontext = 0; /* keep compiler quiet */ break; } /* Rely on ordering of enum for correct behavior here */ if (ccontext >= castcontext) { switch (castForm->castmethod) { case COERCION_METHOD_FUNCTION: result = COERCION_PATH_FUNC; *funcid = castForm->castfunc; break; case COERCION_METHOD_INOUT: result = COERCION_PATH_COERCEVIAIO; break; case COERCION_METHOD_BINARY: result = COERCION_PATH_RELABELTYPE; break; default: elog(ERROR, "unrecognized castmethod: %d", (int) castForm->castmethod); break; } } ReleaseSysCache(tuple); } else { /* * If there's no pg_cast entry, perhaps we are dealing with a pair of * array types. If so, and if the element types have a suitable cast, * report that we can coerce with an ArrayCoerceExpr. * * Note that the source type can be a domain over array, but not the * target, because ArrayCoerceExpr won't check domain constraints. * * Hack: disallow coercions to oidvector and int2vector, which * otherwise tend to capture coercions that should go to "real" array * types. We want those types to be considered "real" arrays for many * purposes, but not this one. (Also, ArrayCoerceExpr isn't * guaranteed to produce an output that meets the restrictions of * these datatypes, such as being 1-dimensional.) */ if (targetTypeId != OIDVECTOROID && targetTypeId != INT2VECTOROID) { Oid targetElem; Oid sourceElem; if ((targetElem = get_element_type(targetTypeId)) != InvalidOid && (sourceElem = get_base_element_type(sourceTypeId)) != InvalidOid) { CoercionPathType elempathtype; Oid elemfuncid; elempathtype = find_coercion_pathway(targetElem, sourceElem, ccontext, &elemfuncid); if (elempathtype != COERCION_PATH_NONE && elempathtype != COERCION_PATH_ARRAYCOERCE) { *funcid = elemfuncid; if (elempathtype == COERCION_PATH_COERCEVIAIO) result = COERCION_PATH_COERCEVIAIO; else result = COERCION_PATH_ARRAYCOERCE; } } } /* * If we still haven't found a possibility, consider automatic casting * using I/O functions. We allow assignment casts to string types and * explicit casts from string types to be handled this way. (The * CoerceViaIO mechanism is a lot more general than that, but this is * all we want to allow in the absence of a pg_cast entry.) It would * probably be better to insist on explicit casts in both directions, * but this is a compromise to preserve something of the pre-8.3 * behavior that many types had implicit (yipes!) casts to text. */ if (result == COERCION_PATH_NONE) { if (ccontext >= COERCION_ASSIGNMENT && TypeCategory(targetTypeId) == TYPCATEGORY_STRING) result = COERCION_PATH_COERCEVIAIO; else if (ccontext >= COERCION_EXPLICIT && TypeCategory(sourceTypeId) == TYPCATEGORY_STRING) result = COERCION_PATH_COERCEVIAIO; } } return result; }
CoercionPathType find_typmod_coercion_function | ( | Oid | typeId, | |
Oid * | funcid | |||
) |
Definition at line 2256 of file parse_coerce.c.
References CASTSOURCETARGET, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, OidIsValid, ReleaseSysCache(), SearchSysCache2, and typeidType().
Referenced by coerce_type_typmod().
{ CoercionPathType result; Type targetType; Form_pg_type typeForm; HeapTuple tuple; *funcid = InvalidOid; result = COERCION_PATH_FUNC; targetType = typeidType(typeId); typeForm = (Form_pg_type) GETSTRUCT(targetType); /* Check for a varlena array type */ if (typeForm->typelem != InvalidOid && typeForm->typlen == -1) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; result = COERCION_PATH_ARRAYCOERCE; } ReleaseSysCache(targetType); /* Look in pg_cast */ tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(typeId), ObjectIdGetDatum(typeId)); if (HeapTupleIsValid(tuple)) { Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); *funcid = castForm->castfunc; ReleaseSysCache(tuple); } if (!OidIsValid(*funcid)) result = COERCION_PATH_NONE; return result; }
Definition at line 2002 of file parse_coerce.c.
References ANYARRAYOID, ANYENUMOID, ANYNONARRAYOID, ANYRANGEOID, CASTSOURCETARGET, COERCION_METHOD_BINARY, getBaseType(), GETSTRUCT, HeapTupleIsValid, is_complex_array(), ISCOMPLEX, ObjectIdGetDatum, OidIsValid, RECORDARRAYOID, RECORDOID, ReleaseSysCache(), SearchSysCache2, type_is_array, type_is_enum(), and type_is_range().
Referenced by AggregateCreate(), check_sql_fn_retval(), compatible_oper(), CreateCast(), ExecInitAgg(), findRangeSubOpclass(), GetDefaultOpClass(), GetIndexOpClass(), initialize_peragg(), lookup_agg_function(), ri_HashCompareOp(), and tupledesc_match().
{ HeapTuple tuple; Form_pg_cast castForm; bool result; /* Fast path if same type */ if (srctype == targettype) return true; /* If srctype is a domain, reduce to its base type */ if (OidIsValid(srctype)) srctype = getBaseType(srctype); /* Somewhat-fast path for domain -> base type case */ if (srctype == targettype) return true; /* Also accept any array type as coercible to ANYARRAY */ if (targettype == ANYARRAYOID) if (type_is_array(srctype)) return true; /* Also accept any non-array type as coercible to ANYNONARRAY */ if (targettype == ANYNONARRAYOID) if (!type_is_array(srctype)) return true; /* Also accept any enum type as coercible to ANYENUM */ if (targettype == ANYENUMOID) if (type_is_enum(srctype)) return true; /* Also accept any range type as coercible to ANYRANGE */ if (targettype == ANYRANGEOID) if (type_is_range(srctype)) return true; /* Also accept any composite type as coercible to RECORD */ if (targettype == RECORDOID) if (ISCOMPLEX(srctype)) return true; /* Also accept any composite array type as coercible to RECORD[] */ if (targettype == RECORDARRAYOID) if (is_complex_array(srctype)) return true; /* Else look in pg_cast */ tuple = SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(srctype), ObjectIdGetDatum(targettype)); if (!HeapTupleIsValid(tuple)) return false; /* no cast */ castForm = (Form_pg_cast) GETSTRUCT(tuple); result = (castForm->castmethod == COERCION_METHOD_BINARY && castForm->castcontext == COERCION_CODE_IMPLICIT); ReleaseSysCache(tuple); return result; }
bool IsPreferredType | ( | TYPCATEGORY | category, | |
Oid | type | |||
) |
Definition at line 1967 of file parse_coerce.c.
References get_type_category_preferred(), and TYPCATEGORY_INVALID.
Referenced by func_select_candidate(), and GetDefaultOpClass().
{ char typcategory; bool typispreferred; get_type_category_preferred(type, &typcategory, &typispreferred); if (category == typcategory || category == TYPCATEGORY_INVALID) return typispreferred; else return false; }
int parser_coercion_errposition | ( | ParseState * | pstate, | |
int | coerce_location, | |||
Node * | input_expr | |||
) |
Definition at line 1120 of file parse_coerce.c.
References exprLocation(), and parser_errposition().
Referenced by coerce_record_to_complex(), and transformTypeCast().
{ if (coerce_location >= 0) return parser_errposition(pstate, coerce_location); else return parser_errposition(pstate, exprLocation(input_expr)); }
Definition at line 1853 of file parse_coerce.c.
References ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYRANGEOID, elog, ereport, errcode(), errmsg(), ERROR, format_type_be(), get_array_type(), get_element_type(), get_range_subtype(), getBaseType(), and OidIsValid.
Referenced by resolve_polymorphic_argtypes(), and resolve_polymorphic_tupdesc().
{ if (declared_type == ANYARRAYOID) { if (context_declared_type == ANYARRAYOID) { /* * Use actual type, but it must be an array; or if it's a domain * over array, use the base array type. */ Oid context_base_type = getBaseType(context_actual_type); Oid array_typelem = get_element_type(context_base_type); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not an array but type %s", format_type_be(context_base_type)))); return context_base_type; } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || context_declared_type == ANYENUMOID || context_declared_type == ANYRANGEOID) { /* Use the array type corresponding to actual type */ Oid array_typeid = get_array_type(context_actual_type); if (!OidIsValid(array_typeid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(context_actual_type)))); return array_typeid; } } else if (declared_type == ANYELEMENTOID || declared_type == ANYNONARRAYOID || declared_type == ANYENUMOID || declared_type == ANYRANGEOID) { if (context_declared_type == ANYARRAYOID) { /* Use the element type corresponding to actual type */ Oid context_base_type = getBaseType(context_actual_type); Oid array_typelem = get_element_type(context_base_type); if (!OidIsValid(array_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyarray\" is not an array but type %s", format_type_be(context_base_type)))); return array_typelem; } else if (context_declared_type == ANYRANGEOID) { /* Use the element type corresponding to actual type */ Oid context_base_type = getBaseType(context_actual_type); Oid range_typelem = get_range_subtype(context_base_type); if (!OidIsValid(range_typelem)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("argument declared \"anyrange\" is not a range type but type %s", format_type_be(context_base_type)))); return range_typelem; } else if (context_declared_type == ANYELEMENTOID || context_declared_type == ANYNONARRAYOID || context_declared_type == ANYENUMOID) { /* Use the actual type; it doesn't matter if array or not */ return context_actual_type; } } else { /* declared_type isn't polymorphic, so return it as-is */ return declared_type; } /* If we get here, declared_type is polymorphic and context isn't */ /* NB: this is a calling-code logic error, not a user error */ elog(ERROR, "could not determine polymorphic type because context isn't polymorphic"); return InvalidOid; /* keep compiler quiet */ }
Oid select_common_type | ( | ParseState * | pstate, | |
List * | exprs, | |||
const char * | context, | |||
Node ** | which_expr | |||
) |
Definition at line 1146 of file parse_coerce.c.
References Assert, can_coerce_type(), COERCION_IMPLICIT, ereport, errcode(), errmsg(), ERROR, exprLocation(), exprType(), for_each_cell, format_type_be(), get_type_category_preferred(), getBaseType(), lfirst, linitial, list_head(), lnext, NIL, NULL, parser_errposition(), and UNKNOWNOID.
Referenced by buildMergedJoinVar(), transformAExprIn(), transformArrayExpr(), transformCaseExpr(), transformCoalesceExpr(), transformMinMaxExpr(), transformSetOperationTree(), and transformValuesClause().
{ Node *pexpr; Oid ptype; TYPCATEGORY pcategory; bool pispreferred; ListCell *lc; Assert(exprs != NIL); pexpr = (Node *) linitial(exprs); lc = lnext(list_head(exprs)); ptype = exprType(pexpr); /* * If all input types are valid and exactly the same, just pick that type. * This is the only way that we will resolve the result as being a domain * type; otherwise domains are smashed to their base types for comparison. */ if (ptype != UNKNOWNOID) { for_each_cell(lc, lc) { Node *nexpr = (Node *) lfirst(lc); Oid ntype = exprType(nexpr); if (ntype != ptype) break; } if (lc == NULL) /* got to the end of the list? */ { if (which_expr) *which_expr = pexpr; return ptype; } } /* * Nope, so set up for the full algorithm. Note that at this point, lc * points to the first list item with type different from pexpr's; we need * not re-examine any items the previous loop advanced over. */ ptype = getBaseType(ptype); get_type_category_preferred(ptype, &pcategory, &pispreferred); for_each_cell(lc, lc) { Node *nexpr = (Node *) lfirst(lc); Oid ntype = getBaseType(exprType(nexpr)); /* move on to next one if no new information... */ if (ntype != UNKNOWNOID && ntype != ptype) { TYPCATEGORY ncategory; bool nispreferred; get_type_category_preferred(ntype, &ncategory, &nispreferred); if (ptype == UNKNOWNOID) { /* so far, only unknowns so take anything... */ pexpr = nexpr; ptype = ntype; pcategory = ncategory; pispreferred = nispreferred; } else if (ncategory != pcategory) { /* * both types in different categories? then not much hope... */ if (context == NULL) return InvalidOid; ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), /*------ translator: first %s is name of a SQL construct, eg CASE */ errmsg("%s types %s and %s cannot be matched", context, format_type_be(ptype), format_type_be(ntype)), parser_errposition(pstate, exprLocation(nexpr)))); } else if (!pispreferred && can_coerce_type(1, &ptype, &ntype, COERCION_IMPLICIT) && !can_coerce_type(1, &ntype, &ptype, COERCION_IMPLICIT)) { /* * take new type if can coerce to it implicitly but not the * other way; but if we have a preferred type, stay on it. */ pexpr = nexpr; ptype = ntype; pcategory = ncategory; pispreferred = nispreferred; } } } /* * If all the inputs were UNKNOWN type --- ie, unknown-type literals --- * then resolve as type TEXT. This situation comes up with constructs * like SELECT (CASE WHEN foo THEN 'bar' ELSE 'baz' END); SELECT 'foo' * UNION SELECT 'bar'; It might seem desirable to leave the construct's * output type as UNKNOWN, but that really doesn't work, because we'd * probably end up needing a runtime coercion from UNKNOWN to something * else, and we usually won't have it. We need to coerce the unknown * literals while they are still literals, so a decision has to be made * now. */ if (ptype == UNKNOWNOID) ptype = TEXTOID; if (which_expr) *which_expr = pexpr; return ptype; }
TYPCATEGORY TypeCategory | ( | Oid | type | ) |
Definition at line 1948 of file parse_coerce.c.
References Assert, get_type_category_preferred(), and TYPCATEGORY_INVALID.
Referenced by array_to_json_internal(), composite_to_json(), find_coercion_pathway(), func_get_detail(), func_select_candidate(), GetDefaultOpClass(), json_agg_transfn(), and to_json().
{ char typcategory; bool typispreferred; get_type_category_preferred(type, &typcategory, &typispreferred); Assert(typcategory != TYPCATEGORY_INVALID); return (TYPCATEGORY) typcategory; }