@@ -64,13 +64,14 @@ static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
6464RangeTblEntry * rte );
6565static RelOptInfo * make_rel_from_joinlist (PlannerInfo * root ,List * joinlist );
6666static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
67- bool * differentTypes );
67+ bool * unsafeColumns );
6868static bool recurse_pushdown_safe (Node * setOp ,Query * topquery ,
69- bool * differentTypes );
69+ bool * unsafeColumns );
70+ static void check_output_expressions (Query * subquery ,bool * unsafeColumns );
7071static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
71- bool * differentTypes );
72+ bool * unsafeColumns );
7273static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
73- bool * differentTypes );
74+ bool * unsafeColumns );
7475static void subquery_push_qual (Query * subquery ,
7576RangeTblEntry * rte ,Index rti ,Node * qual );
7677static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -545,7 +546,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
545546{
546547Query * parse = root -> parse ;
547548Query * subquery = rte -> subquery ;
548- bool * differentTypes ;
549+ bool * unsafeColumns ;
549550double tuple_fraction ;
550551PlannerInfo * subroot ;
551552List * pathkeys ;
@@ -557,8 +558,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
557558 */
558559subquery = copyObject (subquery );
559560
560- /* We need a workspace for keeping track of set-op type coercions */
561- differentTypes = (bool * )
561+ /*
562+ * We need a workspace for keeping track of unsafe-to-reference columns.
563+ * unsafeColumns[i] is set TRUE if we've found that output column i of the
564+ * subquery is unsafe to use in a pushed-down qual.
565+ */
566+ unsafeColumns = (bool * )
562567palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (bool ));
563568
564569/*
@@ -582,7 +587,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
582587 * push down a pushable qual, because it'd result in a worse plan?
583588 */
584589if (rel -> baserestrictinfo != NIL &&
585- subquery_is_pushdown_safe (subquery ,subquery ,differentTypes ))
590+ subquery_is_pushdown_safe (subquery ,subquery ,unsafeColumns ))
586591{
587592/* OK to consider pushing down individual quals */
588593List * upperrestrictlist = NIL ;
@@ -594,7 +599,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
594599Node * clause = (Node * )rinfo -> clause ;
595600
596601if (!rinfo -> pseudoconstant &&
597- qual_is_pushdown_safe (subquery ,rti ,clause ,differentTypes ))
602+ qual_is_pushdown_safe (subquery ,rti ,clause ,unsafeColumns ))
598603{
599604/* Push it down */
600605subquery_push_qual (subquery ,rte ,rti ,clause );
@@ -608,7 +613,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
608613rel -> baserestrictinfo = upperrestrictlist ;
609614}
610615
611- pfree (differentTypes );
616+ pfree (unsafeColumns );
612617
613618/*
614619 * We can safely pass the outer tuple_fraction down to the subquery if the
@@ -995,17 +1000,19 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
9951000 * 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
9961001 * quals into it, because that could change the results.
9971002 *
998- * 4. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
999- * push quals into each component query, but the quals can only reference
1000- * subquery columns that suffer no type coercions in the set operation.
1001- * Otherwise there are possible semantic gotchas. So, we check the
1002- * component queries to see if any of them have different output types;
1003- * differentTypes[k] is set true if column k has different type in any
1004- * component.
1003+ * In addition, we make several checks on the subquery's output columns
1004+ * to see if it is safe to reference them in pushed-down quals. If output
1005+ * column k is found to be unsafe to reference, we set unsafeColumns[k] to
1006+ * TRUE, but we don't reject the subquery overall since column k might
1007+ * not be referenced by some/all quals. The unsafeColumns[] array will be
1008+ * consulted later by qual_is_pushdown_safe().It's better to do it this
1009+ * way than to make the checks directly in qual_is_pushdown_safe(), because
1010+ * when the subquery involves set operations we have to check the output
1011+ * expressions in each arm of the set op.
10051012 */
10061013static bool
10071014subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
1008- bool * differentTypes )
1015+ bool * unsafeColumns )
10091016{
10101017SetOperationStmt * topop ;
10111018
@@ -1017,13 +1024,22 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
10171024if (subquery -> hasWindowFuncs )
10181025return false;
10191026
1027+ /*
1028+ * If we're at a leaf query, check for unsafe expressions in its target
1029+ * list, and mark any unsafe ones in unsafeColumns[]. (Non-leaf nodes in
1030+ * setop trees have only simple Vars in their tlists, so no need to check
1031+ * them.)
1032+ */
1033+ if (subquery -> setOperations == NULL )
1034+ check_output_expressions (subquery ,unsafeColumns );
1035+
10201036/* Are we at top level, or looking at a setop component? */
10211037if (subquery == topquery )
10221038{
10231039/* Top level, so check any component queries */
10241040if (subquery -> setOperations != NULL )
10251041if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ,
1026- differentTypes ))
1042+ unsafeColumns ))
10271043return false;
10281044}
10291045else
@@ -1036,7 +1052,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
10361052Assert (topop && IsA (topop ,SetOperationStmt ));
10371053compare_tlist_datatypes (subquery -> targetList ,
10381054topop -> colTypes ,
1039- differentTypes );
1055+ unsafeColumns );
10401056}
10411057return true;
10421058}
@@ -1046,7 +1062,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
10461062 */
10471063static bool
10481064recurse_pushdown_safe (Node * setOp ,Query * topquery ,
1049- bool * differentTypes )
1065+ bool * unsafeColumns )
10501066{
10511067if (IsA (setOp ,RangeTblRef ))
10521068{
@@ -1055,19 +1071,19 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
10551071Query * subquery = rte -> subquery ;
10561072
10571073Assert (subquery != NULL );
1058- return subquery_is_pushdown_safe (subquery ,topquery ,differentTypes );
1074+ return subquery_is_pushdown_safe (subquery ,topquery ,unsafeColumns );
10591075}
10601076else if (IsA (setOp ,SetOperationStmt ))
10611077{
10621078SetOperationStmt * op = (SetOperationStmt * )setOp ;
10631079
1064- /* EXCEPT is no good */
1080+ /* EXCEPT is no good(point 3 for subquery_is_pushdown_safe) */
10651081if (op -> op == SETOP_EXCEPT )
10661082return false;
10671083/* Else recurse */
1068- if (!recurse_pushdown_safe (op -> larg ,topquery ,differentTypes ))
1084+ if (!recurse_pushdown_safe (op -> larg ,topquery ,unsafeColumns ))
10691085return false;
1070- if (!recurse_pushdown_safe (op -> rarg ,topquery ,differentTypes ))
1086+ if (!recurse_pushdown_safe (op -> rarg ,topquery ,unsafeColumns ))
10711087return false;
10721088}
10731089else
@@ -1079,17 +1095,92 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
10791095}
10801096
10811097/*
1082- * Compare tlist's datatypes against the list of set-operation result types.
1083- * For any items that are different, mark the appropriate element of
1084- * differentTypes[] to show that this column will have type conversions.
1098+ * check_output_expressions - check subquery's output expressions for safety
1099+ *
1100+ * There are several cases in which it's unsafe to push down an upper-level
1101+ * qual if it references a particular output column of a subquery.We check
1102+ * each output column of the subquery and set unsafeColumns[k] to TRUE if
1103+ * that column is unsafe for a pushed-down qual to reference. The conditions
1104+ * checked here are:
1105+ *
1106+ * 1. We must not push down any quals that refer to subselect outputs that
1107+ * return sets, else we'd introduce functions-returning-sets into the
1108+ * subquery's WHERE/HAVING quals.
1109+ *
1110+ * 2. We must not push down any quals that refer to subselect outputs that
1111+ * contain volatile functions, for fear of introducing strange results due
1112+ * to multiple evaluation of a volatile function.
1113+ *
1114+ * 3. If the subquery uses DISTINCT ON, we must not push down any quals that
1115+ * refer to non-DISTINCT output columns, because that could change the set
1116+ * of rows returned. (This condition is vacuous for DISTINCT, because then
1117+ * there are no non-DISTINCT output columns, so we needn't check. But note
1118+ * we are assuming that the qual can't distinguish values that the DISTINCT
1119+ * operator sees as equal.This is a bit shaky but we have no way to test
1120+ * for the case, and it's unlikely enough that we shouldn't refuse the
1121+ * optimization just because it could theoretically happen.)
1122+ */
1123+ static void
1124+ check_output_expressions (Query * subquery ,bool * unsafeColumns )
1125+ {
1126+ ListCell * lc ;
1127+
1128+ foreach (lc ,subquery -> targetList )
1129+ {
1130+ TargetEntry * tle = (TargetEntry * )lfirst (lc );
1131+
1132+ if (tle -> resjunk )
1133+ continue ;/* ignore resjunk columns */
1134+
1135+ /* We need not check further if output col is already known unsafe */
1136+ if (unsafeColumns [tle -> resno ])
1137+ continue ;
1138+
1139+ /* Functions returning sets are unsafe (point 1) */
1140+ if (expression_returns_set ((Node * )tle -> expr ))
1141+ {
1142+ unsafeColumns [tle -> resno ]= true;
1143+ continue ;
1144+ }
1145+
1146+ /* Volatile functions are unsafe (point 2) */
1147+ if (contain_volatile_functions ((Node * )tle -> expr ))
1148+ {
1149+ unsafeColumns [tle -> resno ]= true;
1150+ continue ;
1151+ }
1152+
1153+ /* If subquery uses DISTINCT ON, check point 3 */
1154+ if (subquery -> hasDistinctOn &&
1155+ !targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
1156+ {
1157+ /* non-DISTINCT column, so mark it unsafe */
1158+ unsafeColumns [tle -> resno ]= true;
1159+ continue ;
1160+ }
1161+ }
1162+ }
1163+
1164+ /*
1165+ * For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
1166+ * push quals into each component query, but the quals can only reference
1167+ * subquery columns that suffer no type coercions in the set operation.
1168+ * Otherwise there are possible semantic gotchas. So, we check the
1169+ * component queries to see if any of them have output types different from
1170+ * the top-level setop outputs. unsafeColumns[k] is set true if column k
1171+ * has different type in any component.
10851172 *
10861173 * We don't have to care about typmods here: the only allowed difference
10871174 * between set-op input and output typmods is input is a specific typmod
10881175 * and output is -1, and that does not require a coercion.
1176+ *
1177+ * tlist is a subquery tlist.
1178+ * colTypes is an OID list of the top-level setop's output column types.
1179+ * unsafeColumns[] is the result array.
10891180 */
10901181static void
10911182compare_tlist_datatypes (List * tlist ,List * colTypes ,
1092- bool * differentTypes )
1183+ bool * unsafeColumns )
10931184{
10941185ListCell * l ;
10951186ListCell * colType = list_head (colTypes );
@@ -1103,7 +1194,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
11031194if (colType == NULL )
11041195elog (ERROR ,"wrong number of tlist entries" );
11051196if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
1106- differentTypes [tle -> resno ]= true;
1197+ unsafeColumns [tle -> resno ]= true;
11071198colType = lnext (colType );
11081199}
11091200if (colType != NULL )
@@ -1126,34 +1217,15 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
11261217 * (since there is no easy way to name that within the subquery itself).
11271218 *
11281219 * 3. The qual must not refer to any subquery output columns that were
1129- * found to have inconsistent types across a set operation tree by
1130- * subquery_is_pushdown_safe().
1131- *
1132- * 4. If the subquery uses DISTINCT ON, we must not push down any quals that
1133- * refer to non-DISTINCT output columns, because that could change the set
1134- * of rows returned. (This condition is vacuous for DISTINCT, because then
1135- * there are no non-DISTINCT output columns, so we needn't check. But note
1136- * we are assuming that the qual can't distinguish values that the DISTINCT
1137- * operator sees as equal.This is a bit shaky but we have no way to test
1138- * for the case, and it's unlikely enough that we shouldn't refuse the
1139- * optimization just because it could theoretically happen.)
1140- *
1141- * 5. We must not push down any quals that refer to subselect outputs that
1142- * return sets, else we'd introduce functions-returning-sets into the
1143- * subquery's WHERE/HAVING quals.
1144- *
1145- * 6. We must not push down any quals that refer to subselect outputs that
1146- * contain volatile functions, for fear of introducing strange results due
1147- * to multiple evaluation of a volatile function.
1220+ * found to be unsafe to reference by subquery_is_pushdown_safe().
11481221 */
11491222static bool
11501223qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
1151- bool * differentTypes )
1224+ bool * unsafeColumns )
11521225{
11531226bool safe = true;
11541227List * vars ;
11551228ListCell * vl ;
1156- Bitmapset * tested = NULL ;
11571229
11581230/* Refuse subselects (point 1) */
11591231if (contain_subplans (qual ))
@@ -1173,7 +1245,6 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
11731245foreach (vl ,vars )
11741246{
11751247Var * var = (Var * )lfirst (vl );
1176- TargetEntry * tle ;
11771248
11781249/*
11791250 * XXX Punt if we find any PlaceHolderVars in the restriction clause.
@@ -1189,6 +1260,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
11891260}
11901261
11911262Assert (var -> varno == rti );
1263+ Assert (var -> varattno >=0 );
11921264
11931265/* Check point 2 */
11941266if (var -> varattno == 0 )
@@ -1197,53 +1269,15 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
11971269break ;
11981270}
11991271
1200- /*
1201- * We use a bitmapset to avoid testing the same attno more than once.
1202- * (NB: this only works because subquery outputs can't have negative
1203- * attnos.)
1204- */
1205- if (bms_is_member (var -> varattno ,tested ))
1206- continue ;
1207- tested = bms_add_member (tested ,var -> varattno );
1208-
12091272/* Check point 3 */
1210- if (differentTypes [var -> varattno ])
1211- {
1212- safe = false;
1213- break ;
1214- }
1215-
1216- /* Must find the tlist element referenced by the Var */
1217- tle = get_tle_by_resno (subquery -> targetList ,var -> varattno );
1218- Assert (tle != NULL );
1219- Assert (!tle -> resjunk );
1220-
1221- /* If subquery uses DISTINCT ON, check point 4 */
1222- if (subquery -> hasDistinctOn &&
1223- !targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
1224- {
1225- /* non-DISTINCT column, so fail */
1226- safe = false;
1227- break ;
1228- }
1229-
1230- /* Refuse functions returning sets (point 5) */
1231- if (expression_returns_set ((Node * )tle -> expr ))
1232- {
1233- safe = false;
1234- break ;
1235- }
1236-
1237- /* Refuse volatile functions (point 6) */
1238- if (contain_volatile_functions ((Node * )tle -> expr ))
1273+ if (unsafeColumns [var -> varattno ])
12391274{
12401275safe = false;
12411276break ;
12421277}
12431278}
12441279
12451280list_free (vars );
1246- bms_free (tested );
12471281
12481282return safe ;
12491283}