5252#include "utils/lsyscache.h"
5353
5454
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+
5562/* results of subquery_is_pushdown_safe */
5663typedef struct pushdown_safety_info
5764{
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. */
5968 bool unsafeVolatile ; /* don't push down volatile quals */
6069 bool unsafeLeaky ; /* don't push down leaky quals */
6170} pushdown_safety_info ;
6271
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+
6381/* These parameters are set by GUC */
6482bool enable_geqo = false; /* just in case GUC doesn't set it */
6583int geqo_threshold ;
@@ -136,9 +154,9 @@ static void check_output_expressions(Query *subquery,
136154static void compare_tlist_datatypes (List * tlist , List * colTypes ,
137155 pushdown_safety_info * safetyInfo );
138156static 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 );
142160static void subquery_push_qual (Query * subquery ,
143161 RangeTblEntry * rte , Index rti , Node * qual );
144162static void recurse_push_qual (Node * setOp , Query * topquery ,
@@ -2218,6 +2236,10 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
22182236 if (!IsA (wfunc , WindowFunc ))
22192237 return false;
22202238
2239+ /* can't use it if there are subplans in the WindowFunc */
2240+ if (contain_subplans ((Node * ) wfunc ))
2241+ return false;
2242+
22212243 prosupport = get_func_support (wfunc -> winfnoid );
22222244
22232245 /* Check if there's a support function for 'wfunc' */
@@ -2500,13 +2522,14 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
25002522 /*
25012523 * Zero out result area for subquery_is_pushdown_safe, so that it can set
25022524 * 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.
25062529 */
25072530 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 ));
25102533
25112534 /*
25122535 * If the subquery has the "security_barrier" flag, it means the subquery
@@ -2549,37 +2572,50 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
25492572 RestrictInfo * rinfo = (RestrictInfo * ) lfirst (l );
25502573 Node * clause = (Node * ) rinfo -> clause ;
25512574
2552- if (!rinfo -> pseudoconstant &&
2553- qual_is_pushdown_safe (subquery , rti , rinfo , & safetyInfo ))
2575+ if (rinfo -> pseudoconstant )
25542576 {
2555- /* Push it down */
2556- subquery_push_qual ( subquery , rte , rti , clause ) ;
2577+ upperrestrictlist = lappend ( upperrestrictlist , rinfo );
2578+ continue ;
25572579 }
2558- else
2580+
2581+ switch (qual_is_pushdown_safe (subquery , rti , rinfo , & safetyInfo ))
25592582 {
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+
25692590 /*
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.
25732595 */
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 :
25742610 upperrestrictlist = lappend (upperrestrictlist , rinfo );
2575- }
2611+ break ;
25762612 }
25772613 }
25782614 rel -> baserestrictinfo = upperrestrictlist ;
25792615 /* We don't bother recomputing baserestrict_min_security */
25802616 }
25812617
2582- pfree (safetyInfo .unsafeColumns );
2618+ pfree (safetyInfo .unsafeFlags );
25832619
25842620 /*
25852621 * 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)
35173553 *
35183554 * In addition, we make several checks on the subquery's output columns to see
35193555 * 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.
35273563 *
35283564 * Note: pushing quals into a DISTINCT subquery is theoretically dubious:
35293565 * we're effectively assuming that the quals cannot distinguish values that
@@ -3571,9 +3607,9 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
35713607
35723608 /*
35733609 * 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.)
35773613 */
35783614 if (subquery -> setOperations == NULL )
35793615 check_output_expressions (subquery , safetyInfo );
@@ -3644,9 +3680,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
36443680 *
36453681 * There are several cases in which it's unsafe to push down an upper-level
36463682 * 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:
36503686 *
36513687 * 1. We must not push down any quals that refer to subselect outputs that
36523688 * return sets, else we'd introduce functions-returning-sets into the
@@ -3670,7 +3706,9 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
36703706 * every row of any one window partition, and totally excluding some
36713707 * partitions will not change a window function's results for remaining
36723708 * 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.
36743712 */
36753713static void
36763714check_output_expressions (Query * subquery , pushdown_safety_info * safetyInfo )
@@ -3684,40 +3722,44 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
36843722 if (tle -> resjunk )
36853723 continue ; /* ignore resjunk columns */
36863724
3687- /* We need not check further if output col is already known unsafe */
3688- if (safetyInfo -> unsafeColumns [tle -> resno ])
3689- continue ;
3690-
36913725 /* Functions returning sets are unsafe (point 1) */
36923726 if (subquery -> hasTargetSRFs &&
3727+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3728+ UNSAFE_HAS_SET_FUNC ) == 0 &&
36933729 expression_returns_set ((Node * ) tle -> expr ))
36943730 {
3695- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3731+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_HAS_SET_FUNC ;
36963732 continue ;
36973733 }
36983734
36993735 /* 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 ))
37013739 {
3702- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3740+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_HAS_VOLATILE_FUNC ;
37033741 continue ;
37043742 }
37053743
37063744 /* If subquery uses DISTINCT ON, check point 3 */
37073745 if (subquery -> hasDistinctOn &&
3746+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3747+ UNSAFE_NOTIN_DISTINCTON_CLAUSE ) == 0 &&
37083748 !targetIsInSortList (tle , InvalidOid , subquery -> distinctClause ))
37093749 {
37103750 /* non-DISTINCT column, so mark it unsafe */
3711- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3751+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_NOTIN_DISTINCTON_CLAUSE ;
37123752 continue ;
37133753 }
37143754
37153755 /* If subquery uses window functions, check point 4 */
37163756 if (subquery -> hasWindowFuncs &&
3757+ (safetyInfo -> unsafeFlags [tle -> resno ] &
3758+ UNSAFE_NOTIN_DISTINCTON_CLAUSE ) == 0 &&
37173759 !targetIsInAllPartitionLists (tle , subquery ))
37183760 {
37193761 /* 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 ;
37213763 continue ;
37223764 }
37233765 }
@@ -3729,16 +3771,16 @@ check_output_expressions(Query *subquery, pushdown_safety_info *safetyInfo)
37293771 * subquery columns that suffer no type coercions in the set operation.
37303772 * Otherwise there are possible semantic gotchas. So, we check the
37313773 * 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.
37343776 *
37353777 * We don't have to care about typmods here: the only allowed difference
37363778 * between set-op input and output typmods is input is a specific typmod
37373779 * and output is -1, and that does not require a coercion.
37383780 *
37393781 * tlist is a subquery tlist.
37403782 * 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 .
37423784 */
37433785static void
37443786compare_tlist_datatypes (List * tlist , List * colTypes ,
@@ -3756,7 +3798,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
37563798 if (colType == NULL )
37573799 elog (ERROR , "wrong number of tlist entries" );
37583800 if (exprType ((Node * ) tle -> expr ) != lfirst_oid (colType ))
3759- safetyInfo -> unsafeColumns [tle -> resno ] = true ;
3801+ safetyInfo -> unsafeFlags [tle -> resno ] |= UNSAFE_TYPE_MISMATCH ;
37603802 colType = lnext (colTypes , colType );
37613803 }
37623804 if (colType != NULL )
@@ -3816,28 +3858,28 @@ targetIsInAllPartitionLists(TargetEntry *tle, Query *query)
38163858 * 5. rinfo's clause must not refer to any subquery output columns that were
38173859 * found to be unsafe to reference by subquery_is_pushdown_safe().
38183860 */
3819- static bool
3861+ static pushdown_safe_type
38203862qual_is_pushdown_safe (Query * subquery , Index rti , RestrictInfo * rinfo ,
38213863 pushdown_safety_info * safetyInfo )
38223864{
3823- bool safe = true ;
3865+ pushdown_safe_type safe = PUSHDOWN_SAFE ;
38243866 Node * qual = (Node * ) rinfo -> clause ;
38253867 List * vars ;
38263868 ListCell * vl ;
38273869
38283870 /* Refuse subselects (point 1) */
38293871 if (contain_subplans (qual ))
3830- return false ;
3872+ return PUSHDOWN_UNSAFE ;
38313873
38323874 /* Refuse volatile quals if we found they'd be unsafe (point 2) */
38333875 if (safetyInfo -> unsafeVolatile &&
38343876 contain_volatile_functions ((Node * ) rinfo ))
3835- return false ;
3877+ return PUSHDOWN_UNSAFE ;
38363878
38373879 /* Refuse leaky quals if told to (point 3) */
38383880 if (safetyInfo -> unsafeLeaky &&
38393881 contain_leaked_vars (qual ))
3840- return false ;
3882+ return PUSHDOWN_UNSAFE ;
38413883
38423884 /*
38433885 * 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,
38643906 */
38653907 if (!IsA (var , Var ))
38663908 {
3867- safe = false ;
3909+ safe = PUSHDOWN_UNSAFE ;
38683910 break ;
38693911 }
38703912
@@ -3876,7 +3918,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
38763918 */
38773919 if (var -> varno != rti )
38783920 {
3879- safe = false ;
3921+ safe = PUSHDOWN_UNSAFE ;
38803922 break ;
38813923 }
38823924
@@ -3886,15 +3928,26 @@ qual_is_pushdown_safe(Query *subquery, Index rti, RestrictInfo *rinfo,
38863928 /* Check point 4 */
38873929 if (var -> varattno == 0 )
38883930 {
3889- safe = false ;
3931+ safe = PUSHDOWN_UNSAFE ;
38903932 break ;
38913933 }
38923934
38933935 /* Check point 5 */
3894- if (safetyInfo -> unsafeColumns [var -> varattno ])
3936+ if (safetyInfo -> unsafeFlags [var -> varattno ] != 0 )
38953937 {
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+ }
38983951 }
38993952 }
39003953
0 commit comments