@@ -52,9 +52,9 @@ static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt);
52
52
static Query * transformValuesClause (ParseState * pstate , SelectStmt * stmt );
53
53
static Query * transformSetOperationStmt (ParseState * pstate , SelectStmt * stmt );
54
54
static Node * transformSetOperationTree (ParseState * pstate , SelectStmt * stmt ,
55
- bool isTopLevel , List * * colInfo );
55
+ bool isTopLevel , List * * targetlist );
56
56
static void determineRecursiveColTypes (ParseState * pstate ,
57
- Node * larg , List * lcolinfo );
57
+ Node * larg , List * nrtargetlist );
58
58
static void applyColumnNames (List * dst , List * src );
59
59
static Query * transformUpdateStmt (ParseState * pstate , UpdateStmt * stmt );
60
60
static List * transformReturningList (ParseState * pstate , List * returningList );
@@ -1197,7 +1197,6 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
1197
1197
int leftmostRTI ;
1198
1198
Query * leftmostQuery ;
1199
1199
SetOperationStmt * sostmt ;
1200
- List * socolinfo ;
1201
1200
List * intoColNames = NIL ;
1202
1201
List * sortClause ;
1203
1202
Node * limitOffset ;
@@ -1271,7 +1270,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
1271
1270
*/
1272
1271
sostmt = (SetOperationStmt * ) transformSetOperationTree (pstate , stmt ,
1273
1272
true,
1274
- & socolinfo );
1273
+ NULL );
1275
1274
Assert (sostmt && IsA (sostmt , SetOperationStmt ));
1276
1275
qry -> setOperations = (Node * ) sostmt ;
1277
1276
@@ -1425,16 +1424,19 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
1425
1424
* transformSetOperationTree
1426
1425
* Recursively transform leaves and internal nodes of a set-op tree
1427
1426
*
1428
- * In addition to returning the transformed node, we return a list of
1429
- * expression nodes showing the type, typmod, collation, and location (for error messages)
1430
- * of each output column of the set-op node. This is used only during the
1431
- * internal recursion of this function. At the upper levels we use
1432
- * SetToDefault nodes for this purpose, since they carry exactly the fields
1433
- * needed, but any other expression node type would do as well.
1427
+ * In addition to returning the transformed node, if targetlist isn't NULL
1428
+ * then we return a list of its non-resjunk TargetEntry nodes. For a leaf
1429
+ * set-op node these are the actual targetlist entries; otherwise they are
1430
+ * dummy entries created to carry the type, typmod, collation, and location
1431
+ * (for error messages) of each output column of the set-op node. This info
1432
+ * is needed only during the internal recursion of this function, so outside
1433
+ * callers pass NULL for targetlist. Note: the reason for passing the
1434
+ * actual targetlist entries of a leaf node is so that upper levels can
1435
+ * replace UNKNOWN Consts with properly-coerced constants.
1434
1436
*/
1435
1437
static Node *
1436
1438
transformSetOperationTree (ParseState * pstate , SelectStmt * stmt ,
1437
- bool isTopLevel , List * * colInfo )
1439
+ bool isTopLevel , List * * targetlist )
1438
1440
{
1439
1441
bool isLeaf ;
1440
1442
@@ -1512,15 +1514,18 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1512
1514
}
1513
1515
1514
1516
/*
1515
- * Extract a list of the result expressions for upper-level checking .
1517
+ * Extract a list of the non-junk TLEs for upper-level processing .
1516
1518
*/
1517
- * colInfo = NIL ;
1518
- foreach (tl , selectQuery -> targetList )
1519
+ if (targetlist )
1519
1520
{
1520
- TargetEntry * tle = (TargetEntry * ) lfirst (tl );
1521
+ * targetlist = NIL ;
1522
+ foreach (tl , selectQuery -> targetList )
1523
+ {
1524
+ TargetEntry * tle = (TargetEntry * ) lfirst (tl );
1521
1525
1522
- if (!tle -> resjunk )
1523
- * colInfo = lappend (* colInfo , tle -> expr );
1526
+ if (!tle -> resjunk )
1527
+ * targetlist = lappend (* targetlist , tle );
1528
+ }
1524
1529
}
1525
1530
1526
1531
/*
@@ -1546,10 +1551,10 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1546
1551
{
1547
1552
/* Process an internal node (set operation node) */
1548
1553
SetOperationStmt * op = makeNode (SetOperationStmt );
1549
- List * lcolinfo ;
1550
- List * rcolinfo ;
1551
- ListCell * lci ;
1552
- ListCell * rci ;
1554
+ List * ltargetlist ;
1555
+ List * rtargetlist ;
1556
+ ListCell * ltl ;
1557
+ ListCell * rtl ;
1553
1558
const char * context ;
1554
1559
1555
1560
context = (stmt -> op == SETOP_UNION ? "UNION" :
@@ -1564,7 +1569,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1564
1569
*/
1565
1570
op -> larg = transformSetOperationTree (pstate , stmt -> larg ,
1566
1571
false,
1567
- & lcolinfo );
1572
+ & ltargetlist );
1568
1573
1569
1574
/*
1570
1575
* If we are processing a recursive union query, now is the time to
@@ -1575,42 +1580,45 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1575
1580
if (isTopLevel &&
1576
1581
pstate -> p_parent_cte &&
1577
1582
pstate -> p_parent_cte -> cterecursive )
1578
- determineRecursiveColTypes (pstate , op -> larg , lcolinfo );
1583
+ determineRecursiveColTypes (pstate , op -> larg , ltargetlist );
1579
1584
1580
1585
/*
1581
1586
* Recursively transform the right child node.
1582
1587
*/
1583
1588
op -> rarg = transformSetOperationTree (pstate , stmt -> rarg ,
1584
1589
false,
1585
- & rcolinfo );
1590
+ & rtargetlist );
1586
1591
1587
1592
/*
1588
1593
* Verify that the two children have the same number of non-junk
1589
1594
* columns, and determine the types of the merged output columns.
1590
1595
*/
1591
- if (list_length (lcolinfo ) != list_length (rcolinfo ))
1596
+ if (list_length (ltargetlist ) != list_length (rtargetlist ))
1592
1597
ereport (ERROR ,
1593
1598
(errcode (ERRCODE_SYNTAX_ERROR ),
1594
1599
errmsg ("each %s query must have the same number of columns" ,
1595
1600
context ),
1596
1601
parser_errposition (pstate ,
1597
- exprLocation ((Node * ) rcolinfo ))));
1602
+ exprLocation ((Node * ) rtargetlist ))));
1598
1603
1599
- * colInfo = NIL ;
1604
+ if (targetlist )
1605
+ * targetlist = NIL ;
1600
1606
op -> colTypes = NIL ;
1601
1607
op -> colTypmods = NIL ;
1602
1608
op -> colCollations = NIL ;
1603
1609
op -> groupClauses = NIL ;
1604
- forboth (lci , lcolinfo , rci , rcolinfo )
1610
+ forboth (ltl , ltargetlist , rtl , rtargetlist )
1605
1611
{
1606
- Node * lcolnode = (Node * ) lfirst (lci );
1607
- Node * rcolnode = (Node * ) lfirst (rci );
1612
+ TargetEntry * ltle = (TargetEntry * ) lfirst (ltl );
1613
+ TargetEntry * rtle = (TargetEntry * ) lfirst (rtl );
1614
+ Node * lcolnode = (Node * ) ltle -> expr ;
1615
+ Node * rcolnode = (Node * ) rtle -> expr ;
1608
1616
Oid lcoltype = exprType (lcolnode );
1609
1617
Oid rcoltype = exprType (rcolnode );
1610
1618
int32 lcoltypmod = exprTypmod (lcolnode );
1611
1619
int32 rcoltypmod = exprTypmod (rcolnode );
1612
1620
Node * bestexpr ;
1613
- SetToDefault * rescolnode ;
1621
+ int bestlocation ;
1614
1622
Oid rescoltype ;
1615
1623
int32 rescoltypmod ;
1616
1624
Oid rescolcoll ;
@@ -1620,6 +1628,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1620
1628
list_make2 (lcolnode , rcolnode ),
1621
1629
context ,
1622
1630
& bestexpr );
1631
+ bestlocation = exprLocation (bestexpr );
1623
1632
/* if same type and same typmod, use typmod; else default */
1624
1633
if (lcoltype == rcoltype && lcoltypmod == rcoltypmod )
1625
1634
rescoltypmod = lcoltypmod ;
@@ -1637,32 +1646,44 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1637
1646
* later anyway, but we want to fail now while we have sufficient
1638
1647
* context to produce an error cursor position.
1639
1648
*
1640
- * The if-tests might look wrong, but they are correct: we should
1641
- * verify if the input is non-UNKNOWN *or* if it is an UNKNOWN
1642
- * Const (to verify the literal is valid for the target data type)
1643
- * or Param (to possibly resolve the Param's type). We should do
1644
- * nothing if the input is say an UNKNOWN Var, which can happen in
1645
- * some cases. The planner is sometimes able to fold the Var to a
1649
+ * For all non-UNKNOWN-type cases, we verify coercibility but we
1650
+ * don't modify the child's expression, for fear of changing the
1651
+ * child query's semantics.
1652
+ *
1653
+ * If a child expression is an UNKNOWN-type Const or Param, we
1654
+ * want to replace it with the coerced expression. This can only
1655
+ * happen when the child is a leaf set-op node. It's safe to
1656
+ * replace the expression because if the child query's semantics
1657
+ * depended on the type of this output column, it'd have already
1658
+ * coerced the UNKNOWN to something else. We want to do this
1659
+ * because (a) we want to verify that a Const is valid for the
1660
+ * target type, or resolve the actual type of an UNKNOWN Param,
1661
+ * and (b) we want to avoid unnecessary discrepancies between the
1662
+ * output type of the child query and the resolved target type.
1663
+ * Such a discrepancy would disable optimization in the planner.
1664
+ *
1665
+ * If it's some other UNKNOWN-type node, eg a Var, we do nothing.
1666
+ * The planner is sometimes able to fold an UNKNOWN Var to a
1646
1667
* constant before it has to coerce the type, so failing now would
1647
1668
* just break cases that might work.
1648
1669
*/
1649
- if (lcoltype != UNKNOWNOID ||
1650
- IsA (lcolnode , Const ) || IsA (lcolnode , Param ))
1670
+ if (lcoltype != UNKNOWNOID )
1651
1671
(void ) coerce_to_common_type (pstate , lcolnode ,
1652
1672
rescoltype , context );
1653
- if (rcoltype != UNKNOWNOID ||
1654
- IsA (rcolnode , Const ) || IsA (rcolnode , Param ))
1673
+ else if (IsA (lcolnode , Const ) || IsA (lcolnode , Param ))
1674
+ ltle -> expr = (Expr * )
1675
+ coerce_to_common_type (pstate , lcolnode ,
1676
+ rescoltype , context );
1677
+
1678
+ if (rcoltype != UNKNOWNOID )
1655
1679
(void ) coerce_to_common_type (pstate , rcolnode ,
1656
1680
rescoltype , context );
1681
+ else if (IsA (rcolnode , Const ) || IsA (rcolnode , Param ))
1682
+ rtle -> expr = (Expr * )
1683
+ coerce_to_common_type (pstate , rcolnode ,
1684
+ rescoltype , context );
1657
1685
1658
1686
/* emit results */
1659
- rescolnode = makeNode (SetToDefault );
1660
- rescolnode -> typeId = rescoltype ;
1661
- rescolnode -> typeMod = rescoltypmod ;
1662
- rescolnode -> collid = rescolcoll ;
1663
- rescolnode -> location = exprLocation (bestexpr );
1664
- * colInfo = lappend (* colInfo , rescolnode );
1665
-
1666
1687
op -> colTypes = lappend_oid (op -> colTypes , rescoltype );
1667
1688
op -> colTypmods = lappend_int (op -> colTypmods , rescoltypmod );
1668
1689
op -> colCollations = lappend_oid (op -> colCollations , rescolcoll );
@@ -1681,7 +1702,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1681
1702
ParseCallbackState pcbstate ;
1682
1703
1683
1704
setup_parser_errposition_callback (& pcbstate , pstate ,
1684
- rescolnode -> location );
1705
+ bestlocation );
1685
1706
1686
1707
/* determine the eqop and optional sortop */
1687
1708
get_sort_group_operators (rescoltype ,
@@ -1700,6 +1721,27 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1700
1721
1701
1722
op -> groupClauses = lappend (op -> groupClauses , grpcl );
1702
1723
}
1724
+
1725
+ /*
1726
+ * Construct a dummy tlist entry to return. We use a SetToDefault
1727
+ * node for the expression, since it carries exactly the fields
1728
+ * needed, but any other expression node type would do as well.
1729
+ */
1730
+ if (targetlist )
1731
+ {
1732
+ SetToDefault * rescolnode = makeNode (SetToDefault );
1733
+ TargetEntry * restle ;
1734
+
1735
+ rescolnode -> typeId = rescoltype ;
1736
+ rescolnode -> typeMod = rescoltypmod ;
1737
+ rescolnode -> collid = rescolcoll ;
1738
+ rescolnode -> location = bestlocation ;
1739
+ restle = makeTargetEntry ((Expr * ) rescolnode ,
1740
+ 0 , /* no need to set resno */
1741
+ NULL ,
1742
+ false);
1743
+ * targetlist = lappend (* targetlist , restle );
1744
+ }
1703
1745
}
1704
1746
1705
1747
return (Node * ) op ;
@@ -1711,14 +1753,14 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt,
1711
1753
* to set up the parent CTE's columns
1712
1754
*/
1713
1755
static void
1714
- determineRecursiveColTypes (ParseState * pstate , Node * larg , List * lcolinfo )
1756
+ determineRecursiveColTypes (ParseState * pstate , Node * larg , List * nrtargetlist )
1715
1757
{
1716
1758
Node * node ;
1717
1759
int leftmostRTI ;
1718
1760
Query * leftmostQuery ;
1719
1761
List * targetList ;
1720
1762
ListCell * left_tlist ;
1721
- ListCell * lci ;
1763
+ ListCell * nrtl ;
1722
1764
int next_resno ;
1723
1765
1724
1766
/*
@@ -1740,16 +1782,16 @@ determineRecursiveColTypes(ParseState *pstate, Node *larg, List *lcolinfo)
1740
1782
left_tlist = list_head (leftmostQuery -> targetList );
1741
1783
next_resno = 1 ;
1742
1784
1743
- foreach (lci , lcolinfo )
1785
+ foreach (nrtl , nrtargetlist )
1744
1786
{
1745
- Expr * lcolexpr = (Expr * ) lfirst (lci );
1787
+ TargetEntry * nrtle = (TargetEntry * ) lfirst (nrtl );
1746
1788
TargetEntry * lefttle = (TargetEntry * ) lfirst (left_tlist );
1747
1789
char * colName ;
1748
1790
TargetEntry * tle ;
1749
1791
1750
1792
Assert (!lefttle -> resjunk );
1751
1793
colName = pstrdup (lefttle -> resname );
1752
- tle = makeTargetEntry (lcolexpr ,
1794
+ tle = makeTargetEntry (nrtle -> expr ,
1753
1795
next_resno ++ ,
1754
1796
colName ,
1755
1797
false);
0 commit comments