001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.expression;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.ErrorCode;
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.Database;
013: import org.h2.engine.Session;
014: import org.h2.index.IndexCondition;
015: import org.h2.message.Message;
016: import org.h2.table.ColumnResolver;
017: import org.h2.table.TableFilter;
018: import org.h2.value.Value;
019: import org.h2.value.ValueBoolean;
020: import org.h2.value.ValueNull;
021:
022: /**
023: * Example comparison expressions are ID=1, NAME=NAME, NAME IS NULL.
024: */
025: public class Comparison extends Condition {
026:
027: public static final int EQUAL = 0, BIGGER_EQUAL = 1, BIGGER = 2,
028: SMALLER_EQUAL = 3, SMALLER = 4, NOT_EQUAL = 5, IS_NULL = 6,
029: IS_NOT_NULL = 7;
030:
031: // TODO refactor: comparison: a comparison is never 'false'; the constant is
032: // used only for index conditions
033:
034: public static final int FALSE = 8;
035:
036: private final Database database;
037: private final int compareType;
038: private Expression left;
039: private Expression right;
040: private int dataType = -2;
041:
042: public Comparison(Session session, int compareType,
043: Expression left, Expression right) {
044: this .database = session.getDatabase();
045: this .left = left;
046: this .right = right;
047: this .compareType = compareType;
048: }
049:
050: public String getSQL() {
051: String sql;
052: switch (compareType) {
053: case EQUAL:
054: sql = left.getSQL() + " = " + right.getSQL();
055: break;
056: case BIGGER_EQUAL:
057: sql = left.getSQL() + " >= " + right.getSQL();
058: break;
059: case BIGGER:
060: sql = left.getSQL() + " > " + right.getSQL();
061: break;
062: case SMALLER_EQUAL:
063: sql = left.getSQL() + " <= " + right.getSQL();
064: break;
065: case SMALLER:
066: sql = left.getSQL() + " < " + right.getSQL();
067: break;
068: case NOT_EQUAL:
069: sql = left.getSQL() + " <> " + right.getSQL();
070: break;
071: case IS_NULL:
072: sql = left.getSQL() + " IS NULL";
073: break;
074: case IS_NOT_NULL:
075: sql = left.getSQL() + " IS NOT NULL";
076: break;
077: default:
078: throw Message
079: .getInternalError("compareType=" + compareType);
080: }
081: return "(" + sql + ")";
082: }
083:
084: private Expression getCast(Expression expr, int dataType,
085: long precision, int scale, int displaySize, Session session)
086: throws SQLException {
087: if (expr == ValueExpression.NULL) {
088: return expr;
089: }
090: Function function = Function.getFunction(session.getDatabase(),
091: "CAST");
092: function.setParameter(0, expr);
093: function.setDataType(dataType, precision, scale, displaySize);
094: function.doneWithParameters();
095: return function.optimize(session);
096: }
097:
098: public Expression optimize(Session session) throws SQLException {
099: left = left.optimize(session);
100: if (right == null) {
101: dataType = left.getType();
102: } else {
103: right = right.optimize(session);
104: try {
105: if (left instanceof ExpressionColumn
106: && right.isConstant()) {
107: right = getCast(right, left.getType(), left
108: .getPrecision(), left.getScale(), left
109: .getDisplaySize(), session);
110: } else if (right instanceof ExpressionColumn
111: && left.isConstant()) {
112: left = getCast(left, right.getType(), right
113: .getPrecision(), right.getScale(), right
114: .getDisplaySize(), session);
115: }
116: } catch (SQLException e) {
117: int code = e.getErrorCode();
118: switch (code) {
119: case ErrorCode.NUMERIC_VALUE_OUT_OF_RANGE:
120: // WHERE ID=100000000000
121: return ValueExpression.get(ValueBoolean.get(false));
122: default:
123: throw e;
124: }
125: }
126: int lt = left.getType(), rt = right.getType();
127: if (lt == rt) {
128: if (lt == Value.UNKNOWN) {
129: throw Message.getSQLException(
130: ErrorCode.UNKNOWN_DATA_TYPE_1, getSQL());
131: }
132: dataType = lt;
133: } else {
134: dataType = Value.getHigherOrder(left.getType(), right
135: .getType());
136: long precision = Math.max(left.getPrecision(), right
137: .getPrecision());
138: int scale = Math.max(left.getScale(), right.getScale());
139: int displaySize = Math.max(left.getDisplaySize(), right
140: .getDisplaySize());
141: if (dataType != lt) {
142: left = getCast(left, dataType, precision, scale,
143: displaySize, session);
144: }
145: if (dataType != rt) {
146: right = getCast(right, dataType, precision, scale,
147: displaySize, session);
148: }
149: }
150: }
151: if (compareType == IS_NULL || compareType == IS_NOT_NULL) {
152: if (left.isConstant()) {
153: return ValueExpression.get(getValue(session));
154: }
155: } else {
156: if (SysProperties.CHECK && (left == null || right == null)) {
157: throw Message.getInternalError();
158: }
159: if (left == ValueExpression.NULL
160: || right == ValueExpression.NULL) {
161: // TODO NULL handling: maybe issue a warning when comparing with
162: // a NULL constants
163: return ValueExpression.NULL;
164: }
165: if (left.isConstant() && right.isConstant()) {
166: return ValueExpression.get(getValue(session));
167: }
168: }
169: return this ;
170: }
171:
172: public Value getValue(Session session) throws SQLException {
173: Value l = left.getValue(session);
174: if (right == null) {
175: boolean result;
176: switch (compareType) {
177: case IS_NULL:
178: result = l == ValueNull.INSTANCE;
179: break;
180: case IS_NOT_NULL:
181: result = !(l == ValueNull.INSTANCE);
182: break;
183: default:
184: throw Message.getInternalError("type=" + compareType);
185: }
186: return ValueBoolean.get(result);
187: }
188: l = l.convertTo(dataType);
189: Value r = right.getValue(session).convertTo(dataType);
190: if (l == ValueNull.INSTANCE || r == ValueNull.INSTANCE) {
191: return ValueNull.INSTANCE;
192: }
193: boolean result = compareNotNull(database, l, r, compareType);
194: return ValueBoolean.get(result);
195: }
196:
197: static boolean compareNotNull(Database database, Value l, Value r,
198: int compareType) throws SQLException {
199: boolean result;
200: switch (compareType) {
201: case EQUAL:
202: result = database.areEqual(l, r);
203: break;
204: case NOT_EQUAL:
205: result = !database.areEqual(l, r);
206: break;
207: case BIGGER_EQUAL:
208: result = database.compare(l, r) >= 0;
209: break;
210: case BIGGER:
211: result = database.compare(l, r) > 0;
212: break;
213: case SMALLER_EQUAL:
214: result = database.compare(l, r) <= 0;
215: break;
216: case SMALLER:
217: result = database.compare(l, r) < 0;
218: break;
219: default:
220: throw Message.getInternalError("type=" + compareType);
221: }
222: return result;
223: }
224:
225: private int getReversedCompareType(int type) {
226: switch (compareType) {
227: case EQUAL:
228: case NOT_EQUAL:
229: return type;
230: case BIGGER_EQUAL:
231: return SMALLER_EQUAL;
232: case BIGGER:
233: return SMALLER;
234: case SMALLER_EQUAL:
235: return BIGGER_EQUAL;
236: case SMALLER:
237: return BIGGER;
238: default:
239: throw Message.getInternalError("type=" + compareType);
240: }
241: }
242:
243: private int getNotCompareType(int type) {
244: switch (compareType) {
245: case EQUAL:
246: return NOT_EQUAL;
247: case NOT_EQUAL:
248: return EQUAL;
249: case BIGGER_EQUAL:
250: return SMALLER;
251: case BIGGER:
252: return SMALLER_EQUAL;
253: case SMALLER_EQUAL:
254: return BIGGER;
255: case SMALLER:
256: return BIGGER_EQUAL;
257: case IS_NULL:
258: return IS_NOT_NULL;
259: case IS_NOT_NULL:
260: return IS_NULL;
261: default:
262: throw Message.getInternalError("type=" + compareType);
263: }
264: }
265:
266: public Expression getNotIfPossible(Session session) {
267: int type = getNotCompareType(compareType);
268: return new Comparison(session, type, left, right);
269: }
270:
271: public void createIndexConditions(Session session,
272: TableFilter filter) {
273: if (right == null) {
274: // TODO index usage: IS [NOT] NULL index usage is possible
275: return;
276: }
277: ExpressionColumn l = null;
278: if (left instanceof ExpressionColumn) {
279: l = (ExpressionColumn) left;
280: if (filter != l.getTableFilter()) {
281: l = null;
282: }
283: }
284: ExpressionColumn r = null;
285: if (right instanceof ExpressionColumn) {
286: r = (ExpressionColumn) right;
287: if (filter != r.getTableFilter()) {
288: r = null;
289: }
290: }
291: // one side must be from the current filter
292: if (l == null && r == null) {
293: return;
294: }
295: if (l != null && r != null) {
296: return;
297: }
298: if (l == null) {
299: if (!left.isEverything(ExpressionVisitor
300: .getNotFromResolver(filter))) {
301: return;
302: }
303: } else if (r == null) {
304: if (!right.isEverything(ExpressionVisitor
305: .getNotFromResolver(filter))) {
306: return;
307: }
308: } else {
309: // if both sides are part of the same filter, it can't be used for
310: // index lookup
311: return;
312: }
313: boolean addIndex;
314: switch (compareType) {
315: case NOT_EQUAL:
316: addIndex = false;
317: break;
318: case EQUAL:
319: case BIGGER:
320: case BIGGER_EQUAL:
321: case SMALLER_EQUAL:
322: case SMALLER:
323: addIndex = true;
324: break;
325: default:
326: throw Message.getInternalError("type=" + compareType);
327: }
328: if (addIndex) {
329: if (l != null) {
330: filter.addIndexCondition(new IndexCondition(
331: compareType, l, right));
332: } else if (r != null) {
333: int compareRev = getReversedCompareType(compareType);
334: filter.addIndexCondition(new IndexCondition(compareRev,
335: r, left));
336: }
337: }
338: return;
339: }
340:
341: public void setEvaluatable(TableFilter tableFilter, boolean b) {
342: left.setEvaluatable(tableFilter, b);
343: if (right != null) {
344: right.setEvaluatable(tableFilter, b);
345: }
346: }
347:
348: public void updateAggregate(Session session) throws SQLException {
349: left.updateAggregate(session);
350: if (right != null) {
351: right.updateAggregate(session);
352: }
353: }
354:
355: public void addFilterConditions(TableFilter filter,
356: boolean outerJoin) {
357: if (compareType == IS_NULL && outerJoin) {
358: // can not optimize:
359: // select * from test t1 left join test t2 on t1.id = t2.id where t2.id is null
360: // to
361: // select * from test t1 left join test t2 on t1.id = t2.id and t2.id is null
362: return;
363: }
364: super .addFilterConditions(filter, outerJoin);
365: }
366:
367: public void mapColumns(ColumnResolver resolver, int level)
368: throws SQLException {
369: left.mapColumns(resolver, level);
370: if (right != null) {
371: right.mapColumns(resolver, level);
372: }
373: }
374:
375: public boolean isEverything(ExpressionVisitor visitor) {
376: return left.isEverything(visitor)
377: && (right == null || right.isEverything(visitor));
378: }
379:
380: public int getCost() {
381: return left.getCost() + (right == null ? 0 : right.getCost())
382: + 1;
383: }
384:
385: public Comparison getAdditional(Session session, Comparison other) {
386: if (compareType == other.compareType && compareType == EQUAL) {
387: boolean lc = left.isConstant(), rc = right.isConstant();
388: boolean l2c = other.left.isConstant(), r2c = other.right
389: .isConstant();
390: String l = left.getSQL();
391: String l2 = other.left.getSQL();
392: String r = right.getSQL();
393: String r2 = other.right.getSQL();
394: // must not compare constants. example: NOT(B=2 AND B=3)
395: if (!(rc && r2c) && l.equals(l2)) {
396: return new Comparison(session, EQUAL, right,
397: other.right);
398: } else if (!(rc && l2c) && l.equals(r2)) {
399: return new Comparison(session, EQUAL, right, other.left);
400: } else if (!(lc && r2c) && r.equals(l2)) {
401: return new Comparison(session, EQUAL, left, other.right);
402: } else if (!(lc && l2c) && r.equals(r2)) {
403: return new Comparison(session, EQUAL, left, other.left);
404: }
405: }
406: return null;
407: }
408:
409: public Expression getExpression(boolean left) {
410: return left ? this.left : right;
411: }
412:
413: }
|