@@ -82,13 +82,14 @@ static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
82
82
RangeTblEntry * rte );
83
83
static RelOptInfo * make_rel_from_joinlist (PlannerInfo * root , List * joinlist );
84
84
static bool subquery_is_pushdown_safe (Query * subquery , Query * topquery ,
85
- bool * differentTypes );
85
+ bool * unsafeColumns );
86
86
static bool recurse_pushdown_safe (Node * setOp , Query * topquery ,
87
- bool * differentTypes );
87
+ bool * unsafeColumns );
88
+ static void check_output_expressions (Query * subquery , bool * unsafeColumns );
88
89
static void compare_tlist_datatypes (List * tlist , List * colTypes ,
89
- bool * differentTypes );
90
+ bool * unsafeColumns );
90
91
static bool qual_is_pushdown_safe (Query * subquery , Index rti , Node * qual ,
91
- bool * differentTypes );
92
+ bool * unsafeColumns );
92
93
static void subquery_push_qual (Query * subquery ,
93
94
RangeTblEntry * rte , Index rti , Node * qual );
94
95
static void recurse_push_qual (Node * setOp , Query * topquery ,
@@ -1021,7 +1022,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
1021
1022
{
1022
1023
Query * parse = root -> parse ;
1023
1024
Query * subquery = rte -> subquery ;
1024
- bool * differentTypes ;
1025
+ bool * unsafeColumns ;
1025
1026
double tuple_fraction ;
1026
1027
PlannerInfo * subroot ;
1027
1028
List * pathkeys ;
@@ -1033,8 +1034,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
1033
1034
*/
1034
1035
subquery = copyObject (subquery );
1035
1036
1036
- /* We need a workspace for keeping track of set-op type coercions */
1037
- differentTypes = (bool * )
1037
+ /*
1038
+ * We need a workspace for keeping track of unsafe-to-reference columns.
1039
+ * unsafeColumns[i] is set TRUE if we've found that output column i of the
1040
+ * subquery is unsafe to use in a pushed-down qual.
1041
+ */
1042
+ unsafeColumns = (bool * )
1038
1043
palloc0 ((list_length (subquery -> targetList ) + 1 ) * sizeof (bool ));
1039
1044
1040
1045
/*
@@ -1063,7 +1068,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
1063
1068
* push down a pushable qual, because it'd result in a worse plan?
1064
1069
*/
1065
1070
if (rel -> baserestrictinfo != NIL &&
1066
- subquery_is_pushdown_safe (subquery , subquery , differentTypes ))
1071
+ subquery_is_pushdown_safe (subquery , subquery , unsafeColumns ))
1067
1072
{
1068
1073
/* OK to consider pushing down individual quals */
1069
1074
List * upperrestrictlist = NIL ;
@@ -1077,7 +1082,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
1077
1082
if (!rinfo -> pseudoconstant &&
1078
1083
(!rte -> security_barrier ||
1079
1084
!contain_leaky_functions (clause )) &&
1080
- qual_is_pushdown_safe (subquery , rti , clause , differentTypes ))
1085
+ qual_is_pushdown_safe (subquery , rti , clause , unsafeColumns ))
1081
1086
{
1082
1087
/* Push it down */
1083
1088
subquery_push_qual (subquery , rte , rti , clause );
@@ -1091,7 +1096,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
1091
1096
rel -> baserestrictinfo = upperrestrictlist ;
1092
1097
}
1093
1098
1094
- pfree (differentTypes );
1099
+ pfree (unsafeColumns );
1095
1100
1096
1101
/*
1097
1102
* We can safely pass the outer tuple_fraction down to the subquery if the
@@ -1485,17 +1490,19 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
1485
1490
* 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
1486
1491
* quals into it, because that could change the results.
1487
1492
*
1488
- * 4. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
1489
- * push quals into each component query, but the quals can only reference
1490
- * subquery columns that suffer no type coercions in the set operation.
1491
- * Otherwise there are possible semantic gotchas. So, we check the
1492
- * component queries to see if any of them have different output types;
1493
- * differentTypes[k] is set true if column k has different type in any
1494
- * component.
1493
+ * In addition, we make several checks on the subquery's output columns
1494
+ * to see if it is safe to reference them in pushed-down quals. If output
1495
+ * column k is found to be unsafe to reference, we set unsafeColumns[k] to
1496
+ * TRUE, but we don't reject the subquery overall since column k might
1497
+ * not be referenced by some/all quals. The unsafeColumns[] array will be
1498
+ * consulted later by qual_is_pushdown_safe(). It's better to do it this
1499
+ * way than to make the checks directly in qual_is_pushdown_safe(), because
1500
+ * when the subquery involves set operations we have to check the output
1501
+ * expressions in each arm of the set op.
1495
1502
*/
1496
1503
static bool
1497
1504
subquery_is_pushdown_safe (Query * subquery , Query * topquery ,
1498
- bool * differentTypes )
1505
+ bool * unsafeColumns )
1499
1506
{
1500
1507
SetOperationStmt * topop ;
1501
1508
@@ -1507,13 +1514,22 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1507
1514
if (subquery -> hasWindowFuncs )
1508
1515
return false;
1509
1516
1517
+ /*
1518
+ * If we're at a leaf query, check for unsafe expressions in its target
1519
+ * list, and mark any unsafe ones in unsafeColumns[]. (Non-leaf nodes in
1520
+ * setop trees have only simple Vars in their tlists, so no need to check
1521
+ * them.)
1522
+ */
1523
+ if (subquery -> setOperations == NULL )
1524
+ check_output_expressions (subquery , unsafeColumns );
1525
+
1510
1526
/* Are we at top level, or looking at a setop component? */
1511
1527
if (subquery == topquery )
1512
1528
{
1513
1529
/* Top level, so check any component queries */
1514
1530
if (subquery -> setOperations != NULL )
1515
1531
if (!recurse_pushdown_safe (subquery -> setOperations , topquery ,
1516
- differentTypes ))
1532
+ unsafeColumns ))
1517
1533
return false;
1518
1534
}
1519
1535
else
@@ -1526,7 +1542,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1526
1542
Assert (topop && IsA (topop , SetOperationStmt ));
1527
1543
compare_tlist_datatypes (subquery -> targetList ,
1528
1544
topop -> colTypes ,
1529
- differentTypes );
1545
+ unsafeColumns );
1530
1546
}
1531
1547
return true;
1532
1548
}
@@ -1536,7 +1552,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1536
1552
*/
1537
1553
static bool
1538
1554
recurse_pushdown_safe (Node * setOp , Query * topquery ,
1539
- bool * differentTypes )
1555
+ bool * unsafeColumns )
1540
1556
{
1541
1557
if (IsA (setOp , RangeTblRef ))
1542
1558
{
@@ -1545,19 +1561,19 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1545
1561
Query * subquery = rte -> subquery ;
1546
1562
1547
1563
Assert (subquery != NULL );
1548
- return subquery_is_pushdown_safe (subquery , topquery , differentTypes );
1564
+ return subquery_is_pushdown_safe (subquery , topquery , unsafeColumns );
1549
1565
}
1550
1566
else if (IsA (setOp , SetOperationStmt ))
1551
1567
{
1552
1568
SetOperationStmt * op = (SetOperationStmt * ) setOp ;
1553
1569
1554
- /* EXCEPT is no good */
1570
+ /* EXCEPT is no good (point 3 for subquery_is_pushdown_safe) */
1555
1571
if (op -> op == SETOP_EXCEPT )
1556
1572
return false;
1557
1573
/* Else recurse */
1558
- if (!recurse_pushdown_safe (op -> larg , topquery , differentTypes ))
1574
+ if (!recurse_pushdown_safe (op -> larg , topquery , unsafeColumns ))
1559
1575
return false;
1560
- if (!recurse_pushdown_safe (op -> rarg , topquery , differentTypes ))
1576
+ if (!recurse_pushdown_safe (op -> rarg , topquery , unsafeColumns ))
1561
1577
return false;
1562
1578
}
1563
1579
else
@@ -1569,17 +1585,92 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1569
1585
}
1570
1586
1571
1587
/*
1572
- * Compare tlist's datatypes against the list of set-operation result types.
1573
- * For any items that are different, mark the appropriate element of
1574
- * differentTypes[] to show that this column will have type conversions.
1588
+ * check_output_expressions - check subquery's output expressions for safety
1589
+ *
1590
+ * There are several cases in which it's unsafe to push down an upper-level
1591
+ * qual if it references a particular output column of a subquery. We check
1592
+ * each output column of the subquery and set unsafeColumns[k] to TRUE if
1593
+ * that column is unsafe for a pushed-down qual to reference. The conditions
1594
+ * checked here are:
1595
+ *
1596
+ * 1. We must not push down any quals that refer to subselect outputs that
1597
+ * return sets, else we'd introduce functions-returning-sets into the
1598
+ * subquery's WHERE/HAVING quals.
1599
+ *
1600
+ * 2. We must not push down any quals that refer to subselect outputs that
1601
+ * contain volatile functions, for fear of introducing strange results due
1602
+ * to multiple evaluation of a volatile function.
1603
+ *
1604
+ * 3. If the subquery uses DISTINCT ON, we must not push down any quals that
1605
+ * refer to non-DISTINCT output columns, because that could change the set
1606
+ * of rows returned. (This condition is vacuous for DISTINCT, because then
1607
+ * there are no non-DISTINCT output columns, so we needn't check. But note
1608
+ * we are assuming that the qual can't distinguish values that the DISTINCT
1609
+ * operator sees as equal. This is a bit shaky but we have no way to test
1610
+ * for the case, and it's unlikely enough that we shouldn't refuse the
1611
+ * optimization just because it could theoretically happen.)
1612
+ */
1613
+ static void
1614
+ check_output_expressions (Query * subquery , bool * unsafeColumns )
1615
+ {
1616
+ ListCell * lc ;
1617
+
1618
+ foreach (lc , subquery -> targetList )
1619
+ {
1620
+ TargetEntry * tle = (TargetEntry * ) lfirst (lc );
1621
+
1622
+ if (tle -> resjunk )
1623
+ continue ; /* ignore resjunk columns */
1624
+
1625
+ /* We need not check further if output col is already known unsafe */
1626
+ if (unsafeColumns [tle -> resno ])
1627
+ continue ;
1628
+
1629
+ /* Functions returning sets are unsafe (point 1) */
1630
+ if (expression_returns_set ((Node * ) tle -> expr ))
1631
+ {
1632
+ unsafeColumns [tle -> resno ] = true;
1633
+ continue ;
1634
+ }
1635
+
1636
+ /* Volatile functions are unsafe (point 2) */
1637
+ if (contain_volatile_functions ((Node * ) tle -> expr ))
1638
+ {
1639
+ unsafeColumns [tle -> resno ] = true;
1640
+ continue ;
1641
+ }
1642
+
1643
+ /* If subquery uses DISTINCT ON, check point 3 */
1644
+ if (subquery -> hasDistinctOn &&
1645
+ !targetIsInSortList (tle , InvalidOid , subquery -> distinctClause ))
1646
+ {
1647
+ /* non-DISTINCT column, so mark it unsafe */
1648
+ unsafeColumns [tle -> resno ] = true;
1649
+ continue ;
1650
+ }
1651
+ }
1652
+ }
1653
+
1654
+ /*
1655
+ * For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
1656
+ * push quals into each component query, but the quals can only reference
1657
+ * subquery columns that suffer no type coercions in the set operation.
1658
+ * Otherwise there are possible semantic gotchas. So, we check the
1659
+ * component queries to see if any of them have output types different from
1660
+ * the top-level setop outputs. unsafeColumns[k] is set true if column k
1661
+ * has different type in any component.
1575
1662
*
1576
1663
* We don't have to care about typmods here: the only allowed difference
1577
1664
* between set-op input and output typmods is input is a specific typmod
1578
1665
* and output is -1, and that does not require a coercion.
1666
+ *
1667
+ * tlist is a subquery tlist.
1668
+ * colTypes is an OID list of the top-level setop's output column types.
1669
+ * unsafeColumns[] is the result array.
1579
1670
*/
1580
1671
static void
1581
1672
compare_tlist_datatypes (List * tlist , List * colTypes ,
1582
- bool * differentTypes )
1673
+ bool * unsafeColumns )
1583
1674
{
1584
1675
ListCell * l ;
1585
1676
ListCell * colType = list_head (colTypes );
@@ -1593,7 +1684,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1593
1684
if (colType == NULL )
1594
1685
elog (ERROR , "wrong number of tlist entries" );
1595
1686
if (exprType ((Node * ) tle -> expr ) != lfirst_oid (colType ))
1596
- differentTypes [tle -> resno ] = true;
1687
+ unsafeColumns [tle -> resno ] = true;
1597
1688
colType = lnext (colType );
1598
1689
}
1599
1690
if (colType != NULL )
@@ -1616,34 +1707,15 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1616
1707
* (since there is no easy way to name that within the subquery itself).
1617
1708
*
1618
1709
* 3. The qual must not refer to any subquery output columns that were
1619
- * found to have inconsistent types across a set operation tree by
1620
- * subquery_is_pushdown_safe().
1621
- *
1622
- * 4. If the subquery uses DISTINCT ON, we must not push down any quals that
1623
- * refer to non-DISTINCT output columns, because that could change the set
1624
- * of rows returned. (This condition is vacuous for DISTINCT, because then
1625
- * there are no non-DISTINCT output columns, so we needn't check. But note
1626
- * we are assuming that the qual can't distinguish values that the DISTINCT
1627
- * operator sees as equal. This is a bit shaky but we have no way to test
1628
- * for the case, and it's unlikely enough that we shouldn't refuse the
1629
- * optimization just because it could theoretically happen.)
1630
- *
1631
- * 5. We must not push down any quals that refer to subselect outputs that
1632
- * return sets, else we'd introduce functions-returning-sets into the
1633
- * subquery's WHERE/HAVING quals.
1634
- *
1635
- * 6. We must not push down any quals that refer to subselect outputs that
1636
- * contain volatile functions, for fear of introducing strange results due
1637
- * to multiple evaluation of a volatile function.
1710
+ * found to be unsafe to reference by subquery_is_pushdown_safe().
1638
1711
*/
1639
1712
static bool
1640
1713
qual_is_pushdown_safe (Query * subquery , Index rti , Node * qual ,
1641
- bool * differentTypes )
1714
+ bool * unsafeColumns )
1642
1715
{
1643
1716
bool safe = true;
1644
1717
List * vars ;
1645
1718
ListCell * vl ;
1646
- Bitmapset * tested = NULL ;
1647
1719
1648
1720
/* Refuse subselects (point 1) */
1649
1721
if (contain_subplans (qual ))
@@ -1666,7 +1738,6 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1666
1738
foreach (vl , vars )
1667
1739
{
1668
1740
Var * var = (Var * ) lfirst (vl );
1669
- TargetEntry * tle ;
1670
1741
1671
1742
/*
1672
1743
* XXX Punt if we find any PlaceHolderVars in the restriction clause.
@@ -1682,6 +1753,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1682
1753
}
1683
1754
1684
1755
Assert (var -> varno == rti );
1756
+ Assert (var -> varattno >= 0 );
1685
1757
1686
1758
/* Check point 2 */
1687
1759
if (var -> varattno == 0 )
@@ -1690,53 +1762,15 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1690
1762
break ;
1691
1763
}
1692
1764
1693
- /*
1694
- * We use a bitmapset to avoid testing the same attno more than once.
1695
- * (NB: this only works because subquery outputs can't have negative
1696
- * attnos.)
1697
- */
1698
- if (bms_is_member (var -> varattno , tested ))
1699
- continue ;
1700
- tested = bms_add_member (tested , var -> varattno );
1701
-
1702
1765
/* Check point 3 */
1703
- if (differentTypes [var -> varattno ])
1704
- {
1705
- safe = false;
1706
- break ;
1707
- }
1708
-
1709
- /* Must find the tlist element referenced by the Var */
1710
- tle = get_tle_by_resno (subquery -> targetList , var -> varattno );
1711
- Assert (tle != NULL );
1712
- Assert (!tle -> resjunk );
1713
-
1714
- /* If subquery uses DISTINCT ON, check point 4 */
1715
- if (subquery -> hasDistinctOn &&
1716
- !targetIsInSortList (tle , InvalidOid , subquery -> distinctClause ))
1717
- {
1718
- /* non-DISTINCT column, so fail */
1719
- safe = false;
1720
- break ;
1721
- }
1722
-
1723
- /* Refuse functions returning sets (point 5) */
1724
- if (expression_returns_set ((Node * ) tle -> expr ))
1725
- {
1726
- safe = false;
1727
- break ;
1728
- }
1729
-
1730
- /* Refuse volatile functions (point 6) */
1731
- if (contain_volatile_functions ((Node * ) tle -> expr ))
1766
+ if (unsafeColumns [var -> varattno ])
1732
1767
{
1733
1768
safe = false;
1734
1769
break ;
1735
1770
}
1736
1771
}
1737
1772
1738
1773
list_free (vars );
1739
- bms_free (tested );
1740
1774
1741
1775
return safe ;
1742
1776
}
0 commit comments