@@ -69,13 +69,14 @@ static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
6969RangeTblEntry * rte );
7070static RelOptInfo * make_rel_from_joinlist (PlannerInfo * root ,List * joinlist );
7171static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
72- bool * differentTypes );
72+ bool * unsafeColumns );
7373static bool recurse_pushdown_safe (Node * setOp ,Query * topquery ,
74- bool * differentTypes );
74+ bool * unsafeColumns );
75+ static void check_output_expressions (Query * subquery ,bool * unsafeColumns );
7576static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
76- bool * differentTypes );
77+ bool * unsafeColumns );
7778static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
78- bool * differentTypes );
79+ bool * unsafeColumns );
7980static void subquery_push_qual (Query * subquery ,
8081RangeTblEntry * rte ,Index rti ,Node * qual );
8182static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -705,7 +706,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
705706{
706707Query * parse = root -> parse ;
707708Query * subquery = rte -> subquery ;
708- bool * differentTypes ;
709+ bool * unsafeColumns ;
709710double tuple_fraction ;
710711PlannerInfo * subroot ;
711712List * pathkeys ;
@@ -717,8 +718,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
717718 */
718719subquery = copyObject (subquery );
719720
720- /* We need a workspace for keeping track of set-op type coercions */
721- differentTypes = (bool * )
721+ /*
722+ * We need a workspace for keeping track of unsafe-to-reference columns.
723+ * unsafeColumns[i] is set TRUE if we've found that output column i of the
724+ * subquery is unsafe to use in a pushed-down qual.
725+ */
726+ unsafeColumns = (bool * )
722727palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (bool ));
723728
724729/*
@@ -742,7 +747,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
742747 * push down a pushable qual, because it'd result in a worse plan?
743748 */
744749if (rel -> baserestrictinfo != NIL &&
745- subquery_is_pushdown_safe (subquery ,subquery ,differentTypes ))
750+ subquery_is_pushdown_safe (subquery ,subquery ,unsafeColumns ))
746751{
747752/* OK to consider pushing down individual quals */
748753List * upperrestrictlist = NIL ;
@@ -754,7 +759,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
754759Node * clause = (Node * )rinfo -> clause ;
755760
756761if (!rinfo -> pseudoconstant &&
757- qual_is_pushdown_safe (subquery ,rti ,clause ,differentTypes ))
762+ qual_is_pushdown_safe (subquery ,rti ,clause ,unsafeColumns ))
758763{
759764/* Push it down */
760765subquery_push_qual (subquery ,rte ,rti ,clause );
@@ -768,7 +773,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
768773rel -> baserestrictinfo = upperrestrictlist ;
769774}
770775
771- pfree (differentTypes );
776+ pfree (unsafeColumns );
772777
773778/*
774779 * We can safely pass the outer tuple_fraction down to the subquery if the
@@ -1169,17 +1174,19 @@ standard_join_search(PlannerInfo *root, int levels_needed, List *initial_rels)
11691174 * 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
11701175 * quals into it, because that could change the results.
11711176 *
1172- * 4. For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
1173- * push quals into each component query, but the quals can only reference
1174- * subquery columns that suffer no type coercions in the set operation.
1175- * Otherwise there are possible semantic gotchas. So, we check the
1176- * component queries to see if any of them have different output types;
1177- * differentTypes[k] is set true if column k has different type in any
1178- * component.
1177+ * In addition, we make several checks on the subquery's output columns
1178+ * to see if it is safe to reference them in pushed-down quals. If output
1179+ * column k is found to be unsafe to reference, we set unsafeColumns[k] to
1180+ * TRUE, but we don't reject the subquery overall since column k might
1181+ * not be referenced by some/all quals. The unsafeColumns[] array will be
1182+ * consulted later by qual_is_pushdown_safe().It's better to do it this
1183+ * way than to make the checks directly in qual_is_pushdown_safe(), because
1184+ * when the subquery involves set operations we have to check the output
1185+ * expressions in each arm of the set op.
11791186 */
11801187static bool
11811188subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
1182- bool * differentTypes )
1189+ bool * unsafeColumns )
11831190{
11841191SetOperationStmt * topop ;
11851192
@@ -1191,13 +1198,22 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
11911198if (subquery -> hasWindowFuncs )
11921199return false;
11931200
1201+ /*
1202+ * If we're at a leaf query, check for unsafe expressions in its target
1203+ * list, and mark any unsafe ones in unsafeColumns[]. (Non-leaf nodes in
1204+ * setop trees have only simple Vars in their tlists, so no need to check
1205+ * them.)
1206+ */
1207+ if (subquery -> setOperations == NULL )
1208+ check_output_expressions (subquery ,unsafeColumns );
1209+
11941210/* Are we at top level, or looking at a setop component? */
11951211if (subquery == topquery )
11961212{
11971213/* Top level, so check any component queries */
11981214if (subquery -> setOperations != NULL )
11991215if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ,
1200- differentTypes ))
1216+ unsafeColumns ))
12011217return false;
12021218}
12031219else
@@ -1210,7 +1226,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
12101226Assert (topop && IsA (topop ,SetOperationStmt ));
12111227compare_tlist_datatypes (subquery -> targetList ,
12121228topop -> colTypes ,
1213- differentTypes );
1229+ unsafeColumns );
12141230}
12151231return true;
12161232}
@@ -1220,7 +1236,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
12201236 */
12211237static bool
12221238recurse_pushdown_safe (Node * setOp ,Query * topquery ,
1223- bool * differentTypes )
1239+ bool * unsafeColumns )
12241240{
12251241if (IsA (setOp ,RangeTblRef ))
12261242{
@@ -1229,19 +1245,19 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
12291245Query * subquery = rte -> subquery ;
12301246
12311247Assert (subquery != NULL );
1232- return subquery_is_pushdown_safe (subquery ,topquery ,differentTypes );
1248+ return subquery_is_pushdown_safe (subquery ,topquery ,unsafeColumns );
12331249}
12341250else if (IsA (setOp ,SetOperationStmt ))
12351251{
12361252SetOperationStmt * op = (SetOperationStmt * )setOp ;
12371253
1238- /* EXCEPT is no good */
1254+ /* EXCEPT is no good(point 3 for subquery_is_pushdown_safe) */
12391255if (op -> op == SETOP_EXCEPT )
12401256return false;
12411257/* Else recurse */
1242- if (!recurse_pushdown_safe (op -> larg ,topquery ,differentTypes ))
1258+ if (!recurse_pushdown_safe (op -> larg ,topquery ,unsafeColumns ))
12431259return false;
1244- if (!recurse_pushdown_safe (op -> rarg ,topquery ,differentTypes ))
1260+ if (!recurse_pushdown_safe (op -> rarg ,topquery ,unsafeColumns ))
12451261return false;
12461262}
12471263else
@@ -1253,17 +1269,92 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
12531269}
12541270
12551271/*
1256- * Compare tlist's datatypes against the list of set-operation result types.
1257- * For any items that are different, mark the appropriate element of
1258- * differentTypes[] to show that this column will have type conversions.
1272+ * check_output_expressions - check subquery's output expressions for safety
1273+ *
1274+ * There are several cases in which it's unsafe to push down an upper-level
1275+ * qual if it references a particular output column of a subquery.We check
1276+ * each output column of the subquery and set unsafeColumns[k] to TRUE if
1277+ * that column is unsafe for a pushed-down qual to reference. The conditions
1278+ * checked here are:
1279+ *
1280+ * 1. We must not push down any quals that refer to subselect outputs that
1281+ * return sets, else we'd introduce functions-returning-sets into the
1282+ * subquery's WHERE/HAVING quals.
1283+ *
1284+ * 2. We must not push down any quals that refer to subselect outputs that
1285+ * contain volatile functions, for fear of introducing strange results due
1286+ * to multiple evaluation of a volatile function.
1287+ *
1288+ * 3. If the subquery uses DISTINCT ON, we must not push down any quals that
1289+ * refer to non-DISTINCT output columns, because that could change the set
1290+ * of rows returned. (This condition is vacuous for DISTINCT, because then
1291+ * there are no non-DISTINCT output columns, so we needn't check. But note
1292+ * we are assuming that the qual can't distinguish values that the DISTINCT
1293+ * operator sees as equal.This is a bit shaky but we have no way to test
1294+ * for the case, and it's unlikely enough that we shouldn't refuse the
1295+ * optimization just because it could theoretically happen.)
1296+ */
1297+ static void
1298+ check_output_expressions (Query * subquery ,bool * unsafeColumns )
1299+ {
1300+ ListCell * lc ;
1301+
1302+ foreach (lc ,subquery -> targetList )
1303+ {
1304+ TargetEntry * tle = (TargetEntry * )lfirst (lc );
1305+
1306+ if (tle -> resjunk )
1307+ continue ;/* ignore resjunk columns */
1308+
1309+ /* We need not check further if output col is already known unsafe */
1310+ if (unsafeColumns [tle -> resno ])
1311+ continue ;
1312+
1313+ /* Functions returning sets are unsafe (point 1) */
1314+ if (expression_returns_set ((Node * )tle -> expr ))
1315+ {
1316+ unsafeColumns [tle -> resno ]= true;
1317+ continue ;
1318+ }
1319+
1320+ /* Volatile functions are unsafe (point 2) */
1321+ if (contain_volatile_functions ((Node * )tle -> expr ))
1322+ {
1323+ unsafeColumns [tle -> resno ]= true;
1324+ continue ;
1325+ }
1326+
1327+ /* If subquery uses DISTINCT ON, check point 3 */
1328+ if (subquery -> hasDistinctOn &&
1329+ !targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
1330+ {
1331+ /* non-DISTINCT column, so mark it unsafe */
1332+ unsafeColumns [tle -> resno ]= true;
1333+ continue ;
1334+ }
1335+ }
1336+ }
1337+
1338+ /*
1339+ * For subqueries using UNION/UNION ALL/INTERSECT/INTERSECT ALL, we can
1340+ * push quals into each component query, but the quals can only reference
1341+ * subquery columns that suffer no type coercions in the set operation.
1342+ * Otherwise there are possible semantic gotchas. So, we check the
1343+ * component queries to see if any of them have output types different from
1344+ * the top-level setop outputs. unsafeColumns[k] is set true if column k
1345+ * has different type in any component.
12591346 *
12601347 * We don't have to care about typmods here: the only allowed difference
12611348 * between set-op input and output typmods is input is a specific typmod
12621349 * and output is -1, and that does not require a coercion.
1350+ *
1351+ * tlist is a subquery tlist.
1352+ * colTypes is an OID list of the top-level setop's output column types.
1353+ * unsafeColumns[] is the result array.
12631354 */
12641355static void
12651356compare_tlist_datatypes (List * tlist ,List * colTypes ,
1266- bool * differentTypes )
1357+ bool * unsafeColumns )
12671358{
12681359ListCell * l ;
12691360ListCell * colType = list_head (colTypes );
@@ -1277,7 +1368,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
12771368if (colType == NULL )
12781369elog (ERROR ,"wrong number of tlist entries" );
12791370if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
1280- differentTypes [tle -> resno ]= true;
1371+ unsafeColumns [tle -> resno ]= true;
12811372colType = lnext (colType );
12821373}
12831374if (colType != NULL )
@@ -1300,34 +1391,15 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
13001391 * (since there is no easy way to name that within the subquery itself).
13011392 *
13021393 * 3. The qual must not refer to any subquery output columns that were
1303- * found to have inconsistent types across a set operation tree by
1304- * subquery_is_pushdown_safe().
1305- *
1306- * 4. If the subquery uses DISTINCT ON, we must not push down any quals that
1307- * refer to non-DISTINCT output columns, because that could change the set
1308- * of rows returned. (This condition is vacuous for DISTINCT, because then
1309- * there are no non-DISTINCT output columns, so we needn't check. But note
1310- * we are assuming that the qual can't distinguish values that the DISTINCT
1311- * operator sees as equal.This is a bit shaky but we have no way to test
1312- * for the case, and it's unlikely enough that we shouldn't refuse the
1313- * optimization just because it could theoretically happen.)
1314- *
1315- * 5. We must not push down any quals that refer to subselect outputs that
1316- * return sets, else we'd introduce functions-returning-sets into the
1317- * subquery's WHERE/HAVING quals.
1318- *
1319- * 6. We must not push down any quals that refer to subselect outputs that
1320- * contain volatile functions, for fear of introducing strange results due
1321- * to multiple evaluation of a volatile function.
1394+ * found to be unsafe to reference by subquery_is_pushdown_safe().
13221395 */
13231396static bool
13241397qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
1325- bool * differentTypes )
1398+ bool * unsafeColumns )
13261399{
13271400bool safe = true;
13281401List * vars ;
13291402ListCell * vl ;
1330- Bitmapset * tested = NULL ;
13311403
13321404/* Refuse subselects (point 1) */
13331405if (contain_subplans (qual ))
@@ -1350,7 +1422,6 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
13501422foreach (vl ,vars )
13511423{
13521424Var * var = (Var * )lfirst (vl );
1353- TargetEntry * tle ;
13541425
13551426/*
13561427 * XXX Punt if we find any PlaceHolderVars in the restriction clause.
@@ -1366,6 +1437,7 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
13661437}
13671438
13681439Assert (var -> varno == rti );
1440+ Assert (var -> varattno >=0 );
13691441
13701442/* Check point 2 */
13711443if (var -> varattno == 0 )
@@ -1374,53 +1446,15 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
13741446break ;
13751447}
13761448
1377- /*
1378- * We use a bitmapset to avoid testing the same attno more than once.
1379- * (NB: this only works because subquery outputs can't have negative
1380- * attnos.)
1381- */
1382- if (bms_is_member (var -> varattno ,tested ))
1383- continue ;
1384- tested = bms_add_member (tested ,var -> varattno );
1385-
13861449/* Check point 3 */
1387- if (differentTypes [var -> varattno ])
1388- {
1389- safe = false;
1390- break ;
1391- }
1392-
1393- /* Must find the tlist element referenced by the Var */
1394- tle = get_tle_by_resno (subquery -> targetList ,var -> varattno );
1395- Assert (tle != NULL );
1396- Assert (!tle -> resjunk );
1397-
1398- /* If subquery uses DISTINCT ON, check point 4 */
1399- if (subquery -> hasDistinctOn &&
1400- !targetIsInSortList (tle ,InvalidOid ,subquery -> distinctClause ))
1401- {
1402- /* non-DISTINCT column, so fail */
1403- safe = false;
1404- break ;
1405- }
1406-
1407- /* Refuse functions returning sets (point 5) */
1408- if (expression_returns_set ((Node * )tle -> expr ))
1409- {
1410- safe = false;
1411- break ;
1412- }
1413-
1414- /* Refuse volatile functions (point 6) */
1415- if (contain_volatile_functions ((Node * )tle -> expr ))
1450+ if (unsafeColumns [var -> varattno ])
14161451{
14171452safe = false;
14181453break ;
14191454}
14201455}
14211456
14221457list_free (vars );
1423- bms_free (tested );
14241458
14251459return safe ;
14261460}