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.sql.ResultSet; |
9 | import java.sql.ResultSetMetaData; |
10 | import java.sql.SQLException; |
11 | import org.h2.engine.Database; |
12 | import org.h2.engine.Session; |
13 | import org.h2.message.DbException; |
14 | import org.h2.table.Column; |
15 | import org.h2.table.ColumnResolver; |
16 | import org.h2.table.TableFilter; |
17 | import org.h2.util.StringUtils; |
18 | import org.h2.value.DataType; |
19 | import org.h2.value.Value; |
20 | import org.h2.value.ValueArray; |
21 | |
22 | /** |
23 | * An expression is a operation, a value, or a function in a query. |
24 | */ |
25 | public abstract class Expression { |
26 | |
27 | private boolean addedToFilter; |
28 | |
29 | /** |
30 | * Return the resulting value for the current row. |
31 | * |
32 | * @param session the session |
33 | * @return the result |
34 | */ |
35 | public abstract Value getValue(Session session); |
36 | |
37 | /** |
38 | * Return the data type. The data type may not be known before the |
39 | * optimization phase. |
40 | * |
41 | * @return the type |
42 | */ |
43 | public abstract int getType(); |
44 | |
45 | /** |
46 | * Map the columns of the resolver to expression columns. |
47 | * |
48 | * @param resolver the column resolver |
49 | * @param level the subquery nesting level |
50 | */ |
51 | public abstract void mapColumns(ColumnResolver resolver, int level); |
52 | |
53 | /** |
54 | * Try to optimize the expression. |
55 | * |
56 | * @param session the session |
57 | * @return the optimized expression |
58 | */ |
59 | public abstract Expression optimize(Session session); |
60 | |
61 | /** |
62 | * Tell the expression columns whether the table filter can return values |
63 | * now. This is used when optimizing the query. |
64 | * |
65 | * @param tableFilter the table filter |
66 | * @param value true if the table filter can return value |
67 | */ |
68 | public abstract void setEvaluatable(TableFilter tableFilter, boolean value); |
69 | |
70 | /** |
71 | * Get the scale of this expression. |
72 | * |
73 | * @return the scale |
74 | */ |
75 | public abstract int getScale(); |
76 | |
77 | /** |
78 | * Get the precision of this expression. |
79 | * |
80 | * @return the precision |
81 | */ |
82 | public abstract long getPrecision(); |
83 | |
84 | /** |
85 | * Get the display size of this expression. |
86 | * |
87 | * @return the display size |
88 | */ |
89 | public abstract int getDisplaySize(); |
90 | |
91 | /** |
92 | * Get the SQL statement of this expression. |
93 | * This may not always be the original SQL statement, |
94 | * specially after optimization. |
95 | * |
96 | * @return the SQL statement |
97 | */ |
98 | public abstract String getSQL(); |
99 | |
100 | /** |
101 | * Update an aggregate value. This method is called at statement execution |
102 | * time. It is usually called once for each row, but if the expression is |
103 | * used multiple times (for example in the column list, and as part of the |
104 | * HAVING expression) it is called multiple times - the row counter needs to |
105 | * be used to make sure the internal state is only updated once. |
106 | * |
107 | * @param session the session |
108 | */ |
109 | public abstract void updateAggregate(Session session); |
110 | |
111 | /** |
112 | * Check if this expression and all sub-expressions can fulfill a criteria. |
113 | * If any part returns false, the result is false. |
114 | * |
115 | * @param visitor the visitor |
116 | * @return if the criteria can be fulfilled |
117 | */ |
118 | public abstract boolean isEverything(ExpressionVisitor visitor); |
119 | |
120 | /** |
121 | * Estimate the cost to process the expression. |
122 | * Used when optimizing the query, to calculate the query plan |
123 | * with the lowest estimated cost. |
124 | * |
125 | * @return the estimated cost |
126 | */ |
127 | public abstract int getCost(); |
128 | |
129 | /** |
130 | * If it is possible, return the negated expression. This is used |
131 | * to optimize NOT expressions: NOT ID>10 can be converted to |
132 | * ID<=10. Returns null if negating is not possible. |
133 | * |
134 | * @param session the session |
135 | * @return the negated expression, or null |
136 | */ |
137 | public Expression getNotIfPossible(Session session) { |
138 | // by default it is not possible |
139 | return null; |
140 | } |
141 | |
142 | /** |
143 | * Check if this expression will always return the same value. |
144 | * |
145 | * @return if the expression is constant |
146 | */ |
147 | public boolean isConstant() { |
148 | return false; |
149 | } |
150 | |
151 | /** |
152 | * Is the value of a parameter set. |
153 | * |
154 | * @return true if set |
155 | */ |
156 | public boolean isValueSet() { |
157 | return false; |
158 | } |
159 | |
160 | /** |
161 | * Check if this is an auto-increment column. |
162 | * |
163 | * @return true if it is an auto-increment column |
164 | */ |
165 | public boolean isAutoIncrement() { |
166 | return false; |
167 | } |
168 | |
169 | /** |
170 | * Get the value in form of a boolean expression. |
171 | * Returns true, false, or null. |
172 | * In this database, everything can be a condition. |
173 | * |
174 | * @param session the session |
175 | * @return the result |
176 | */ |
177 | public Boolean getBooleanValue(Session session) { |
178 | return getValue(session).getBoolean(); |
179 | } |
180 | |
181 | /** |
182 | * Create index conditions if possible and attach them to the table filter. |
183 | * |
184 | * @param session the session |
185 | * @param filter the table filter |
186 | */ |
187 | public void createIndexConditions(Session session, TableFilter filter) { |
188 | // default is do nothing |
189 | } |
190 | |
191 | /** |
192 | * Get the column name or alias name of this expression. |
193 | * |
194 | * @return the column name |
195 | */ |
196 | public String getColumnName() { |
197 | return getAlias(); |
198 | } |
199 | |
200 | /** |
201 | * Get the schema name, or null |
202 | * |
203 | * @return the schema name |
204 | */ |
205 | public String getSchemaName() { |
206 | return null; |
207 | } |
208 | |
209 | /** |
210 | * Get the table name, or null |
211 | * |
212 | * @return the table name |
213 | */ |
214 | public String getTableName() { |
215 | return null; |
216 | } |
217 | |
218 | /** |
219 | * Check whether this expression is a column and can store NULL. |
220 | * |
221 | * @return whether NULL is allowed |
222 | */ |
223 | public int getNullable() { |
224 | return Column.NULLABLE_UNKNOWN; |
225 | } |
226 | |
227 | /** |
228 | * Get the table alias name or null |
229 | * if this expression does not represent a column. |
230 | * |
231 | * @return the table alias name |
232 | */ |
233 | public String getTableAlias() { |
234 | return null; |
235 | } |
236 | |
237 | /** |
238 | * Get the alias name of a column or SQL expression |
239 | * if it is not an aliased expression. |
240 | * |
241 | * @return the alias name |
242 | */ |
243 | public String getAlias() { |
244 | return StringUtils.unEnclose(getSQL()); |
245 | } |
246 | |
247 | /** |
248 | * Only returns true if the expression is a wildcard. |
249 | * |
250 | * @return if this expression is a wildcard |
251 | */ |
252 | public boolean isWildcard() { |
253 | return false; |
254 | } |
255 | |
256 | /** |
257 | * Returns the main expression, skipping aliases. |
258 | * |
259 | * @return the expression |
260 | */ |
261 | public Expression getNonAliasExpression() { |
262 | return this; |
263 | } |
264 | |
265 | /** |
266 | * Allows to check if the related expression is under conjunctive format. |
267 | * |
268 | * @return if the related expression has the logic operator OR. |
269 | */ |
270 | public boolean isDisjunctive() { |
271 | return false; |
272 | } |
273 | |
274 | /** |
275 | * Add conditions to a table filter if they can be evaluated. |
276 | * |
277 | * @param filter the table filter |
278 | * @param outerJoin if the expression is part of an outer join |
279 | */ |
280 | public void addFilterConditions(TableFilter filter, boolean outerJoin) { |
281 | if (!addedToFilter && !outerJoin && |
282 | isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { |
283 | filter.addFilterCondition(this, false); |
284 | addedToFilter = true; |
285 | } |
286 | } |
287 | |
288 | /** |
289 | * Convert this expression to a String. |
290 | * |
291 | * @return the string representation |
292 | */ |
293 | @Override |
294 | public String toString() { |
295 | return getSQL(); |
296 | } |
297 | |
298 | /** |
299 | * If this expression consists of column expressions it should return them. |
300 | * |
301 | * @param session the session |
302 | * @return array of expression columns if applicable, null otherwise |
303 | */ |
304 | public Expression[] getExpressionColumns(Session session) { |
305 | return null; |
306 | } |
307 | |
308 | /** |
309 | * Extracts expression columns from ValueArray |
310 | * |
311 | * @param session the current session |
312 | * @param value the value to extract columns from |
313 | * @return array of expression columns |
314 | */ |
315 | static Expression[] getExpressionColumns(Session session, ValueArray value) { |
316 | Value[] list = value.getList(); |
317 | ExpressionColumn[] expr = new ExpressionColumn[list.length]; |
318 | for (int i = 0, len = list.length; i < len; i++) { |
319 | Value v = list[i]; |
320 | Column col = new Column("C" + (i + 1), v.getType(), |
321 | v.getPrecision(), v.getScale(), |
322 | v.getDisplaySize()); |
323 | expr[i] = new ExpressionColumn(session.getDatabase(), col); |
324 | } |
325 | return expr; |
326 | } |
327 | |
328 | /** |
329 | * Extracts expression columns from the given result set. |
330 | * |
331 | * @param session the session |
332 | * @param rs the result set |
333 | * @return an array of expression columns |
334 | */ |
335 | public static Expression[] getExpressionColumns(Session session, ResultSet rs) { |
336 | try { |
337 | ResultSetMetaData meta = rs.getMetaData(); |
338 | int columnCount = meta.getColumnCount(); |
339 | Expression[] expressions = new Expression[columnCount]; |
340 | Database db = session == null ? null : session.getDatabase(); |
341 | for (int i = 0; i < columnCount; i++) { |
342 | String name = meta.getColumnLabel(i + 1); |
343 | int type = DataType.getValueTypeFromResultSet(meta, i + 1); |
344 | int precision = meta.getPrecision(i + 1); |
345 | int scale = meta.getScale(i + 1); |
346 | int displaySize = meta.getColumnDisplaySize(i + 1); |
347 | Column col = new Column(name, type, precision, scale, displaySize); |
348 | Expression expr = new ExpressionColumn(db, col); |
349 | expressions[i] = expr; |
350 | } |
351 | return expressions; |
352 | } catch (SQLException e) { |
353 | throw DbException.convert(e); |
354 | } |
355 | } |
356 | |
357 | } |