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.Constants;
013: import org.h2.engine.Database;
014: import org.h2.engine.Session;
015: import org.h2.index.IndexCondition;
016: import org.h2.schema.Schema;
017: import org.h2.table.Column;
018: import org.h2.table.ColumnResolver;
019: import org.h2.table.FunctionTable;
020: import org.h2.table.TableFilter;
021: import org.h2.util.ObjectArray;
022: import org.h2.value.CompareMode;
023: import org.h2.value.Value;
024: import org.h2.value.ValueBoolean;
025: import org.h2.value.ValueNull;
026:
027: /**
028: * An 'in' condition with a list of values, as in WHERE NAME IN(...)
029: */
030: public class ConditionIn extends Condition {
031:
032: private final Database database;
033: private Expression left;
034: private final ObjectArray values;
035: private Value min, max;
036: private int queryLevel;
037:
038: public ConditionIn(Database database, Expression left,
039: ObjectArray values) {
040: this .database = database;
041: this .left = left;
042: this .values = values;
043: }
044:
045: public Value getValue(Session session) throws SQLException {
046: Value l = left.getValue(session);
047: if (l == ValueNull.INSTANCE) {
048: return l;
049: }
050: boolean result = false;
051: boolean hasNull = false;
052: for (int i = 0; i < values.size(); i++) {
053: Expression e = (Expression) values.get(i);
054: Value r = e.getValue(session);
055: if (r == ValueNull.INSTANCE) {
056: hasNull = true;
057: } else {
058: result = Comparison.compareNotNull(database, l, r,
059: Comparison.EQUAL);
060: if (result) {
061: break;
062: }
063: }
064: }
065: if (!result && hasNull) {
066: return ValueNull.INSTANCE;
067: }
068: return ValueBoolean.get(result);
069: }
070:
071: public void mapColumns(ColumnResolver resolver, int queryLevel)
072: throws SQLException {
073: left.mapColumns(resolver, queryLevel);
074: for (int i = 0; i < values.size(); i++) {
075: Expression e = (Expression) values.get(i);
076: e.mapColumns(resolver, queryLevel);
077: }
078: this .queryLevel = Math.max(queryLevel, this .queryLevel);
079: }
080:
081: public Expression optimize(Session session) throws SQLException {
082: left = left.optimize(session);
083: boolean constant = left.isConstant();
084: if (constant && left == ValueExpression.NULL) {
085: return left;
086: }
087: boolean allValuesConstant = true;
088: for (int i = 0; i < values.size(); i++) {
089: Expression e = (Expression) values.get(i);
090: e = e.optimize(session);
091: if (allValuesConstant && !e.isConstant()) {
092: allValuesConstant = false;
093: }
094: values.set(i, e);
095: }
096: if (constant && allValuesConstant) {
097: return ValueExpression.get(getValue(session));
098: }
099: // TODO optimization: could use index in some cases (sort, use min and max)
100: if (values.size() == 1) {
101: Expression right = (Expression) values.get(0);
102: Expression expr = new Comparison(session, Comparison.EQUAL,
103: left, right);
104: expr = expr.optimize(session);
105: return expr;
106: }
107: if (SysProperties.OPTIMIZE_IN) {
108: int dataType = left.getType();
109: ExpressionVisitor independent = ExpressionVisitor
110: .get(ExpressionVisitor.INDEPENDENT);
111: independent.queryLevel = queryLevel;
112: if (areAllValues(independent)) {
113: if (left instanceof ExpressionColumn) {
114: Column column = ((ExpressionColumn) left)
115: .getColumn();
116: boolean nullable = column.getNullable();
117: CompareMode mode = session.getDatabase()
118: .getCompareMode();
119: for (int i = 0; i < values.size(); i++) {
120: Expression e = (Expression) values.get(i);
121: Value v = e.getValue(session);
122: v = v.convertTo(dataType);
123: values.set(i, ValueExpression.get(v));
124: if (min == null || min.compareTo(v, mode) > 0) {
125: if (v != ValueNull.INSTANCE || nullable) {
126: min = v;
127: }
128: }
129: if (max == null || max.compareTo(v, mode) < 0) {
130: max = v;
131: }
132: }
133: }
134: }
135: }
136: return this ;
137: }
138:
139: public void createIndexConditions(Session session,
140: TableFilter filter) {
141: if (!SysProperties.OPTIMIZE_IN) {
142: return;
143: }
144: if (min == null && max == null) {
145: return;
146: }
147: if (!(left instanceof ExpressionColumn)) {
148: return;
149: }
150: ExpressionColumn l = (ExpressionColumn) left;
151: if (filter != l.getTableFilter()) {
152: return;
153: }
154: filter.addIndexCondition(new IndexCondition(
155: Comparison.BIGGER_EQUAL, l, ValueExpression.get(min)));
156: filter.addIndexCondition(new IndexCondition(
157: Comparison.SMALLER_EQUAL, l, ValueExpression.get(max)));
158: }
159:
160: public void setEvaluatable(TableFilter tableFilter, boolean b) {
161: left.setEvaluatable(tableFilter, b);
162: for (int i = 0; i < values.size(); i++) {
163: Expression e = (Expression) values.get(i);
164: e.setEvaluatable(tableFilter, b);
165: }
166: }
167:
168: public String getSQL() {
169: StringBuffer buff = new StringBuffer("(");
170: buff.append(left.getSQL());
171: buff.append(" IN(");
172: for (int i = 0; i < values.size(); i++) {
173: if (i > 0) {
174: buff.append(", ");
175: }
176: Expression e = (Expression) values.get(i);
177: buff.append(e.getSQL());
178: }
179: buff.append("))");
180: return buff.toString();
181: }
182:
183: public void updateAggregate(Session session) throws SQLException {
184: left.updateAggregate(session);
185: for (int i = 0; i < values.size(); i++) {
186: Expression e = (Expression) values.get(i);
187: e.updateAggregate(session);
188: }
189: }
190:
191: public boolean isEverything(ExpressionVisitor visitor) {
192: if (!left.isEverything(visitor)) {
193: return false;
194: }
195: return areAllValues(visitor);
196: }
197:
198: private boolean areAllValues(ExpressionVisitor visitor) {
199: for (int i = 0; i < values.size(); i++) {
200: Expression e = (Expression) values.get(i);
201: if (!e.isEverything(visitor)) {
202: return false;
203: }
204: }
205: return true;
206: }
207:
208: public int getCost() {
209: int cost = left.getCost();
210: for (int i = 0; i < values.size(); i++) {
211: Expression e = (Expression) values.get(i);
212: cost += e.getCost();
213: }
214: return cost;
215: }
216:
217: public Expression optimizeInJoin(Session session, Select select)
218: throws SQLException {
219: if (!areAllValues(ExpressionVisitor
220: .get(ExpressionVisitor.EVALUATABLE))) {
221: return this ;
222: }
223: Database db = session.getDatabase();
224: Schema mainSchema = db.getSchema(Constants.SCHEMA_MAIN);
225: TableFunction function = new TableFunction(database, Function
226: .getFunctionInfo("TABLE_DISTINCT"));
227: Expression[] array = new Expression[values.size()];
228: for (int i = 0; i < values.size(); i++) {
229: Expression e = (Expression) values.get(i);
230: array[i] = e;
231: }
232: ExpressionList list = new ExpressionList(array);
233: function.setParameter(0, list);
234: function.doneWithParameters();
235: ObjectArray columns = new ObjectArray();
236: int dataType = left.getType();
237: String columnName = session.getNextTempViewName() + "_X";
238: Column col = new Column(columnName, dataType);
239: columns.add(col);
240: function.setColumns(columns);
241: FunctionTable table = new FunctionTable(mainSchema, session,
242: function);
243: String viewName = session.getNextTempViewName();
244: TableFilter filter = new TableFilter(session, table, viewName,
245: false, select);
246: select.addTableFilter(filter, true);
247: ExpressionColumn column = new ExpressionColumn(db, null,
248: viewName, columnName);
249: Comparison on = new Comparison(session, Comparison.EQUAL, left,
250: column);
251: on.mapColumns(filter, 0);
252: filter.addFilterCondition(on, true);
253: return ValueExpression.get(ValueBoolean.get(true));
254: }
255:
256: }
|