@@ -69,13 +69,14 @@ static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
69
69
RangeTblEntry * rte );
70
70
static RelOptInfo * make_rel_from_joinlist (PlannerInfo * root ,List * joinlist );
71
71
static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
72
- bool * differentTypes );
72
+ bool * unsafeColumns );
73
73
static bool recurse_pushdown_safe (Node * setOp ,Query * topquery ,
74
- bool * differentTypes );
74
+ bool * unsafeColumns );
75
+ static void check_output_expressions (Query * subquery ,bool * unsafeColumns );
75
76
static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
76
- bool * differentTypes );
77
+ bool * unsafeColumns );
77
78
static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
78
- bool * differentTypes );
79
+ bool * unsafeColumns );
79
80
static void subquery_push_qual (Query * subquery ,
80
81
RangeTblEntry * rte ,Index rti ,Node * qual );
81
82
static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -705,7 +706,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
705
706
{
706
707
Query * parse = root -> parse ;
707
708
Query * subquery = rte -> subquery ;
708
- bool * differentTypes ;
709
+ bool * unsafeColumns ;
709
710
double tuple_fraction ;
710
711
PlannerInfo * subroot ;
711
712
List * pathkeys ;
@@ -717,8 +718,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
717
718
*/
718
719
subquery = copyObject (subquery );
719
720
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 * )
722
727
palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (bool ));
723
728
724
729
/*
@@ -742,7 +747,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
742
747
* push down a pushable qual, because it'd result in a worse plan?
743
748
*/
744
749
if (rel -> baserestrictinfo != NIL &&
745
- subquery_is_pushdown_safe (subquery ,subquery ,differentTypes ))
750
+ subquery_is_pushdown_safe (subquery ,subquery ,unsafeColumns ))
746
751
{
747
752
/* OK to consider pushing down individual quals */
748
753
List * upperrestrictlist = NIL ;
@@ -754,7 +759,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
754
759
Node * clause = (Node * )rinfo -> clause ;
755
760
756
761
if (!rinfo -> pseudoconstant &&
757
- qual_is_pushdown_safe (subquery ,rti ,clause ,differentTypes ))
762
+ qual_is_pushdown_safe (subquery ,rti ,clause ,unsafeColumns ))
758
763
{
759
764
/* Push it down */
760
765
subquery_push_qual (subquery ,rte ,rti ,clause );
@@ -768,7 +773,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
768
773
rel -> baserestrictinfo = upperrestrictlist ;
769
774
}
770
775
771
- pfree (differentTypes );
776
+ pfree (unsafeColumns );
772
777
773
778
/*
774
779
* 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)
1169
1174
* 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
1170
1175
* quals into it, because that could change the results.
1171
1176
*
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.
1179
1186
*/
1180
1187
static bool
1181
1188
subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
1182
- bool * differentTypes )
1189
+ bool * unsafeColumns )
1183
1190
{
1184
1191
SetOperationStmt * topop ;
1185
1192
@@ -1191,13 +1198,22 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1191
1198
if (subquery -> hasWindowFuncs )
1192
1199
return false;
1193
1200
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
+
1194
1210
/* Are we at top level, or looking at a setop component? */
1195
1211
if (subquery == topquery )
1196
1212
{
1197
1213
/* Top level, so check any component queries */
1198
1214
if (subquery -> setOperations != NULL )
1199
1215
if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ,
1200
- differentTypes ))
1216
+ unsafeColumns ))
1201
1217
return false;
1202
1218
}
1203
1219
else
@@ -1210,7 +1226,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1210
1226
Assert (topop && IsA (topop ,SetOperationStmt ));
1211
1227
compare_tlist_datatypes (subquery -> targetList ,
1212
1228
topop -> colTypes ,
1213
- differentTypes );
1229
+ unsafeColumns );
1214
1230
}
1215
1231
return true;
1216
1232
}
@@ -1220,7 +1236,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1220
1236
*/
1221
1237
static bool
1222
1238
recurse_pushdown_safe (Node * setOp ,Query * topquery ,
1223
- bool * differentTypes )
1239
+ bool * unsafeColumns )
1224
1240
{
1225
1241
if (IsA (setOp ,RangeTblRef ))
1226
1242
{
@@ -1229,19 +1245,19 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1229
1245
Query * subquery = rte -> subquery ;
1230
1246
1231
1247
Assert (subquery != NULL );
1232
- return subquery_is_pushdown_safe (subquery ,topquery ,differentTypes );
1248
+ return subquery_is_pushdown_safe (subquery ,topquery ,unsafeColumns );
1233
1249
}
1234
1250
else if (IsA (setOp ,SetOperationStmt ))
1235
1251
{
1236
1252
SetOperationStmt * op = (SetOperationStmt * )setOp ;
1237
1253
1238
- /* EXCEPT is no good */
1254
+ /* EXCEPT is no good(point 3 for subquery_is_pushdown_safe) */
1239
1255
if (op -> op == SETOP_EXCEPT )
1240
1256
return false;
1241
1257
/* Else recurse */
1242
- if (!recurse_pushdown_safe (op -> larg ,topquery ,differentTypes ))
1258
+ if (!recurse_pushdown_safe (op -> larg ,topquery ,unsafeColumns ))
1243
1259
return false;
1244
- if (!recurse_pushdown_safe (op -> rarg ,topquery ,differentTypes ))
1260
+ if (!recurse_pushdown_safe (op -> rarg ,topquery ,unsafeColumns ))
1245
1261
return false;
1246
1262
}
1247
1263
else
@@ -1253,17 +1269,92 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1253
1269
}
1254
1270
1255
1271
/*
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.
1259
1346
*
1260
1347
* We don't have to care about typmods here: the only allowed difference
1261
1348
* between set-op input and output typmods is input is a specific typmod
1262
1349
* 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.
1263
1354
*/
1264
1355
static void
1265
1356
compare_tlist_datatypes (List * tlist ,List * colTypes ,
1266
- bool * differentTypes )
1357
+ bool * unsafeColumns )
1267
1358
{
1268
1359
ListCell * l ;
1269
1360
ListCell * colType = list_head (colTypes );
@@ -1277,7 +1368,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1277
1368
if (colType == NULL )
1278
1369
elog (ERROR ,"wrong number of tlist entries" );
1279
1370
if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
1280
- differentTypes [tle -> resno ]= true;
1371
+ unsafeColumns [tle -> resno ]= true;
1281
1372
colType = lnext (colType );
1282
1373
}
1283
1374
if (colType != NULL )
@@ -1300,34 +1391,15 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1300
1391
* (since there is no easy way to name that within the subquery itself).
1301
1392
*
1302
1393
* 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().
1322
1395
*/
1323
1396
static bool
1324
1397
qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
1325
- bool * differentTypes )
1398
+ bool * unsafeColumns )
1326
1399
{
1327
1400
bool safe = true;
1328
1401
List * vars ;
1329
1402
ListCell * vl ;
1330
- Bitmapset * tested = NULL ;
1331
1403
1332
1404
/* Refuse subselects (point 1) */
1333
1405
if (contain_subplans (qual ))
@@ -1350,7 +1422,6 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1350
1422
foreach (vl ,vars )
1351
1423
{
1352
1424
Var * var = (Var * )lfirst (vl );
1353
- TargetEntry * tle ;
1354
1425
1355
1426
/*
1356
1427
* 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,
1366
1437
}
1367
1438
1368
1439
Assert (var -> varno == rti );
1440
+ Assert (var -> varattno >=0 );
1369
1441
1370
1442
/* Check point 2 */
1371
1443
if (var -> varattno == 0 )
@@ -1374,53 +1446,15 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1374
1446
break ;
1375
1447
}
1376
1448
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
-
1386
1449
/* 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 ])
1416
1451
{
1417
1452
safe = false;
1418
1453
break ;
1419
1454
}
1420
1455
}
1421
1456
1422
1457
list_free (vars );
1423
- bms_free (tested );
1424
1458
1425
1459
return safe ;
1426
1460
}