File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\expression\Comparison.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.expression;
8
9 import java.util.Arrays;
10 import org.h2.constant.SysProperties;
11 import org.h2.engine.Database;
12 import org.h2.engine.Session;
13 import org.h2.index.IndexCondition;
14 import org.h2.message.DbException;
15 import org.h2.table.ColumnResolver;
16 import org.h2.table.TableFilter;
17 import org.h2.util.New;
18 import org.h2.value.Value;
19 import org.h2.value.ValueBoolean;
20 import org.h2.value.ValueNull;
21
22 /**
23 * Example comparison expressions are ID=1, NAME=NAME, NAME IS NULL.
24 */
25 public class Comparison extends Condition {
26
27 /**
28 * This is a flag meaning the comparison is null safe (meaning never returns
29 * NULL even if one operand is NULL). Only EQUAL and NOT_EQUAL are supported
30 * currently.
31 */
32 public static final int NULL_SAFE = 16;
33
34 /**
35 * The comparison type meaning = as in ID=1.
36 */
37 public static final int EQUAL = 0;
38
39 /**
40 * The comparison type meaning ID IS 1 (ID IS NOT DISTINCT FROM 1).
41 */
42 public static final int EQUAL_NULL_SAFE = EQUAL | NULL_SAFE;
43
44 /**
45 * The comparison type meaning &gt;= as in ID&gt;=1.
46 */
47 public static final int BIGGER_EQUAL = 1;
48
49 /**
50 * The comparison type meaning &gt; as in ID&gt;1.
51 */
52 public static final int BIGGER = 2;
53
54 /**
55 * The comparison type meaning &lt;= as in ID&lt;=1.
56 */
57 public static final int SMALLER_EQUAL = 3;
58
59 /**
60 * The comparison type meaning &lt; as in ID&lt;1.
61 */
62 public static final int SMALLER = 4;
63
64 /**
65 * The comparison type meaning &lt;&gt; as in ID&lt;&gt;1.
66 */
67 public static final int NOT_EQUAL = 5;
68
69 /**
70 * The comparison type meaning ID IS NOT 1 (ID IS DISTINCT FROM 1).
71 */
72 public static final int NOT_EQUAL_NULL_SAFE = NOT_EQUAL | NULL_SAFE;
73
74 /**
75 * The comparison type meaning IS NULL as in NAME IS NULL.
76 */
77 public static final int IS_NULL = 6;
78
79 /**
80 * The comparison type meaning IS NOT NULL as in NAME IS NOT NULL.
81 */
82 public static final int IS_NOT_NULL = 7;
83
84 /**
85 * This is a pseudo comparison type that is only used for index conditions.
86 * It means the comparison will always yield FALSE. Example: 1=0.
87 */
88 public static final int FALSE = 8;
89
90 /**
91 * This is a pseudo comparison type that is only used for index conditions.
92 * It means equals any value of a list. Example: IN(1, 2, 3).
93 */
94 public static final int IN_LIST = 9;
95
96 /**
97 * This is a pseudo comparison type that is only used for index conditions.
98 * It means equals any value of a list. Example: IN(SELECT ...).
99 */
100 public static final int IN_QUERY = 10;
101
102 private final Database database;
103 private int compareType;
104 private Expression left;
105 private Expression right;
106
107 public Comparison(Session session, int compareType, Expression left, Expression right) {
108 this.database = session.getDatabase();
109 this.left = left;
110 this.right = right;
111 this.compareType = compareType;
112 }
113
114 public String getSQL() {
115 String sql;
116 switch (compareType) {
117 case IS_NULL:
118 sql = left.getSQL() + " IS NULL";
119 break;
120 case IS_NOT_NULL:
121 sql = left.getSQL() + " IS NOT NULL";
122 break;
123 default:
124 sql = left.getSQL() + " " + getCompareOperator(compareType) + " " + right.getSQL();
125 }
126 return "(" + sql + ")";
127 }
128
129 /**
130 * Get the comparison operator string ("=", ">",...).
131 *
132 * @param compareType the compare type
133 * @return the string
134 */
135 static String getCompareOperator(int compareType) {
136 switch (compareType) {
137 case EQUAL:
138 return "=";
139 case EQUAL_NULL_SAFE:
140 return "IS";
141 case BIGGER_EQUAL:
142 return ">=";
143 case BIGGER:
144 return ">";
145 case SMALLER_EQUAL:
146 return "<=";
147 case SMALLER:
148 return "<";
149 case NOT_EQUAL:
150 return "<>";
151 case NOT_EQUAL_NULL_SAFE:
152 return "IS NOT";
153 default:
154 throw DbException.throwInternalError("compareType=" + compareType);
155 }
156 }
157
158 public Expression optimize(Session session) {
159 left = left.optimize(session);
160 if (right != null) {
161 right = right.optimize(session);
162 if (right instanceof ExpressionColumn) {
163 if (left.isConstant() || left instanceof Parameter) {
164 Expression temp = left;
165 left = right;
166 right = temp;
167 compareType = getReversedCompareType(compareType);
168 }
169 }
170 if (left instanceof ExpressionColumn) {
171 if (right.isConstant()) {
172 Value r = right.getValue(session);
173 if (r == ValueNull.INSTANCE) {
174 if ((compareType & NULL_SAFE) == 0) {
175 return ValueExpression.getNull();
176 }
177 }
178 } else if (right instanceof Parameter) {
179 ((Parameter) right).setColumn(((ExpressionColumn) left).getColumn());
180 }
181 }
182 }
183 if (compareType == IS_NULL || compareType == IS_NOT_NULL) {
184 if (left.isConstant()) {
185 return ValueExpression.get(getValue(session));
186 }
187 } else {
188 if (SysProperties.CHECK && (left == null || right == null)) {
189 DbException.throwInternalError();
190 }
191 if (left == ValueExpression.getNull() || right == ValueExpression.getNull()) {
192 // TODO NULL handling: maybe issue a warning when comparing with
193 // a NULL constants
194 if ((compareType & NULL_SAFE) == 0) {
195 return ValueExpression.getNull();
196 }
197 }
198 if (left.isConstant() && right.isConstant()) {
199 return ValueExpression.get(getValue(session));
200 }
201 }
202 return this;
203 }
204
205 public Value getValue(Session session) {
206 Value l = left.getValue(session);
207 if (right == null) {
208 boolean result;
209 switch (compareType) {
210 case IS_NULL:
211 result = l == ValueNull.INSTANCE;
212 break;
213 case IS_NOT_NULL:
214 result = !(l == ValueNull.INSTANCE);
215 break;
216 default:
217 throw DbException.throwInternalError("type=" + compareType);
218 }
219 return ValueBoolean.get(result);
220 }
221 if (l == ValueNull.INSTANCE) {
222 if ((compareType & NULL_SAFE) == 0) {
223 return ValueNull.INSTANCE;
224 }
225 }
226 Value r = right.getValue(session);
227 if (r == ValueNull.INSTANCE) {
228 if ((compareType & NULL_SAFE) == 0) {
229 return ValueNull.INSTANCE;
230 }
231 }
232 int dataType = Value.getHigherOrder(left.getType(), right.getType());
233 l = l.convertTo(dataType);
234 r = r.convertTo(dataType);
235 boolean result = compareNotNull(database, l, r, compareType);
236 return ValueBoolean.get(result);
237 }
238
239 /**
240 * Compare two values, given the values are not NULL.
241 *
242 * @param database the database
243 * @param l the first value
244 * @param r the second value
245 * @param compareType the compare type
246 * @return the result of the comparison (1 if the first value is bigger, -1
247 * if smaller, 0 if both are equal)
248 */
249 static boolean compareNotNull(Database database, Value l, Value r, int compareType) {
250 boolean result;
251 switch (compareType) {
252 case EQUAL:
253 case EQUAL_NULL_SAFE:
254 result = database.areEqual(l, r);
255 break;
256 case NOT_EQUAL:
257 case NOT_EQUAL_NULL_SAFE:
258 result = !database.areEqual(l, r);
259 break;
260 case BIGGER_EQUAL:
261 result = database.compare(l, r) >= 0;
262 break;
263 case BIGGER:
264 result = database.compare(l, r) > 0;
265 break;
266 case SMALLER_EQUAL:
267 result = database.compare(l, r) <= 0;
268 break;
269 case SMALLER:
270 result = database.compare(l, r) < 0;
271 break;
272 default:
273 throw DbException.throwInternalError("type=" + compareType);
274 }
275 return result;
276 }
277
278 private int getReversedCompareType(int type) {
279 switch (compareType) {
280 case EQUAL:
281 case EQUAL_NULL_SAFE:
282 case NOT_EQUAL:
283 case NOT_EQUAL_NULL_SAFE:
284 return type;
285 case BIGGER_EQUAL:
286 return SMALLER_EQUAL;
287 case BIGGER:
288 return SMALLER;
289 case SMALLER_EQUAL:
290 return BIGGER_EQUAL;
291 case SMALLER:
292 return BIGGER;
293 default:
294 throw DbException.throwInternalError("type=" + compareType);
295 }
296 }
297
298 private int getNotCompareType() {
299 switch (compareType) {
300 case EQUAL:
301 return NOT_EQUAL;
302 case EQUAL_NULL_SAFE:
303 return NOT_EQUAL_NULL_SAFE;
304 case NOT_EQUAL:
305 return EQUAL;
306 case NOT_EQUAL_NULL_SAFE:
307 return EQUAL_NULL_SAFE;
308 case BIGGER_EQUAL:
309 return SMALLER;
310 case BIGGER:
311 return SMALLER_EQUAL;
312 case SMALLER_EQUAL:
313 return BIGGER;
314 case SMALLER:
315 return BIGGER_EQUAL;
316 case IS_NULL:
317 return IS_NOT_NULL;
318 case IS_NOT_NULL:
319 return IS_NULL;
320 default:
321 throw DbException.throwInternalError("type=" + compareType);
322 }
323 }
324
325 public Expression getNotIfPossible(Session session) {
326 int type = getNotCompareType();
327 return new Comparison(session, type, left, right);
328 }
329
330 public void createIndexConditions(Session session, TableFilter filter) {
331 ExpressionColumn l = null;
332 if (left instanceof ExpressionColumn) {
333 l = (ExpressionColumn) left;
334 if (filter != l.getTableFilter()) {
335 l = null;
336 }
337 }
338 if (right == null) {
339 if (l != null) {
340 switch (compareType) {
341 case IS_NULL:
342 if (session.getDatabase().getSettings().optimizeIsNull) {
343 filter.addIndexCondition(IndexCondition.get(Comparison.EQUAL_NULL_SAFE, l, ValueExpression.getNull()));
344 }
345 }
346 }
347 return;
348 }
349 ExpressionColumn r = null;
350 if (right instanceof ExpressionColumn) {
351 r = (ExpressionColumn) right;
352 if (filter != r.getTableFilter()) {
353 r = null;
354 }
355 }
356 // one side must be from the current filter
357 if (l == null && r == null) {
358 return;
359 }
360 if (l != null && r != null) {
361 return;
362 }
363 if (l == null) {
364 ExpressionVisitor visitor = ExpressionVisitor.getNotFromResolverVisitor(filter);
365 if (!left.isEverything(visitor)) {
366 return;
367 }
368 } else if (r == null) {
369 ExpressionVisitor visitor = ExpressionVisitor.getNotFromResolverVisitor(filter);
370 if (!right.isEverything(visitor)) {
371 return;
372 }
373 } else {
374 // if both sides are part of the same filter, it can't be used for
375 // index lookup
376 return;
377 }
378 boolean addIndex;
379 switch (compareType) {
380 case NOT_EQUAL:
381 case NOT_EQUAL_NULL_SAFE:
382 addIndex = false;
383 break;
384 case EQUAL:
385 case EQUAL_NULL_SAFE:
386 case BIGGER:
387 case BIGGER_EQUAL:
388 case SMALLER_EQUAL:
389 case SMALLER:
390 addIndex = true;
391 break;
392 default:
393 throw DbException.throwInternalError("type=" + compareType);
394 }
395 if (addIndex) {
396 if (l != null) {
397 filter.addIndexCondition(IndexCondition.get(compareType, l, right));
398 } else if (r != null) {
399 int compareRev = getReversedCompareType(compareType);
400 filter.addIndexCondition(IndexCondition.get(compareRev, r, left));
401 }
402 }
403 }
404
405 public void setEvaluatable(TableFilter tableFilter, boolean b) {
406 left.setEvaluatable(tableFilter, b);
407 if (right != null) {
408 right.setEvaluatable(tableFilter, b);
409 }
410 }
411
412 public void updateAggregate(Session session) {
413 left.updateAggregate(session);
414 if (right != null) {
415 right.updateAggregate(session);
416 }
417 }
418
419 public void addFilterConditions(TableFilter filter, boolean outerJoin) {
420 if (compareType == IS_NULL && outerJoin) {
421 // can not optimize:
422 // select * from test t1 left join test t2 on t1.id = t2.id where t2.id is null
423 // to
424 // select * from test t1 left join test t2 on t1.id = t2.id and t2.id is null
425 return;
426 }
427 super.addFilterConditions(filter, outerJoin);
428 }
429
430 public void mapColumns(ColumnResolver resolver, int level) {
431 left.mapColumns(resolver, level);
432 if (right != null) {
433 right.mapColumns(resolver, level);
434 }
435 }
436
437 public boolean isEverything(ExpressionVisitor visitor) {
438 return left.isEverything(visitor) && (right == null || right.isEverything(visitor));
439 }
440
441 public int getCost() {
442 return left.getCost() + (right == null ? 0 : right.getCost()) + 1;
443 }
444
445 /**
446 * Get the other expression if this is an equals comparison and the other
447 * expression matches.
448 *
449 * @param match the expression that should match
450 * @return null if no match, the other expression if there is a match
451 */
452 Expression getIfEquals(Expression match) {
453 if (compareType == EQUAL) {
454 String sql = match.getSQL();
455 if (left.getSQL().equals(sql)) {
456 return right;
457 } else if (right.getSQL().equals(sql)) {
458 return left;
459 }
460 }
461 return null;
462 }
463
464 /**
465 * Get an additional condition if possible. Example: given two conditions
466 * A=B AND B=C, the new condition A=C is returned. Given the two conditions
467 * A=1 OR A=2, the new condition A IN(1, 2) is returned.
468 *
469 * @param session the session
470 * @param other the second condition
471 * @param and true for AND, false for OR
472 * @return null or the third condition
473 */
474 Expression getAdditional(Session session, Comparison other, boolean and) {
475 if (compareType == other.compareType && compareType == EQUAL) {
476 boolean lc = left.isConstant(), rc = right.isConstant();
477 boolean l2c = other.left.isConstant(), r2c = other.right.isConstant();
478 String l = left.getSQL();
479 String l2 = other.left.getSQL();
480 String r = right.getSQL();
481 String r2 = other.right.getSQL();
482 if (and) {
483 // a=b AND a=c
484 // must not compare constants. example: NOT(B=2 AND B=3)
485 if (!(rc && r2c) && l.equals(l2)) {
486 return new Comparison(session, EQUAL, right, other.right);
487 } else if (!(rc && l2c) && l.equals(r2)) {
488 return new Comparison(session, EQUAL, right, other.left);
489 } else if (!(lc && r2c) && r.equals(l2)) {
490 return new Comparison(session, EQUAL, left, other.right);
491 } else if (!(lc && l2c) && r.equals(r2)) {
492 return new Comparison(session, EQUAL, left, other.left);
493 }
494 } else {
495 // a=b OR a=c
496 Database db = session.getDatabase();
497 if (rc && r2c && l.equals(l2)) {
498 return new ConditionIn(db, left, New.arrayList(Arrays.asList(right, other.right)));
499 } else if (rc && l2c && l.equals(r2)) {
500 return new ConditionIn(db, left, New.arrayList(Arrays.asList(right, other.left)));
501 } else if (lc && r2c && r.equals(l2)) {
502 return new ConditionIn(db, right, New.arrayList(Arrays.asList(left, other.right)));
503 } else if (lc && l2c && r.equals(r2)) {
504 return new ConditionIn(db, right, New.arrayList(Arrays.asList(left, other.left)));
505 }
506 }
507 }
508 return null;
509 }
510
511 /**
512 * Get the left or the right sub-expression of this condition.
513 *
514 * @param getLeft true to get the left sub-expression, false to get the right
515 * sub-expression.
516 * @return the sub-expression
517 */
518 public Expression getExpression(boolean getLeft) {
519 return getLeft ? this.left : right;
520 }
521
522 }
523