52
52
#include "utils/lsyscache.h"
53
53
54
54
55
+ /* Bitmask flags for pushdown_safety_info.unsafeFlags */
56
+ #define UNSAFE_HAS_VOLATILE_FUNC (1 << 0)
57
+ #define UNSAFE_HAS_SET_FUNC (1 << 1)
58
+ #define UNSAFE_NOTIN_DISTINCTON_CLAUSE (1 << 2)
59
+ #define UNSAFE_NOTIN_PARTITIONBY_CLAUSE (1 << 3)
60
+ #define UNSAFE_TYPE_MISMATCH (1 << 4)
61
+
55
62
/* results of subquery_is_pushdown_safe */
56
63
typedef struct pushdown_safety_info
57
64
{
58
- bool * unsafeColumns ; /* which output columns are unsafe to use */
65
+ unsigned char * unsafeFlags ; /* bitmask of reasons why this target list
66
+ * column is unsafe for qual pushdown, or 0 if
67
+ * no reason. */
59
68
bool unsafeVolatile ; /* don't push down volatile quals */
60
69
bool unsafeLeaky ; /* don't push down leaky quals */
61
70
} pushdown_safety_info ;
62
71
72
+ /* Return type for qual_is_pushdown_safe */
73
+ typedef enum pushdown_safe_type
74
+ {
75
+ PUSHDOWN_UNSAFE , /* unsafe to push qual into subquery */
76
+ PUSHDOWN_SAFE , /* safe to push qual into subquery */
77
+ PUSHDOWN_WINDOWCLAUSE_RUNCOND /* unsafe, but may work as WindowClause
78
+ * run condition */
79
+ } pushdown_safe_type ;
80
+
63
81
/* These parameters are set by GUC */
64
82
bool enable_geqo = false; /* just in case GUC doesn't set it */
65
83
int geqo_threshold ;
@@ -136,9 +154,9 @@ static void check_output_expressions(Query *subquery,
136
154
static void compare_tlist_datatypes (List * tlist , List * colTypes ,
137
155
pushdown_safety_info * safetyInfo );
138
156
static bool targetIsInAllPartitionLists (TargetEntry * tle , Query * query );
139
- static bool qual_is_pushdown_safe (Query * subquery , Index rti ,
140
- RestrictInfo * rinfo ,
141
- pushdown_safety_info * safetyInfo );
157
+ static pushdown_safe_type qual_is_pushdown_safe (Query * subquery , Index rti ,
158
+ RestrictInfo * rinfo ,
159
+ pushdown_safety_info * safetyInfo );
142
160
static void subquery_push_qual (Query * subquery ,
143
161
RangeTblEntry * rte , Index rti , Node * qual );
144
162
static void recurse_push_qual (Node * setOp , Query * topquery ,
@@ -2218,6 +2236,10 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
2218
2236
if (!IsA (wfunc , WindowFunc ))
2219
2237
return false;
2220
2238
2239
+ /* can't use it if there are subplans in the WindowFunc */
2240
+ if (contain_subplans ((Node * ) wfunc ))
2241
+ return false;
2242
+
2221
2243
prosupport = get_func_support (wfunc -> winfnoid );
2222
2244
2223
2245
/* Check if there's a support function for 'wfunc' */
@@ -2500,13 +2522,14 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
2500
2522
/*
2501
2523
* Zero out result area for subquery_is_pushdown_safe, so that it can set
2502
2524
* flags as needed while recursing. In particular, we need a workspace
2503
- * for keeping track of unsafe-to-reference columns. unsafeColumns[i]
2504
- * will be set true if we find that output column i of the subquery is
2505
- * unsafe to use in a pushed-down qual.
2525
+ * for keeping track of the reasons why columns are unsafe to reference.
2526
+ * These reasons are stored in the bits inside unsafeFlags[i] when we
2527
+ * discover reasons that column i of the subquery is unsafe to be used in
2528
+ * a pushed-down qual.
2506
2529
*/
2507
2530
memset (& safetyInfo , 0 , sizeof (safetyInfo ));
2508
- safetyInfo .unsafeColumns = (bool * )
2509
- palloc0 ((list_length (subquery -> targetList ) + 1 ) * sizeof (bool ));
2531
+ safetyInfo .unsafeFlags = (unsigned char * )
2532
+ palloc0 ((list_length (subquery -> targetList ) + 1 ) * sizeof (unsigned char ));
2510
2533
2511
2534
/*
2512
2535
* If the subquery has the "security_barrier" flag, it means the subquery
@@ -2549,37 +2572,50 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
2549
2572
RestrictInfo * rinfo = (RestrictInfo * ) lfirst (l );
2550
2573
Node * clause = (Node * ) rinfo -> clause ;
2551
2574
2552
- if (!rinfo -> pseudoconstant &&
2553
- qual_is_pushdown_safe (subquery , rti , rinfo , & safetyInfo ))
2575
+ if (rinfo -> pseudoconstant )
2554
2576
{
2555
- /* Push it down */
2556
- subquery_push_qual ( subquery , rte , rti , clause ) ;
2577
+ upperrestrictlist = lappend ( upperrestrictlist , rinfo );
2578
+ continue ;
2557
2579
}
2558
- else
2580
+
2581
+ switch (qual_is_pushdown_safe (subquery , rti , rinfo , & safetyInfo ))
2559
2582
{
2560
- /*
2561
- * Since we can't push the qual down into the subquery, check
2562
- * if it happens to reference a window function. If so then
2563
- * it might be useful to use for the WindowAgg's runCondition.
2564
- */
2565
- if (!subquery -> hasWindowFuncs ||
2566
- check_and_push_window_quals (subquery , rte , rti , clause ,
2567
- & run_cond_attrs ))
2568
- {
2583
+ case PUSHDOWN_SAFE :
2584
+ /* Push it down */
2585
+ subquery_push_qual (subquery , rte , rti , clause );
2586
+ break ;
2587
+
2588
+ case PUSHDOWN_WINDOWCLAUSE_RUNCOND :
2589
+
2569
2590
/*
2570
- * subquery has no window funcs or the clause is not a
2571
- * suitable window run condition qual or it is, but the
2572
- * original must also be kept in the upper query.
2591
+ * Since we can't push the qual down into the subquery,
2592
+ * check if it happens to reference a window function. If
2593
+ * so then it might be useful to use for the WindowAgg's
2594
+ * runCondition.
2573
2595
*/
2596
+ if (!subquery -> hasWindowFuncs ||
2597
+ check_and_push_window_quals (subquery , rte , rti , clause ,
2598
+ & run_cond_attrs ))
2599
+ {
2600
+ /*
2601
+ * subquery has no window funcs or the clause is not a
2602
+ * suitable window run condition qual or it is, but
2603
+ * the original must also be kept in the upper query.
2604
+ */
2605
+ upperrestrictlist = lappend (upperrestrictlist , rinfo );
2606
+ }
2607
+ break ;
2608
+
2609
+ case PUSHDOWN_UNSAFE :
2574
2610
upperrestrictlist = lappend (upperrestrictlist , rinfo );
2575
- }
2611
+ break ;
2576
2612
}
2577
2613
}
2578
2614
rel -> baserestrictinfo = upperrestrictlist ;
2579
2615
/* We don't bother recomputing baserestrict_min_security */
2580
2616
}
2581
2617
2582
- pfree (safetyInfo .unsafeColumns );
2618
+ pfree (safetyInfo .unsafeFlags );
2583
2619
2584
2620
/*
2585
2621
* The upper query might not use all the subquery's output columns; if
@@ -3517,13 +3553,13 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
3517
3553
*
3518
3554
* In addition, we make several checks on the subquery's output columns to see
3519
3555
* if it is safe to reference them in pushed-down quals. If output column k
3520
- * is found to be unsafe to reference, we set safetyInfo->unsafeColumns[k]
3521
- * to true , but we don't reject the subquery overall since column k might not
3522
- * be referenced by some/all quals. The unsafeColumns[] array will be
3523
- * consulted later by qual_is_pushdown_safe(). It's better to do it this way
3524
- * than to make the checks directly in qual_is_pushdown_safe(), because when
3525
- * the subquery involves set operations we have to check the output
3526
- * expressions in each arm of the set op.
3556
+ * is found to be unsafe to reference, we set the reason for that inside
3557
+ * safetyInfo->unsafeFlags[k] , but we don't reject the subquery overall since
3558
+ * column k might not be referenced by some/all quals. The unsafeFlags[]
3559
+ * array will be consulted later by qual_is_pushdown_safe(). It's better to
3560
+ * do it this way than to make the checks directly in qual_is_pushdown_safe(),
3561
+ * because when the subquery involves set operations we have to check the
3562
+ * output expressions in each arm of the set op.
3527
3563
*
3528
3564
* Note: pushing quals into a DISTINCT subquery is theoretically dubious:
3529
3565
* we're effectively assuming that the quals cannot distinguish values that
@@ -3571,9 +3607,9 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
3571
3607
3572
3608
/*
3573
3609
* If we're at a leaf query, check for unsafe expressions in its target
3574
- * list, and mark any unsafe ones in unsafeColumns []. (Non-leaf nodes in
3575
- * setop trees have only simple Vars in their tlists, so no need to check
3576
- * them.)
3610
+ * list, and mark any reasons why they're unsafe in unsafeFlags [].
3611
+ * (Non-leaf nodes in setop trees have only simple Vars in their tlists,
3612
+ * so no need to check them.)
3577
3613
*/
3578
3614
if (subquery -> setOperations == NULL )
3579
3615
check_output_expressions (subquery , safetyInfo );
@@ -3644,9 +3680,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
3644
3680
*
3645
3681
* There are several cases in which it's unsafe to push down an upper-level
3646
3682
* qual if it references a particular output column of a subquery. We check
3647
- * each output column of the subquery and set unsafeColumns [k] to true if
3648
- * that column is unsafe for a pushed-down qual to reference. The conditions
3649
- * checked here are:
3683
+ * each output column of the subquery and set flags in unsafeFlags [k] when we
3684
+ * see that column is unsafe for a pushed-down qual to reference. The
3685
+ * conditions checked here are:
3650
3686
*
3651
3687
* 1. We must not push down any quals that refer to subselect outputs that
3652
3688
* return sets, else we'd introduce functions-returning-sets into the
@@ -3670,7 +3706,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
3670
3706
* every row of any one window partition, and totally excluding some
3671
3707
* partitions will not change a window function's results for remaining
3672
3708
* partitions. (Again, this also requires nonvolatile quals, but
3673
- * subquery_is_pushdown_safe handles that.)
3709
+ * subquery_is_pushdown_safe handles that.). Subquery columns marked as
3710
+ * unsafe for this reason can still have WindowClause run conditions pushed
3711
+ * down.
3674
3712
*/
3675
3713
static void
3676
3714
check_output_expressions (Query * subquery , pushdown_safety_info * safetyInfo )
@@ -3684,40 +3722,44 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
3684
3722
if (tle -> resjunk )
3685
3723
continue ; /* ignore resjunk columns */
3686
3724
3687
- /* We need not check further if output col is already known unsafe */
3688
- if (safetyInfo -> unsafeColumns [tle -> resno ])
3689
- continue ;
3690
-
3691
3725
/* Functions returning sets are unsafe (point 1) */
3692
3726
if (subquery -> hasTargetSRFs &&
3727
+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3728
+ UNSAFE_HAS_SET_FUNC ) == 0 &&
3693
3729
expression_returns_set ((Node * ) tle -> expr ))
3694
3730
{
3695
- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3731
+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_HAS_SET_FUNC ;
3696
3732
continue ;
3697
3733
}
3698
3734
3699
3735
/* Volatile functions are unsafe (point 2) */
3700
- if (contain_volatile_functions ((Node * ) tle -> expr ))
3736
+ if ((safetyInfo -> unsafeFlags [tle -> resno ] &
3737
+ UNSAFE_HAS_VOLATILE_FUNC ) == 0 &&
3738
+ contain_volatile_functions ((Node * ) tle -> expr ))
3701
3739
{
3702
- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3740
+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_HAS_VOLATILE_FUNC ;
3703
3741
continue ;
3704
3742
}
3705
3743
3706
3744
/* If subquery uses DISTINCT ON, check point 3 */
3707
3745
if (subquery -> hasDistinctOn &&
3746
+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3747
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE ) == 0 &&
3708
3748
!targetIsInSortList (tle , InvalidOid , subquery -> distinctClause ))
3709
3749
{
3710
3750
/* non-DISTINCT column, so mark it unsafe */
3711
- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3751
+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_NOTIN_DISTINCTON_CLAUSE ;
3712
3752
continue ;
3713
3753
}
3714
3754
3715
3755
/* If subquery uses window functions, check point 4 */
3716
3756
if (subquery -> hasWindowFuncs &&
3757
+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3758
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE ) == 0 &&
3717
3759
!targetIsInAllPartitionLists (tle , subquery ))
3718
3760
{
3719
3761
/* not present in all PARTITION BY clauses, so mark it unsafe */
3720
- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3762
+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_NOTIN_PARTITIONBY_CLAUSE ;
3721
3763
continue ;
3722
3764
}
3723
3765
}
@@ -3729,16 +3771,16 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
3729
3771
* subquery columns that suffer no type coercions in the set operation.
3730
3772
* Otherwise there are possible semantic gotchas. So, we check the
3731
3773
* component queries to see if any of them have output types different from
3732
- * the top-level setop outputs. unsafeColumns[k] is set true if column k
3733
- * has different type in any component.
3774
+ * the top-level setop outputs. We set the UNSAFE_TYPE_MISMATCH bit in
3775
+ * unsafeFlags[k] if column k has different type in any component.
3734
3776
*
3735
3777
* We don't have to care about typmods here: the only allowed difference
3736
3778
* between set-op input and output typmods is input is a specific typmod
3737
3779
* and output is -1, and that does not require a coercion.
3738
3780
*
3739
3781
* tlist is a subquery tlist.
3740
3782
* colTypes is an OID list of the top-level setop's output column types.
3741
- * safetyInfo->unsafeColumns[] is the result array .
3783
+ * safetyInfo is the pushdown_safety_info to set unsafeFlags[] for .
3742
3784
*/
3743
3785
static void
3744
3786
compare_tlist_datatypes (List * tlist , List * colTypes ,
@@ -3756,7 +3798,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
3756
3798
if (colType == NULL )
3757
3799
elog (ERROR , "wrong number of tlist entries" );
3758
3800
if (exprType ((Node * ) tle -> expr ) != lfirst_oid (colType ))
3759
- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3801
+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_TYPE_MISMATCH ;
3760
3802
colType = lnext (colTypes , colType );
3761
3803
}
3762
3804
if (colType != NULL )
@@ -3816,28 +3858,28 @@ targetIsInAllPartitionLists(TargetEntry *tle, Query *query)
3816
3858
* 5. rinfo's clause must not refer to any subquery output columns that were
3817
3859
* found to be unsafe to reference by subquery_is_pushdown_safe().
3818
3860
*/
3819
- static bool
3861
+ static pushdown_safe_type
3820
3862
qual_is_pushdown_safe (Query * subquery , Index rti , RestrictInfo * rinfo ,
3821
3863
pushdown_safety_info * safetyInfo )
3822
3864
{
3823
- bool safe = true ;
3865
+ pushdown_safe_type safe = PUSHDOWN_SAFE ;
3824
3866
Node * qual = (Node * ) rinfo -> clause ;
3825
3867
List * vars ;
3826
3868
ListCell * vl ;
3827
3869
3828
3870
/* Refuse subselects (point 1) */
3829
3871
if (contain_subplans (qual ))
3830
- return false ;
3872
+ return PUSHDOWN_UNSAFE ;
3831
3873
3832
3874
/* Refuse volatile quals if we found they'd be unsafe (point 2) */
3833
3875
if (safetyInfo -> unsafeVolatile &&
3834
3876
contain_volatile_functions ((Node * ) rinfo ))
3835
- return false ;
3877
+ return PUSHDOWN_UNSAFE ;
3836
3878
3837
3879
/* Refuse leaky quals if told to (point 3) */
3838
3880
if (safetyInfo -> unsafeLeaky &&
3839
3881
contain_leaked_vars (qual ))
3840
- return false ;
3882
+ return PUSHDOWN_UNSAFE ;
3841
3883
3842
3884
/*
3843
3885
* Examine all Vars used in clause. Since it's a restriction clause, all
@@ -3864,7 +3906,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3864
3906
*/
3865
3907
if (!IsA (var , Var ))
3866
3908
{
3867
- safe = false ;
3909
+ safe = PUSHDOWN_UNSAFE ;
3868
3910
break ;
3869
3911
}
3870
3912
@@ -3876,7 +3918,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3876
3918
*/
3877
3919
if (var -> varno != rti )
3878
3920
{
3879
- safe = false ;
3921
+ safe = PUSHDOWN_UNSAFE ;
3880
3922
break ;
3881
3923
}
3882
3924
@@ -3886,15 +3928,26 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
3886
3928
/* Check point 4 */
3887
3929
if (var -> varattno == 0 )
3888
3930
{
3889
- safe = false ;
3931
+ safe = PUSHDOWN_UNSAFE ;
3890
3932
break ;
3891
3933
}
3892
3934
3893
3935
/* Check point 5 */
3894
- if (safetyInfo -> unsafeColumns [var -> varattno ])
3936
+ if (safetyInfo -> unsafeFlags [var -> varattno ] != 0 )
3895
3937
{
3896
- safe = false;
3897
- break ;
3938
+ if (safetyInfo -> unsafeFlags [var -> varattno ] &
3939
+ (UNSAFE_HAS_VOLATILE_FUNC | UNSAFE_HAS_SET_FUNC |
3940
+ UNSAFE_NOTIN_DISTINCTON_CLAUSE | UNSAFE_TYPE_MISMATCH ))
3941
+ {
3942
+ safe = PUSHDOWN_UNSAFE ;
3943
+ break ;
3944
+ }
3945
+ else
3946
+ {
3947
+ /* UNSAFE_NOTIN_PARTITIONBY_CLAUSE is ok for run conditions */
3948
+ safe = PUSHDOWN_WINDOWCLAUSE_RUNCOND ;
3949
+ /* don't break, we might find another Var that's unsafe */
3950
+ }
3898
3951
}
3899
3952
}
3900
3953
0 commit comments