5151#include "utils/lsyscache.h"
5252
5353
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+
5461/* results of subquery_is_pushdown_safe */
5562typedef struct pushdown_safety_info
5663{
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. */
5867bool unsafeVolatile ;/* don't push down volatile quals */
5968bool unsafeLeaky ;/* don't push down leaky quals */
6069}pushdown_safety_info ;
6170
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+
6280/* These parameters are set by GUC */
6381bool enable_geqo = false;/* just in case GUC doesn't set it */
6482int geqo_threshold ;
@@ -135,9 +153,9 @@ static void check_output_expressions(Query *subquery,
135153static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
136154pushdown_safety_info * safetyInfo );
137155static 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 );
141159static void subquery_push_qual (Query * subquery ,
142160RangeTblEntry * rte ,Index rti ,Node * qual );
143161static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -2207,6 +2225,10 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
22072225if (!IsA (wfunc ,WindowFunc ))
22082226return false;
22092227
2228+ /* can't use it if there are subplans in the WindowFunc */
2229+ if (contain_subplans ((Node * )wfunc ))
2230+ return false;
2231+
22102232prosupport = get_func_support (wfunc -> winfnoid );
22112233
22122234/* Check if there's a support function for 'wfunc' */
@@ -2488,13 +2510,14 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
24882510/*
24892511 * Zero out result area for subquery_is_pushdown_safe, so that it can set
24902512 * 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.
24942517 */
24952518memset (& 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 ));
24982521
24992522/*
25002523 * If the subquery has the "security_barrier" flag, it means the subquery
@@ -2537,37 +2560,50 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
25372560RestrictInfo * rinfo = (RestrictInfo * )lfirst (l );
25382561Node * clause = (Node * )rinfo -> clause ;
25392562
2540- if (!rinfo -> pseudoconstant &&
2541- qual_is_pushdown_safe (subquery ,rti ,rinfo ,& safetyInfo ))
2563+ if (rinfo -> pseudoconstant )
25422564{
2543- /* Push it down */
2544- subquery_push_qual ( subquery , rte , rti , clause ) ;
2565+ upperrestrictlist = lappend ( upperrestrictlist , rinfo );
2566+ continue ;
25452567}
2546- else
2568+
2569+ switch (qual_is_pushdown_safe (subquery ,rti ,rinfo ,& safetyInfo ))
25472570{
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+
25572578/*
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.
25612583 */
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 :
25622598upperrestrictlist = lappend (upperrestrictlist ,rinfo );
2563- }
2599+ break ;
25642600}
25652601}
25662602rel -> baserestrictinfo = upperrestrictlist ;
25672603/* We don't bother recomputing baserestrict_min_security */
25682604}
25692605
2570- pfree (safetyInfo .unsafeColumns );
2606+ pfree (safetyInfo .unsafeFlags );
25712607
25722608/*
25732609 * 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)
34923528 *
34933529 * In addition, we make several checks on the subquery's output columns to see
34943530 * 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.
35023538 *
35033539 * Note: pushing quals into a DISTINCT subquery is theoretically dubious:
35043540 * we're effectively assuming that the quals cannot distinguish values that
@@ -3546,9 +3582,9 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
35463582
35473583/*
35483584 * 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.)
35523588 */
35533589if (subquery -> setOperations == NULL )
35543590check_output_expressions (subquery ,safetyInfo );
@@ -3619,9 +3655,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
36193655 *
36203656 * There are several cases in which it's unsafe to push down an upper-level
36213657 * 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:
36253661 *
36263662 * 1. We must not push down any quals that refer to subselect outputs that
36273663 * return sets, else we'd introduce functions-returning-sets into the
@@ -3645,7 +3681,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
36453681 * every row of any one window partition, and totally excluding some
36463682 * partitions will not change a window function's results for remaining
36473683 * 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.
36493687 */
36503688static void
36513689check_output_expressions (Query * subquery ,pushdown_safety_info * safetyInfo )
@@ -3659,40 +3697,44 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
36593697if (tle -> resjunk )
36603698continue ;/* ignore resjunk columns */
36613699
3662- /* We need not check further if output col is already known unsafe */
3663- if (safetyInfo -> unsafeColumns [tle -> resno ])
3664- continue ;
3665-
36663700/* Functions returning sets are unsafe (point 1) */
36673701if (subquery -> hasTargetSRFs &&
3702+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3703+ UNSAFE_HAS_SET_FUNC )== 0 &&
36683704expression_returns_set ((Node * )tle -> expr ))
36693705{
3670- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3706+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_HAS_SET_FUNC ;
36713707continue ;
36723708}
36733709
36743710/* 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 ))
36763714{
3677- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3715+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_HAS_VOLATILE_FUNC ;
36783716continue ;
36793717}
36803718
36813719/* If subquery uses DISTINCT ON, check point 3 */
36823720if (subquery -> hasDistinctOn &&
3721+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3722+ UNSAFE_NOTIN_DISTINCTON_CLAUSE )== 0 &&
36833723!targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
36843724{
36853725/* non-DISTINCT column, so mark it unsafe */
3686- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3726+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_NOTIN_DISTINCTON_CLAUSE ;
36873727continue ;
36883728}
36893729
36903730/* If subquery uses window functions, check point 4 */
36913731if (subquery -> hasWindowFuncs &&
3732+ (safetyInfo -> unsafeFlags [tle -> resno ]&
3733+ UNSAFE_NOTIN_DISTINCTON_CLAUSE )== 0 &&
36923734!targetIsInAllPartitionLists (tle ,subquery ))
36933735{
36943736/* 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 ;
36963738continue ;
36973739}
36983740}
@@ -3704,16 +3746,16 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
37043746 * subquery columns that suffer no type coercions in the set operation.
37053747 * Otherwise there are possible semantic gotchas. So, we check the
37063748 * 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.
37093751 *
37103752 * We don't have to care about typmods here: the only allowed difference
37113753 * between set-op input and output typmods is input is a specific typmod
37123754 * and output is -1, and that does not require a coercion.
37133755 *
37143756 * tlist is a subquery tlist.
37153757 * 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 .
37173759 */
37183760static void
37193761compare_tlist_datatypes (List * tlist ,List * colTypes ,
@@ -3731,7 +3773,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
37313773if (colType == NULL )
37323774elog (ERROR ,"wrong number of tlist entries" );
37333775if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
3734- safetyInfo -> unsafeColumns [tle -> resno ]= true ;
3776+ safetyInfo -> unsafeFlags [tle -> resno ]|= UNSAFE_TYPE_MISMATCH ;
37353777colType = lnext (colTypes ,colType );
37363778}
37373779if (colType != NULL )
@@ -3791,28 +3833,28 @@ targetIsInAllPartitionLists(TargetEntry *tle, Query *query)
37913833 * 5. rinfo's clause must not refer to any subquery output columns that were
37923834 * found to be unsafe to reference by subquery_is_pushdown_safe().
37933835 */
3794- static bool
3836+ static pushdown_safe_type
37953837qual_is_pushdown_safe (Query * subquery ,Index rti ,RestrictInfo * rinfo ,
37963838pushdown_safety_info * safetyInfo )
37973839{
3798- bool safe = true ;
3840+ pushdown_safe_type safe = PUSHDOWN_SAFE ;
37993841Node * qual = (Node * )rinfo -> clause ;
38003842List * vars ;
38013843ListCell * vl ;
38023844
38033845/* Refuse subselects (point 1) */
38043846if (contain_subplans (qual ))
3805- return false ;
3847+ return PUSHDOWN_UNSAFE ;
38063848
38073849/* Refuse volatile quals if we found they'd be unsafe (point 2) */
38083850if (safetyInfo -> unsafeVolatile &&
38093851contain_volatile_functions ((Node * )rinfo ))
3810- return false ;
3852+ return PUSHDOWN_UNSAFE ;
38113853
38123854/* Refuse leaky quals if told to (point 3) */
38133855if (safetyInfo -> unsafeLeaky &&
38143856contain_leaked_vars (qual ))
3815- return false ;
3857+ return PUSHDOWN_UNSAFE ;
38163858
38173859/*
38183860 * 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,
38413883 */
38423884if (!IsA (var ,Var ))
38433885{
3844- safe = false ;
3886+ safe = PUSHDOWN_UNSAFE ;
38453887break ;
38463888}
38473889
@@ -3853,7 +3895,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
38533895 */
38543896if (var -> varno != rti )
38553897{
3856- safe = false ;
3898+ safe = PUSHDOWN_UNSAFE ;
38573899break ;
38583900}
38593901
@@ -3863,15 +3905,26 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
38633905/* Check point 4 */
38643906if (var -> varattno == 0 )
38653907{
3866- safe = false ;
3908+ safe = PUSHDOWN_UNSAFE ;
38673909break ;
38683910}
38693911
38703912/* Check point 5 */
3871- if (safetyInfo -> unsafeColumns [var -> varattno ])
3913+ if (safetyInfo -> unsafeFlags [var -> varattno ]!= 0 )
38723914{
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+ }
38753928}
38763929}
38773930