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.command.dml.Select;
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.Session;
013: import org.h2.message.Message;
014: import org.h2.table.ColumnResolver;
015: import org.h2.table.TableFilter;
016: import org.h2.value.Value;
017: import org.h2.value.ValueBoolean;
018: import org.h2.value.ValueNull;
019:
020: /**
021: * An 'and' or 'or' condition as in WHERE ID=1 AND NAME=?
022: */
023: public class ConditionAndOr extends Condition {
024:
025: public static final int AND = 0, OR = 1;
026:
027: private final int andOrType;
028: private Expression left, right;
029:
030: public ConditionAndOr(int andOrType, Expression left,
031: Expression right) {
032: this .andOrType = andOrType;
033: this .left = left;
034: this .right = right;
035: if (SysProperties.CHECK && (left == null || right == null)) {
036: throw Message.getInternalError();
037: }
038: }
039:
040: public String getSQL() {
041: String sql;
042: switch (andOrType) {
043: case AND:
044: sql = left.getSQL() + " AND " + right.getSQL();
045: break;
046: case OR:
047: sql = left.getSQL() + " OR " + right.getSQL();
048: break;
049: default:
050: throw Message.getInternalError("andOrType=" + andOrType);
051: }
052: return "(" + sql + ")";
053: }
054:
055: public void createIndexConditions(Session session,
056: TableFilter filter) throws SQLException {
057: if (andOrType == AND) {
058: left.createIndexConditions(session, filter);
059: right.createIndexConditions(session, filter);
060: }
061: }
062:
063: public Expression getNotIfPossible(Session session) {
064: // (NOT (A OR B)): (NOT(A) AND NOT(B))
065: // (NOT (A AND B)): (NOT(A) OR NOT(B))
066: Expression l = left.getNotIfPossible(session);
067: if (l == null) {
068: l = new ConditionNot(left);
069: }
070: Expression r = right.getNotIfPossible(session);
071: if (r == null) {
072: r = new ConditionNot(right);
073: }
074: int reversed = andOrType == AND ? OR : AND;
075: return new ConditionAndOr(reversed, l, r);
076: }
077:
078: public Value getValue(Session session) throws SQLException {
079: Value l = left.getValue(session);
080: Value r;
081: switch (andOrType) {
082: case AND: {
083: if (Boolean.FALSE.equals(l.getBoolean())) {
084: return l;
085: }
086: r = right.getValue(session);
087: if (Boolean.FALSE.equals(r.getBoolean())) {
088: return r;
089: }
090: if (l == ValueNull.INSTANCE) {
091: return l;
092: }
093: if (r == ValueNull.INSTANCE) {
094: return r;
095: }
096: return ValueBoolean.get(true);
097: }
098: case OR: {
099: if (Boolean.TRUE.equals(l.getBoolean())) {
100: return l;
101: }
102: r = right.getValue(session);
103: if (Boolean.TRUE.equals(r.getBoolean())) {
104: return r;
105: }
106: if (l == ValueNull.INSTANCE) {
107: return l;
108: }
109: if (r == ValueNull.INSTANCE) {
110: return r;
111: }
112: return ValueBoolean.get(false);
113: }
114: default:
115: throw Message.getInternalError("type=" + andOrType);
116: }
117: }
118:
119: public Expression optimize(Session session) throws SQLException {
120: // TODO NULL: see wikipedia,
121: // http://www-cs-students.stanford.edu/~wlam/compsci/sqlnulls
122: // TODO test if all optimizations are switched off against all on
123: // (including performance)
124: left = left.optimize(session);
125: right = right.optimize(session);
126: int lc = left.getCost(), rc = right.getCost();
127: if (rc < lc) {
128: Expression t = left;
129: left = right;
130: right = t;
131: }
132: // TODO optimization: convert ((A=1 AND B=2) OR (A=1 AND B=3)) to
133: // (A=1 AND (B=2 OR B=3))
134: // this optimization does not work in the following case,
135: // but NOT is optimized before:
136: // CREATE TABLE TEST(A INT, B INT);
137: // INSERT INTO TEST VALUES(1, NULL);
138: // SELECT * FROM TEST WHERE NOT (B=A AND B=0); // no rows
139: // SELECT * FROM TEST WHERE NOT (B=A AND B=0 AND A=0); // 1, NULL
140: if (SysProperties.OPTIMIZE_NOT
141: && SysProperties.OPTIMIZE_TWO_EQUALS
142: && andOrType == AND) {
143: // try to add conditions (A=B AND B=1: add A=1)
144: if (left instanceof Comparison
145: && right instanceof Comparison) {
146: Comparison compLeft = (Comparison) left;
147: Comparison compRight = (Comparison) right;
148: Expression added = compLeft.getAdditional(session,
149: compRight);
150: if (added != null) {
151: added = added.optimize(session);
152: ConditionAndOr a = new ConditionAndOr(AND, this ,
153: added);
154: return a;
155: }
156: }
157: }
158: Value l = left.isConstant() ? left.getValue(session) : null;
159: Value r = right.isConstant() ? right.getValue(session) : null;
160: if (l == null && r == null) {
161: return this ;
162: }
163: if (l != null && r != null) {
164: return ValueExpression.get(getValue(session));
165: }
166: switch (andOrType) {
167: case AND:
168: if (l != null) {
169: if (Boolean.FALSE.equals(l.getBoolean())) {
170: return ValueExpression.get(l);
171: } else if (Boolean.TRUE.equals(l.getBoolean())) {
172: return right;
173: }
174: } else if (r != null) {
175: if (Boolean.FALSE.equals(r.getBoolean())) {
176: return ValueExpression.get(r);
177: } else if (Boolean.TRUE.equals(r.getBoolean())) {
178: return left;
179: }
180: }
181: break;
182: case OR:
183: if (l != null) {
184: if (Boolean.TRUE.equals(l.getBoolean())) {
185: return ValueExpression.get(l);
186: } else if (Boolean.FALSE.equals(l.getBoolean())) {
187: return right;
188: }
189: } else if (r != null) {
190: if (Boolean.TRUE.equals(r.getBoolean())) {
191: return ValueExpression.get(r);
192: } else if (Boolean.FALSE.equals(r.getBoolean())) {
193: return left;
194: }
195: }
196: break;
197: default:
198: throw Message.getInternalError("type=" + andOrType);
199: }
200: return this ;
201: }
202:
203: public void addFilterConditions(TableFilter filter,
204: boolean outerJoin) {
205: if (andOrType == AND) {
206: left.addFilterConditions(filter, outerJoin);
207: right.addFilterConditions(filter, outerJoin);
208: } else {
209: super .addFilterConditions(filter, outerJoin);
210: }
211: }
212:
213: public void mapColumns(ColumnResolver resolver, int level)
214: throws SQLException {
215: left.mapColumns(resolver, level);
216: right.mapColumns(resolver, level);
217: }
218:
219: public void setEvaluatable(TableFilter tableFilter, boolean b) {
220: left.setEvaluatable(tableFilter, b);
221: right.setEvaluatable(tableFilter, b);
222: }
223:
224: public void updateAggregate(Session session) throws SQLException {
225: left.updateAggregate(session);
226: right.updateAggregate(session);
227: }
228:
229: public boolean isEverything(ExpressionVisitor visitor) {
230: return left.isEverything(visitor)
231: && right.isEverything(visitor);
232: }
233:
234: public int getCost() {
235: return left.getCost() + right.getCost();
236: }
237:
238: public Expression optimizeInJoin(Session session, Select select)
239: throws SQLException {
240: if (andOrType == AND) {
241: Expression l = left.optimizeInJoin(session, select);
242: Expression r = right.optimizeInJoin(session, select);
243: if (l != left || r != right) {
244: left = l;
245: right = r;
246: // only optimize again if there was some change
247: // otherwise some expressions are 'over-optimized'
248: return optimize(session);
249: }
250: }
251: return this ;
252: }
253:
254: public Expression getExpression(boolean left) {
255: return left ? this.left : right;
256: }
257:
258: }
|