File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\command\dml\Query.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.command.dml;
8
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import org.h2.command.Prepared;
12 import org.h2.constant.ErrorCode;
13 import org.h2.engine.Database;
14 import org.h2.engine.Session;
15 import org.h2.expression.Alias;
16 import org.h2.expression.Expression;
17 import org.h2.expression.ExpressionColumn;
18 import org.h2.expression.ExpressionVisitor;
19 import org.h2.expression.Parameter;
20 import org.h2.expression.ValueExpression;
21 import org.h2.message.DbException;
22 import org.h2.result.LocalResult;
23 import org.h2.result.ResultTarget;
24 import org.h2.result.SortOrder;
25 import org.h2.table.ColumnResolver;
26 import org.h2.table.Table;
27 import org.h2.table.TableFilter;
28 import org.h2.util.New;
29 import org.h2.value.Value;
30 import org.h2.value.ValueInt;
31 import org.h2.value.ValueNull;
32
33 /**
34 * Represents a SELECT statement (simple, or union).
35 */
36 public abstract class Query extends Prepared {
37
38 /**
39 * The limit expression as specified in the LIMIT or TOP clause.
40 */
41 protected Expression limitExpr;
42
43 /**
44 * The offset expression as specified in the LIMIT .. OFFSET clause.
45 */
46 protected Expression offsetExpr;
47
48 /**
49 * The sample size
50 */
51 protected int sampleSize;
52
53 /**
54 * Whether the result must only contain distinct rows.
55 */
56 protected boolean distinct;
57
58 /**
59 * Whether the result needs to support random access.
60 */
61 protected boolean randomAccessResult;
62
63 private boolean noCache;
64 private int lastLimit;
65 private long lastEvaluated;
66 private LocalResult lastResult;
67 private Value[] lastParameters;
68 private boolean cacheableChecked;
69
70 Query(Session session) {
71 super(session);
72 }
73
74 /**
75 * Execute the query without checking the cache. If a target is specified,
76 * the results are written to it, and the method returns null. If no target
77 * is specified, a new LocalResult is created and returned.
78 *
79 * @param limit the limit as specified in the JDBC method call
80 * @param target the target to write results to
81 * @return the result
82 */
83 protected abstract LocalResult queryWithoutCache(int limit, ResultTarget target);
84
85 /**
86 * Initialize the query.
87 */
88 public abstract void init();
89
90 /**
91 * The the list of select expressions.
92 * This may include invisible expressions such as order by expressions.
93 *
94 * @return the list of expressions
95 */
96 public abstract ArrayList<Expression> getExpressions();
97
98 /**
99 * Calculate the cost to execute this query.
100 *
101 * @return the cost
102 */
103 public abstract double getCost();
104
105 /**
106 * Calculate the cost when used as a subquery.
107 * This method returns a value between 10 and 1000000,
108 * to ensure adding other values can't result in an integer overflow.
109 *
110 * @return the estimated cost as an integer
111 */
112 public int getCostAsExpression() {
113 // ensure the cost is not larger than 1 million,
114 // so that adding other values can't overflow
115 return (int) Math.min(1000000.0, 10.0 + 10.0 * getCost());
116 }
117
118 /**
119 * Get all tables that are involved in this query.
120 *
121 * @return the set of tables
122 */
123 public abstract HashSet<Table> getTables();
124
125 /**
126 * Set the order by list.
127 *
128 * @param order the order by list
129 */
130 public abstract void setOrder(ArrayList<SelectOrderBy> order);
131
132 /**
133 * Set the 'for update' flag.
134 *
135 * @param forUpdate the new setting
136 */
137 public abstract void setForUpdate(boolean forUpdate);
138
139 /**
140 * Get the column count of this query.
141 *
142 * @return the column count
143 */
144 public abstract int getColumnCount();
145
146 /**
147 * Map the columns to the given column resolver.
148 *
149 * @param resolver
150 * the resolver
151 * @param level
152 * the subquery level (0 is the top level query, 1 is the first
153 * subquery level)
154 */
155 public abstract void mapColumns(ColumnResolver resolver, int level);
156
157 /**
158 * Change the evaluatable flag. This is used when building the execution plan.
159 *
160 * @param tableFilter the table filter
161 * @param b the new value
162 */
163 public abstract void setEvaluatable(TableFilter tableFilter, boolean b);
164
165 /**
166 * Add a condition to the query. This is used for views.
167 *
168 * @param param the parameter
169 * @param columnId the column index (0 meaning the first column)
170 * @param comparisonType the comparison type
171 */
172 public abstract void addGlobalCondition(Parameter param, int columnId, int comparisonType);
173
174 /**
175 * Check whether adding condition to the query is allowed. This is not
176 * allowed for views that have an order by and a limit, as it would affect
177 * the returned results.
178 *
179 * @return true if adding global conditions is allowed
180 */
181 public abstract boolean allowGlobalConditions();
182
183 /**
184 * Check if this expression and all sub-expressions can fulfill a criteria.
185 * If any part returns false, the result is false.
186 *
187 * @param visitor the visitor
188 * @return if the criteria can be fulfilled
189 */
190 public abstract boolean isEverything(ExpressionVisitor visitor);
191
192 /**
193 * Update all aggregate function values.
194 *
195 * @param s the session
196 */
197 public abstract void updateAggregate(Session s);
198
199 /**
200 * Call the before triggers on all tables.
201 */
202 public abstract void fireBeforeSelectTriggers();
203
204 /**
205 * Set the distinct flag.
206 *
207 * @param b the new value
208 */
209 public void setDistinct(boolean b) {
210 distinct = b;
211 }
212
213 /**
214 * Whether results need to support random access.
215 *
216 * @param b the new value
217 */
218 public void setRandomAccessResult(boolean b) {
219 randomAccessResult = b;
220 }
221
222 public boolean isQuery() {
223 return true;
224 }
225
226 public boolean isTransactional() {
227 return true;
228 }
229
230 /**
231 * Disable caching of result sets.
232 */
233 public void disableCache() {
234 this.noCache = true;
235 }
236
237 private boolean sameResultAsLast(Session s, Value[] params, Value[] lastParams, long lastEval) {
238 if (!cacheableChecked) {
239 long max = getMaxDataModificationId();
240 noCache = max == Long.MAX_VALUE;
241 cacheableChecked = true;
242 }
243 if (noCache) {
244 return false;
245 }
246 Database db = s.getDatabase();
247 for (int i = 0; i < params.length; i++) {
248 Value a = lastParams[i], b = params[i];
249 if (a.getType() != b.getType() || !db.areEqual(a, b)) {
250 return false;
251 }
252 }
253 if (!isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR) || !isEverything(ExpressionVisitor.INDEPENDENT_VISITOR)) {
254 return false;
255 }
256 if (db.getModificationDataId() > lastEval && getMaxDataModificationId() > lastEval) {
257 return false;
258 }
259 return true;
260 }
261
262 public final Value[] getParameterValues() {
263 ArrayList<Parameter> list = getParameters();
264 if (list == null) {
265 list = New.arrayList();
266 }
267 int size = list.size();
268 Value[] params = new Value[size];
269 for (int i = 0; i < size; i++) {
270 Value v = list.get(i).getParamValue();
271 params[i] = v;
272 }
273 return params;
274 }
275
276 public LocalResult query(int maxrows) {
277 return query(maxrows, null);
278 }
279
280 /**
281 * Execute the query, writing the result to the target result.
282 *
283 * @param limit the maximum number of rows to return
284 * @param target the target result (null will return the result)
285 * @return the result set (if the target is not set).
286 */
287 LocalResult query(int limit, ResultTarget target) {
288 fireBeforeSelectTriggers();
289 if (noCache || !session.getDatabase().getOptimizeReuseResults()) {
290 return queryWithoutCache(limit, target);
291 }
292 Value[] params = getParameterValues();
293 long now = session.getDatabase().getModificationDataId();
294 if (isEverything(ExpressionVisitor.DETERMINISTIC_VISITOR)) {
295 if (lastResult != null && !lastResult.isClosed() && limit == lastLimit) {
296 if (sameResultAsLast(session, params, lastParameters, lastEvaluated)) {
297 lastResult = lastResult.createShallowCopy(session);
298 if (lastResult != null) {
299 lastResult.reset();
300 return lastResult;
301 }
302 }
303 }
304 }
305 lastParameters = params;
306 closeLastResult();
307 LocalResult r = queryWithoutCache(limit, target);
308 lastResult = r;
309 this.lastEvaluated = now;
310 lastLimit = limit;
311 return r;
312 }
313
314 private void closeLastResult() {
315 if (lastResult != null) {
316 lastResult.close();
317 }
318 }
319
320 /**
321 * Initialize the order by list. This call may extend the expressions list.
322 *
323 * @param session the session
324 * @param expressions the select list expressions
325 * @param expressionSQL the select list SQL snippets
326 * @param orderList the order by list
327 * @param visible the number of visible columns in the select list
328 * @param mustBeInResult all order by expressions must be in the select list
329 * @param filters the table filters
330 */
331 static void initOrder(Session session,
332 ArrayList<Expression> expressions,
333 ArrayList<String> expressionSQL,
334 ArrayList<SelectOrderBy> orderList,
335 int visible,
336 boolean mustBeInResult,
337 ArrayList<TableFilter> filters) {
338 Database db = session.getDatabase();
339 for (SelectOrderBy o : orderList) {
340 Expression e = o.expression;
341 if (e == null) {
342 continue;
343 }
344 // special case: SELECT 1 AS A FROM DUAL ORDER BY A
345 // (oracle supports it, but only in order by, not in group by and
346 // not in having):
347 // SELECT 1 AS A FROM DUAL ORDER BY -A
348 boolean isAlias = false;
349 int idx = expressions.size();
350 if (e instanceof ExpressionColumn) {
351 // order by expression
352 ExpressionColumn exprCol = (ExpressionColumn) e;
353 String tableAlias = exprCol.getOriginalTableAliasName();
354 String col = exprCol.getOriginalColumnName();
355 for (int j = 0; j < visible; j++) {
356 boolean found = false;
357 Expression ec = expressions.get(j);
358 if (ec instanceof ExpressionColumn) {
359 // select expression
360 ExpressionColumn c = (ExpressionColumn) ec;
361 found = db.equalsIdentifiers(col, c.getColumnName());
362 if (found && tableAlias != null) {
363 String ca = c.getOriginalTableAliasName();
364 if (ca == null) {
365 found = false;
366 if (filters != null) {
367 // select id from test order by test.id
368 for (int i = 0, size = filters.size(); i < size; i++) {
369 TableFilter f = filters.get(i);
370 if (db.equalsIdentifiers(f.getTableAlias(), tableAlias)) {
371 found = true;
372 break;
373 }
374 }
375 }
376 } else {
377 found = db.equalsIdentifiers(ca, tableAlias);
378 }
379 }
380 } else if (!(ec instanceof Alias)) {
381 continue;
382 } else if (tableAlias == null && db.equalsIdentifiers(col, ec.getAlias())) {
383 found = true;
384 } else {
385 Expression ec2 = ec.getNonAliasExpression();
386 if (ec2 instanceof ExpressionColumn) {
387 ExpressionColumn c2 = (ExpressionColumn) ec2;
388 String ta = exprCol.getSQL();
389 // exprCol.getTableAlias();
390 String tb = c2.getSQL();
391 // getTableAlias();
392 String s2 = c2.getColumnName();
393 found = db.equalsIdentifiers(col, s2);
394 if (!db.equalsIdentifiers(ta, tb)) {
395 found = false;
396 }
397 }
398 }
399 if (found) {
400 idx = j;
401 isAlias = true;
402 break;
403 }
404 }
405 } else {
406 String s = e.getSQL();
407 if (expressionSQL != null) {
408 for (int j = 0, size = expressionSQL.size(); j < size; j++) {
409 String s2 = expressionSQL.get(j);
410 if (db.equalsIdentifiers(s2, s)) {
411 idx = j;
412 isAlias = true;
413 break;
414 }
415 }
416 }
417 }
418 if (!isAlias) {
419 if (mustBeInResult) {
420 throw DbException.get(ErrorCode.ORDER_BY_NOT_IN_RESULT, e.getSQL());
421 }
422 expressions.add(e);
423 String sql = e.getSQL();
424 expressionSQL.add(sql);
425 }
426 o.columnIndexExpr = ValueExpression.get(ValueInt.get(idx + 1));
427 }
428 }
429
430 /**
431 * Create a {@link SortOrder} object given the list of {@link SelectOrderBy}
432 * objects. The expression list is extended if necessary.
433 *
434 * @param orderList a list of {@link SelectOrderBy} elements
435 * @param expressionCount the number of columns in the query
436 * @return the {@link SortOrder} object
437 */
438 public SortOrder prepareOrder(ArrayList<SelectOrderBy> orderList, int expressionCount) {
439 int size = orderList.size();
440 int[] index = new int[size];
441 int[] sortType = new int[size];
442 for (int i = 0; i < size; i++) {
443 SelectOrderBy o = orderList.get(i);
444 int idx;
445 boolean reverse = false;
446 Expression expr = o.columnIndexExpr;
447 Value v = expr.getValue(null);
448 if (v == ValueNull.INSTANCE) {
449 // parameter not yet set - order by first column
450 idx = 0;
451 } else {
452 idx = v.getInt();
453 if (idx < 0) {
454 reverse = true;
455 idx = -idx;
456 }
457 idx -= 1;
458 if (idx < 0 || idx >= expressionCount) {
459 throw DbException.get(ErrorCode.ORDER_BY_NOT_IN_RESULT, "" + (idx + 1));
460 }
461 }
462 index[i] = idx;
463 boolean desc = o.descending;
464 if (reverse) {
465 desc = !desc;
466 }
467 int type = desc ? SortOrder.DESCENDING : SortOrder.ASCENDING;
468 if (o.nullsFirst) {
469 type += SortOrder.NULLS_FIRST;
470 } else if (o.nullsLast) {
471 type += SortOrder.NULLS_LAST;
472 }
473 sortType[i] = type;
474 }
475 return new SortOrder(session.getDatabase(), index, sortType);
476 }
477
478 public void setOffset(Expression offset) {
479 this.offsetExpr = offset;
480 }
481
482 public void setLimit(Expression limit) {
483 this.limitExpr = limit;
484 }
485
486 /**
487 * Add a parameter to the parameter list.
488 *
489 * @param param the parameter to add
490 */
491 void addParameter(Parameter param) {
492 if (parameters == null) {
493 parameters = New.arrayList();
494 }
495 parameters.add(param);
496 }
497
498 public void setSampleSize(int sampleSize) {
499 this.sampleSize = sampleSize;
500 }
501
502 public final long getMaxDataModificationId() {
503 ExpressionVisitor visitor = ExpressionVisitor.getMaxModificationIdVisitor();
504 isEverything(visitor);
505 return visitor.getMaxDataModificationId();
506 }
507
508 }
509