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.command.dml; |
7 | |
8 | import java.util.ArrayList; |
9 | import java.util.Arrays; |
10 | import java.util.HashMap; |
11 | import java.util.HashSet; |
12 | |
13 | import org.h2.api.ErrorCode; |
14 | import org.h2.api.Trigger; |
15 | import org.h2.command.CommandInterface; |
16 | import org.h2.engine.Constants; |
17 | import org.h2.engine.Database; |
18 | import org.h2.engine.Session; |
19 | import org.h2.engine.SysProperties; |
20 | import org.h2.expression.Comparison; |
21 | import org.h2.expression.ConditionAndOr; |
22 | import org.h2.expression.Expression; |
23 | import org.h2.expression.ExpressionColumn; |
24 | import org.h2.expression.ExpressionVisitor; |
25 | import org.h2.expression.Parameter; |
26 | import org.h2.expression.Wildcard; |
27 | import org.h2.index.Cursor; |
28 | import org.h2.index.Index; |
29 | import org.h2.index.IndexCondition; |
30 | import org.h2.index.IndexType; |
31 | import org.h2.message.DbException; |
32 | import org.h2.result.LocalResult; |
33 | import org.h2.result.ResultInterface; |
34 | import org.h2.result.ResultTarget; |
35 | import org.h2.result.Row; |
36 | import org.h2.result.SearchRow; |
37 | import org.h2.result.SortOrder; |
38 | import org.h2.table.Column; |
39 | import org.h2.table.ColumnResolver; |
40 | import org.h2.table.IndexColumn; |
41 | import org.h2.table.Table; |
42 | import org.h2.table.TableFilter; |
43 | import org.h2.util.New; |
44 | import org.h2.util.StatementBuilder; |
45 | import org.h2.util.StringUtils; |
46 | import org.h2.util.ValueHashMap; |
47 | import org.h2.value.Value; |
48 | import org.h2.value.ValueArray; |
49 | import org.h2.value.ValueNull; |
50 | |
51 | /** |
52 | * This class represents a simple SELECT statement. |
53 | * |
54 | * For each select statement, |
55 | * visibleColumnCount <= distinctColumnCount <= expressionCount. |
56 | * The expression list count could include ORDER BY and GROUP BY expressions |
57 | * that are not in the select list. |
58 | * |
59 | * The call sequence is init(), mapColumns() if it's a subquery, prepare(). |
60 | * |
61 | * @author Thomas Mueller |
62 | * @author Joel Turkel (Group sorted query) |
63 | */ |
64 | public class Select extends Query { |
65 | private TableFilter topTableFilter; |
66 | private final ArrayList<TableFilter> filters = New.arrayList(); |
67 | private final ArrayList<TableFilter> topFilters = New.arrayList(); |
68 | private ArrayList<Expression> expressions; |
69 | private Expression[] expressionArray; |
70 | private Expression having; |
71 | private Expression condition; |
72 | private int visibleColumnCount, distinctColumnCount; |
73 | private ArrayList<SelectOrderBy> orderList; |
74 | private ArrayList<Expression> group; |
75 | private int[] groupIndex; |
76 | private boolean[] groupByExpression; |
77 | private HashMap<Expression, Object> currentGroup; |
78 | private int havingIndex; |
79 | private boolean isGroupQuery, isGroupSortedQuery; |
80 | private boolean isForUpdate, isForUpdateMvcc; |
81 | private double cost; |
82 | private boolean isQuickAggregateQuery, isDistinctQuery; |
83 | private boolean isPrepared, checkInit; |
84 | private boolean sortUsingIndex; |
85 | private SortOrder sort; |
86 | private int currentGroupRowId; |
87 | |
88 | public Select(Session session) { |
89 | super(session); |
90 | } |
91 | |
92 | /** |
93 | * Add a table to the query. |
94 | * |
95 | * @param filter the table to add |
96 | * @param isTop if the table can be the first table in the query plan |
97 | */ |
98 | public void addTableFilter(TableFilter filter, boolean isTop) { |
99 | // Oracle doesn't check on duplicate aliases |
100 | // String alias = filter.getAlias(); |
101 | // if (filterNames.contains(alias)) { |
102 | // throw Message.getSQLException( |
103 | // ErrorCode.DUPLICATE_TABLE_ALIAS, alias); |
104 | // } |
105 | // filterNames.add(alias); |
106 | filters.add(filter); |
107 | if (isTop) { |
108 | topFilters.add(filter); |
109 | } |
110 | } |
111 | |
112 | public ArrayList<TableFilter> getTopFilters() { |
113 | return topFilters; |
114 | } |
115 | |
116 | public void setExpressions(ArrayList<Expression> expressions) { |
117 | this.expressions = expressions; |
118 | } |
119 | |
120 | /** |
121 | * Called if this query contains aggregate functions. |
122 | */ |
123 | public void setGroupQuery() { |
124 | isGroupQuery = true; |
125 | } |
126 | |
127 | public void setGroupBy(ArrayList<Expression> group) { |
128 | this.group = group; |
129 | } |
130 | |
131 | public ArrayList<Expression> getGroupBy() { |
132 | return group; |
133 | } |
134 | |
135 | public HashMap<Expression, Object> getCurrentGroup() { |
136 | return currentGroup; |
137 | } |
138 | |
139 | public int getCurrentGroupRowId() { |
140 | return currentGroupRowId; |
141 | } |
142 | |
143 | @Override |
144 | public void setOrder(ArrayList<SelectOrderBy> order) { |
145 | orderList = order; |
146 | } |
147 | |
148 | /** |
149 | * Add a condition to the list of conditions. |
150 | * |
151 | * @param cond the condition to add |
152 | */ |
153 | public void addCondition(Expression cond) { |
154 | if (condition == null) { |
155 | condition = cond; |
156 | } else { |
157 | condition = new ConditionAndOr(ConditionAndOr.AND, cond, condition); |
158 | } |
159 | } |
160 | |
161 | private void queryGroupSorted(int columnCount, ResultTarget result) { |
162 | int rowNumber = 0; |
163 | setCurrentRowNumber(0); |
164 | currentGroup = null; |
165 | Value[] previousKeyValues = null; |
166 | while (topTableFilter.next()) { |
167 | setCurrentRowNumber(rowNumber + 1); |
168 | if (condition == null || |
169 | Boolean.TRUE.equals(condition.getBooleanValue(session))) { |
170 | rowNumber++; |
171 | Value[] keyValues = new Value[groupIndex.length]; |
172 | // update group |
173 | for (int i = 0; i < groupIndex.length; i++) { |
174 | int idx = groupIndex[i]; |
175 | Expression expr = expressions.get(idx); |
176 | keyValues[i] = expr.getValue(session); |
177 | } |
178 | |
179 | if (previousKeyValues == null) { |
180 | previousKeyValues = keyValues; |
181 | currentGroup = New.hashMap(); |
182 | } else if (!Arrays.equals(previousKeyValues, keyValues)) { |
183 | addGroupSortedRow(previousKeyValues, columnCount, result); |
184 | previousKeyValues = keyValues; |
185 | currentGroup = New.hashMap(); |
186 | } |
187 | currentGroupRowId++; |
188 | |
189 | for (int i = 0; i < columnCount; i++) { |
190 | if (groupByExpression == null || !groupByExpression[i]) { |
191 | Expression expr = expressions.get(i); |
192 | expr.updateAggregate(session); |
193 | } |
194 | } |
195 | } |
196 | } |
197 | if (previousKeyValues != null) { |
198 | addGroupSortedRow(previousKeyValues, columnCount, result); |
199 | } |
200 | } |
201 | |
202 | private void addGroupSortedRow(Value[] keyValues, int columnCount, |
203 | ResultTarget result) { |
204 | Value[] row = new Value[columnCount]; |
205 | for (int j = 0; groupIndex != null && j < groupIndex.length; j++) { |
206 | row[groupIndex[j]] = keyValues[j]; |
207 | } |
208 | for (int j = 0; j < columnCount; j++) { |
209 | if (groupByExpression != null && groupByExpression[j]) { |
210 | continue; |
211 | } |
212 | Expression expr = expressions.get(j); |
213 | row[j] = expr.getValue(session); |
214 | } |
215 | if (isHavingNullOrFalse(row)) { |
216 | return; |
217 | } |
218 | row = keepOnlyDistinct(row, columnCount); |
219 | result.addRow(row); |
220 | } |
221 | |
222 | private Value[] keepOnlyDistinct(Value[] row, int columnCount) { |
223 | if (columnCount == distinctColumnCount) { |
224 | return row; |
225 | } |
226 | // remove columns so that 'distinct' can filter duplicate rows |
227 | Value[] r2 = new Value[distinctColumnCount]; |
228 | System.arraycopy(row, 0, r2, 0, distinctColumnCount); |
229 | return r2; |
230 | } |
231 | |
232 | private boolean isHavingNullOrFalse(Value[] row) { |
233 | if (havingIndex >= 0) { |
234 | Value v = row[havingIndex]; |
235 | if (v == ValueNull.INSTANCE) { |
236 | return true; |
237 | } |
238 | if (!Boolean.TRUE.equals(v.getBoolean())) { |
239 | return true; |
240 | } |
241 | } |
242 | return false; |
243 | } |
244 | |
245 | private Index getGroupSortedIndex() { |
246 | if (groupIndex == null || groupByExpression == null) { |
247 | return null; |
248 | } |
249 | ArrayList<Index> indexes = topTableFilter.getTable().getIndexes(); |
250 | if (indexes != null) { |
251 | for (int i = 0, size = indexes.size(); i < size; i++) { |
252 | Index index = indexes.get(i); |
253 | if (index.getIndexType().isScan()) { |
254 | continue; |
255 | } |
256 | if (index.getIndexType().isHash()) { |
257 | // does not allow scanning entries |
258 | continue; |
259 | } |
260 | if (isGroupSortedIndex(topTableFilter, index)) { |
261 | return index; |
262 | } |
263 | } |
264 | } |
265 | return null; |
266 | } |
267 | |
268 | private boolean isGroupSortedIndex(TableFilter tableFilter, Index index) { |
269 | // check that all the GROUP BY expressions are part of the index |
270 | Column[] indexColumns = index.getColumns(); |
271 | // also check that the first columns in the index are grouped |
272 | boolean[] grouped = new boolean[indexColumns.length]; |
273 | outerLoop: |
274 | for (int i = 0, size = expressions.size(); i < size; i++) { |
275 | if (!groupByExpression[i]) { |
276 | continue; |
277 | } |
278 | Expression expr = expressions.get(i).getNonAliasExpression(); |
279 | if (!(expr instanceof ExpressionColumn)) { |
280 | return false; |
281 | } |
282 | ExpressionColumn exprCol = (ExpressionColumn) expr; |
283 | for (int j = 0; j < indexColumns.length; ++j) { |
284 | if (tableFilter == exprCol.getTableFilter()) { |
285 | if (indexColumns[j].equals(exprCol.getColumn())) { |
286 | grouped[j] = true; |
287 | continue outerLoop; |
288 | } |
289 | } |
290 | } |
291 | // We didn't find a matching index column |
292 | // for one group by expression |
293 | return false; |
294 | } |
295 | // check that the first columns in the index are grouped |
296 | // good: index(a, b, c); group by b, a |
297 | // bad: index(a, b, c); group by a, c |
298 | for (int i = 1; i < grouped.length; i++) { |
299 | if (!grouped[i - 1] && grouped[i]) { |
300 | return false; |
301 | } |
302 | } |
303 | return true; |
304 | } |
305 | |
306 | private int getGroupByExpressionCount() { |
307 | if (groupByExpression == null) { |
308 | return 0; |
309 | } |
310 | int count = 0; |
311 | for (boolean b : groupByExpression) { |
312 | if (b) { |
313 | ++count; |
314 | } |
315 | } |
316 | return count; |
317 | } |
318 | |
319 | private void queryGroup(int columnCount, LocalResult result) { |
320 | ValueHashMap<HashMap<Expression, Object>> groups = |
321 | ValueHashMap.newInstance(); |
322 | int rowNumber = 0; |
323 | setCurrentRowNumber(0); |
324 | currentGroup = null; |
325 | ValueArray defaultGroup = ValueArray.get(new Value[0]); |
326 | int sampleSize = getSampleSizeValue(session); |
327 | while (topTableFilter.next()) { |
328 | setCurrentRowNumber(rowNumber + 1); |
329 | if (condition == null || |
330 | Boolean.TRUE.equals(condition.getBooleanValue(session))) { |
331 | Value key; |
332 | rowNumber++; |
333 | if (groupIndex == null) { |
334 | key = defaultGroup; |
335 | } else { |
336 | Value[] keyValues = new Value[groupIndex.length]; |
337 | // update group |
338 | for (int i = 0; i < groupIndex.length; i++) { |
339 | int idx = groupIndex[i]; |
340 | Expression expr = expressions.get(idx); |
341 | keyValues[i] = expr.getValue(session); |
342 | } |
343 | key = ValueArray.get(keyValues); |
344 | } |
345 | HashMap<Expression, Object> values = groups.get(key); |
346 | if (values == null) { |
347 | values = new HashMap<Expression, Object>(); |
348 | groups.put(key, values); |
349 | } |
350 | currentGroup = values; |
351 | currentGroupRowId++; |
352 | int len = columnCount; |
353 | for (int i = 0; i < len; i++) { |
354 | if (groupByExpression == null || !groupByExpression[i]) { |
355 | Expression expr = expressions.get(i); |
356 | expr.updateAggregate(session); |
357 | } |
358 | } |
359 | if (sampleSize > 0 && rowNumber >= sampleSize) { |
360 | break; |
361 | } |
362 | } |
363 | } |
364 | if (groupIndex == null && groups.size() == 0) { |
365 | groups.put(defaultGroup, new HashMap<Expression, Object>()); |
366 | } |
367 | ArrayList<Value> keys = groups.keys(); |
368 | for (Value v : keys) { |
369 | ValueArray key = (ValueArray) v; |
370 | currentGroup = groups.get(key); |
371 | Value[] keyValues = key.getList(); |
372 | Value[] row = new Value[columnCount]; |
373 | for (int j = 0; groupIndex != null && j < groupIndex.length; j++) { |
374 | row[groupIndex[j]] = keyValues[j]; |
375 | } |
376 | for (int j = 0; j < columnCount; j++) { |
377 | if (groupByExpression != null && groupByExpression[j]) { |
378 | continue; |
379 | } |
380 | Expression expr = expressions.get(j); |
381 | row[j] = expr.getValue(session); |
382 | } |
383 | if (isHavingNullOrFalse(row)) { |
384 | continue; |
385 | } |
386 | row = keepOnlyDistinct(row, columnCount); |
387 | result.addRow(row); |
388 | } |
389 | } |
390 | |
391 | /** |
392 | * Get the index that matches the ORDER BY list, if one exists. This is to |
393 | * avoid running a separate ORDER BY if an index can be used. This is |
394 | * specially important for large result sets, if only the first few rows are |
395 | * important (LIMIT is used) |
396 | * |
397 | * @return the index if one is found |
398 | */ |
399 | private Index getSortIndex() { |
400 | if (sort == null) { |
401 | return null; |
402 | } |
403 | ArrayList<Column> sortColumns = New.arrayList(); |
404 | for (int idx : sort.getQueryColumnIndexes()) { |
405 | if (idx < 0 || idx >= expressions.size()) { |
406 | throw DbException.getInvalidValueException("ORDER BY", idx + 1); |
407 | } |
408 | Expression expr = expressions.get(idx); |
409 | expr = expr.getNonAliasExpression(); |
410 | if (expr.isConstant()) { |
411 | continue; |
412 | } |
413 | if (!(expr instanceof ExpressionColumn)) { |
414 | return null; |
415 | } |
416 | ExpressionColumn exprCol = (ExpressionColumn) expr; |
417 | if (exprCol.getTableFilter() != topTableFilter) { |
418 | return null; |
419 | } |
420 | sortColumns.add(exprCol.getColumn()); |
421 | } |
422 | Column[] sortCols = sortColumns.toArray(new Column[sortColumns.size()]); |
423 | int[] sortTypes = sort.getSortTypes(); |
424 | if (sortCols.length == 0) { |
425 | // sort just on constants - can use scan index |
426 | return topTableFilter.getTable().getScanIndex(session); |
427 | } |
428 | ArrayList<Index> list = topTableFilter.getTable().getIndexes(); |
429 | if (list != null) { |
430 | for (int i = 0, size = list.size(); i < size; i++) { |
431 | Index index = list.get(i); |
432 | if (index.getCreateSQL() == null) { |
433 | // can't use the scan index |
434 | continue; |
435 | } |
436 | if (index.getIndexType().isHash()) { |
437 | continue; |
438 | } |
439 | IndexColumn[] indexCols = index.getIndexColumns(); |
440 | if (indexCols.length < sortCols.length) { |
441 | continue; |
442 | } |
443 | boolean ok = true; |
444 | for (int j = 0; j < sortCols.length; j++) { |
445 | // the index and the sort order must start |
446 | // with the exact same columns |
447 | IndexColumn idxCol = indexCols[j]; |
448 | Column sortCol = sortCols[j]; |
449 | boolean implicitSortColumn = false; |
450 | if (idxCol.column != sortCol) { |
451 | implicitSortColumn = isSortColumnImplicit( |
452 | topTableFilter, idxCol.column); |
453 | if (!implicitSortColumn) { |
454 | ok = false; |
455 | break; |
456 | } |
457 | } |
458 | if (!implicitSortColumn && idxCol.sortType != sortTypes[j]) { |
459 | // NULL FIRST for ascending and NULLS LAST |
460 | // for descending would actually match the default |
461 | ok = false; |
462 | break; |
463 | } |
464 | } |
465 | if (ok) { |
466 | return index; |
467 | } |
468 | } |
469 | } |
470 | if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) { |
471 | // special case: order by _ROWID_ |
472 | Index index = topTableFilter.getTable().getScanIndex(session); |
473 | if (index.isRowIdIndex()) { |
474 | return index; |
475 | } |
476 | } |
477 | return null; |
478 | } |
479 | |
480 | /** |
481 | * Validates the cases where ORDER BY clause do not contains all indexed |
482 | * columns, but the related index path still would be valid for such search. |
483 | * Sometimes, the absence of a column in the ORDER BY clause does not alter |
484 | * the expected final result, and an index sorted scan could still be used. |
485 | * <pre> |
486 | * CREATE TABLE test(a, b); |
487 | * CREATE UNIQUE INDEX idx_test ON test(a, b); |
488 | * SELECT b FROM test WHERE a=22 AND b>10 order by b; |
489 | * </pre> |
490 | * More restrictive rule where one table query with indexed column not |
491 | * present in the ORDER BY clause is filtered with equality conditions (at |
492 | * least one) of type COLUMN = CONSTANT in a conjunctive fashion. |
493 | * |
494 | * @param sortColumn Column to be validated |
495 | * @return true if the column can be used implicitly, or false otherwise. |
496 | */ |
497 | private boolean isSortColumnImplicit(TableFilter tableFilter, |
498 | Column sortColumn) { |
499 | if (filters.size() == 1 && condition != null |
500 | && !condition.isDisjunctive()) { |
501 | ArrayList<IndexCondition> conditions = tableFilter |
502 | .getIndexConditionsForColumn(sortColumn); |
503 | if (conditions.isEmpty()) { |
504 | return false; |
505 | } |
506 | for (IndexCondition conditionExp : conditions) { |
507 | if (!conditionExp.isEquality(true)) { |
508 | return false; |
509 | } |
510 | } |
511 | return true; |
512 | } |
513 | return false; |
514 | } |
515 | |
516 | private void queryDistinct(ResultTarget result, long limitRows) { |
517 | // limitRows must be long, otherwise we get an int overflow |
518 | // if limitRows is at or near Integer.MAX_VALUE |
519 | // limitRows is never 0 here |
520 | if (limitRows > 0 && offsetExpr != null) { |
521 | int offset = offsetExpr.getValue(session).getInt(); |
522 | if (offset > 0) { |
523 | limitRows += offset; |
524 | } |
525 | } |
526 | int rowNumber = 0; |
527 | setCurrentRowNumber(0); |
528 | Index index = topTableFilter.getIndex(); |
529 | SearchRow first = null; |
530 | int columnIndex = index.getColumns()[0].getColumnId(); |
531 | int sampleSize = getSampleSizeValue(session); |
532 | while (true) { |
533 | setCurrentRowNumber(rowNumber + 1); |
534 | Cursor cursor = index.findNext(session, first, null); |
535 | if (!cursor.next()) { |
536 | break; |
537 | } |
538 | SearchRow found = cursor.getSearchRow(); |
539 | Value value = found.getValue(columnIndex); |
540 | if (first == null) { |
541 | first = topTableFilter.getTable().getTemplateSimpleRow(true); |
542 | } |
543 | first.setValue(columnIndex, value); |
544 | Value[] row = { value }; |
545 | result.addRow(row); |
546 | rowNumber++; |
547 | if ((sort == null || sortUsingIndex) && limitRows > 0 && |
548 | rowNumber >= limitRows) { |
549 | break; |
550 | } |
551 | if (sampleSize > 0 && rowNumber >= sampleSize) { |
552 | break; |
553 | } |
554 | } |
555 | } |
556 | |
557 | private void queryFlat(int columnCount, ResultTarget result, long limitRows) { |
558 | // limitRows must be long, otherwise we get an int overflow |
559 | // if limitRows is at or near Integer.MAX_VALUE |
560 | // limitRows is never 0 here |
561 | if (limitRows > 0 && offsetExpr != null) { |
562 | int offset = offsetExpr.getValue(session).getInt(); |
563 | if (offset > 0) { |
564 | limitRows += offset; |
565 | } |
566 | } |
567 | int rowNumber = 0; |
568 | setCurrentRowNumber(0); |
569 | ArrayList<Row> forUpdateRows = null; |
570 | if (isForUpdateMvcc) { |
571 | forUpdateRows = New.arrayList(); |
572 | } |
573 | int sampleSize = getSampleSizeValue(session); |
574 | while (topTableFilter.next()) { |
575 | setCurrentRowNumber(rowNumber + 1); |
576 | if (condition == null || |
577 | Boolean.TRUE.equals(condition.getBooleanValue(session))) { |
578 | Value[] row = new Value[columnCount]; |
579 | for (int i = 0; i < columnCount; i++) { |
580 | Expression expr = expressions.get(i); |
581 | row[i] = expr.getValue(session); |
582 | } |
583 | if (isForUpdateMvcc) { |
584 | topTableFilter.lockRowAdd(forUpdateRows); |
585 | } |
586 | result.addRow(row); |
587 | rowNumber++; |
588 | if ((sort == null || sortUsingIndex) && limitRows > 0 && |
589 | result.getRowCount() >= limitRows) { |
590 | break; |
591 | } |
592 | if (sampleSize > 0 && rowNumber >= sampleSize) { |
593 | break; |
594 | } |
595 | } |
596 | } |
597 | if (isForUpdateMvcc) { |
598 | topTableFilter.lockRows(forUpdateRows); |
599 | } |
600 | } |
601 | |
602 | private void queryQuick(int columnCount, ResultTarget result) { |
603 | Value[] row = new Value[columnCount]; |
604 | for (int i = 0; i < columnCount; i++) { |
605 | Expression expr = expressions.get(i); |
606 | row[i] = expr.getValue(session); |
607 | } |
608 | result.addRow(row); |
609 | } |
610 | |
611 | @Override |
612 | public ResultInterface queryMeta() { |
613 | LocalResult result = new LocalResult(session, expressionArray, |
614 | visibleColumnCount); |
615 | result.done(); |
616 | return result; |
617 | } |
618 | |
619 | @Override |
620 | protected LocalResult queryWithoutCache(int maxRows, ResultTarget target) { |
621 | int limitRows = maxRows == 0 ? -1 : maxRows; |
622 | if (limitExpr != null) { |
623 | Value v = limitExpr.getValue(session); |
624 | int l = v == ValueNull.INSTANCE ? -1 : v.getInt(); |
625 | if (limitRows < 0) { |
626 | limitRows = l; |
627 | } else if (l >= 0) { |
628 | limitRows = Math.min(l, limitRows); |
629 | } |
630 | } |
631 | int columnCount = expressions.size(); |
632 | LocalResult result = null; |
633 | if (target == null || |
634 | !session.getDatabase().getSettings().optimizeInsertFromSelect) { |
635 | result = createLocalResult(result); |
636 | } |
637 | if (sort != null && (!sortUsingIndex || distinct)) { |
638 | result = createLocalResult(result); |
639 | result.setSortOrder(sort); |
640 | } |
641 | if (distinct && !isDistinctQuery) { |
642 | result = createLocalResult(result); |
643 | result.setDistinct(); |
644 | } |
645 | if (randomAccessResult) { |
646 | result = createLocalResult(result); |
647 | } |
648 | if (isGroupQuery && !isGroupSortedQuery) { |
649 | result = createLocalResult(result); |
650 | } |
651 | if (limitRows >= 0 || offsetExpr != null) { |
652 | result = createLocalResult(result); |
653 | } |
654 | topTableFilter.startQuery(session); |
655 | topTableFilter.reset(); |
656 | boolean exclusive = isForUpdate && !isForUpdateMvcc; |
657 | if (isForUpdateMvcc) { |
658 | if (isGroupQuery) { |
659 | throw DbException.getUnsupportedException( |
660 | "MVCC=TRUE && FOR UPDATE && GROUP"); |
661 | } else if (distinct) { |
662 | throw DbException.getUnsupportedException( |
663 | "MVCC=TRUE && FOR UPDATE && DISTINCT"); |
664 | } else if (isQuickAggregateQuery) { |
665 | throw DbException.getUnsupportedException( |
666 | "MVCC=TRUE && FOR UPDATE && AGGREGATE"); |
667 | } else if (topTableFilter.getJoin() != null) { |
668 | throw DbException.getUnsupportedException( |
669 | "MVCC=TRUE && FOR UPDATE && JOIN"); |
670 | } |
671 | } |
672 | topTableFilter.lock(session, exclusive, exclusive); |
673 | ResultTarget to = result != null ? result : target; |
674 | if (limitRows != 0) { |
675 | if (isQuickAggregateQuery) { |
676 | queryQuick(columnCount, to); |
677 | } else if (isGroupQuery) { |
678 | if (isGroupSortedQuery) { |
679 | queryGroupSorted(columnCount, to); |
680 | } else { |
681 | queryGroup(columnCount, result); |
682 | } |
683 | } else if (isDistinctQuery) { |
684 | queryDistinct(to, limitRows); |
685 | } else { |
686 | queryFlat(columnCount, to, limitRows); |
687 | } |
688 | } |
689 | if (offsetExpr != null) { |
690 | result.setOffset(offsetExpr.getValue(session).getInt()); |
691 | } |
692 | if (limitRows >= 0) { |
693 | result.setLimit(limitRows); |
694 | } |
695 | if (result != null) { |
696 | result.done(); |
697 | if (target != null) { |
698 | while (result.next()) { |
699 | target.addRow(result.currentRow()); |
700 | } |
701 | result.close(); |
702 | return null; |
703 | } |
704 | return result; |
705 | } |
706 | return null; |
707 | } |
708 | |
709 | private LocalResult createLocalResult(LocalResult old) { |
710 | return old != null ? old : new LocalResult(session, expressionArray, |
711 | visibleColumnCount); |
712 | } |
713 | |
714 | private void expandColumnList() { |
715 | Database db = session.getDatabase(); |
716 | |
717 | // the expressions may change within the loop |
718 | for (int i = 0; i < expressions.size(); i++) { |
719 | Expression expr = expressions.get(i); |
720 | if (!expr.isWildcard()) { |
721 | continue; |
722 | } |
723 | String schemaName = expr.getSchemaName(); |
724 | String tableAlias = expr.getTableAlias(); |
725 | if (tableAlias == null) { |
726 | int temp = i; |
727 | expressions.remove(i); |
728 | for (TableFilter filter : filters) { |
729 | Wildcard c2 = new Wildcard(filter.getTable().getSchema() |
730 | .getName(), filter.getTableAlias()); |
731 | expressions.add(i++, c2); |
732 | } |
733 | i = temp - 1; |
734 | } else { |
735 | TableFilter filter = null; |
736 | for (TableFilter f : filters) { |
737 | if (db.equalsIdentifiers(tableAlias, f.getTableAlias())) { |
738 | if (schemaName == null || |
739 | db.equalsIdentifiers(schemaName, |
740 | f.getSchemaName())) { |
741 | filter = f; |
742 | break; |
743 | } |
744 | } |
745 | } |
746 | if (filter == null) { |
747 | throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, |
748 | tableAlias); |
749 | } |
750 | Table t = filter.getTable(); |
751 | String alias = filter.getTableAlias(); |
752 | expressions.remove(i); |
753 | Column[] columns = t.getColumns(); |
754 | for (Column c : columns) { |
755 | if (filter.isNaturalJoinColumn(c)) { |
756 | continue; |
757 | } |
758 | ExpressionColumn ec = new ExpressionColumn( |
759 | session.getDatabase(), null, alias, c.getName()); |
760 | expressions.add(i++, ec); |
761 | } |
762 | i--; |
763 | } |
764 | } |
765 | } |
766 | |
767 | @Override |
768 | public void init() { |
769 | if (SysProperties.CHECK && checkInit) { |
770 | DbException.throwInternalError(); |
771 | } |
772 | expandColumnList(); |
773 | visibleColumnCount = expressions.size(); |
774 | ArrayList<String> expressionSQL; |
775 | if (orderList != null || group != null) { |
776 | expressionSQL = New.arrayList(); |
777 | for (int i = 0; i < visibleColumnCount; i++) { |
778 | Expression expr = expressions.get(i); |
779 | expr = expr.getNonAliasExpression(); |
780 | String sql = expr.getSQL(); |
781 | expressionSQL.add(sql); |
782 | } |
783 | } else { |
784 | expressionSQL = null; |
785 | } |
786 | if (orderList != null) { |
787 | initOrder(session, expressions, expressionSQL, orderList, |
788 | visibleColumnCount, distinct, filters); |
789 | } |
790 | distinctColumnCount = expressions.size(); |
791 | if (having != null) { |
792 | expressions.add(having); |
793 | havingIndex = expressions.size() - 1; |
794 | having = null; |
795 | } else { |
796 | havingIndex = -1; |
797 | } |
798 | |
799 | Database db = session.getDatabase(); |
800 | |
801 | // first the select list (visible columns), |
802 | // then 'ORDER BY' expressions, |
803 | // then 'HAVING' expressions, |
804 | // and 'GROUP BY' expressions at the end |
805 | if (group != null) { |
806 | int size = group.size(); |
807 | int expSize = expressionSQL.size(); |
808 | groupIndex = new int[size]; |
809 | for (int i = 0; i < size; i++) { |
810 | Expression expr = group.get(i); |
811 | String sql = expr.getSQL(); |
812 | int found = -1; |
813 | for (int j = 0; j < expSize; j++) { |
814 | String s2 = expressionSQL.get(j); |
815 | if (db.equalsIdentifiers(s2, sql)) { |
816 | found = j; |
817 | break; |
818 | } |
819 | } |
820 | if (found < 0) { |
821 | // special case: GROUP BY a column alias |
822 | for (int j = 0; j < expSize; j++) { |
823 | Expression e = expressions.get(j); |
824 | if (db.equalsIdentifiers(sql, e.getAlias())) { |
825 | found = j; |
826 | break; |
827 | } |
828 | sql = expr.getAlias(); |
829 | if (db.equalsIdentifiers(sql, e.getAlias())) { |
830 | found = j; |
831 | break; |
832 | } |
833 | } |
834 | } |
835 | if (found < 0) { |
836 | int index = expressions.size(); |
837 | groupIndex[i] = index; |
838 | expressions.add(expr); |
839 | } else { |
840 | groupIndex[i] = found; |
841 | } |
842 | } |
843 | groupByExpression = new boolean[expressions.size()]; |
844 | for (int gi : groupIndex) { |
845 | groupByExpression[gi] = true; |
846 | } |
847 | group = null; |
848 | } |
849 | // map columns in select list and condition |
850 | for (TableFilter f : filters) { |
851 | mapColumns(f, 0); |
852 | } |
853 | if (havingIndex >= 0) { |
854 | Expression expr = expressions.get(havingIndex); |
855 | SelectListColumnResolver res = new SelectListColumnResolver(this); |
856 | expr.mapColumns(res, 0); |
857 | } |
858 | checkInit = true; |
859 | } |
860 | |
861 | @Override |
862 | public void prepare() { |
863 | if (isPrepared) { |
864 | // sometimes a subquery is prepared twice (CREATE TABLE AS SELECT) |
865 | return; |
866 | } |
867 | if (SysProperties.CHECK && !checkInit) { |
868 | DbException.throwInternalError("not initialized"); |
869 | } |
870 | if (orderList != null) { |
871 | sort = prepareOrder(orderList, expressions.size()); |
872 | orderList = null; |
873 | } |
874 | for (int i = 0; i < expressions.size(); i++) { |
875 | Expression e = expressions.get(i); |
876 | expressions.set(i, e.optimize(session)); |
877 | } |
878 | if (condition != null) { |
879 | condition = condition.optimize(session); |
880 | for (TableFilter f : filters) { |
881 | // outer joins: must not add index conditions such as |
882 | // "c is null" - example: |
883 | // create table parent(p int primary key) as select 1; |
884 | // create table child(c int primary key, pc int); |
885 | // insert into child values(2, 1); |
886 | // select p, c from parent |
887 | // left outer join child on p = pc where c is null; |
888 | if (!f.isJoinOuter() && !f.isJoinOuterIndirect()) { |
889 | condition.createIndexConditions(session, f); |
890 | } |
891 | } |
892 | } |
893 | if (isGroupQuery && groupIndex == null && |
894 | havingIndex < 0 && filters.size() == 1) { |
895 | if (condition == null) { |
896 | Table t = filters.get(0).getTable(); |
897 | ExpressionVisitor optimizable = ExpressionVisitor. |
898 | getOptimizableVisitor(t); |
899 | isQuickAggregateQuery = isEverything(optimizable); |
900 | } |
901 | } |
902 | cost = preparePlan(); |
903 | if (distinct && session.getDatabase().getSettings().optimizeDistinct && |
904 | !isGroupQuery && filters.size() == 1 && |
905 | expressions.size() == 1 && condition == null) { |
906 | Expression expr = expressions.get(0); |
907 | expr = expr.getNonAliasExpression(); |
908 | if (expr instanceof ExpressionColumn) { |
909 | Column column = ((ExpressionColumn) expr).getColumn(); |
910 | int selectivity = column.getSelectivity(); |
911 | Index columnIndex = topTableFilter.getTable(). |
912 | getIndexForColumn(column); |
913 | if (columnIndex != null && |
914 | selectivity != Constants.SELECTIVITY_DEFAULT && |
915 | selectivity < 20) { |
916 | // the first column must be ascending |
917 | boolean ascending = columnIndex. |
918 | getIndexColumns()[0].sortType == SortOrder.ASCENDING; |
919 | Index current = topTableFilter.getIndex(); |
920 | // if another index is faster |
921 | if (columnIndex.canFindNext() && ascending && |
922 | (current == null || |
923 | current.getIndexType().isScan() || |
924 | columnIndex == current)) { |
925 | IndexType type = columnIndex.getIndexType(); |
926 | // hash indexes don't work, and unique single column |
927 | // indexes don't work |
928 | if (!type.isHash() && (!type.isUnique() || |
929 | columnIndex.getColumns().length > 1)) { |
930 | topTableFilter.setIndex(columnIndex); |
931 | isDistinctQuery = true; |
932 | } |
933 | } |
934 | } |
935 | } |
936 | } |
937 | if (sort != null && !isQuickAggregateQuery && !isGroupQuery) { |
938 | Index index = getSortIndex(); |
939 | if (index != null) { |
940 | Index current = topTableFilter.getIndex(); |
941 | if (current.getIndexType().isScan() || current == index) { |
942 | topTableFilter.setIndex(index); |
943 | if (!topTableFilter.hasInComparisons()) { |
944 | // in(select ...) and in(1,2,3) may return the key in |
945 | // another order |
946 | sortUsingIndex = true; |
947 | } |
948 | } else if (index.getIndexColumns().length >= |
949 | current.getIndexColumns().length) { |
950 | IndexColumn[] sortColumns = index.getIndexColumns(); |
951 | IndexColumn[] currentColumns = current.getIndexColumns(); |
952 | boolean swapIndex = false; |
953 | for (int i = 0; i < currentColumns.length; i++) { |
954 | if (sortColumns[i].column != currentColumns[i].column) { |
955 | swapIndex = false; |
956 | break; |
957 | } |
958 | if (sortColumns[i].sortType != currentColumns[i].sortType) { |
959 | swapIndex = true; |
960 | } |
961 | } |
962 | if (swapIndex) { |
963 | topTableFilter.setIndex(index); |
964 | sortUsingIndex = true; |
965 | } |
966 | } |
967 | } |
968 | } |
969 | if (!isQuickAggregateQuery && isGroupQuery && |
970 | getGroupByExpressionCount() > 0) { |
971 | Index index = getGroupSortedIndex(); |
972 | Index current = topTableFilter.getIndex(); |
973 | if (index != null && (current.getIndexType().isScan() || |
974 | current == index)) { |
975 | topTableFilter.setIndex(index); |
976 | isGroupSortedQuery = true; |
977 | } |
978 | } |
979 | expressionArray = new Expression[expressions.size()]; |
980 | expressions.toArray(expressionArray); |
981 | isPrepared = true; |
982 | } |
983 | |
984 | @Override |
985 | public double getCost() { |
986 | return cost; |
987 | } |
988 | |
989 | @Override |
990 | public HashSet<Table> getTables() { |
991 | HashSet<Table> set = New.hashSet(); |
992 | for (TableFilter filter : filters) { |
993 | set.add(filter.getTable()); |
994 | } |
995 | return set; |
996 | } |
997 | |
998 | @Override |
999 | public void fireBeforeSelectTriggers() { |
1000 | for (int i = 0, size = filters.size(); i < size; i++) { |
1001 | TableFilter filter = filters.get(i); |
1002 | filter.getTable().fire(session, Trigger.SELECT, true); |
1003 | } |
1004 | } |
1005 | |
1006 | private double preparePlan() { |
1007 | TableFilter[] topArray = topFilters.toArray( |
1008 | new TableFilter[topFilters.size()]); |
1009 | for (TableFilter t : topArray) { |
1010 | t.setFullCondition(condition); |
1011 | } |
1012 | |
1013 | Optimizer optimizer = new Optimizer(topArray, condition, session); |
1014 | optimizer.optimize(); |
1015 | topTableFilter = optimizer.getTopFilter(); |
1016 | double planCost = optimizer.getCost(); |
1017 | |
1018 | setEvaluatableRecursive(topTableFilter); |
1019 | |
1020 | topTableFilter.prepare(); |
1021 | return planCost; |
1022 | } |
1023 | |
1024 | private void setEvaluatableRecursive(TableFilter f) { |
1025 | for (; f != null; f = f.getJoin()) { |
1026 | f.setEvaluatable(f, true); |
1027 | if (condition != null) { |
1028 | condition.setEvaluatable(f, true); |
1029 | } |
1030 | TableFilter n = f.getNestedJoin(); |
1031 | if (n != null) { |
1032 | setEvaluatableRecursive(n); |
1033 | } |
1034 | Expression on = f.getJoinCondition(); |
1035 | if (on != null) { |
1036 | if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { |
1037 | if (session.getDatabase().getSettings().nestedJoins) { |
1038 | // need to check that all added are bound to a table |
1039 | on = on.optimize(session); |
1040 | if (!f.isJoinOuter() && !f.isJoinOuterIndirect()) { |
1041 | f.removeJoinCondition(); |
1042 | addCondition(on); |
1043 | } |
1044 | } else { |
1045 | if (f.isJoinOuter()) { |
1046 | // this will check if all columns exist - it may or |
1047 | // may not throw an exception |
1048 | on = on.optimize(session); |
1049 | // it is not supported even if the columns exist |
1050 | throw DbException.get( |
1051 | ErrorCode.UNSUPPORTED_OUTER_JOIN_CONDITION_1, |
1052 | on.getSQL()); |
1053 | } |
1054 | f.removeJoinCondition(); |
1055 | // need to check that all added are bound to a table |
1056 | on = on.optimize(session); |
1057 | addCondition(on); |
1058 | } |
1059 | } |
1060 | } |
1061 | on = f.getFilterCondition(); |
1062 | if (on != null) { |
1063 | if (!on.isEverything(ExpressionVisitor.EVALUATABLE_VISITOR)) { |
1064 | f.removeFilterCondition(); |
1065 | addCondition(on); |
1066 | } |
1067 | } |
1068 | // this is only important for subqueries, so they know |
1069 | // the result columns are evaluatable |
1070 | for (Expression e : expressions) { |
1071 | e.setEvaluatable(f, true); |
1072 | } |
1073 | } |
1074 | } |
1075 | |
1076 | @Override |
1077 | public String getPlanSQL() { |
1078 | // can not use the field sqlStatement because the parameter |
1079 | // indexes may be incorrect: ? may be in fact ?2 for a subquery |
1080 | // but indexes may be set manually as well |
1081 | Expression[] exprList = expressions.toArray( |
1082 | new Expression[expressions.size()]); |
1083 | StatementBuilder buff = new StatementBuilder("SELECT"); |
1084 | if (distinct) { |
1085 | buff.append(" DISTINCT"); |
1086 | } |
1087 | for (int i = 0; i < visibleColumnCount; i++) { |
1088 | buff.appendExceptFirst(","); |
1089 | buff.append('\n'); |
1090 | buff.append(StringUtils.indent(exprList[i].getSQL(), 4, false)); |
1091 | } |
1092 | buff.append("\nFROM "); |
1093 | TableFilter filter = topTableFilter; |
1094 | if (filter != null) { |
1095 | buff.resetCount(); |
1096 | int i = 0; |
1097 | do { |
1098 | buff.appendExceptFirst("\n"); |
1099 | buff.append(filter.getPlanSQL(i++ > 0)); |
1100 | filter = filter.getJoin(); |
1101 | } while (filter != null); |
1102 | } else { |
1103 | buff.resetCount(); |
1104 | int i = 0; |
1105 | for (TableFilter f : topFilters) { |
1106 | do { |
1107 | buff.appendExceptFirst("\n"); |
1108 | buff.append(f.getPlanSQL(i++ > 0)); |
1109 | f = f.getJoin(); |
1110 | } while (f != null); |
1111 | } |
1112 | } |
1113 | if (condition != null) { |
1114 | buff.append("\nWHERE ").append( |
1115 | StringUtils.unEnclose(condition.getSQL())); |
1116 | } |
1117 | if (groupIndex != null) { |
1118 | buff.append("\nGROUP BY "); |
1119 | buff.resetCount(); |
1120 | for (int gi : groupIndex) { |
1121 | Expression g = exprList[gi]; |
1122 | g = g.getNonAliasExpression(); |
1123 | buff.appendExceptFirst(", "); |
1124 | buff.append(StringUtils.unEnclose(g.getSQL())); |
1125 | } |
1126 | } |
1127 | if (group != null) { |
1128 | buff.append("\nGROUP BY "); |
1129 | buff.resetCount(); |
1130 | for (Expression g : group) { |
1131 | buff.appendExceptFirst(", "); |
1132 | buff.append(StringUtils.unEnclose(g.getSQL())); |
1133 | } |
1134 | } |
1135 | if (having != null) { |
1136 | // could be set in addGlobalCondition |
1137 | // in this case the query is not run directly, just getPlanSQL is |
1138 | // called |
1139 | Expression h = having; |
1140 | buff.append("\nHAVING ").append( |
1141 | StringUtils.unEnclose(h.getSQL())); |
1142 | } else if (havingIndex >= 0) { |
1143 | Expression h = exprList[havingIndex]; |
1144 | buff.append("\nHAVING ").append( |
1145 | StringUtils.unEnclose(h.getSQL())); |
1146 | } |
1147 | if (sort != null) { |
1148 | buff.append("\nORDER BY ").append( |
1149 | sort.getSQL(exprList, visibleColumnCount)); |
1150 | } |
1151 | if (orderList != null) { |
1152 | buff.append("\nORDER BY "); |
1153 | buff.resetCount(); |
1154 | for (SelectOrderBy o : orderList) { |
1155 | buff.appendExceptFirst(", "); |
1156 | buff.append(StringUtils.unEnclose(o.getSQL())); |
1157 | } |
1158 | } |
1159 | if (limitExpr != null) { |
1160 | buff.append("\nLIMIT ").append( |
1161 | StringUtils.unEnclose(limitExpr.getSQL())); |
1162 | if (offsetExpr != null) { |
1163 | buff.append(" OFFSET ").append( |
1164 | StringUtils.unEnclose(offsetExpr.getSQL())); |
1165 | } |
1166 | } |
1167 | if (sampleSizeExpr != null) { |
1168 | buff.append("\nSAMPLE_SIZE ").append( |
1169 | StringUtils.unEnclose(sampleSizeExpr.getSQL())); |
1170 | } |
1171 | if (isForUpdate) { |
1172 | buff.append("\nFOR UPDATE"); |
1173 | } |
1174 | if (isQuickAggregateQuery) { |
1175 | buff.append("\n/* direct lookup */"); |
1176 | } |
1177 | if (isDistinctQuery) { |
1178 | buff.append("\n/* distinct */"); |
1179 | } |
1180 | if (sortUsingIndex) { |
1181 | buff.append("\n/* index sorted */"); |
1182 | } |
1183 | if (isGroupQuery) { |
1184 | if (isGroupSortedQuery) { |
1185 | buff.append("\n/* group sorted */"); |
1186 | } |
1187 | } |
1188 | // buff.append("\n/* cost: " + cost + " */"); |
1189 | return buff.toString(); |
1190 | } |
1191 | |
1192 | public void setHaving(Expression having) { |
1193 | this.having = having; |
1194 | } |
1195 | |
1196 | public Expression getHaving() { |
1197 | return having; |
1198 | } |
1199 | |
1200 | @Override |
1201 | public int getColumnCount() { |
1202 | return visibleColumnCount; |
1203 | } |
1204 | |
1205 | public TableFilter getTopTableFilter() { |
1206 | return topTableFilter; |
1207 | } |
1208 | |
1209 | @Override |
1210 | public ArrayList<Expression> getExpressions() { |
1211 | return expressions; |
1212 | } |
1213 | |
1214 | @Override |
1215 | public void setForUpdate(boolean b) { |
1216 | this.isForUpdate = b; |
1217 | if (session.getDatabase().getSettings().selectForUpdateMvcc && |
1218 | session.getDatabase().isMultiVersion()) { |
1219 | isForUpdateMvcc = b; |
1220 | } |
1221 | } |
1222 | |
1223 | @Override |
1224 | public void mapColumns(ColumnResolver resolver, int level) { |
1225 | for (Expression e : expressions) { |
1226 | e.mapColumns(resolver, level); |
1227 | } |
1228 | if (condition != null) { |
1229 | condition.mapColumns(resolver, level); |
1230 | } |
1231 | } |
1232 | |
1233 | @Override |
1234 | public void setEvaluatable(TableFilter tableFilter, boolean b) { |
1235 | for (Expression e : expressions) { |
1236 | e.setEvaluatable(tableFilter, b); |
1237 | } |
1238 | if (condition != null) { |
1239 | condition.setEvaluatable(tableFilter, b); |
1240 | } |
1241 | } |
1242 | |
1243 | /** |
1244 | * Check if this is an aggregate query with direct lookup, for example a |
1245 | * query of the type SELECT COUNT(*) FROM TEST or |
1246 | * SELECT MAX(ID) FROM TEST. |
1247 | * |
1248 | * @return true if a direct lookup is possible |
1249 | */ |
1250 | public boolean isQuickAggregateQuery() { |
1251 | return isQuickAggregateQuery; |
1252 | } |
1253 | |
1254 | @Override |
1255 | public void addGlobalCondition(Parameter param, int columnId, |
1256 | int comparisonType) { |
1257 | addParameter(param); |
1258 | Expression comp; |
1259 | Expression col = expressions.get(columnId); |
1260 | col = col.getNonAliasExpression(); |
1261 | if (col.isEverything(ExpressionVisitor.QUERY_COMPARABLE_VISITOR)) { |
1262 | comp = new Comparison(session, comparisonType, col, param); |
1263 | } else { |
1264 | // this condition will always evaluate to true, but need to |
1265 | // add the parameter, so it can be set later |
1266 | comp = new Comparison(session, Comparison.EQUAL_NULL_SAFE, param, param); |
1267 | } |
1268 | comp = comp.optimize(session); |
1269 | boolean addToCondition = true; |
1270 | if (isGroupQuery) { |
1271 | addToCondition = false; |
1272 | for (int i = 0; groupIndex != null && i < groupIndex.length; i++) { |
1273 | if (groupIndex[i] == columnId) { |
1274 | addToCondition = true; |
1275 | break; |
1276 | } |
1277 | } |
1278 | if (!addToCondition) { |
1279 | if (havingIndex >= 0) { |
1280 | having = expressions.get(havingIndex); |
1281 | } |
1282 | if (having == null) { |
1283 | having = comp; |
1284 | } else { |
1285 | having = new ConditionAndOr(ConditionAndOr.AND, having, comp); |
1286 | } |
1287 | } |
1288 | } |
1289 | if (addToCondition) { |
1290 | if (condition == null) { |
1291 | condition = comp; |
1292 | } else { |
1293 | condition = new ConditionAndOr(ConditionAndOr.AND, condition, comp); |
1294 | } |
1295 | } |
1296 | } |
1297 | |
1298 | @Override |
1299 | public void updateAggregate(Session s) { |
1300 | for (Expression e : expressions) { |
1301 | e.updateAggregate(s); |
1302 | } |
1303 | if (condition != null) { |
1304 | condition.updateAggregate(s); |
1305 | } |
1306 | if (having != null) { |
1307 | having.updateAggregate(s); |
1308 | } |
1309 | } |
1310 | |
1311 | @Override |
1312 | public boolean isEverything(ExpressionVisitor visitor) { |
1313 | switch(visitor.getType()) { |
1314 | case ExpressionVisitor.DETERMINISTIC: { |
1315 | if (isForUpdate) { |
1316 | return false; |
1317 | } |
1318 | for (int i = 0, size = filters.size(); i < size; i++) { |
1319 | TableFilter f = filters.get(i); |
1320 | if (!f.getTable().isDeterministic()) { |
1321 | return false; |
1322 | } |
1323 | } |
1324 | break; |
1325 | } |
1326 | case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID: { |
1327 | for (int i = 0, size = filters.size(); i < size; i++) { |
1328 | TableFilter f = filters.get(i); |
1329 | long m = f.getTable().getMaxDataModificationId(); |
1330 | visitor.addDataModificationId(m); |
1331 | } |
1332 | break; |
1333 | } |
1334 | case ExpressionVisitor.EVALUATABLE: { |
1335 | if (!session.getDatabase().getSettings().optimizeEvaluatableSubqueries) { |
1336 | return false; |
1337 | } |
1338 | break; |
1339 | } |
1340 | case ExpressionVisitor.GET_DEPENDENCIES: { |
1341 | for (int i = 0, size = filters.size(); i < size; i++) { |
1342 | TableFilter f = filters.get(i); |
1343 | Table table = f.getTable(); |
1344 | visitor.addDependency(table); |
1345 | table.addDependencies(visitor.getDependencies()); |
1346 | } |
1347 | break; |
1348 | } |
1349 | default: |
1350 | } |
1351 | ExpressionVisitor v2 = visitor.incrementQueryLevel(1); |
1352 | boolean result = true; |
1353 | for (int i = 0, size = expressions.size(); i < size; i++) { |
1354 | Expression e = expressions.get(i); |
1355 | if (!e.isEverything(v2)) { |
1356 | result = false; |
1357 | break; |
1358 | } |
1359 | } |
1360 | if (result && condition != null && !condition.isEverything(v2)) { |
1361 | result = false; |
1362 | } |
1363 | if (result && having != null && !having.isEverything(v2)) { |
1364 | result = false; |
1365 | } |
1366 | return result; |
1367 | } |
1368 | |
1369 | @Override |
1370 | public boolean isReadOnly() { |
1371 | return isEverything(ExpressionVisitor.READONLY_VISITOR); |
1372 | } |
1373 | |
1374 | |
1375 | @Override |
1376 | public boolean isCacheable() { |
1377 | return !isForUpdate; |
1378 | } |
1379 | |
1380 | @Override |
1381 | public int getType() { |
1382 | return CommandInterface.SELECT; |
1383 | } |
1384 | |
1385 | @Override |
1386 | public boolean allowGlobalConditions() { |
1387 | if (offsetExpr == null && (limitExpr == null || sort == null)) { |
1388 | return true; |
1389 | } |
1390 | return false; |
1391 | } |
1392 | |
1393 | public SortOrder getSortOrder() { |
1394 | return sort; |
1395 | } |
1396 | |
1397 | } |