📄 parse_coerce.c
字号:
* has not bothered to look this up) * 'typeId': target type to coerce to * 'cformat': coercion format * 'hideInputCoercion': if true, hide the input coercion under this one. * 'lengthCoercionDone': if true, caller already accounted for length. * * If the target type isn't a domain, the given 'arg' is returned as-is. */Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat, bool hideInputCoercion, bool lengthCoercionDone){ CoerceToDomain *result; /* Get the base type if it hasn't been supplied */ if (baseTypeId == InvalidOid) baseTypeId = getBaseType(typeId); /* 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) { int32 typmod = get_typtypmod(typeId); if (typmod >= 0) arg = coerce_type_typmod(arg, baseTypeId, typmod, COERCE_IMPLICIT_CAST, (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 */ result->coercionformat = cformat; return (Node *) result;}/* * coerce_type_typmod() * Force a value to a particular typmod, if meaningful and possible. * * This is applied to values that are going to be stored in a relation * (where we have an atttypmod for the column) as well as values being * explicitly CASTed (where the typmod comes from the target type spec). * * The caller must have already ensured that the value is of the correct * type, typically by applying coerce_type. * * cformat determines the display properties of the generated node (if any), * while isExplicit may affect semantics. If hideInputCoercion is true * *and* we generate a node, the input node is forced to IMPLICIT display * form, so that only the typmod coercion node will be visible when * displaying the expression. * * NOTE: this does not need to work on domain types, because any typmod * coercion for a domain is considered to be part of the type coercion * needed to produce the domain value in the first place. So, no getBaseType. */static Node *coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit, bool hideInputCoercion){ Oid funcId; /* * A negative typmod is assumed to mean that no coercion is wanted. Also, * skip coercion if already done. */ if (targetTypMod < 0 || targetTypMod == exprTypmod(node)) return node; funcId = find_typmod_coercion_function(targetTypeId); if (OidIsValid(funcId)) { /* Suppress display of nested coercion steps */ if (hideInputCoercion) hide_coercion_node(node); node = build_coercion_expression(node, funcId, targetTypeId, targetTypMod, cformat, isExplicit); } return node;}/* * Mark a coercion node as IMPLICIT so it will never be displayed by * ruleutils.c. We use this when we generate a nest of coercion nodes * to implement what is logically one conversion; the inner nodes are * forced to IMPLICIT_CAST format. This does not change their semantics, * only display behavior. * * It is caller error to call this on something that doesn't have a * CoercionForm field. */static voidhide_coercion_node(Node *node){ if (IsA(node, FuncExpr)) ((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST; else if (IsA(node, RelabelType)) ((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST; else if (IsA(node, ConvertRowtypeExpr)) ((ConvertRowtypeExpr *) node)->convertformat = COERCE_IMPLICIT_CAST; else if (IsA(node, RowExpr)) ((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST; else if (IsA(node, CoerceToDomain)) ((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST; else elog(ERROR, "unsupported node type: %d", (int) nodeTag(node));}/* * build_coercion_expression() * Construct a function-call expression for applying a pg_cast entry. * * This is used for both type-coercion and length-coercion functions, * since there is no difference in terms of the calling convention. */static Node *build_coercion_expression(Node *node, Oid funcId, Oid targetTypeId, int32 targetTypMod, CoercionForm cformat, bool isExplicit){ HeapTuple tp; Form_pg_proc procstruct; int nargs; List *args; Const *cons; tp = SearchSysCache(PROCOID, ObjectIdGetDatum(funcId), 0, 0, 0); if (!HeapTupleIsValid(tp)) elog(ERROR, "cache lookup failed for function %u", funcId); procstruct = (Form_pg_proc) GETSTRUCT(tp); /* * Asserts essentially check that function is a legal coercion function. * We can't make the seemingly obvious tests on prorettype and * proargtypes[0], because of various binary-compatibility cases. */ /* Assert(targetTypeId == procstruct->prorettype); */ Assert(!procstruct->proretset); Assert(!procstruct->proisagg); nargs = procstruct->pronargs; Assert(nargs >= 1 && nargs <= 3); /* Assert(procstruct->proargtypes.values[0] == exprType(node)); */ Assert(nargs < 2 || procstruct->proargtypes.values[1] == INT4OID); Assert(nargs < 3 || procstruct->proargtypes.values[2] == BOOLOID); ReleaseSysCache(tp); args = list_make1(node); if (nargs >= 2) { /* Pass target typmod as an int4 constant */ cons = makeConst(INT4OID, sizeof(int32), Int32GetDatum(targetTypMod), false, true); args = lappend(args, cons); } if (nargs == 3) { /* Pass it a boolean isExplicit parameter, too */ cons = makeConst(BOOLOID, sizeof(bool), BoolGetDatum(isExplicit), false, true); args = lappend(args, cons); } return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);}/* * coerce_record_to_complex * Coerce a RECORD to a specific composite type. * * Currently we only support this for inputs that are RowExprs or whole-row * Vars. */static Node *coerce_record_to_complex(ParseState *pstate, Node *node, Oid targetTypeId, CoercionContext ccontext, CoercionForm cformat){ RowExpr *rowexpr; TupleDesc tupdesc; List *args = NIL; List *newargs; int i; int ucolno; ListCell *arg; if (node && IsA(node, RowExpr)) { /* * Since the RowExpr must be of type RECORD, we needn't worry about it * containing any dropped columns. */ args = ((RowExpr *) node)->args; } else if (node && IsA(node, Var) && ((Var *) node)->varattno == InvalidAttrNumber) { int rtindex = ((Var *) node)->varno; int sublevels_up = ((Var *) node)->varlevelsup; RangeTblEntry *rte; rte = GetRTEByRangeTablePosn(pstate, rtindex, sublevels_up); expandRTE(rte, rtindex, sublevels_up, false, NULL, &args); } else ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", format_type_be(RECORDOID), format_type_be(targetTypeId)))); tupdesc = CreateTupleDescCopy(lookup_rowtype_tupdesc(targetTypeId, -1)); newargs = NIL; ucolno = 1; arg = list_head(args); for (i = 0; i < tupdesc->natts; i++) { Node *expr; Oid exprtype; /* Fill in NULLs for dropped columns in rowtype */ if (tupdesc->attrs[i]->attisdropped) { /* * can't use atttypid here, but it doesn't really matter what type * the Const claims to be. */ newargs = lappend(newargs, makeNullConst(INT4OID)); continue; } if (arg == NULL) ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", format_type_be(RECORDOID), format_type_be(targetTypeId)), errdetail("Input has too few columns."))); expr = (Node *) lfirst(arg); exprtype = exprType(expr); expr = coerce_to_target_type(pstate, expr, exprtype, tupdesc->attrs[i]->atttypid, tupdesc->attrs[i]->atttypmod, ccontext, COERCE_IMPLICIT_CAST); if (expr == NULL) ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", format_type_be(RECORDOID), format_type_be(targetTypeId)), errdetail("Cannot cast type %s to %s in column %d.", format_type_be(exprtype), format_type_be(tupdesc->attrs[i]->atttypid), ucolno))); newargs = lappend(newargs, expr); ucolno++; arg = lnext(arg); } if (arg != NULL) ereport(ERROR, (errcode(ERRCODE_CANNOT_COERCE), errmsg("cannot cast type %s to %s", format_type_be(RECORDOID), format_type_be(targetTypeId)), errdetail("Input has too many columns."))); FreeTupleDesc(tupdesc); rowexpr = makeNode(RowExpr); rowexpr->args = newargs; rowexpr->row_typeid = targetTypeId; rowexpr->row_format = cformat; return (Node *) rowexpr;}/* coerce_to_boolean() * Coerce an argument of a construct that requires boolean input * (AND, OR, NOT, etc). Also check that input is not a set. * * Returns the possibly-transformed node tree. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */Node *coerce_to_boolean(ParseState *pstate, Node *node, const char *constructName){ Oid inputTypeId = exprType(node); if (inputTypeId != BOOLOID) { node = coerce_to_target_type(pstate, node, inputTypeId, BOOLOID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (node == 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)))); } 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))); return node;}/* coerce_to_integer() * Coerce an argument of a construct that requires integer input * (LIMIT, OFFSET, etc). Also check that input is not a set. * * Returns the possibly-transformed node tree. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */Node *coerce_to_integer(ParseState *pstate, Node *node, const char *constructName){ Oid inputTypeId = exprType(node); if (inputTypeId != INT4OID) { node = coerce_to_target_type(pstate, node, inputTypeId, INT4OID, -1, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (node == 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 integer, not type %s", constructName, format_type_be(inputTypeId)))); } 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))); return node;}/* select_common_type() * Determine the common supertype of a list of input expression types. * This is used for determining the output type of CASE and UNION * constructs. * * typeids is a nonempty list of type OIDs. Note that earlier items * in the list will be preferred if there is doubt. * 'context' is a phrase to use in the error message if we fail to select * a usable type. */Oidselect_common_type(List *typeids, const char *context){ Oid ptype; CATEGORY pcategory; ListCell *type_item; Assert(typeids != NIL); ptype = getBaseType(linitial_oid(typeids)); pcategory = TypeCategory(ptype); for_each_cell(type_item, lnext(list_head(typeids))) { Oid ntype = getBaseType(lfirst_oid(type_item)); /* move on to next one if no new information... */ if ((ntype != InvalidOid) && (ntype != UNKNOWNOID) && (ntype != ptype)) { if ((ptype == InvalidOid) || ptype == UNKNOWNOID) { /* so far, only nulls so take anything... */ ptype = ntype; pcategory = TypeCategory(ptype); } else if (TypeCategory(ntype) != pcategory) { /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -