From 04dc797d6e99d7fe5902a31ae5fdd73950c8c48f Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Thu, 21 Sep 2017 12:13:38 -0700 Subject: [PATCH] WIP: Combine expr{Type,Typmod,Collation} into one function. Slight speedup, removal of some duplication. Not entirely sure it's worth it. --- src/backend/executor/execTuples.c | 12 +- src/backend/nodes/nodeFuncs.c | 929 +++++++++++++++++--------------------- src/include/nodes/nodeFuncs.h | 1 + 3 files changed, 435 insertions(+), 507 deletions(-) diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 51d2c5d166..1035efb4aa 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -919,18 +919,24 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) foreach(l, targetList) { TargetEntry *tle = lfirst(l); + Oid type; + int32 typmod; + Oid collation; if (skipjunk && tle->resjunk) continue; + exprTypeInfo((Node *) tle->expr, + &type, &typmod, &collation); + TupleDescInitEntry(typeInfo, cur_resno, tle->resname, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), + type, + typmod, 0); TupleDescInitEntryCollation(typeInfo, cur_resno, - exprCollation((Node *) tle->expr)); + collation); cur_resno++; } diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 8e6f27e153..caf5ee6a42 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -35,69 +35,134 @@ static bool planstate_walk_members(List *plans, PlanState **planstates, /* - * exprType - - * returns the Oid of the type of the expression's result. + * exprTypeInfo - + * lookup an expression result's type, typmod, collation. */ -Oid -exprType(const Node *expr) +void +exprTypeInfo(const Node *expr, Oid *type, int32 *typmod, Oid *collation) { - Oid type; + /* initialize to defaults, overridden where appropriate */ + *type = InvalidOid; + *typmod = -1; + *collation = InvalidOid; if (!expr) - return InvalidOid; + return; switch (nodeTag(expr)) { case T_Var: - type = ((const Var *) expr)->vartype; - break; + { + const Var *var = (const Var *) expr; + *type = var->vartype; + *typmod = var->vartypmod; + *collation = var->varcollid; + break; + } case T_Const: - type = ((const Const *) expr)->consttype; - break; + { + const Const *c = (const Const *) expr; + *type = c->consttype; + *typmod = c->consttypmod; + *collation = c->constcollid; + break; + } case T_Param: - type = ((const Param *) expr)->paramtype; - break; + { + const Param *p = (const Param *) expr; + *type = p->paramtype; + *typmod = p->paramtypmod; + *collation = p->paramcollid; + break; + } case T_Aggref: - type = ((const Aggref *) expr)->aggtype; - break; + { + const Aggref *a = (const Aggref *) expr; + *type = a->aggtype; + *collation = a->aggcollid; + break; + } case T_GroupingFunc: - type = INT4OID; + *type = INT4OID; break; case T_WindowFunc: - type = ((const WindowFunc *) expr)->wintype; - break; + { + const WindowFunc *w = (const WindowFunc *) expr; + *type = w->wintype; + *collation = w->wincollid; + break; + } case T_ArrayRef: { const ArrayRef *arrayref = (const ArrayRef *) expr; /* slice and/or store operations yield the array type */ if (arrayref->reflowerindexpr || arrayref->refassgnexpr) - type = arrayref->refarraytype; + *type = arrayref->refarraytype; else - type = arrayref->refelemtype; + *type = arrayref->refelemtype; + + *typmod = arrayref->reftypmod; + *collation = arrayref->refcollid; } break; case T_FuncExpr: - type = ((const FuncExpr *) expr)->funcresulttype; - break; + { + const FuncExpr *func = (const FuncExpr *) expr; + int32 coercedTypmod; + + *type = func->funcresulttype; + + /* Be smart about length-coercion functions... */ + if (exprIsLengthCoercion(expr, &coercedTypmod)) + *typmod = coercedTypmod; + else + *typmod = -1; + *collation = func->funccollid; + break; + } case T_NamedArgExpr: - type = exprType((Node *) ((const NamedArgExpr *) expr)->arg); - break; + { + exprTypeInfo((Node *) ((const NamedArgExpr *) expr)->arg, + type, typmod, collation); + break; + } case T_OpExpr: - type = ((const OpExpr *) expr)->opresulttype; - break; + { + const OpExpr *op = (const OpExpr *) expr; + *type = op->opresulttype; + *collation = op->opcollid; + break; + } case T_DistinctExpr: - type = ((const DistinctExpr *) expr)->opresulttype; - break; + { + const DistinctExpr *d = (const DistinctExpr *) expr; + + *type = d->opresulttype; + *collation = d->opcollid; + break; + } case T_NullIfExpr: - type = ((const NullIfExpr *) expr)->opresulttype; - break; + { + const NullIfExpr *nullif = (const NullIfExpr *) expr; + + *type = nullif->opresulttype; + /* + * Result is either first argument or NULL, so we can report + * first argument's typmod if known. + */ + *typmod = exprTypmod(linitial(nullif->args)); + *collation = nullif->opcollid; + break; + } case T_ScalarArrayOpExpr: - type = BOOLOID; + /* result is always boolean */ + *type = BOOLOID; break; case T_BoolExpr: - type = BOOLOID; - break; + /* result is always boolean */ + *type = BOOLOID; + break;; case T_SubLink: { const SubLink *sublink = (const SubLink *) expr; @@ -113,29 +178,32 @@ exprType(const Node *expr) elog(ERROR, "cannot get type for untransformed sublink"); tent = linitial_node(TargetEntry, qtree->targetList); Assert(!tent->resjunk); - type = exprType((Node *) tent->expr); + + exprTypeInfo((Node *) tent->expr, type, typmod, collation); + if (sublink->subLinkType == ARRAY_SUBLINK) { - type = get_promoted_array_type(type); - if (!OidIsValid(type)) + Oid promoted = get_promoted_array_type(*type); + if (!OidIsValid(promoted)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", - format_type_be(exprType((Node *) tent->expr))))); + format_type_be(*type)))); + *type = promoted; } } else if (sublink->subLinkType == MULTIEXPR_SUBLINK) { /* MULTIEXPR is always considered to return RECORD */ - type = RECORDOID; + *type = RECORDOID; } else { /* for all other sublink types, result is boolean */ - type = BOOLOID; + *type = BOOLOID; } } - break; + break;; case T_SubPlan: { const SubPlan *subplan = (const SubPlan *) expr; @@ -144,26 +212,31 @@ exprType(const Node *expr) subplan->subLinkType == ARRAY_SUBLINK) { /* get the type of the subselect's first target column */ - type = subplan->firstColType; + *type = subplan->firstColType; + *typmod = subplan->firstColTypmod; + *collation = subplan->firstColCollation; + if (subplan->subLinkType == ARRAY_SUBLINK) { - type = get_promoted_array_type(type); - if (!OidIsValid(type)) + Oid promoted = get_promoted_array_type(*type); + + if (!OidIsValid(promoted)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", - format_type_be(subplan->firstColType)))); + format_type_be(*type)))); + *type = promoted; } } else if (subplan->subLinkType == MULTIEXPR_SUBLINK) { /* MULTIEXPR is always considered to return RECORD */ - type = RECORDOID; + *type = RECORDOID; } else { /* for all other subplan types, result is boolean */ - type = BOOLOID; + *type = BOOLOID; } } break; @@ -172,98 +245,343 @@ exprType(const Node *expr) const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; /* subplans should all return the same thing */ - type = exprType((Node *) linitial(asplan->subplans)); + exprTypeInfo((Node *) linitial(asplan->subplans), + type, typmod, collation); + break; } - break; case T_FieldSelect: - type = ((const FieldSelect *) expr)->resulttype; - break; + { + const FieldSelect *fs = (const FieldSelect *) expr; + + *type = fs->resulttype; + *typmod = fs->resulttypmod; + *collation = fs->resultcollid; + break; + } case T_FieldStore: - type = ((const FieldStore *) expr)->resulttype; + *type = ((const FieldStore *) expr)->resulttype; break; case T_RelabelType: - type = ((const RelabelType *) expr)->resulttype; - break; + { + const RelabelType *rt = (const RelabelType *) expr; + *type = rt->resulttype; + *typmod = rt->resulttypmod; + *collation = rt->resultcollid; + break; + } case T_CoerceViaIO: - type = ((const CoerceViaIO *) expr)->resulttype; - break; + { + const CoerceViaIO *coerce = (const CoerceViaIO *) expr; + *type = coerce->resulttype; + *collation = coerce->resultcollid; + break; + } case T_ArrayCoerceExpr: - type = ((const ArrayCoerceExpr *) expr)->resulttype; - break; + { + const ArrayCoerceExpr *acoerce = (const ArrayCoerceExpr *) expr; + + *type = acoerce->resulttype; + *typmod = acoerce->resulttypmod; + *collation = acoerce->resultcollid; + break; + } case T_ConvertRowtypeExpr: - type = ((const ConvertRowtypeExpr *) expr)->resulttype; - break; + { + const ConvertRowtypeExpr *c = (const ConvertRowtypeExpr *) expr; + *type = c->resulttype; + break; + } case T_CollateExpr: - type = exprType((Node *) ((const CollateExpr *) expr)->arg); - break; + { + const CollateExpr *col = (const CollateExpr *) expr; + exprTypeInfo((Node *) col->arg, type, typmod, collation); + *collation = col->collOid; + } + break;; case T_CaseExpr: - type = ((const CaseExpr *) expr)->casetype; - break; + { + const CaseExpr *cexpr = (const CaseExpr *) expr; + Oid casetype = cexpr->casetype; + ListCell *arg; + + *type = cexpr->casetype; + + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + if (!cexpr->defresult) + *typmod = -1; + else if (exprType((Node *) cexpr->defresult) != casetype) + *typmod = -1; + else + { + int32 deftypmod; + + deftypmod = exprTypmod((Node *) cexpr->defresult); + if (*typmod >= 0) + { + foreach(arg, cexpr->args) + { + CaseWhen *w = lfirst_node(CaseWhen, arg); + + if (exprType((Node *) w->result) != casetype) + *typmod = -1; + if (exprTypmod((Node *) w->result) != deftypmod) + *typmod = -1; + } + } + *typmod = deftypmod; + } + *collation = cexpr->casecollid; + break; + } case T_CaseTestExpr: - type = ((const CaseTestExpr *) expr)->typeId; - break; + { + const CaseTestExpr *casetest = (const CaseTestExpr *) expr; + *type = casetest->typeId; + *typmod = casetest->typeMod; + *collation = casetest->collation; + break; + } + case T_ArrayExpr: - type = ((const ArrayExpr *) expr)->array_typeid; - break; + { + const ArrayExpr *arrayexpr = (const ArrayExpr *) expr; + Oid commontype; + ListCell *elem; + + *type = arrayexpr->array_typeid; + + /* + * If all the elements agree on type/typmod, return that + * typmod, else use -1 + */ + if (arrayexpr->elements != NIL) + { + Oid elemtype; + int32 elemtypmod; + Oid elemcollation; + + exprTypeInfo((Node *) linitial(arrayexpr->elements), + &elemtype, &elemtypmod, &elemcollation); + + if (elemtypmod < 0) + { + if (arrayexpr->multidims) + commontype = arrayexpr->array_typeid; + else + commontype = arrayexpr->element_typeid; + + *typmod = elemtypmod; + + foreach(elem, arrayexpr->elements) + { + Node *e = (Node *) lfirst(elem); + + exprTypeInfo(e, &elemtype, &elemtypmod, + &elemcollation); + + if (elemtype != commontype) + { + *typmod = -1; + break; + } + if (elemtypmod != *typmod) + { + *typmod = -1; + break; + } + } + } + } + *collation = arrayexpr->array_collid; + break; + } case T_RowExpr: - type = ((const RowExpr *) expr)->row_typeid; + *type = ((const RowExpr *) expr)->row_typeid; break; case T_RowCompareExpr: - type = BOOLOID; + *type = BOOLOID; break; case T_CoalesceExpr: - type = ((const CoalesceExpr *) expr)->coalescetype; - break; + { + const CoalesceExpr *cexpr = (const CoalesceExpr *) expr; + Oid elemtype; + int32 elemtypmod; + Oid elemcollation; + ListCell *arg; + + *type = cexpr->coalescetype; + *collation = cexpr->coalescecollid; + + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + exprTypeInfo((Node *) linitial(cexpr->args), + &elemtype, &elemtypmod, &elemcollation); + + /* no point in trying harder otherwise */ + if (elemtype == *type && elemtypmod > 0) + { + *typmod = elemtypmod; + + for_each_cell(arg, lnext(list_head(cexpr->args))) + { + Node *e = (Node *) lfirst(arg); + + exprTypeInfo(e, &elemtype, &elemtypmod, + &elemcollation); + + if (elemtype != *type || + elemtypmod != *typmod) + { + *typmod = -1; + break; + } + } + } + break; + } case T_MinMaxExpr: - type = ((const MinMaxExpr *) expr)->minmaxtype; - break; + { + /* + * If all the alternatives agree on type/typmod, return that + * typmod, else use -1 + */ + const MinMaxExpr *mexpr = (const MinMaxExpr *) expr; + Oid elemtype; + int32 elemtypmod; + Oid elemcollation; + ListCell *arg; + + *type = mexpr->minmaxtype; + *collation = mexpr->minmaxcollid; + + exprTypeInfo((Node *) linitial(mexpr->args), + &elemtype, &elemtypmod, &elemcollation); + + /* no point in trying harder otherwise */ + if (elemtype == *type && elemtypmod >= 0) + { + *typmod = elemtypmod; + + for_each_cell(arg, lnext(list_head(mexpr->args))) + { + Node *e = (Node *) lfirst(arg); + + exprTypeInfo((Node *) e, + &elemtype, &elemtypmod, &elemcollation); + + if (elemtype != *type || + elemtypmod != *typmod) + { + *typmod = -1; + break; + } + } + } + + break; + } case T_SQLValueFunction: - type = ((const SQLValueFunction *) expr)->type; - break; + { + const SQLValueFunction *sv = (const SQLValueFunction *) expr; + + *type = sv->type; + *typmod = sv->typmod; + break; + } case T_XmlExpr: - if (((const XmlExpr *) expr)->op == IS_DOCUMENT) - type = BOOLOID; - else if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE) - type = TEXTOID; - else - type = XMLOID; - break; + { + const XmlExpr *xe = (const XmlExpr *) expr; + + if (xe->op == IS_DOCUMENT) + *type = BOOLOID; + else if (xe->op == IS_XMLSERIALIZE) + { + *type = TEXTOID; + *collation = DEFAULT_COLLATION_OID; + } + else + *type = XMLOID; + break; + } case T_NullTest: - type = BOOLOID; + *type = BOOLOID; break; case T_BooleanTest: - type = BOOLOID; + *type = BOOLOID; break; case T_CoerceToDomain: - type = ((const CoerceToDomain *) expr)->resulttype; - break; + { + const CoerceToDomain *cd = (const CoerceToDomain *) expr; + + *type = cd->resulttype; + *typmod = cd->resulttypmod; + *collation = cd->resultcollid; + break; + } case T_CoerceToDomainValue: - type = ((const CoerceToDomainValue *) expr)->typeId; + { + const CoerceToDomainValue *cdv = (const CoerceToDomainValue *) expr; + + *type = cdv->typeId; + *typmod = cdv->typeMod; + *collation = cdv->collation; + break; + } break; case T_SetToDefault: - type = ((const SetToDefault *) expr)->typeId; - break; + { + const SetToDefault *sd = (const SetToDefault *) expr; + + *type = sd->typeId; + *typmod = sd->typeMod; + *collation = sd->collation; + break; + } case T_CurrentOfExpr: - type = BOOLOID; + *type = BOOLOID; break; case T_NextValueExpr: - type = ((const NextValueExpr *) expr)->typeId; + *type = ((const NextValueExpr *) expr)->typeId; break; case T_InferenceElem: { const InferenceElem *n = (const InferenceElem *) expr; - type = exprType((Node *) n->expr); + exprTypeInfo((Node *) n->expr, type, typmod, collation); + *typmod = -1; /* XXX? */ + break; } - break; case T_PlaceHolderVar: - type = exprType((Node *) ((const PlaceHolderVar *) expr)->phexpr); - break; + { + const PlaceHolderVar *phv = (const PlaceHolderVar *) expr; + exprTypeInfo((Node *) phv->phexpr, + type, typmod, collation); + break; + } default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); - type = InvalidOid; /* keep compiler quiet */ break; } +} + +/* + * exprType - + * returns the Oid of the type of the expression's result. + */ +Oid +exprType(const Node *expr) +{ + Oid type; + int32 typmod; + Oid collation; + + exprTypeInfo(expr, &type, &typmod, &collation); + return type; } @@ -275,227 +593,13 @@ exprType(const Node *expr) int32 exprTypmod(const Node *expr) { - if (!expr) - return -1; + Oid type; + int32 typmod; + Oid collation; - switch (nodeTag(expr)) - { - case T_Var: - return ((const Var *) expr)->vartypmod; - case T_Const: - return ((const Const *) expr)->consttypmod; - case T_Param: - return ((const Param *) expr)->paramtypmod; - case T_ArrayRef: - /* typmod is the same for array or element */ - return ((const ArrayRef *) expr)->reftypmod; - case T_FuncExpr: - { - int32 coercedTypmod; + exprTypeInfo(expr, &type, &typmod, &collation); - /* Be smart about length-coercion functions... */ - if (exprIsLengthCoercion(expr, &coercedTypmod)) - return coercedTypmod; - } - break; - case T_NamedArgExpr: - return exprTypmod((Node *) ((const NamedArgExpr *) expr)->arg); - case T_NullIfExpr: - { - /* - * Result is either first argument or NULL, so we can report - * first argument's typmod if known. - */ - const NullIfExpr *nexpr = (const NullIfExpr *) expr; - - return exprTypmod((Node *) linitial(nexpr->args)); - } - break; - case T_SubLink: - { - const SubLink *sublink = (const SubLink *) expr; - - if (sublink->subLinkType == EXPR_SUBLINK || - sublink->subLinkType == ARRAY_SUBLINK) - { - /* get the typmod of the subselect's first target column */ - Query *qtree = (Query *) sublink->subselect; - TargetEntry *tent; - - if (!qtree || !IsA(qtree, Query)) - elog(ERROR, "cannot get type for untransformed sublink"); - tent = linitial_node(TargetEntry, qtree->targetList); - Assert(!tent->resjunk); - return exprTypmod((Node *) tent->expr); - /* note we don't need to care if it's an array */ - } - /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */ - } - break; - case T_SubPlan: - { - const SubPlan *subplan = (const SubPlan *) expr; - - if (subplan->subLinkType == EXPR_SUBLINK || - subplan->subLinkType == ARRAY_SUBLINK) - { - /* get the typmod of the subselect's first target column */ - /* note we don't need to care if it's an array */ - return subplan->firstColTypmod; - } - /* otherwise, result is RECORD or BOOLEAN, typmod is -1 */ - } - break; - case T_AlternativeSubPlan: - { - const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; - - /* subplans should all return the same thing */ - return exprTypmod((Node *) linitial(asplan->subplans)); - } - break; - case T_FieldSelect: - return ((const FieldSelect *) expr)->resulttypmod; - case T_RelabelType: - return ((const RelabelType *) expr)->resulttypmod; - case T_ArrayCoerceExpr: - return ((const ArrayCoerceExpr *) expr)->resulttypmod; - case T_CollateExpr: - return exprTypmod((Node *) ((const CollateExpr *) expr)->arg); - case T_CaseExpr: - { - /* - * If all the alternatives agree on type/typmod, return that - * typmod, else use -1 - */ - const CaseExpr *cexpr = (const CaseExpr *) expr; - Oid casetype = cexpr->casetype; - int32 typmod; - ListCell *arg; - - if (!cexpr->defresult) - return -1; - if (exprType((Node *) cexpr->defresult) != casetype) - return -1; - typmod = exprTypmod((Node *) cexpr->defresult); - if (typmod < 0) - return -1; /* no point in trying harder */ - foreach(arg, cexpr->args) - { - CaseWhen *w = lfirst_node(CaseWhen, arg); - - if (exprType((Node *) w->result) != casetype) - return -1; - if (exprTypmod((Node *) w->result) != typmod) - return -1; - } - return typmod; - } - break; - case T_CaseTestExpr: - return ((const CaseTestExpr *) expr)->typeMod; - case T_ArrayExpr: - { - /* - * If all the elements agree on type/typmod, return that - * typmod, else use -1 - */ - const ArrayExpr *arrayexpr = (const ArrayExpr *) expr; - Oid commontype; - int32 typmod; - ListCell *elem; - - if (arrayexpr->elements == NIL) - return -1; - typmod = exprTypmod((Node *) linitial(arrayexpr->elements)); - if (typmod < 0) - return -1; /* no point in trying harder */ - if (arrayexpr->multidims) - commontype = arrayexpr->array_typeid; - else - commontype = arrayexpr->element_typeid; - foreach(elem, arrayexpr->elements) - { - Node *e = (Node *) lfirst(elem); - - if (exprType(e) != commontype) - return -1; - if (exprTypmod(e) != typmod) - return -1; - } - return typmod; - } - break; - case T_CoalesceExpr: - { - /* - * If all the alternatives agree on type/typmod, return that - * typmod, else use -1 - */ - const CoalesceExpr *cexpr = (const CoalesceExpr *) expr; - Oid coalescetype = cexpr->coalescetype; - int32 typmod; - ListCell *arg; - - if (exprType((Node *) linitial(cexpr->args)) != coalescetype) - return -1; - typmod = exprTypmod((Node *) linitial(cexpr->args)); - if (typmod < 0) - return -1; /* no point in trying harder */ - for_each_cell(arg, lnext(list_head(cexpr->args))) - { - Node *e = (Node *) lfirst(arg); - - if (exprType(e) != coalescetype) - return -1; - if (exprTypmod(e) != typmod) - return -1; - } - return typmod; - } - break; - case T_MinMaxExpr: - { - /* - * If all the alternatives agree on type/typmod, return that - * typmod, else use -1 - */ - const MinMaxExpr *mexpr = (const MinMaxExpr *) expr; - Oid minmaxtype = mexpr->minmaxtype; - int32 typmod; - ListCell *arg; - - if (exprType((Node *) linitial(mexpr->args)) != minmaxtype) - return -1; - typmod = exprTypmod((Node *) linitial(mexpr->args)); - if (typmod < 0) - return -1; /* no point in trying harder */ - for_each_cell(arg, lnext(list_head(mexpr->args))) - { - Node *e = (Node *) lfirst(arg); - - if (exprType(e) != minmaxtype) - return -1; - if (exprTypmod(e) != typmod) - return -1; - } - return typmod; - } - break; - case T_SQLValueFunction: - return ((const SQLValueFunction *) expr)->typmod; - case T_CoerceToDomain: - return ((const CoerceToDomain *) expr)->resulttypmod; - case T_CoerceToDomainValue: - return ((const CoerceToDomainValue *) expr)->typeMod; - case T_SetToDefault: - return ((const SetToDefault *) expr)->typeMod; - case T_PlaceHolderVar: - return exprTypmod((Node *) ((const PlaceHolderVar *) expr)->phexpr); - default: - break; - } - return -1; + return typmod; } /* @@ -713,202 +817,19 @@ expression_returns_set_walker(Node *node, void *context) * "inputcollid" field, which is what the function should use as collation. * That is the resolved common collation of the node's inputs. It is often * but not always the same as the result collation; in particular, if the - * function produces a non-collatable result type from collatable inputs - * or vice versa, the two are different. + * function produces a non-collatable result type from collatable inputs or + * vice versa, the two are different. */ Oid exprCollation(const Node *expr) { - Oid coll; + Oid type; + int32 typmod; + Oid collation; - if (!expr) - return InvalidOid; + exprTypeInfo(expr, &type, &typmod, &collation); - switch (nodeTag(expr)) - { - case T_Var: - coll = ((const Var *) expr)->varcollid; - break; - case T_Const: - coll = ((const Const *) expr)->constcollid; - break; - case T_Param: - coll = ((const Param *) expr)->paramcollid; - break; - case T_Aggref: - coll = ((const Aggref *) expr)->aggcollid; - break; - case T_GroupingFunc: - coll = InvalidOid; - break; - case T_WindowFunc: - coll = ((const WindowFunc *) expr)->wincollid; - break; - case T_ArrayRef: - coll = ((const ArrayRef *) expr)->refcollid; - break; - case T_FuncExpr: - coll = ((const FuncExpr *) expr)->funccollid; - break; - case T_NamedArgExpr: - coll = exprCollation((Node *) ((const NamedArgExpr *) expr)->arg); - break; - case T_OpExpr: - coll = ((const OpExpr *) expr)->opcollid; - break; - case T_DistinctExpr: - coll = ((const DistinctExpr *) expr)->opcollid; - break; - case T_NullIfExpr: - coll = ((const NullIfExpr *) expr)->opcollid; - break; - case T_ScalarArrayOpExpr: - coll = InvalidOid; /* result is always boolean */ - break; - case T_BoolExpr: - coll = InvalidOid; /* result is always boolean */ - break; - case T_SubLink: - { - const SubLink *sublink = (const SubLink *) expr; - - if (sublink->subLinkType == EXPR_SUBLINK || - sublink->subLinkType == ARRAY_SUBLINK) - { - /* get the collation of subselect's first target column */ - Query *qtree = (Query *) sublink->subselect; - TargetEntry *tent; - - if (!qtree || !IsA(qtree, Query)) - elog(ERROR, "cannot get collation for untransformed sublink"); - tent = linitial_node(TargetEntry, qtree->targetList); - Assert(!tent->resjunk); - coll = exprCollation((Node *) tent->expr); - /* collation doesn't change if it's converted to array */ - } - else - { - /* otherwise, result is RECORD or BOOLEAN */ - coll = InvalidOid; - } - } - break; - case T_SubPlan: - { - const SubPlan *subplan = (const SubPlan *) expr; - - if (subplan->subLinkType == EXPR_SUBLINK || - subplan->subLinkType == ARRAY_SUBLINK) - { - /* get the collation of subselect's first target column */ - coll = subplan->firstColCollation; - /* collation doesn't change if it's converted to array */ - } - else - { - /* otherwise, result is RECORD or BOOLEAN */ - coll = InvalidOid; - } - } - break; - case T_AlternativeSubPlan: - { - const AlternativeSubPlan *asplan = (const AlternativeSubPlan *) expr; - - /* subplans should all return the same thing */ - coll = exprCollation((Node *) linitial(asplan->subplans)); - } - break; - case T_FieldSelect: - coll = ((const FieldSelect *) expr)->resultcollid; - break; - case T_FieldStore: - coll = InvalidOid; /* result is always composite */ - break; - case T_RelabelType: - coll = ((const RelabelType *) expr)->resultcollid; - break; - case T_CoerceViaIO: - coll = ((const CoerceViaIO *) expr)->resultcollid; - break; - case T_ArrayCoerceExpr: - coll = ((const ArrayCoerceExpr *) expr)->resultcollid; - break; - case T_ConvertRowtypeExpr: - coll = InvalidOid; /* result is always composite */ - break; - case T_CollateExpr: - coll = ((const CollateExpr *) expr)->collOid; - break; - case T_CaseExpr: - coll = ((const CaseExpr *) expr)->casecollid; - break; - case T_CaseTestExpr: - coll = ((const CaseTestExpr *) expr)->collation; - break; - case T_ArrayExpr: - coll = ((const ArrayExpr *) expr)->array_collid; - break; - case T_RowExpr: - coll = InvalidOid; /* result is always composite */ - break; - case T_RowCompareExpr: - coll = InvalidOid; /* result is always boolean */ - break; - case T_CoalesceExpr: - coll = ((const CoalesceExpr *) expr)->coalescecollid; - break; - case T_MinMaxExpr: - coll = ((const MinMaxExpr *) expr)->minmaxcollid; - break; - case T_SQLValueFunction: - coll = InvalidOid; /* all cases return non-collatable types */ - break; - case T_XmlExpr: - - /* - * XMLSERIALIZE returns text from non-collatable inputs, so its - * collation is always default. The other cases return boolean or - * XML, which are non-collatable. - */ - if (((const XmlExpr *) expr)->op == IS_XMLSERIALIZE) - coll = DEFAULT_COLLATION_OID; - else - coll = InvalidOid; - break; - case T_NullTest: - coll = InvalidOid; /* result is always boolean */ - break; - case T_BooleanTest: - coll = InvalidOid; /* result is always boolean */ - break; - case T_CoerceToDomain: - coll = ((const CoerceToDomain *) expr)->resultcollid; - break; - case T_CoerceToDomainValue: - coll = ((const CoerceToDomainValue *) expr)->collation; - break; - case T_SetToDefault: - coll = ((const SetToDefault *) expr)->collation; - break; - case T_CurrentOfExpr: - coll = InvalidOid; /* result is always boolean */ - break; - case T_NextValueExpr: - coll = InvalidOid; /* result is always an integer type */ - break; - case T_InferenceElem: - coll = exprCollation((Node *) ((const InferenceElem *) expr)->expr); - break; - case T_PlaceHolderVar: - coll = exprCollation((Node *) ((const PlaceHolderVar *) expr)->phexpr); - break; - default: - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); - coll = InvalidOid; /* keep compiler quiet */ - break; - } - return coll; + return collation; } /* diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index 3366983936..6940164370 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -29,6 +29,7 @@ typedef bool (*check_function_callback) (Oid func_id, void *context); +extern void exprTypeInfo(const Node *expr, Oid *type, int32 *typemod, Oid *collation); extern Oid exprType(const Node *expr); extern int32 exprTypmod(const Node *expr); extern bool exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod); -- 2.14.1.536.g6867272d5b.dirty