1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.expression; |
7 | |
8 | import java.util.ArrayList; |
9 | import org.h2.engine.Database; |
10 | import org.h2.engine.Session; |
11 | import org.h2.index.IndexCondition; |
12 | import org.h2.table.ColumnResolver; |
13 | import org.h2.table.TableFilter; |
14 | import org.h2.util.StatementBuilder; |
15 | import org.h2.value.Value; |
16 | import org.h2.value.ValueBoolean; |
17 | import org.h2.value.ValueNull; |
18 | |
19 | /** |
20 | * An 'in' condition with a list of values, as in WHERE NAME IN(...) |
21 | */ |
22 | public class ConditionIn extends Condition { |
23 | |
24 | private final Database database; |
25 | private Expression left; |
26 | private final ArrayList<Expression> valueList; |
27 | private int queryLevel; |
28 | |
29 | /** |
30 | * Create a new IN(..) condition. |
31 | * |
32 | * @param database the database |
33 | * @param left the expression before IN |
34 | * @param values the value list (at least one element) |
35 | */ |
36 | public ConditionIn(Database database, Expression left, |
37 | ArrayList<Expression> values) { |
38 | this.database = database; |
39 | this.left = left; |
40 | this.valueList = values; |
41 | } |
42 | |
43 | @Override |
44 | public Value getValue(Session session) { |
45 | Value l = left.getValue(session); |
46 | if (l == ValueNull.INSTANCE) { |
47 | return l; |
48 | } |
49 | boolean result = false; |
50 | boolean hasNull = false; |
51 | for (Expression e : valueList) { |
52 | Value r = e.getValue(session); |
53 | if (r == ValueNull.INSTANCE) { |
54 | hasNull = true; |
55 | } else { |
56 | r = r.convertTo(l.getType()); |
57 | result = Comparison.compareNotNull(database, l, r, Comparison.EQUAL); |
58 | if (result) { |
59 | break; |
60 | } |
61 | } |
62 | } |
63 | if (!result && hasNull) { |
64 | return ValueNull.INSTANCE; |
65 | } |
66 | return ValueBoolean.get(result); |
67 | } |
68 | |
69 | @Override |
70 | public void mapColumns(ColumnResolver resolver, int level) { |
71 | left.mapColumns(resolver, level); |
72 | for (Expression e : valueList) { |
73 | e.mapColumns(resolver, level); |
74 | } |
75 | this.queryLevel = Math.max(level, this.queryLevel); |
76 | } |
77 | |
78 | @Override |
79 | public Expression optimize(Session session) { |
80 | left = left.optimize(session); |
81 | boolean constant = left.isConstant(); |
82 | if (constant && left == ValueExpression.getNull()) { |
83 | return left; |
84 | } |
85 | boolean allValuesConstant = true; |
86 | boolean allValuesNull = true; |
87 | int size = valueList.size(); |
88 | for (int i = 0; i < size; i++) { |
89 | Expression e = valueList.get(i); |
90 | e = e.optimize(session); |
91 | if (e.isConstant() && e.getValue(session) != ValueNull.INSTANCE) { |
92 | allValuesNull = false; |
93 | } |
94 | if (allValuesConstant && !e.isConstant()) { |
95 | allValuesConstant = false; |
96 | } |
97 | if (left instanceof ExpressionColumn && e instanceof Parameter) { |
98 | ((Parameter) e) |
99 | .setColumn(((ExpressionColumn) left).getColumn()); |
100 | } |
101 | valueList.set(i, e); |
102 | } |
103 | if (constant && allValuesConstant) { |
104 | return ValueExpression.get(getValue(session)); |
105 | } |
106 | if (size == 1) { |
107 | Expression right = valueList.get(0); |
108 | Expression expr = new Comparison(session, Comparison.EQUAL, left, right); |
109 | expr = expr.optimize(session); |
110 | return expr; |
111 | } |
112 | if (allValuesConstant && !allValuesNull) { |
113 | int leftType = left.getType(); |
114 | if (leftType == Value.UNKNOWN) { |
115 | return this; |
116 | } |
117 | Expression expr = new ConditionInConstantSet(session, left, valueList); |
118 | expr = expr.optimize(session); |
119 | return expr; |
120 | } |
121 | return this; |
122 | } |
123 | |
124 | @Override |
125 | public void createIndexConditions(Session session, TableFilter filter) { |
126 | if (!(left instanceof ExpressionColumn)) { |
127 | return; |
128 | } |
129 | ExpressionColumn l = (ExpressionColumn) left; |
130 | if (filter != l.getTableFilter()) { |
131 | return; |
132 | } |
133 | if (session.getDatabase().getSettings().optimizeInList) { |
134 | ExpressionVisitor visitor = ExpressionVisitor.getNotFromResolverVisitor(filter); |
135 | for (Expression e : valueList) { |
136 | if (!e.isEverything(visitor)) { |
137 | return; |
138 | } |
139 | } |
140 | filter.addIndexCondition(IndexCondition.getInList(l, valueList)); |
141 | return; |
142 | } |
143 | } |
144 | |
145 | @Override |
146 | public void setEvaluatable(TableFilter tableFilter, boolean b) { |
147 | left.setEvaluatable(tableFilter, b); |
148 | for (Expression e : valueList) { |
149 | e.setEvaluatable(tableFilter, b); |
150 | } |
151 | } |
152 | |
153 | @Override |
154 | public String getSQL() { |
155 | StatementBuilder buff = new StatementBuilder("("); |
156 | buff.append(left.getSQL()).append(" IN("); |
157 | for (Expression e : valueList) { |
158 | buff.appendExceptFirst(", "); |
159 | buff.append(e.getSQL()); |
160 | } |
161 | return buff.append("))").toString(); |
162 | } |
163 | |
164 | @Override |
165 | public void updateAggregate(Session session) { |
166 | left.updateAggregate(session); |
167 | for (Expression e : valueList) { |
168 | e.updateAggregate(session); |
169 | } |
170 | } |
171 | |
172 | @Override |
173 | public boolean isEverything(ExpressionVisitor visitor) { |
174 | if (!left.isEverything(visitor)) { |
175 | return false; |
176 | } |
177 | return areAllValues(visitor); |
178 | } |
179 | |
180 | private boolean areAllValues(ExpressionVisitor visitor) { |
181 | for (Expression e : valueList) { |
182 | if (!e.isEverything(visitor)) { |
183 | return false; |
184 | } |
185 | } |
186 | return true; |
187 | } |
188 | |
189 | @Override |
190 | public int getCost() { |
191 | int cost = left.getCost(); |
192 | for (Expression e : valueList) { |
193 | cost += e.getCost(); |
194 | } |
195 | return cost; |
196 | } |
197 | |
198 | @Override |
199 | public boolean isDisjunctive() { |
200 | return true; |
201 | } |
202 | |
203 | /** |
204 | * Add an additional element if possible. Example: given two conditions |
205 | * A IN(1, 2) OR A=3, the constant 3 is added: A IN(1, 2, 3). |
206 | * |
207 | * @param other the second condition |
208 | * @return null if the condition was not added, or the new condition |
209 | */ |
210 | Expression getAdditional(Comparison other) { |
211 | Expression add = other.getIfEquals(left); |
212 | if (add != null) { |
213 | valueList.add(add); |
214 | return this; |
215 | } |
216 | return null; |
217 | } |
218 | } |