84
84
import java .util .ArrayList ;
85
85
import java .util .Arrays ;
86
86
import java .util .HashMap ;
87
+ import java .util .Iterator ;
87
88
import java .util .LinkedHashMap ;
88
89
import java .util .LinkedHashSet ;
89
90
import java .util .LinkedList ;
@@ -814,7 +815,7 @@ private Expression simplifyAndOr(BinaryPredicate<?, ?, ?, ?> bc) {
814
815
}
815
816
816
817
//
817
- // common factor extraction -> (a || b) && (a || c) => a && (b || c)
818
+ // common factor extraction -> (a || b) && (a || c) => a || (b && c)
818
819
//
819
820
List <Expression > leftSplit = splitOr (l );
820
821
List <Expression > rightSplit = splitOr (r );
@@ -852,7 +853,7 @@ private Expression simplifyAndOr(BinaryPredicate<?, ?, ?, ?> bc) {
852
853
}
853
854
854
855
//
855
- // common factor extraction -> (a && b) || (a && c) => a || (b & c)
856
+ // common factor extraction -> (a && b) || (a && c) => a && (b || c)
856
857
//
857
858
List <Expression > leftSplit = splitAnd (l );
858
859
List <Expression > rightSplit = splitAnd (r );
@@ -958,9 +959,11 @@ private Expression literalToTheRight(BinaryOperator<?, ?, ?, ?> be) {
958
959
}
959
960
960
961
/**
961
- * Propagate Equals to eliminate conjuncted Ranges.
962
- * When encountering a different Equals or non-containing {@link Range}, the conjunction becomes false.
963
- * When encountering a containing {@link Range}, the range gets eliminated by the equality.
962
+ * Propagate Equals to eliminate conjuncted Ranges or BinaryComparisons.
963
+ * When encountering a different Equals, non-containing {@link Range} or {@link BinaryComparison}, the conjunction becomes false.
964
+ * When encountering a containing {@link Range}, {@link BinaryComparison} or {@link NotEquals}, these get eliminated by the equality.
965
+ *
966
+ * Since this rule can eliminate Ranges and BinaryComparisons, it should be applied before {@link CombineBinaryComparisons}.
964
967
*
965
968
* This rule doesn't perform any promotion of {@link BinaryComparison}s, that is handled by
966
969
* {@link CombineBinaryComparisons} on purpose as the resulting Range might be foldable
@@ -976,14 +979,20 @@ static class PropagateEquals extends OptimizerExpressionRule {
976
979
protected Expression rule (Expression e ) {
977
980
if (e instanceof And ) {
978
981
return propagate ((And ) e );
982
+ } else if (e instanceof Or ) {
983
+ return propagate ((Or ) e );
979
984
}
980
985
return e ;
981
986
}
982
987
983
988
// combine conjunction
984
989
private Expression propagate (And and ) {
985
990
List <Range > ranges = new ArrayList <>();
991
+ // Only equalities, not-equalities and inequalities with a foldable .right are extracted separately;
992
+ // the others go into the general 'exps'.
986
993
List <BinaryComparison > equals = new ArrayList <>();
994
+ List <NotEquals > notEquals = new ArrayList <>();
995
+ List <BinaryComparison > inequalities = new ArrayList <>();
987
996
List <Expression > exps = new ArrayList <>();
988
997
989
998
boolean changed = false ;
@@ -996,35 +1005,42 @@ private Expression propagate(And and) {
996
1005
// equals on different values evaluate to FALSE
997
1006
if (otherEq .right ().foldable ()) {
998
1007
for (BinaryComparison eq : equals ) {
999
- // cannot evaluate equals so skip it
1000
- if (!eq .right ().foldable ()) {
1001
- continue ;
1002
- }
1003
1008
if (otherEq .left ().semanticEquals (eq .left ())) {
1004
- if (eq .right ().foldable () && otherEq .right ().foldable ()) {
1005
- Integer comp = BinaryComparison .compare (eq .right ().fold (), otherEq .right ().fold ());
1006
- if (comp != null ) {
1007
- // var cannot be equal to two different values at the same time
1008
- if (comp != 0 ) {
1009
- return FALSE ;
1010
- }
1009
+ Integer comp = BinaryComparison .compare (eq .right ().fold (), otherEq .right ().fold ());
1010
+ if (comp != null ) {
1011
+ // var cannot be equal to two different values at the same time
1012
+ if (comp != 0 ) {
1013
+ return FALSE ;
1011
1014
}
1012
1015
}
1013
1016
}
1014
1017
}
1018
+ equals .add (otherEq );
1019
+ } else {
1020
+ exps .add (otherEq );
1021
+ }
1022
+ } else if (ex instanceof GreaterThan || ex instanceof GreaterThanOrEqual ||
1023
+ ex instanceof LessThan || ex instanceof LessThanOrEqual ) {
1024
+ BinaryComparison bc = (BinaryComparison ) ex ;
1025
+ if (bc .right ().foldable ()) {
1026
+ inequalities .add (bc );
1027
+ } else {
1028
+ exps .add (ex );
1029
+ }
1030
+ } else if (ex instanceof NotEquals ) {
1031
+ NotEquals otherNotEq = (NotEquals ) ex ;
1032
+ if (otherNotEq .right ().foldable ()) {
1033
+ notEquals .add (otherNotEq );
1034
+ } else {
1035
+ exps .add (ex );
1015
1036
}
1016
- equals .add (otherEq );
1017
1037
} else {
1018
1038
exps .add (ex );
1019
1039
}
1020
1040
}
1021
1041
1022
1042
// check
1023
1043
for (BinaryComparison eq : equals ) {
1024
- // cannot evaluate equals so skip it
1025
- if (!eq .right ().foldable ()) {
1026
- continue ;
1027
- }
1028
1044
Object eqValue = eq .right ().fold ();
1029
1045
1030
1046
for (int i = 0 ; i < ranges .size (); i ++) {
@@ -1060,9 +1076,192 @@ private Expression propagate(And and) {
1060
1076
changed = true ;
1061
1077
}
1062
1078
}
1079
+
1080
+ // evaluate all NotEquals against the Equal
1081
+ for (Iterator <NotEquals > iter = notEquals .iterator (); iter .hasNext (); ) {
1082
+ NotEquals neq = iter .next ();
1083
+ if (eq .left ().semanticEquals (neq .left ())) {
1084
+ Integer comp = BinaryComparison .compare (eqValue , neq .right ().fold ());
1085
+ if (comp != null ) {
1086
+ if (comp == 0 ) {
1087
+ return FALSE ; // clashing and conflicting: a = 1 AND a != 1
1088
+ } else {
1089
+ iter .remove (); // clashing and redundant: a = 1 AND a != 2
1090
+ changed = true ;
1091
+ }
1092
+ }
1093
+ }
1094
+ }
1095
+
1096
+ // evaluate all inequalities against the Equal
1097
+ for (Iterator <BinaryComparison > iter = inequalities .iterator (); iter .hasNext (); ) {
1098
+ BinaryComparison bc = iter .next ();
1099
+ if (eq .left ().semanticEquals (bc .left ())) {
1100
+ Integer compare = BinaryComparison .compare (eqValue , bc .right ().fold ());
1101
+ if (compare != null ) {
1102
+ if (bc instanceof LessThan || bc instanceof LessThanOrEqual ) { // a = 2 AND a </<= ?
1103
+ if ((compare == 0 && bc instanceof LessThan ) || // a = 2 AND a < 2
1104
+ 0 < compare ) { // a = 2 AND a </<= 1
1105
+ return FALSE ;
1106
+ }
1107
+ } else if (bc instanceof GreaterThan || bc instanceof GreaterThanOrEqual ) { // a = 2 AND a >/>= ?
1108
+ if ((compare == 0 && bc instanceof GreaterThan ) || // a = 2 AND a > 2
1109
+ compare < 0 ) { // a = 2 AND a >/>= 3
1110
+ return FALSE ;
1111
+ }
1112
+ }
1113
+
1114
+ iter .remove ();
1115
+ changed = true ;
1116
+ }
1117
+ }
1118
+ }
1119
+ }
1120
+
1121
+ return changed ? Predicates .combineAnd (CollectionUtils .combine (exps , equals , notEquals , inequalities , ranges )) : and ;
1122
+ }
1123
+
1124
+ // combine disjunction:
1125
+ // a = 2 OR a > 3 -> nop; a = 2 OR a > 1 -> a > 1
1126
+ // a = 2 OR a < 3 -> a < 3; a = 2 OR a < 1 -> nop
1127
+ // a = 2 OR 3 < a < 5 -> nop; a = 2 OR 1 < a < 3 -> 1 < a < 3; a = 2 OR 0 < a < 1 -> nop
1128
+ // a = 2 OR a != 2 -> TRUE; a = 2 OR a = 5 -> nop; a = 2 OR a != 5 -> a != 5
1129
+ private Expression propagate (Or or ) {
1130
+ List <Expression > exps = new ArrayList <>();
1131
+ List <Equals > equals = new ArrayList <>(); // foldable right term Equals
1132
+ List <NotEquals > notEquals = new ArrayList <>(); // foldable right term NotEquals
1133
+ List <Range > ranges = new ArrayList <>();
1134
+ List <BinaryComparison > inequalities = new ArrayList <>(); // foldable right term (=limit) BinaryComparision
1135
+
1136
+ // split expressions by type
1137
+ for (Expression ex : Predicates .splitOr (or )) {
1138
+ if (ex instanceof Equals ) {
1139
+ Equals eq = (Equals ) ex ;
1140
+ if (eq .right ().foldable ()) {
1141
+ equals .add (eq );
1142
+ } else {
1143
+ exps .add (ex );
1144
+ }
1145
+ } else if (ex instanceof NotEquals ) {
1146
+ NotEquals neq = (NotEquals ) ex ;
1147
+ if (neq .right ().foldable ()) {
1148
+ notEquals .add (neq );
1149
+ } else {
1150
+ exps .add (ex );
1151
+ }
1152
+ } else if (ex instanceof Range ) {
1153
+ ranges .add ((Range ) ex );
1154
+ } else if (ex instanceof BinaryComparison ) {
1155
+ BinaryComparison bc = (BinaryComparison ) ex ;
1156
+ if (bc .right ().foldable ()) {
1157
+ inequalities .add (bc );
1158
+ } else {
1159
+ exps .add (ex );
1160
+ }
1161
+ } else {
1162
+ exps .add (ex );
1163
+ }
1164
+ }
1165
+
1166
+ boolean updated = false ; // has the expression been modified?
1167
+
1168
+ // evaluate the impact of each Equal over the different types of Expressions
1169
+ for (Iterator <Equals > iterEq = equals .iterator (); iterEq .hasNext (); ) {
1170
+ Equals eq = iterEq .next ();
1171
+ Object eqValue = eq .right ().fold ();
1172
+ boolean removeEquals = false ;
1173
+
1174
+ // Equals OR NotEquals
1175
+ for (NotEquals neq : notEquals ) {
1176
+ if (eq .left ().semanticEquals (neq .left ())) { // a = 2 OR a != ? -> ...
1177
+ Integer comp = BinaryComparison .compare (eqValue , neq .right ().fold ());
1178
+ if (comp != null ) {
1179
+ if (comp == 0 ) { // a = 2 OR a != 2 -> TRUE
1180
+ return TRUE ;
1181
+ } else { // a = 2 OR a != 5 -> a != 5
1182
+ removeEquals = true ;
1183
+ break ;
1184
+ }
1185
+ }
1186
+ }
1187
+ }
1188
+ if (removeEquals ) {
1189
+ iterEq .remove ();
1190
+ updated = true ;
1191
+ continue ;
1192
+ }
1193
+
1194
+ // Equals OR Range
1195
+ for (int i = 0 ; i < ranges .size (); i ++) { // might modify list, so use index loop
1196
+ Range range = ranges .get (i );
1197
+ if (eq .left ().semanticEquals (range .value ())) {
1198
+ Integer lowerComp = range .lower ().foldable () ? BinaryComparison .compare (eqValue , range .lower ().fold ()) : null ;
1199
+ Integer upperComp = range .upper ().foldable () ? BinaryComparison .compare (eqValue , range .upper ().fold ()) : null ;
1200
+
1201
+ if (lowerComp != null && lowerComp == 0 ) {
1202
+ if (!range .includeLower ()) { // a = 2 OR 2 < a < ? -> 2 <= a < ?
1203
+ ranges .set (i , new Range (range .source (), range .value (), range .lower (), true ,
1204
+ range .upper (), range .includeUpper ()));
1205
+ } // else : a = 2 OR 2 <= a < ? -> 2 <= a < ?
1206
+ removeEquals = true ; // update range with lower equality instead or simply superfluous
1207
+ break ;
1208
+ } else if (upperComp != null && upperComp == 0 ) {
1209
+ if (!range .includeUpper ()) { // a = 2 OR ? < a < 2 -> ? < a <= 2
1210
+ ranges .set (i , new Range (range .source (), range .value (), range .lower (), range .includeLower (),
1211
+ range .upper (), true ));
1212
+ } // else : a = 2 OR ? < a <= 2 -> ? < a <= 2
1213
+ removeEquals = true ; // update range with upper equality instead
1214
+ break ;
1215
+ } else if (lowerComp != null && upperComp != null ) {
1216
+ if (0 < lowerComp && upperComp < 0 ) { // a = 2 OR 1 < a < 3
1217
+ removeEquals = true ; // equality is superfluous
1218
+ break ;
1219
+ }
1220
+ }
1221
+ }
1222
+ }
1223
+ if (removeEquals ) {
1224
+ iterEq .remove ();
1225
+ updated = true ;
1226
+ continue ;
1227
+ }
1228
+
1229
+ // Equals OR Inequality
1230
+ for (int i = 0 ; i < inequalities .size (); i ++) {
1231
+ BinaryComparison bc = inequalities .get (i );
1232
+ if (eq .left ().semanticEquals (bc .left ())) {
1233
+ Integer comp = BinaryComparison .compare (eqValue , bc .right ().fold ());
1234
+ if (comp != null ) {
1235
+ if (bc instanceof GreaterThan || bc instanceof GreaterThanOrEqual ) {
1236
+ if (comp < 0 ) { // a = 1 OR a > 2 -> nop
1237
+ continue ;
1238
+ } else if (comp == 0 && bc instanceof GreaterThan ) { // a = 2 OR a > 2 -> a >= 2
1239
+ inequalities .set (i , new GreaterThanOrEqual (bc .source (), bc .left (), bc .right ()));
1240
+ } // else (0 < comp || bc instanceof GreaterThanOrEqual) :
1241
+ // a = 3 OR a > 2 -> a > 2; a = 2 OR a => 2 -> a => 2
1242
+
1243
+ removeEquals = true ; // update range with equality instead or simply superfluous
1244
+ break ;
1245
+ } else if (bc instanceof LessThan || bc instanceof LessThanOrEqual ) {
1246
+ if (comp > 0 ) { // a = 2 OR a < 1 -> nop
1247
+ continue ;
1248
+ }
1249
+ if (comp == 0 && bc instanceof LessThan ) { // a = 2 OR a < 2 -> a <= 2
1250
+ inequalities .set (i , new LessThanOrEqual (bc .source (), bc .left (), bc .right ()));
1251
+ } // else (comp < 0 || bc instanceof LessThanOrEqual) : a = 2 OR a < 3 -> a < 3; a = 2 OR a <= 2 -> a <= 2
1252
+ removeEquals = true ; // update range with equality instead or simply superfluous
1253
+ break ;
1254
+ }
1255
+ }
1256
+ }
1257
+ }
1258
+ if (removeEquals ) {
1259
+ iterEq .remove ();
1260
+ updated = true ;
1261
+ }
1063
1262
}
1064
1263
1065
- return changed ? Predicates .combineAnd (CollectionUtils .combine (exps , equals , ranges )) : and ;
1264
+ return updated ? Predicates .combineOr (CollectionUtils .combine (exps , equals , notEquals , inequalities , ranges )) : or ;
1066
1265
}
1067
1266
}
1068
1267
0 commit comments