|
|
|
|
@ -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,161 +2252,182 @@ 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)))); |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|