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"
@@ -93,8 +94,12 @@ typedef enum PartClauseMatchStatus
93
94
*/
94
95
typedef struct GeneratePruningStepsContext
95
96
{
97
+ /* Input data: */
98
+ bool forplanner ; /* true when generating steps to be used
99
+ * during query planning */
100
+ /* Working state and result data: */
96
101
int next_step_id ;
97
- List * steps ;
102
+ List * steps ; /* output, list of PartitionPruneSteps */
98
103
} GeneratePruningStepsContext ;
99
104
100
105
/* The result of performing one PartitionPruneStep */
@@ -117,7 +122,7 @@ static List *make_partitionedrel_pruneinfo(PlannerInfo *root,
117
122
List * partitioned_rels , List * prunequal ,
118
123
Bitmapset * * matchedsubplans );
119
124
static List * gen_partprune_steps (RelOptInfo * rel , List * clauses ,
120
- bool * contradictory );
125
+ bool forplanner , bool * contradictory );
121
126
static List * gen_partprune_steps_internal (GeneratePruningStepsContext * context ,
122
127
RelOptInfo * rel , List * clauses ,
123
128
bool * contradictory );
@@ -409,8 +414,13 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
409
414
targetpart -> relids );
410
415
}
411
416
412
- /* Convert pruning qual to pruning steps. */
413
- pruning_steps = gen_partprune_steps (subpart , partprunequal ,
417
+ /*
418
+ * Convert pruning qual to pruning steps. Since these steps will be
419
+ * used in the executor, we can pass 'forplanner' as false to allow
420
+ * steps to be generated that are unsafe for evaluation during
421
+ * planning, e.g. evaluation of stable functions.
422
+ */
423
+ pruning_steps = gen_partprune_steps (subpart , partprunequal , false,
414
424
& contradictory );
415
425
416
426
if (contradictory )
@@ -523,22 +533,37 @@ make_partitionedrel_pruneinfo(PlannerInfo *root, RelOptInfo *parentrel,
523
533
/*
524
534
* gen_partprune_steps
525
535
* Process 'clauses' (a rel's baserestrictinfo list of clauses) and return
526
- * a list of "partition pruning steps"
536
+ * a list of "partition pruning steps".
537
+ *
538
+ * 'forplanner' must be true when generating steps to be evaluated during
539
+ * query planning, false when generating steps to be used at run-time.
540
+ *
541
+ * The result generated with forplanner=false includes all clauses that
542
+ * are selected with forplanner=true, because in some cases we need a
543
+ * combination of clauses to prune successfully. For example, if we
544
+ * are partitioning on a hash of columns A and B, and we have clauses
545
+ * "WHERE A=constant AND B=nonconstant", we can't do anything at plan
546
+ * time even though the first clause would be evaluable then. And we
547
+ * must include the first clause when called with forplanner=false,
548
+ * or we'll fail to prune at run-time either. This does mean that when
549
+ * called with forplanner=false, we may return steps that don't actually
550
+ * need to be executed at runtime; it's left to analyze_partkey_exprs()
551
+ * to (re)discover that.
527
552
*
528
553
* If the clauses in the input list are contradictory or there is a
529
- * pseudo-constant "false", *contradictory is set to true upon return.
554
+ * pseudo-constant "false", *contradictory is set to true upon return,
555
+ * else it's set false.
530
556
*/
531
557
static List *
532
- gen_partprune_steps (RelOptInfo * rel , List * clauses , bool * contradictory )
558
+ gen_partprune_steps (RelOptInfo * rel , List * clauses , bool forplanner ,
559
+ bool * contradictory )
533
560
{
534
561
GeneratePruningStepsContext context ;
535
562
563
+ context .forplanner = forplanner ;
536
564
context .next_step_id = 0 ;
537
565
context .steps = NIL ;
538
566
539
- /* The clauses list may be modified below, so better make a copy. */
540
- clauses = list_copy (clauses );
541
-
542
567
/*
543
568
* For sub-partitioned tables there's a corner case where if the
544
569
* sub-partitioned table shares any partition keys with its parent, then
@@ -563,20 +588,23 @@ gen_partprune_steps(RelOptInfo *rel, List *clauses, bool *contradictory)
563
588
if (rel -> relid != 1 )
564
589
ChangeVarNodes ((Node * ) partqual , 1 , rel -> relid , 0 );
565
590
566
- clauses = list_concat (clauses , partqual );
591
+ /* Use list_copy to avoid modifying the passed-in List */
592
+ clauses = list_concat (list_copy (clauses ), partqual );
567
593
}
568
594
569
595
/* Down into the rabbit-hole. */
570
- gen_partprune_steps_internal (& context , rel , clauses , contradictory );
596
+ ( void ) gen_partprune_steps_internal (& context , rel , clauses , contradictory );
571
597
572
598
return context .steps ;
573
599
}
574
600
575
601
/*
576
602
* prune_append_rel_partitions
577
- * Returns indexes into rel->part_rels of the minimum set of child
578
- * partitions which must be scanned to satisfy rel's baserestrictinfo
579
- * quals.
603
+ * Process rel's baserestrictinfo and make use of quals which can be
604
+ * evaluated during query planning in order to determine the minimum set
605
+ * of partitions which must be scanned to satisfy these quals. Returns
606
+ * the matching partitions in the form of a Bitmapset containing the
607
+ * partitions' indexes in the rel's part_rels array.
580
608
*
581
609
* Callers must ensure that 'rel' is a partitioned table.
582
610
*/
@@ -603,9 +631,12 @@ prune_append_rel_partitions(RelOptInfo *rel)
603
631
604
632
/*
605
633
* Process clauses. If the clauses are found to be contradictory, we can
606
- * return the empty set.
634
+ * return the empty set. Pass 'forplanner' as true to indicate to the
635
+ * pruning code that we only want pruning steps that can be evaluated
636
+ * during planning.
607
637
*/
608
- pruning_steps = gen_partprune_steps (rel , clauses , & contradictory );
638
+ pruning_steps = gen_partprune_steps (rel , clauses , true,
639
+ & contradictory );
609
640
if (contradictory )
610
641
return NULL ;
611
642
@@ -635,6 +666,9 @@ prune_append_rel_partitions(RelOptInfo *rel)
635
666
* get_matching_partitions
636
667
* Determine partitions that survive partition pruning
637
668
*
669
+ * Note: context->planstate must be set to a valid PlanState when the
670
+ * pruning_steps were generated with 'forplanner' = false.
671
+ *
638
672
* Returns a Bitmapset of the RelOptInfo->part_rels indexes of the surviving
639
673
* partitions.
640
674
*/
@@ -755,9 +789,6 @@ get_matching_partitions(PartitionPruneContext *context, List *pruning_steps)
755
789
* clause that contains false, we set *contradictory to true and return NIL
756
790
* (that is, no pruning steps). Caller should consider all partitions as
757
791
* pruned in that case. Otherwise, *contradictory is set to false.
758
- *
759
- * Note: the 'clauses' List may be modified inside this function. Callers may
760
- * like to make a copy of it before passing them to this function.
761
792
*/
762
793
static List *
763
794
gen_partprune_steps_internal (GeneratePruningStepsContext * context ,
@@ -1572,8 +1603,7 @@ match_clause_to_partition_key(RelOptInfo *rel,
1572
1603
Expr * expr ;
1573
1604
1574
1605
/*
1575
- * Recognize specially shaped clauses that match with the Boolean
1576
- * partition key.
1606
+ * Recognize specially shaped clauses that match a Boolean partition key.
1577
1607
*/
1578
1608
if (match_boolean_partition_clause (partopfamily , clause , partkey , & expr ))
1579
1609
{
@@ -1648,16 +1678,47 @@ match_clause_to_partition_key(RelOptInfo *rel,
1648
1678
* Matched with this key. Now check various properties of the clause
1649
1679
* to see if it's sane to use it for pruning. In most of these cases,
1650
1680
* we can return UNSUPPORTED because the same failure would occur no
1651
- * matter which partkey it's matched to.
1681
+ * matter which partkey it's matched to. (In particular, now that
1682
+ * we've successfully matched one side of the opclause to a partkey,
1683
+ * there is no chance that matching the other side to another partkey
1684
+ * will produce a usable result, since that'd mean there are Vars on
1685
+ * both sides.)
1652
1686
*/
1687
+ if (context -> forplanner )
1688
+ {
1689
+ /*
1690
+ * When pruning in the planner, we only support pruning using
1691
+ * comparisons to constants. Immutable subexpressions will have
1692
+ * been folded to constants already, and we cannot prune on the
1693
+ * basis of anything that's not immutable.
1694
+ */
1695
+ if (!IsA (expr , Const ))
1696
+ return PARTCLAUSE_UNSUPPORTED ;
1653
1697
1654
- /*
1655
- * We can't prune using an expression with Vars. (Report failure as
1656
- * UNSUPPORTED, not NOMATCH: as in the no-commutator case above, we
1657
- * now know there are Vars on both sides, so it's no good.)
1658
- */
1659
- if (contain_var_clause ((Node * ) expr ))
1660
- return PARTCLAUSE_UNSUPPORTED ;
1698
+ /*
1699
+ * Also, the comparison operator itself must be immutable.
1700
+ */
1701
+ if (op_volatile (opno ) != PROVOLATILE_IMMUTABLE )
1702
+ return PARTCLAUSE_UNSUPPORTED ;
1703
+ }
1704
+ else
1705
+ {
1706
+ /*
1707
+ * Otherwise, non-consts are allowed, but we can't prune using an
1708
+ * expression that contains Vars.
1709
+ */
1710
+ if (contain_var_clause ((Node * ) expr ))
1711
+ return PARTCLAUSE_UNSUPPORTED ;
1712
+
1713
+ /*
1714
+ * And we must reject anything containing a volatile function.
1715
+ * Stable functions are OK though. (We need not check this for
1716
+ * the comparison operator itself: anything that belongs to a
1717
+ * partitioning operator family must be at least stable.)
1718
+ */
1719
+ if (contain_volatile_functions ((Node * ) expr ))
1720
+ return PARTCLAUSE_UNSUPPORTED ;
1721
+ }
1661
1722
1662
1723
/*
1663
1724
* Only allow strict operators. This will guarantee nulls are
@@ -1666,10 +1727,6 @@ match_clause_to_partition_key(RelOptInfo *rel,
1666
1727
if (!op_strict (opno ))
1667
1728
return PARTCLAUSE_UNSUPPORTED ;
1668
1729
1669
- /* We can't use any volatile expressions to prune partitions. */
1670
- if (contain_volatile_functions ((Node * ) expr ))
1671
- return PARTCLAUSE_UNSUPPORTED ;
1672
-
1673
1730
/*
1674
1731
* See if the operator is relevant to the partitioning opfamily.
1675
1732
*
@@ -1798,20 +1855,51 @@ match_clause_to_partition_key(RelOptInfo *rel,
1798
1855
if (IsA (leftop , RelabelType ))
1799
1856
leftop = ((RelabelType * ) leftop )-> arg ;
1800
1857
1801
- /* Check it matches this partition key */
1858
+ /* check if the LHS matches this partition key */
1802
1859
if (!equal (leftop , partkey ) ||
1803
1860
!PartCollMatchesExprColl (partcoll , saop -> inputcollid ))
1804
1861
return PARTCLAUSE_NOMATCH ;
1805
1862
1806
1863
/*
1807
1864
* Matched with this key. Check various properties of the clause to
1808
- * see if it can sanely be used for partition pruning (this is mostly
1809
- * the same as for a plain OpExpr).
1865
+ * see if it can sanely be used for partition pruning (this is
1866
+ * identical to the logic for a plain OpExpr).
1810
1867
*/
1868
+ if (context -> forplanner )
1869
+ {
1870
+ /*
1871
+ * When pruning in the planner, we only support pruning using
1872
+ * comparisons to constants. Immutable subexpressions will have
1873
+ * been folded to constants already, and we cannot prune on the
1874
+ * basis of anything that's not immutable.
1875
+ */
1876
+ if (!IsA (rightop , Const ))
1877
+ return PARTCLAUSE_UNSUPPORTED ;
1811
1878
1812
- /* We can't prune using an expression with Vars. */
1813
- if (contain_var_clause ((Node * ) rightop ))
1814
- return PARTCLAUSE_UNSUPPORTED ;
1879
+ /*
1880
+ * Also, the comparison operator itself must be immutable.
1881
+ */
1882
+ if (op_volatile (saop_op ) != PROVOLATILE_IMMUTABLE )
1883
+ return PARTCLAUSE_UNSUPPORTED ;
1884
+ }
1885
+ else
1886
+ {
1887
+ /*
1888
+ * Otherwise, non-consts are allowed, but we can't prune using an
1889
+ * expression that contains Vars.
1890
+ */
1891
+ if (contain_var_clause ((Node * ) rightop ))
1892
+ return PARTCLAUSE_UNSUPPORTED ;
1893
+
1894
+ /*
1895
+ * And we must reject anything containing a volatile function.
1896
+ * Stable functions are OK though. (We need not check this for
1897
+ * the comparison operator itself: anything that belongs to a
1898
+ * partitioning operator family must be at least stable.)
1899
+ */
1900
+ if (contain_volatile_functions ((Node * ) rightop ))
1901
+ return PARTCLAUSE_UNSUPPORTED ;
1902
+ }
1815
1903
1816
1904
/*
1817
1905
* Only allow strict operators. This will guarantee nulls are
@@ -1820,10 +1908,6 @@ match_clause_to_partition_key(RelOptInfo *rel,
1820
1908
if (!op_strict (saop_op ))
1821
1909
return PARTCLAUSE_UNSUPPORTED ;
1822
1910
1823
- /* We can't use any volatile expressions to prune partitions. */
1824
- if (contain_volatile_functions ((Node * ) rightop ))
1825
- return PARTCLAUSE_UNSUPPORTED ;
1826
-
1827
1911
/*
1828
1912
* In case of NOT IN (..), we get a '<>', which we handle if list
1829
1913
* partitioning is in use and we're able to confirm that it's negator
@@ -2948,8 +3032,14 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2948
3032
2949
3033
/*
2950
3034
* Steps require run-time pruning if they contain EXEC_PARAM Params.
2951
- * Otherwise, if their expressions aren't simple Consts, they require
2952
- * startup-time pruning.
3035
+ * Otherwise, if their expressions aren't simple Consts or they involve
3036
+ * non-immutable comparison operators, they require startup-time pruning.
3037
+ * (Otherwise, the pruning would have been done at plan time.)
3038
+ *
3039
+ * Notice that what we actually check for mutability is the comparison
3040
+ * functions, not the original operators. This relies on the support
3041
+ * functions of the btree or hash opfamily being marked consistently with
3042
+ * the operators.
2953
3043
*/
2954
3044
pinfo -> nexprs = list_length (steps ) * partnatts ;
2955
3045
pinfo -> hasexecparam = (bool * ) palloc0 (sizeof (bool ) * pinfo -> nexprs );
@@ -2961,15 +3051,18 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2961
3051
{
2962
3052
PartitionPruneStepOp * step = (PartitionPruneStepOp * ) lfirst (lc );
2963
3053
ListCell * lc2 ;
3054
+ ListCell * lc3 ;
2964
3055
int keyno ;
2965
3056
2966
3057
if (!IsA (step , PartitionPruneStepOp ))
2967
3058
continue ;
2968
3059
2969
3060
keyno = 0 ;
2970
- foreach (lc2 , step -> exprs )
3061
+ Assert (list_length (step -> exprs ) == list_length (step -> cmpfns ));
3062
+ forboth (lc2 , step -> exprs , lc3 , step -> cmpfns )
2971
3063
{
2972
3064
Expr * expr = lfirst (lc2 );
3065
+ Oid fnoid = lfirst_oid (lc3 );
2973
3066
2974
3067
if (!IsA (expr , Const ))
2975
3068
{
@@ -2992,6 +3085,12 @@ analyze_partkey_exprs(PartitionedRelPruneInfo *pinfo, List *steps,
2992
3085
2993
3086
doruntimeprune = true;
2994
3087
}
3088
+ else if (func_volatile (fnoid ) != PROVOLATILE_IMMUTABLE )
3089
+ {
3090
+ /* No exec params here, but must do initial pruning */
3091
+ pinfo -> do_initial_prune = true;
3092
+ doruntimeprune = true;
3093
+ }
2995
3094
keyno ++ ;
2996
3095
}
2997
3096
}
@@ -3346,6 +3445,12 @@ partkey_datum_from_expr(PartitionPruneContext *context,
3346
3445
}
3347
3446
else
3348
3447
{
3448
+ /*
3449
+ * We should never see a non-Const in a step unless we're running in
3450
+ * the executor.
3451
+ */
3452
+ Assert (context -> planstate != NULL );
3453
+
3349
3454
/*
3350
3455
* When called from the executor we'll have a valid planstate so we
3351
3456
* may be able to evaluate an expression which could not be folded to
@@ -3354,9 +3459,7 @@ partkey_datum_from_expr(PartitionPruneContext *context,
3354
3459
* must be careful here to evaluate expressions containing PARAM_EXEC
3355
3460
* Params only when told it's OK.
3356
3461
*/
3357
- if (context -> planstate &&
3358
- (context -> evalexecparams ||
3359
- !context -> exprhasexecparam [stateidx ]))
3462
+ if (context -> evalexecparams || !context -> exprhasexecparam [stateidx ])
3360
3463
{
3361
3464
ExprState * exprstate ;
3362
3465
ExprContext * ectx ;
0 commit comments