@@ -64,13 +64,14 @@ static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
64
64
RangeTblEntry * rte );
65
65
static RelOptInfo * make_rel_from_joinlist (PlannerInfo * root ,List * joinlist );
66
66
static bool subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
67
- bool * differentTypes );
67
+ bool * unsafeColumns );
68
68
static bool recurse_pushdown_safe (Node * setOp ,Query * topquery ,
69
- bool * differentTypes );
69
+ bool * unsafeColumns );
70
+ static void check_output_expressions (Query * subquery ,bool * unsafeColumns );
70
71
static void compare_tlist_datatypes (List * tlist ,List * colTypes ,
71
- bool * differentTypes );
72
+ bool * unsafeColumns );
72
73
static bool qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
73
- bool * differentTypes );
74
+ bool * unsafeColumns );
74
75
static void subquery_push_qual (Query * subquery ,
75
76
RangeTblEntry * rte ,Index rti ,Node * qual );
76
77
static void recurse_push_qual (Node * setOp ,Query * topquery ,
@@ -545,7 +546,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
545
546
{
546
547
Query * parse = root -> parse ;
547
548
Query * subquery = rte -> subquery ;
548
- bool * differentTypes ;
549
+ bool * unsafeColumns ;
549
550
double tuple_fraction ;
550
551
PlannerInfo * subroot ;
551
552
List * pathkeys ;
@@ -557,8 +558,12 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
557
558
*/
558
559
subquery = copyObject (subquery );
559
560
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 * )
562
567
palloc0 ((list_length (subquery -> targetList )+ 1 )* sizeof (bool ));
563
568
564
569
/*
@@ -582,7 +587,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
582
587
* push down a pushable qual, because it'd result in a worse plan?
583
588
*/
584
589
if (rel -> baserestrictinfo != NIL &&
585
- subquery_is_pushdown_safe (subquery ,subquery ,differentTypes ))
590
+ subquery_is_pushdown_safe (subquery ,subquery ,unsafeColumns ))
586
591
{
587
592
/* OK to consider pushing down individual quals */
588
593
List * upperrestrictlist = NIL ;
@@ -594,7 +599,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
594
599
Node * clause = (Node * )rinfo -> clause ;
595
600
596
601
if (!rinfo -> pseudoconstant &&
597
- qual_is_pushdown_safe (subquery ,rti ,clause ,differentTypes ))
602
+ qual_is_pushdown_safe (subquery ,rti ,clause ,unsafeColumns ))
598
603
{
599
604
/* Push it down */
600
605
subquery_push_qual (subquery ,rte ,rti ,clause );
@@ -608,7 +613,7 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
608
613
rel -> baserestrictinfo = upperrestrictlist ;
609
614
}
610
615
611
- pfree (differentTypes );
616
+ pfree (unsafeColumns );
612
617
613
618
/*
614
619
* 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)
995
1000
* 3. If the subquery contains EXCEPT or EXCEPT ALL set ops we cannot push
996
1001
* quals into it, because that could change the results.
997
1002
*
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.
1005
1012
*/
1006
1013
static bool
1007
1014
subquery_is_pushdown_safe (Query * subquery ,Query * topquery ,
1008
- bool * differentTypes )
1015
+ bool * unsafeColumns )
1009
1016
{
1010
1017
SetOperationStmt * topop ;
1011
1018
@@ -1017,13 +1024,22 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1017
1024
if (subquery -> hasWindowFuncs )
1018
1025
return false;
1019
1026
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
+
1020
1036
/* Are we at top level, or looking at a setop component? */
1021
1037
if (subquery == topquery )
1022
1038
{
1023
1039
/* Top level, so check any component queries */
1024
1040
if (subquery -> setOperations != NULL )
1025
1041
if (!recurse_pushdown_safe (subquery -> setOperations ,topquery ,
1026
- differentTypes ))
1042
+ unsafeColumns ))
1027
1043
return false;
1028
1044
}
1029
1045
else
@@ -1036,7 +1052,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1036
1052
Assert (topop && IsA (topop ,SetOperationStmt ));
1037
1053
compare_tlist_datatypes (subquery -> targetList ,
1038
1054
topop -> colTypes ,
1039
- differentTypes );
1055
+ unsafeColumns );
1040
1056
}
1041
1057
return true;
1042
1058
}
@@ -1046,7 +1062,7 @@ subquery_is_pushdown_safe(Query *subquery, Query *topquery,
1046
1062
*/
1047
1063
static bool
1048
1064
recurse_pushdown_safe (Node * setOp ,Query * topquery ,
1049
- bool * differentTypes )
1065
+ bool * unsafeColumns )
1050
1066
{
1051
1067
if (IsA (setOp ,RangeTblRef ))
1052
1068
{
@@ -1055,19 +1071,19 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1055
1071
Query * subquery = rte -> subquery ;
1056
1072
1057
1073
Assert (subquery != NULL );
1058
- return subquery_is_pushdown_safe (subquery ,topquery ,differentTypes );
1074
+ return subquery_is_pushdown_safe (subquery ,topquery ,unsafeColumns );
1059
1075
}
1060
1076
else if (IsA (setOp ,SetOperationStmt ))
1061
1077
{
1062
1078
SetOperationStmt * op = (SetOperationStmt * )setOp ;
1063
1079
1064
- /* EXCEPT is no good */
1080
+ /* EXCEPT is no good(point 3 for subquery_is_pushdown_safe) */
1065
1081
if (op -> op == SETOP_EXCEPT )
1066
1082
return false;
1067
1083
/* Else recurse */
1068
- if (!recurse_pushdown_safe (op -> larg ,topquery ,differentTypes ))
1084
+ if (!recurse_pushdown_safe (op -> larg ,topquery ,unsafeColumns ))
1069
1085
return false;
1070
- if (!recurse_pushdown_safe (op -> rarg ,topquery ,differentTypes ))
1086
+ if (!recurse_pushdown_safe (op -> rarg ,topquery ,unsafeColumns ))
1071
1087
return false;
1072
1088
}
1073
1089
else
@@ -1079,17 +1095,92 @@ recurse_pushdown_safe(Node *setOp, Query *topquery,
1079
1095
}
1080
1096
1081
1097
/*
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.
1085
1172
*
1086
1173
* We don't have to care about typmods here: the only allowed difference
1087
1174
* between set-op input and output typmods is input is a specific typmod
1088
1175
* 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.
1089
1180
*/
1090
1181
static void
1091
1182
compare_tlist_datatypes (List * tlist ,List * colTypes ,
1092
- bool * differentTypes )
1183
+ bool * unsafeColumns )
1093
1184
{
1094
1185
ListCell * l ;
1095
1186
ListCell * colType = list_head (colTypes );
@@ -1103,7 +1194,7 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1103
1194
if (colType == NULL )
1104
1195
elog (ERROR ,"wrong number of tlist entries" );
1105
1196
if (exprType ((Node * )tle -> expr )!= lfirst_oid (colType ))
1106
- differentTypes [tle -> resno ]= true;
1197
+ unsafeColumns [tle -> resno ]= true;
1107
1198
colType = lnext (colType );
1108
1199
}
1109
1200
if (colType != NULL )
@@ -1126,34 +1217,15 @@ compare_tlist_datatypes(List *tlist, List *colTypes,
1126
1217
* (since there is no easy way to name that within the subquery itself).
1127
1218
*
1128
1219
* 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().
1148
1221
*/
1149
1222
static bool
1150
1223
qual_is_pushdown_safe (Query * subquery ,Index rti ,Node * qual ,
1151
- bool * differentTypes )
1224
+ bool * unsafeColumns )
1152
1225
{
1153
1226
bool safe = true;
1154
1227
List * vars ;
1155
1228
ListCell * vl ;
1156
- Bitmapset * tested = NULL ;
1157
1229
1158
1230
/* Refuse subselects (point 1) */
1159
1231
if (contain_subplans (qual ))
@@ -1173,7 +1245,6 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1173
1245
foreach (vl ,vars )
1174
1246
{
1175
1247
Var * var = (Var * )lfirst (vl );
1176
- TargetEntry * tle ;
1177
1248
1178
1249
/*
1179
1250
* 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,
1189
1260
}
1190
1261
1191
1262
Assert (var -> varno == rti );
1263
+ Assert (var -> varattno >=0 );
1192
1264
1193
1265
/* Check point 2 */
1194
1266
if (var -> varattno == 0 )
@@ -1197,53 +1269,15 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual,
1197
1269
break ;
1198
1270
}
1199
1271
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
-
1209
1272
/* 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 ])
1239
1274
{
1240
1275
safe = false;
1241
1276
break ;
1242
1277
}
1243
1278
}
1244
1279
1245
1280
list_free (vars );
1246
- bms_free (tested );
1247
1281
1248
1282
return safe ;
1249
1283
}