39
39
#include "access/nbtree.h"
40
40
#include "catalog/pg_operator.h"
41
41
#include "catalog/pg_opfamily.h"
42
+ #include "catalog/pg_proc.h"
42
43
#include "catalog/pg_type.h"
43
44
#include "executor/executor.h"
44
45
#include "miscadmin.h"
@@ -94,8 +95,12 @@ typedef enum PartClauseMatchStatus
94
95
*/
95
96
typedef struct GeneratePruningStepsContext
96
97
{
98
+ /* Input data: */
99
+ bool forplanner ; /* true when generating steps to be used
100
+ * during query planning */
101
+ /* Working state and result data: */
97
102
int next_step_id ;
98
- List * steps ;
103
+ List * steps ; /* output, list of PartitionPruneSteps */
99
104
} GeneratePruningStepsContext ;
100
105
101
106
/* The result of performing one PartitionPruneStep */
@@ -118,7 +123,7 @@ static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
118
123
List * partitioned_rels , List * prunequal ,
119
124
Bitmapset * * matchedsubplans );
120
125
static List * gen_partprune_steps (RelOptInfo * rel , List * clauses ,
121
- bool * contradictory );
126
+ bool forplanner , bool * contradictory );
122
127
static List * gen_partprune_steps_internal (GeneratePruningStepsContext * context ,
123
128
RelOptInfo * rel , List * clauses ,
124
129
bool * contradictory );
@@ -413,7 +418,13 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
413
418
targetpart -> relids );
414
419
}
415
420
416
- pruning_steps = gen_partprune_steps (subpart , partprunequal ,
421
+ /*
422
+ * Convert pruning qual to pruning steps. Since these steps will be
423
+ * used in the executor, we can pass 'forplanner' as false to allow
424
+ * steps to be generated that are unsafe for evaluation during
425
+ * planning, e.g. evaluation of stable functions.
426
+ */
427
+ pruning_steps = gen_partprune_steps (subpart , partprunequal , false,
417
428
& contradictory );
418
429
419
430
if (contradictory )
@@ -492,22 +503,37 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
492
503
/*
493
504
* gen_partprune_steps
494
505
* Process 'clauses' (a rel's baserestrictinfo list of clauses) and return
495
- * a list of "partition pruning steps"
506
+ * a list of "partition pruning steps".
507
+ *
508
+ * 'forplanner' must be true when generating steps to be evaluated during
509
+ * query planning, false when generating steps to be used at run-time.
510
+ *
511
+ * The result generated with forplanner=false includes all clauses that
512
+ * are selected with forplanner=true, because in some cases we need a
513
+ * combination of clauses to prune successfully. For example, if we
514
+ * are partitioning on a hash of columns A and B, and we have clauses
515
+ * "WHERE A=constant AND B=nonconstant", we can't do anything at plan
516
+ * time even though the first clause would be evaluable then. And we
517
+ * must include the first clause when called with forplanner=false,
518
+ * or we'll fail to prune at run-time either. This does mean that when
519
+ * called with forplanner=false, we may return steps that don't actually
520
+ * need to be executed at runtime; it's left to analyze_partkey_exprs()
521
+ * to (re)discover that.
496
522
*
497
523
* If the clauses in the input list are contradictory or there is a
498
- * pseudo-constant "false", *contradictory is set to true upon return.
524
+ * pseudo-constant "false", *contradictory is set to true upon return,
525
+ * else it's set false.
499
526
*/
500
527
static List *
501
- gen_partprune_steps (RelOptInfo * rel , List * clauses , bool * contradictory )
528
+ gen_partprune_steps (RelOptInfo * rel , List * clauses , bool forplanner ,
529
+ bool * contradictory )
502
530
{
503
531
GeneratePruningStepsContext context ;
504
532
533
+ context .forplanner = forplanner ;
505
534
context .next_step_id = 0 ;
506
535
context .steps = NIL ;
507
536
508
- /* The clauses list may be modified below, so better make a copy. */
509
- clauses = list_copy (clauses );
510
-
511
537
/*
512
538
* For sub-partitioned tables there's a corner case where if the
513
539
* sub-partitioned table shares any partition keys with its parent, then
@@ -532,19 +558,23 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
532
558
if (rel -> relid != 1 )
533
559
ChangeVarNodes ((Node * ) partqual , 1 , rel -> relid , 0 );
534
560
535
- clauses = list_concat (clauses , partqual );
561
+ /* Use list_copy to avoid modifying the passed-in List */
562
+ clauses = list_concat (list_copy (clauses ), partqual );
536
563
}
537
564
538
565
/* Down into the rabbit-hole. */
539
- gen_partprune_steps_internal (& context , rel , clauses , contradictory );
566
+ ( void ) gen_partprune_steps_internal (& context , rel , clauses , contradictory );
540
567
541
568
return context .steps ;
542
569
}
543
570
544
571
/*
545
572
* prune_append_rel_partitions
546
- * Returns RT indexes of the minimum set of child partitions which must
547
- * be scanned to satisfy rel's baserestrictinfo quals.
573
+ * Process rel's baserestrictinfo and make use of quals which can be
574
+ * evaluated during query planning in order to determine the minimum set
575
+ * of partitions which must be scanned to satisfy these quals. Returns
576
+ * the matching partitions in the form of a Relids set containing the
577
+ * partitions' RT indexes.
548
578
*
549
579
* Callers must ensure that 'rel' is a partitioned table.
550
580
*/
@@ -568,9 +598,12 @@ prune_append_rel_partitions(RelOptInfo *rel)
568
598
569
599
/*
570
600
* Process clauses. If the clauses are found to be contradictory, we can
571
- * return the empty set.
601
+ * return the empty set. Pass 'forplanner' as true to indicate to the
602
+ * pruning code that we only want pruning steps that can be evaluated
603
+ * during planning.
572
604
*/
573
- pruning_steps = gen_partprune_steps (rel , clauses , & contradictory );
605
+ pruning_steps = gen_partprune_steps (rel , clauses , true,
606
+ & contradictory );
574
607
if (contradictory )
575
608
return NULL ;
576
609
@@ -609,6 +642,9 @@ prune_append_rel_partitions(RelOptInfo *rel)
609
642
* get_matching_partitions
610
643
* Determine partitions that survive partition pruning
611
644
*
645
+ * Note: context->planstate must be set to a valid PlanState when the
646
+ * pruning_steps were generated with 'forplanner' = false.
647
+ *
612
648
* Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
613
649
* partitions.
614
650
*/
@@ -729,9 +765,6 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
729
765
* clause that contains false, we set *contradictory to true and return NIL
730
766
* (that is, no pruning steps). Caller should consider all partitions as
731
767
* pruned in that case. Otherwise, *contradictory is set to false.
732
- *
733
- * Note: the 'clauses' List may be modified inside this function. Callers may
734
- * like to make a copy of it before passing them to this function.
735
768
*/
736
769
static List *
737
770
gen_partprune_steps_internal (GeneratePruningStepsContext * context ,
@@ -1546,8 +1579,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
1546
1579
Expr * expr ;
1547
1580
1548
1581
/*
1549
- * Recognize specially shaped clauses that match with the Boolean
1550
- * partition key.
1582
+ * Recognize specially shaped clauses that match a Boolean partition key.
1551
1583
*/
1552
1584
if (match_boolean_partition_clause (partopfamily , clause , partkey , & expr ))
1553
1585
{
@@ -1622,16 +1654,47 @@ match_clause_to_partition_key(RelOptInfo *rel,
1622
1654
* Matched with this key. Now check various properties of the clause
1623
1655
* to see if it's sane to use it for pruning. In most of these cases,
1624
1656
* we can return UNSUPPORTED because the same failure would occur no
1625
- * matter which partkey it's matched to.
1657
+ * matter which partkey it's matched to. (In particular, now that
1658
+ * we've successfully matched one side of the opclause to a partkey,
1659
+ * there is no chance that matching the other side to another partkey
1660
+ * will produce a usable result, since that'd mean there are Vars on
1661
+ * both sides.)
1626
1662
*/
1663
+ if (context -> forplanner )
1664
+ {
1665
+ /*
1666
+ * When pruning in the planner, we only support pruning using
1667
+ * comparisons to constants. Immutable subexpressions will have
1668
+ * been folded to constants already, and we cannot prune on the
1669
+ * basis of anything that's not immutable.
1670
+ */
1671
+ if (!IsA (expr , Const ))
1672
+ return PARTCLAUSE_UNSUPPORTED ;
1627
1673
1628
- /*
1629
- * We can't prune using an expression with Vars. (Report failure as
1630
- * UNSUPPORTED, not NOMATCH: as in the no-commutator case above, we
1631
- * now know there are Vars on both sides, so it's no good.)
1632
- */
1633
- if (contain_var_clause ((Node * ) expr ))
1634
- return PARTCLAUSE_UNSUPPORTED ;
1674
+ /*
1675
+ * Also, the comparison operator itself must be immutable.
1676
+ */
1677
+ if (op_volatile (opno ) != PROVOLATILE_IMMUTABLE )
1678
+ return PARTCLAUSE_UNSUPPORTED ;
1679
+ }
1680
+ else
1681
+ {
1682
+ /*
1683
+ * Otherwise, non-consts are allowed, but we can't prune using an
1684
+ * expression that contains Vars.
1685
+ */
1686
+ if (contain_var_clause ((Node * ) expr ))
1687
+ return PARTCLAUSE_UNSUPPORTED ;
1688
+
1689
+ /*
1690
+ * And we must reject anything containing a volatile function.
1691
+ * Stable functions are OK though. (We need not check this for
1692
+ * the comparison operator itself: anything that belongs to a
1693
+ * partitioning operator family must be at least stable.)
1694
+ */
1695
+ if (contain_volatile_functions ((Node * ) expr ))
1696
+ return PARTCLAUSE_UNSUPPORTED ;
1697
+ }
1635
1698
1636
1699
/*
1637
1700
* Only allow strict operators. This will guarantee nulls are
@@ -1640,10 +1703,6 @@ match_clause_to_partition_key(RelOptInfo *rel,
1640
1703
if (!op_strict (opno ))
1641
1704
return PARTCLAUSE_UNSUPPORTED ;
1642
1705
1643
- /* We can't use any volatile expressions to prune partitions. */
1644
- if (contain_volatile_functions ((Node * ) expr ))
1645
- return PARTCLAUSE_UNSUPPORTED ;
1646
-
1647
1706
/*
1648
1707
* See if the operator is relevant to the partitioning opfamily.
1649
1708
*
@@ -1772,20 +1831,51 @@ match_clause_to_partition_key(RelOptInfo *rel,
1772
1831
if (IsA (leftop , RelabelType ))
1773
1832
leftop = ((RelabelType * ) leftop )-> arg ;
1774
1833
1775
- /* Check it matches this partition key */
1834
+ /* check if the LHS matches this partition key */
1776
1835
if (!equal (leftop , partkey ) ||
1777
1836
!PartCollMatchesExprColl (partcoll , saop -> inputcollid ))
1778
1837
return PARTCLAUSE_NOMATCH ;
1779
1838
1780
1839
/*
1781
1840
* Matched with this key. Check various properties of the clause to
1782
- * see if it can sanely be used for partition pruning (this is mostly
1783
- * the same as for a plain OpExpr).
1841
+ * see if it can sanely be used for partition pruning (this is
1842
+ * identical to the logic for a plain OpExpr).
1784
1843
*/
1844
+ if (context -> forplanner )
1845
+ {
1846
+ /*
1847
+ * When pruning in the planner, we only support pruning using
1848
+ * comparisons to constants. Immutable subexpressions will have
1849
+ * been folded to constants already, and we cannot prune on the
1850
+ * basis of anything that's not immutable.
1851
+ */
1852
+ if (!IsA (rightop , Const ))
1853
+ return PARTCLAUSE_UNSUPPORTED ;
1785
1854
1786
- /* We can't prune using an expression with Vars. */
1787
- if (contain_var_clause ((Node * ) rightop ))
1788
- return PARTCLAUSE_UNSUPPORTED ;
1855
+ /*
1856
+ * Also, the comparison operator itself must be immutable.
1857
+ */
1858
+ if (op_volatile (saop_op ) != PROVOLATILE_IMMUTABLE )
1859
+ return PARTCLAUSE_UNSUPPORTED ;
1860
+ }
1861
+ else
1862
+ {
1863
+ /*
1864
+ * Otherwise, non-consts are allowed, but we can't prune using an
1865
+ * expression that contains Vars.
1866
+ */
1867
+ if (contain_var_clause ((Node * ) rightop ))
1868
+ return PARTCLAUSE_UNSUPPORTED ;
1869
+
1870
+ /*
1871
+ * And we must reject anything containing a volatile function.
1872
+ * Stable functions are OK though. (We need not check this for
1873
+ * the comparison operator itself: anything that belongs to a
1874
+ * partitioning operator family must be at least stable.)
1875
+ */
1876
+ if (contain_volatile_functions ((Node * ) rightop ))
1877
+ return PARTCLAUSE_UNSUPPORTED ;
1878
+ }
1789
1879
1790
1880
/*
1791
1881
* Only allow strict operators. This will guarantee nulls are
@@ -1794,10 +1884,6 @@ match_clause_to_partition_key(RelOptInfo *rel,
1794
1884
if (!op_strict (saop_op ))
1795
1885
return PARTCLAUSE_UNSUPPORTED ;
1796
1886
1797
- /* We can't use any volatile expressions to prune partitions. */
1798
- if (contain_volatile_functions ((Node * ) rightop ))
1799
- return PARTCLAUSE_UNSUPPORTED ;
1800
-
1801
1887
/*
1802
1888
* In case of NOT IN (..), we get a '<>', which we handle if list
1803
1889
* partitioning is in use and we're able to confirm that it's negator
@@ -2918,8 +3004,14 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2918
3004
2919
3005
/*
2920
3006
* Steps require run-time pruning if they contain EXEC_PARAM Params.
2921
- * Otherwise, if their expressions aren't simple Consts, they require
2922
- * startup-time pruning.
3007
+ * Otherwise, if their expressions aren't simple Consts or they involve
3008
+ * non-immutable comparison operators, they require startup-time pruning.
3009
+ * (Otherwise, the pruning would have been done at plan time.)
3010
+ *
3011
+ * Notice that what we actually check for mutability is the comparison
3012
+ * functions, not the original operators. This relies on the support
3013
+ * functions of the btree or hash opfamily being marked consistently with
3014
+ * the operators.
2923
3015
*/
2924
3016
pinfo -> nexprs = list_length (steps ) * partnatts ;
2925
3017
pinfo -> hasexecparam = (bool * ) palloc0 (sizeof (bool ) * pinfo -> nexprs );
@@ -2931,15 +3023,18 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2931
3023
{
2932
3024
PartitionPruneStepOp * step = (PartitionPruneStepOp * ) lfirst (lc );
2933
3025
ListCell * lc2 ;
3026
+ ListCell * lc3 ;
2934
3027
int keyno ;
2935
3028
2936
3029
if (!IsA (step , PartitionPruneStepOp ))
2937
3030
continue ;
2938
3031
2939
3032
keyno = 0 ;
2940
- foreach (lc2 , step -> exprs )
3033
+ Assert (list_length (step -> exprs ) == list_length (step -> cmpfns ));
3034
+ forboth (lc2 , step -> exprs , lc3 , step -> cmpfns )
2941
3035
{
2942
3036
Expr * expr = lfirst (lc2 );
3037
+ Oid fnoid = lfirst_oid (lc3 );
2943
3038
2944
3039
if (!IsA (expr , Const ))
2945
3040
{
@@ -2962,6 +3057,12 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2962
3057
2963
3058
doruntimeprune = true;
2964
3059
}
3060
+ else if (func_volatile (fnoid ) != PROVOLATILE_IMMUTABLE )
3061
+ {
3062
+ /* No exec params here, but must do initial pruning */
3063
+ pinfo -> do_initial_prune = true;
3064
+ doruntimeprune = true;
3065
+ }
2965
3066
keyno ++ ;
2966
3067
}
2967
3068
}
0 commit comments