51
51
#include "utils/lsyscache.h"
52
52
53
53
54
+ /* Bitmask flags for pushdown_safety_info.unsafeFlags */
55
+ #define UNSAFE_HAS_VOLATILE_FUNC (1 << 0)
56
+ #define UNSAFE_HAS_SET_FUNC (1 << 1)
57
+ #define UNSAFE_NOTIN_DISTINCTON_CLAUSE (1 << 2)
58
+ #define UNSAFE_NOTIN_PARTITIONBY_CLAUSE (1 << 3)
59
+ #define UNSAFE_TYPE_MISMATCH (1 << 4)
60
+
54
61
/* results of subquery_is_pushdown_safe */
55
62
typedef struct pushdown_safety_info
56
63
{
57
- bool * unsafeColumns ;/* which output columns are unsafe to use */
64
+ unsignedchar * unsafeFlags ;/* bitmask of reasons why this target list
65
+ * column is unsafe for qual pushdown, or 0 if
66
+ * no reason. */
58
67
bool unsafeVolatile ;/* don't push down volatile quals */
59
68
bool unsafeLeaky ;/* don't push down leaky quals */
60
69
}pushdown_safety_info ;
61
70
71
+ /* Return type for qual_is_pushdown_safe */
72
+ typedef enum pushdown_safe_type
73
+ {
74
+ PUSHDOWN_UNSAFE ,/* unsafe to push qual into subquery */
75
+ PUSHDOWN_SAFE ,/* safe to push qual into subquery */
76
+ PUSHDOWN_WINDOWCLAUSE_RUNCOND /* unsafe, but may work as WindowClause
77
+ * run condition */
78
+ }pushdown_safe_type ;
79
+
62
80
/* These parameters are set by GUC */
63
81
bool enable_geqo = false;/* just in case GUC doesn't set it */
64
82
int geqo_threshold ;
@@ -135,9 +153,9 @@ static void check_output_expressions(Query *subquery,
135
153
static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
136
154
pushdown_safety_info * safetyInfo );
137
155
static bool targetIsInAllPartitionLists (TargetEntry * tle ,Query * query );
138
- static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,
139
- RestrictInfo * rinfo ,
140
- pushdown_safety_info * safetyInfo );
156
+ static pushdown_safe_type qual_is_pushdown_safe (Query * subquery ,Index rti ,
157
+ RestrictInfo * rinfo ,
158
+ pushdown_safety_info * safetyInfo );
141
159
static void subquery_push_qual (Query * subquery ,
142
160
RangeTblEntry * rte ,Index rti ,Node * qual );
143
161
static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -2207,6 +2225,10 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
2207
2225
if (!IsA (wfunc ,WindowFunc ))
2208
2226
return false;
2209
2227
2228
+ /* can't use it if there are subplans in the WindowFunc */
2229
+ if (contain_subplans ((Node * )wfunc ))
2230
+ return false;
2231
+
2210
2232
prosupport = get_func_support (wfunc -> winfnoid );
2211
2233
2212
2234
/* Check if there's a support function for 'wfunc' */
@@ -2488,13 +2510,14 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
2488
2510
/*
2489
2511
* Zero out result area for subquery_is_pushdown_safe, so that it can set
2490
2512
* flags as needed while recursing. In particular, we need a workspace
2491
- * for keeping track of unsafe-to-reference columns. unsafeColumns[i]
2492
- * will be set true if we find that output column i of the subquery is
2493
- * unsafe to use in a pushed-down qual.
2513
+ * for keeping track of the reasons why columns are unsafe to reference.
2514
+ * These reasons are stored in the bits inside unsafeFlags[i] when we
2515
+ * discover reasons that column i of the subquery is unsafe to be used in
2516
+ * a pushed-down qual.
2494
2517
*/
2495
2518
memset (& safetyInfo ,0 ,sizeof (safetyInfo ));
2496
- safetyInfo .unsafeColumns = (bool * )
2497
- palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (bool ));
2519
+ safetyInfo .unsafeFlags = (unsigned char * )
2520
+ palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (unsigned char ));
2498
2521
2499
2522
/*
2500
2523
* If the subquery has the "security_barrier" flag, it means the subquery
@@ -2537,37 +2560,50 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
2537
2560
RestrictInfo * rinfo = (RestrictInfo * )lfirst (l );
2538
2561
Node * clause = (Node * )rinfo -> clause ;
2539
2562
2540
- if (!rinfo -> pseudoconstant &&
2541
- qual_is_pushdown_safe (subquery ,rti ,rinfo ,& safetyInfo ))
2563
+ if (rinfo -> pseudoconstant )
2542
2564
{
2543
- /* Push it down */
2544
- subquery_push_qual ( subquery , rte , rti , clause ) ;
2565
+ upperrestrictlist = lappend ( upperrestrictlist , rinfo );
2566
+ continue ;
2545
2567
}
2546
- else
2568
+
2569
+ switch (qual_is_pushdown_safe (subquery ,rti ,rinfo ,& safetyInfo ))
2547
2570
{
2548
- /*
2549
- * Since we can't push the qual down into the subquery, check
2550
- * if it happens to reference a window function. If so then
2551
- * it might be useful to use for the WindowAgg's runCondition.
2552
- */
2553
- if (!subquery -> hasWindowFuncs ||
2554
- check_and_push_window_quals (subquery ,rte ,rti ,clause ,
2555
- & run_cond_attrs ))
2556
- {
2571
+ case PUSHDOWN_SAFE :
2572
+ /* Push it down */
2573
+ subquery_push_qual (subquery ,rte ,rti ,clause );
2574
+ break ;
2575
+
2576
+ case PUSHDOWN_WINDOWCLAUSE_RUNCOND :
2577
+
2557
2578
/*
2558
- * subquery has no window funcs or the clause is not a
2559
- * suitable window run condition qual or it is, but the
2560
- * original must also be kept in the upper query.
2579
+ * Since we can't push the qual down into the subquery,
2580
+ * check if it happens to reference a window function. If
2581
+ * so then it might be useful to use for the WindowAgg's
2582
+ * runCondition.
2561
2583
*/
2584
+ if (!subquery -> hasWindowFuncs ||
2585
+ check_and_push_window_quals (subquery ,rte ,rti ,clause ,
2586
+ & run_cond_attrs ))
2587
+ {
2588
+ /*
2589
+ * subquery has no window funcs or the clause is not a
2590
+ * suitable window run condition qual or it is, but
2591
+ * the original must also be kept in the upper query.
2592
+ */
2593
+ upperrestrictlist = lappend (upperrestrictlist ,rinfo );
2594
+ }
2595
+ break ;
2596
+
2597
+ case PUSHDOWN_UNSAFE :
2562
2598
upperrestrictlist = lappend (upperrestrictlist ,rinfo );
2563
- }
2599
+ break ;
2564
2600
}
2565
2601
}
2566
2602
rel -> baserestrictinfo = upperrestrictlist ;
2567
2603
/* We don't bother recomputing baserestrict_min_security */
2568
2604
}
2569
2605
2570
- pfree (safetyInfo .unsafeColumns );
2606
+ pfree (safetyInfo .unsafeFlags );
2571
2607
2572
2608
/*
2573
2609
* The upper query might not use all the subquery's output columns; if
@@ -3492,13 +3528,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
3492
3528
*
3493
3529
* In addition, we make several checks on the subquery's output columns to see
3494
3530
* if it is safe to reference them in pushed-down quals. If output column k
3495
- * is found to be unsafe to reference, we setsafetyInfo->unsafeColumns[k]
3496
- *to true , but we don't reject the subquery overall since column k might not
3497
- * be referenced by some/all quals. TheunsafeColumns[] array will be
3498
- * consulted later by qual_is_pushdown_safe(). It's better to do it this way
3499
- * than to make the checks directly in qual_is_pushdown_safe(), because when
3500
- * the subquery involves set operations we have to check the output
3501
- * expressions in each arm of the set op.
3531
+ * is found to be unsafe to reference, we setthe reason for that inside
3532
+ *safetyInfo->unsafeFlags[k] , but we don't reject the subquery overall since
3533
+ *column k might not be referenced by some/all quals. TheunsafeFlags[]
3534
+ *array will be consulted later by qual_is_pushdown_safe(). It's better to
3535
+ *do it this way than to make the checks directly in qual_is_pushdown_safe(),
3536
+ *because when the subquery involves set operations we have to check the
3537
+ *output expressions in each arm of the set op.
3502
3538
*
3503
3539
* Note: pushing quals into a DISTINCT subquery is theoretically dubious:
3504
3540
* we're effectively assuming that the quals cannot distinguish values that
@@ -3546,9 +3582,9 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
3546
3582
3547
3583
/*
3548
3584
* If we're at a leaf query, check for unsafe expressions in its target
3549
- * list, and mark anyunsafe ones inunsafeColumns []. (Non-leaf nodes in
3550
- * setop trees have only simple Vars in their tlists, so no need to check
3551
- * them.)
3585
+ * list, and mark anyreasons why they're unsafe inunsafeFlags [].
3586
+ *(Non-leaf nodes in setop trees have only simple Vars in their tlists,
3587
+ *so no need to check them.)
3552
3588
*/
3553
3589
if (subquery -> setOperations == NULL )
3554
3590
check_output_expressions (subquery ,safetyInfo );
@@ -3619,9 +3655,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
3619
3655
*
3620
3656
* There are several cases in which it's unsafe to push down an upper-level
3621
3657
* qual if it references a particular output column of a subquery. We check
3622
- * each output column of the subquery and setunsafeColumns [k]to true if
3623
- * that column is unsafe for a pushed-down qual to reference. The conditions
3624
- * checked here are:
3658
+ * each output column of the subquery and setflags in unsafeFlags [k]when we
3659
+ *see that column is unsafe for a pushed-down qual to reference. The
3660
+ *conditions checked here are:
3625
3661
*
3626
3662
* 1. We must not push down any quals that refer to subselect outputs that
3627
3663
* return sets, else we'd introduce functions-returning-sets into the
@@ -3645,7 +3681,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
3645
3681
* every row of any one window partition, and totally excluding some
3646
3682
* partitions will not change a window function's results for remaining
3647
3683
* partitions. (Again, this also requires nonvolatile quals, but
3648
- * subquery_is_pushdown_safe handles that.)
3684
+ * subquery_is_pushdown_safe handles that.). Subquery columns marked as
3685
+ * unsafe for this reason can still have WindowClause run conditions pushed
3686
+ * down.
3649
3687
*/
3650
3688
static void
3651
3689
check_output_expressions (Query * subquery ,pushdown_safety_info * safetyInfo )
@@ -3659,40 +3697,44 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
3659
3697
if (tle -> resjunk )
3660
3698
continue ;/* ignore resjunk columns */
3661
3699
3662
- /* We need not check further if output col is already known unsafe */
3663
- if (safetyInfo -> unsafeColumns [tle -> resno ])
3664
- continue ;
3665
-
3666
3700
/* Functions returning sets are unsafe (point 1) */
3667
3701
if (subquery -> hasTargetSRFs &&
3702
+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3703
+ UNSAFE_HAS_SET_FUNC )== 0 &&
3668
3704
expression_returns_set ((Node * )tle -> expr ))
3669
3705
{
3670
- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3706
+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_HAS_SET_FUNC ;
3671
3707
continue ;
3672
3708
}
3673
3709
3674
3710
/* Volatile functions are unsafe (point 2) */
3675
- if (contain_volatile_functions ((Node * )tle -> expr ))
3711
+ if ((safetyInfo -> unsafeFlags [tle -> resno ]&
3712
+ UNSAFE_HAS_VOLATILE_FUNC )== 0 &&
3713
+ contain_volatile_functions ((Node * )tle -> expr ))
3676
3714
{
3677
- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3715
+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_HAS_VOLATILE_FUNC ;
3678
3716
continue ;
3679
3717
}
3680
3718
3681
3719
/* If subquery uses DISTINCT ON, check point 3 */
3682
3720
if (subquery -> hasDistinctOn &&
3721
+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3722
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE )== 0 &&
3683
3723
!targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
3684
3724
{
3685
3725
/* non-DISTINCT column, so mark it unsafe */
3686
- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3726
+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_NOTIN_DISTINCTON_CLAUSE ;
3687
3727
continue ;
3688
3728
}
3689
3729
3690
3730
/* If subquery uses window functions, check point 4 */
3691
3731
if (subquery -> hasWindowFuncs &&
3732
+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3733
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE )== 0 &&
3692
3734
!targetIsInAllPartitionLists (tle ,subquery ))
3693
3735
{
3694
3736
/* not present in all PARTITION BY clauses, so mark it unsafe */
3695
- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3737
+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_NOTIN_PARTITIONBY_CLAUSE ;
3696
3738
continue ;
3697
3739
}
3698
3740
}
@@ -3704,16 +3746,16 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
3704
3746
* subquery columns that suffer no type coercions in the set operation.
3705
3747
* Otherwise there are possible semantic gotchas. So, we check the
3706
3748
* component queries to see if any of them have output types different from
3707
- * the top-level setop outputs.unsafeColumns[k] is settrue if column k
3708
- * has different type in any component.
3749
+ * the top-level setop outputs.We setthe UNSAFE_TYPE_MISMATCH bit in
3750
+ *unsafeFlags[k] if column k has different type in any component.
3709
3751
*
3710
3752
* We don't have to care about typmods here: the only allowed difference
3711
3753
* between set-op input and output typmods is input is a specific typmod
3712
3754
* and output is -1, and that does not require a coercion.
3713
3755
*
3714
3756
* tlist is a subquery tlist.
3715
3757
* colTypes is an OID list of the top-level setop's output column types.
3716
- * safetyInfo->unsafeColumns[] is theresult array .
3758
+ * safetyInfo is thepushdown_safety_info to set unsafeFlags[] for .
3717
3759
*/
3718
3760
static void
3719
3761
compare_tlist_datatypes (List * tlist ,List * colTypes ,
@@ -3731,7 +3773,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
3731
3773
if (colType == NULL )
3732
3774
elog (ERROR ,"wrong number of tlist entries" );
3733
3775
if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
3734
- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3776
+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_TYPE_MISMATCH ;
3735
3777
colType = lnext (colTypes ,colType );
3736
3778
}
3737
3779
if (colType != NULL )
@@ -3791,28 +3833,28 @@ targetIsInAllPartitionLists(TargetEntry *tle, Query *query)
3791
3833
* 5. rinfo's clause must not refer to any subquery output columns that were
3792
3834
* found to be unsafe to reference by subquery_is_pushdown_safe().
3793
3835
*/
3794
- static bool
3836
+ static pushdown_safe_type
3795
3837
qual_is_pushdown_safe (Query * subquery ,Index rti ,RestrictInfo * rinfo ,
3796
3838
pushdown_safety_info * safetyInfo )
3797
3839
{
3798
- bool safe = true ;
3840
+ pushdown_safe_type safe = PUSHDOWN_SAFE ;
3799
3841
Node * qual = (Node * )rinfo -> clause ;
3800
3842
List * vars ;
3801
3843
ListCell * vl ;
3802
3844
3803
3845
/* Refuse subselects (point 1) */
3804
3846
if (contain_subplans (qual ))
3805
- return false ;
3847
+ return PUSHDOWN_UNSAFE ;
3806
3848
3807
3849
/* Refuse volatile quals if we found they'd be unsafe (point 2) */
3808
3850
if (safetyInfo -> unsafeVolatile &&
3809
3851
contain_volatile_functions ((Node * )rinfo ))
3810
- return false ;
3852
+ return PUSHDOWN_UNSAFE ;
3811
3853
3812
3854
/* Refuse leaky quals if told to (point 3) */
3813
3855
if (safetyInfo -> unsafeLeaky &&
3814
3856
contain_leaked_vars (qual ))
3815
- return false ;
3857
+ return PUSHDOWN_UNSAFE ;
3816
3858
3817
3859
/*
3818
3860
* It would be unsafe to push down window function calls, but at least for
@@ -3841,7 +3883,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3841
3883
*/
3842
3884
if (!IsA (var ,Var ))
3843
3885
{
3844
- safe = false ;
3886
+ safe = PUSHDOWN_UNSAFE ;
3845
3887
break ;
3846
3888
}
3847
3889
@@ -3853,7 +3895,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3853
3895
*/
3854
3896
if (var -> varno != rti )
3855
3897
{
3856
- safe = false ;
3898
+ safe = PUSHDOWN_UNSAFE ;
3857
3899
break ;
3858
3900
}
3859
3901
@@ -3863,15 +3905,26 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3863
3905
/* Check point 4 */
3864
3906
if (var -> varattno == 0 )
3865
3907
{
3866
- safe = false ;
3908
+ safe = PUSHDOWN_UNSAFE ;
3867
3909
break ;
3868
3910
}
3869
3911
3870
3912
/* Check point 5 */
3871
- if (safetyInfo -> unsafeColumns [var -> varattno ])
3913
+ if (safetyInfo -> unsafeFlags [var -> varattno ]!= 0 )
3872
3914
{
3873
- safe = false;
3874
- break ;
3915
+ if (safetyInfo -> unsafeFlags [var -> varattno ]&
3916
+ (UNSAFE_HAS_VOLATILE_FUNC |UNSAFE_HAS_SET_FUNC |
3917
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE |UNSAFE_TYPE_MISMATCH ))
3918
+ {
3919
+ safe = PUSHDOWN_UNSAFE ;
3920
+ break ;
3921
+ }
3922
+ else
3923
+ {
3924
+ /* UNSAFE_NOTIN_PARTITIONBY_CLAUSE is ok for run conditions */
3925
+ safe = PUSHDOWN_WINDOWCLAUSE_RUNCOND ;
3926
+ /* don't break, we might find another Var that's unsafe */
3927
+ }
3875
3928
}
3876
3929
}
3877
3930