diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 539c16c4f79..15cc6240203 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -79,6 +79,9 @@ static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt); static Query *transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt); static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, bool isTopLevel, List **targetlist); +static void constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op, + const List *ltargetlist, const List *rtargetlist, + List **targetlist, const char *context, bool recursive); static void determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist); static Query *transformReturnStmt(ParseState *pstate, ReturnStmt *stmt); @@ -2142,7 +2145,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, Query *selectQuery; ParseNamespaceItem *nsitem; RangeTblRef *rtr; - ListCell *tl; /* * Transform SelectStmt into a Query. @@ -2182,6 +2184,8 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, */ if (targetlist) { + ListCell *tl; + *targetlist = NIL; foreach(tl, selectQuery->targetList) { @@ -2214,8 +2218,6 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, SetOperationStmt *op = makeNode(SetOperationStmt); List *ltargetlist; List *rtargetlist; - ListCell *ltl; - ListCell *rtl; const char *context; bool recursive = (pstate->p_parent_cte && pstate->p_parent_cte->cterecursive); @@ -2250,164 +2252,185 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, false, &rtargetlist); - /* - * Verify that the two children have the same number of non-junk - * columns, and determine the types of the merged output columns. - */ - if (list_length(ltargetlist) != list_length(rtargetlist)) - ereport(ERROR, - (errcode(ERRCODE_SYNTAX_ERROR), - errmsg("each %s query must have the same number of columns", - context), - parser_errposition(pstate, - exprLocation((Node *) rtargetlist)))); - - if (targetlist) - *targetlist = NIL; - op->colTypes = NIL; - op->colTypmods = NIL; - op->colCollations = NIL; - op->groupClauses = NIL; - forboth(ltl, ltargetlist, rtl, rtargetlist) - { - TargetEntry *ltle = (TargetEntry *) lfirst(ltl); - TargetEntry *rtle = (TargetEntry *) lfirst(rtl); - Node *lcolnode = (Node *) ltle->expr; - Node *rcolnode = (Node *) rtle->expr; - Oid lcoltype = exprType(lcolnode); - Oid rcoltype = exprType(rcolnode); - Node *bestexpr; - int bestlocation; - Oid rescoltype; - int32 rescoltypmod; - Oid rescolcoll; - - /* select common type, same as CASE et al */ - rescoltype = select_common_type(pstate, - list_make2(lcolnode, rcolnode), - context, - &bestexpr); - bestlocation = exprLocation(bestexpr); - - /* - * Verify the coercions are actually possible. If not, we'd fail - * later anyway, but we want to fail now while we have sufficient - * context to produce an error cursor position. - * - * For all non-UNKNOWN-type cases, we verify coercibility but we - * don't modify the child's expression, for fear of changing the - * child query's semantics. - * - * If a child expression is an UNKNOWN-type Const or Param, we - * want to replace it with the coerced expression. This can only - * happen when the child is a leaf set-op node. It's safe to - * replace the expression because if the child query's semantics - * depended on the type of this output column, it'd have already - * coerced the UNKNOWN to something else. We want to do this - * because (a) we want to verify that a Const is valid for the - * target type, or resolve the actual type of an UNKNOWN Param, - * and (b) we want to avoid unnecessary discrepancies between the - * output type of the child query and the resolved target type. - * Such a discrepancy would disable optimization in the planner. - * - * If it's some other UNKNOWN-type node, eg a Var, we do nothing - * (knowing that coerce_to_common_type would fail). The planner - * is sometimes able to fold an UNKNOWN Var to a constant before - * it has to coerce the type, so failing now would just break - * cases that might work. - */ - if (lcoltype != UNKNOWNOID) - lcolnode = coerce_to_common_type(pstate, lcolnode, - rescoltype, context); - else if (IsA(lcolnode, Const) || - IsA(lcolnode, Param)) - { - lcolnode = coerce_to_common_type(pstate, lcolnode, - rescoltype, context); - ltle->expr = (Expr *) lcolnode; - } - - if (rcoltype != UNKNOWNOID) - rcolnode = coerce_to_common_type(pstate, rcolnode, - rescoltype, context); - else if (IsA(rcolnode, Const) || - IsA(rcolnode, Param)) - { - rcolnode = coerce_to_common_type(pstate, rcolnode, - rescoltype, context); - rtle->expr = (Expr *) rcolnode; - } - - rescoltypmod = select_common_typmod(pstate, - list_make2(lcolnode, rcolnode), - rescoltype); - - /* - * Select common collation. A common collation is required for - * all set operators except UNION ALL; see SQL:2008 7.13 Syntax Rule 15c. (If we fail to identify a common - * collation for a UNION ALL column, the colCollations element - * will be set to InvalidOid, which may result in a runtime error - * if something at a higher query level wants to use the column's - * collation.) - */ - rescolcoll = select_common_collation(pstate, - list_make2(lcolnode, rcolnode), - (op->op == SETOP_UNION && op->all)); - - /* emit results */ - op->colTypes = lappend_oid(op->colTypes, rescoltype); - op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); - op->colCollations = lappend_oid(op->colCollations, rescolcoll); - - /* - * For all cases except UNION ALL, identify the grouping operators - * (and, if available, sorting operators) that will be used to - * eliminate duplicates. - */ - if (op->op != SETOP_UNION || !op->all) - { - ParseCallbackState pcbstate; - - setup_parser_errposition_callback(&pcbstate, pstate, - bestlocation); - - /* - * If it's a recursive union, we need to require hashing - * support. - */ - op->groupClauses = lappend(op->groupClauses, - makeSortGroupClauseForSetOp(rescoltype, recursive)); - - cancel_parser_errposition_callback(&pcbstate); - } - - /* - * Construct a dummy tlist entry to return. We use a SetToDefault - * node for the expression, since it carries exactly the fields - * needed, but any other expression node type would do as well. - */ - if (targetlist) - { - SetToDefault *rescolnode = makeNode(SetToDefault); - TargetEntry *restle; - - rescolnode->typeId = rescoltype; - rescolnode->typeMod = rescoltypmod; - rescolnode->collation = rescolcoll; - rescolnode->location = bestlocation; - restle = makeTargetEntry((Expr *) rescolnode, - 0, /* no need to set resno */ - NULL, - false); - *targetlist = lappend(*targetlist, restle); - } - } + constructSetOpTargetlist(pstate, op, ltargetlist, rtargetlist, targetlist, + context, recursive); return (Node *) op; } } +/* + * constructSetOpTargetlist + * Compute the types, typmods and collations of the columns in the target + * list of the given set operation. + * + * For every pair of columns in the targetlists of the children, compute the + * common type, typmod, and collation representing the output (UNION) column. + * If targetlist is not NULL, also build the dummy output targetlist + * containing non-resjunk output columns. The values are stored into the + * given SetOperationStmt node. context is a string for error messages + * ("UNION" etc.). recursive is true if it is a recursive union. + */ +static void +constructSetOpTargetlist(ParseState *pstate, SetOperationStmt *op, + const List *ltargetlist, const List *rtargetlist, + List **targetlist, const char *context, bool recursive) +{ + ListCell *ltl; + ListCell *rtl; + + /* + * Verify that the two children have the same number of non-junk columns, + * and determine the types of the merged output columns. + */ + if (list_length(ltargetlist) != list_length(rtargetlist)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("each %s query must have the same number of columns", + context), + parser_errposition(pstate, + exprLocation((Node *) rtargetlist)))); + + if (targetlist) + *targetlist = NIL; + op->colTypes = NIL; + op->colTypmods = NIL; + op->colCollations = NIL; + op->groupClauses = NIL; + + forboth(ltl, ltargetlist, rtl, rtargetlist) + { + TargetEntry *ltle = (TargetEntry *) lfirst(ltl); + TargetEntry *rtle = (TargetEntry *) lfirst(rtl); + Node *lcolnode = (Node *) ltle->expr; + Node *rcolnode = (Node *) rtle->expr; + Oid lcoltype = exprType(lcolnode); + Oid rcoltype = exprType(rcolnode); + Node *bestexpr; + int bestlocation; + Oid rescoltype; + int32 rescoltypmod; + Oid rescolcoll; + + /* select common type, same as CASE et al */ + rescoltype = select_common_type(pstate, + list_make2(lcolnode, rcolnode), + context, + &bestexpr); + bestlocation = exprLocation(bestexpr); + + /* + * Verify the coercions are actually possible. If not, we'd fail + * later anyway, but we want to fail now while we have sufficient + * context to produce an error cursor position. + * + * For all non-UNKNOWN-type cases, we verify coercibility but we don't + * modify the child's expression, for fear of changing the child + * query's semantics. + * + * If a child expression is an UNKNOWN-type Const or Param, we want to + * replace it with the coerced expression. This can only happen when + * the child is a leaf set-op node. It's safe to replace the + * expression because if the child query's semantics depended on the + * type of this output column, it'd have already coerced the UNKNOWN + * to something else. We want to do this because (a) we want to + * verify that a Const is valid for the target type, or resolve the + * actual type of an UNKNOWN Param, and (b) we want to avoid + * unnecessary discrepancies between the output type of the child + * query and the resolved target type. Such a discrepancy would + * disable optimization in the planner. + * + * If it's some other UNKNOWN-type node, eg a Var, we do nothing + * (knowing that coerce_to_common_type would fail). The planner is + * sometimes able to fold an UNKNOWN Var to a constant before it has + * to coerce the type, so failing now would just break cases that + * might work. + */ + if (lcoltype != UNKNOWNOID) + lcolnode = coerce_to_common_type(pstate, lcolnode, + rescoltype, context); + else if (IsA(lcolnode, Const) || + IsA(lcolnode, Param)) + { + lcolnode = coerce_to_common_type(pstate, lcolnode, + rescoltype, context); + ltle->expr = (Expr *) lcolnode; + } + + if (rcoltype != UNKNOWNOID) + rcolnode = coerce_to_common_type(pstate, rcolnode, + rescoltype, context); + else if (IsA(rcolnode, Const) || + IsA(rcolnode, Param)) + { + rcolnode = coerce_to_common_type(pstate, rcolnode, + rescoltype, context); + rtle->expr = (Expr *) rcolnode; + } + + rescoltypmod = select_common_typmod(pstate, + list_make2(lcolnode, rcolnode), + rescoltype); + + /* + * Select common collation. A common collation is required for all + * set operators except UNION ALL; see SQL:2008 7.13 Syntax Rule 15c. (If we fail to identify a common + * collation for a UNION ALL column, the colCollations element will be + * set to InvalidOid, which may result in a runtime error if something + * at a higher query level wants to use the column's collation.) + */ + rescolcoll = select_common_collation(pstate, + list_make2(lcolnode, rcolnode), + (op->op == SETOP_UNION && op->all)); + + /* emit results */ + op->colTypes = lappend_oid(op->colTypes, rescoltype); + op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); + op->colCollations = lappend_oid(op->colCollations, rescolcoll); + + /* + * For all cases except UNION ALL, identify the grouping operators + * (and, if available, sorting operators) that will be used to + * eliminate duplicates. + */ + if (op->op != SETOP_UNION || !op->all) + { + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, + bestlocation); + + /* If it's a recursive union, we need to require hashing support. */ + op->groupClauses = lappend(op->groupClauses, + makeSortGroupClauseForSetOp(rescoltype, recursive)); + + cancel_parser_errposition_callback(&pcbstate); + } + + /* + * Construct a dummy tlist entry to return. We use a SetToDefault + * node for the expression, since it carries exactly the fields + * needed, but any other expression node type would do as well. + */ + if (targetlist) + { + SetToDefault *rescolnode = makeNode(SetToDefault); + TargetEntry *restle; + + rescolnode->typeId = rescoltype; + rescolnode->typeMod = rescoltypmod; + rescolnode->collation = rescolcoll; + rescolnode->location = bestlocation; + restle = makeTargetEntry((Expr *) rescolnode, + 0, /* no need to set resno */ + NULL, + false); + *targetlist = lappend(*targetlist, restle); + } + } +} + /* * Process the outputs of the non-recursive term of a recursive union * to set up the parent CTE's columns