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);
Query *selectQuery;
ParseNamespaceItem *nsitem;
RangeTblRef *rtr;
- ListCell *tl;
/*
* Transform SelectStmt into a Query.
*/
if (targetlist)
{
+ ListCell *tl;
+
*targetlist = NIL;
foreach(tl, selectQuery->targetList)
{
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);
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))));
+ constructSetOpTargetlist(pstate, op, ltargetlist, rtargetlist, targetlist,
+ context, recursive);
- 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);
+ return (Node *) op;
+ }
+}
- /*
- * 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;
- }
+/*
+ * 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;
- 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;
- }
+ /*
+ * 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))));
- rescoltypmod = select_common_typmod(pstate,
- list_make2(lcolnode, rcolnode),
- rescoltype);
+ if (targetlist)
+ *targetlist = NIL;
+ op->colTypes = NIL;
+ op->colTypmods = NIL;
+ op->colCollations = NIL;
+ op->groupClauses = NIL;
- /*
- * Select common collation. A common collation is required for
- * all set operators except UNION ALL; see SQL:2008 7.13 <query
- * expression> 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));
+ 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;
+ }
- /* emit results */
- op->colTypes = lappend_oid(op->colTypes, rescoltype);
- op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
- op->colCollations = lappend_oid(op->colCollations, rescolcoll);
+ 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;
+ }
- /*
- * 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;
+ rescoltypmod = select_common_typmod(pstate,
+ list_make2(lcolnode, rcolnode),
+ rescoltype);
- setup_parser_errposition_callback(&pcbstate, pstate,
- bestlocation);
+ /*
+ * Select common collation. A common collation is required for all
+ * set operators except UNION ALL; see SQL:2008 7.13 <query
+ * expression> 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));
- /*
- * If it's a recursive union, we need to require hashing
- * support.
- */
- op->groupClauses = lappend(op->groupClauses,
- makeSortGroupClauseForSetOp(rescoltype, recursive));
+ /* emit results */
+ op->colTypes = lappend_oid(op->colTypes, rescoltype);
+ op->colTypmods = lappend_int(op->colTypmods, rescoltypmod);
+ op->colCollations = lappend_oid(op->colCollations, rescolcoll);
- cancel_parser_errposition_callback(&pcbstate);
- }
+ /*
+ * 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;
- /*
- * 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);
- }
+ 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);
}
- return (Node *) op;
+ /*
+ * 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);
+ }
}
}