EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.command.dml]

COVERAGE SUMMARY FOR SOURCE FILE [Select.java]

nameclass, %method, %block, %line, %
Select.java100% (1/1)94%  (50/53)95%  (3118/3289)94%  (702.3/748)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Select100% (1/1)94%  (50/53)95%  (3118/3289)94%  (702.3/748)
getGroupBy (): ArrayList 0%   (0/1)0%   (0/3)0%   (0/1)
getHaving (): Expression 0%   (0/1)0%   (0/3)0%   (0/1)
getTopFilters (): ArrayList 0%   (0/1)0%   (0/3)0%   (0/1)
setEvaluatableRecursive (TableFilter): void 100% (1/1)60%  (67/112)60%  (18/30)
addGlobalCondition (Parameter, int, int): void 100% (1/1)85%  (99/116)92%  (23/25)
updateAggregate (Session): void 100% (1/1)87%  (26/30)88%  (7/8)
getPlanSQL (): String 100% (1/1)92%  (327/355)92%  (70/76)
prepare (): void 100% (1/1)93%  (344/368)90%  (61.4/68)
queryWithoutCache (int, ResultTarget): LocalResult 100% (1/1)94%  (230/245)92%  (56/61)
isEverything (ExpressionVisitor): boolean 100% (1/1)94%  (140/149)92%  (30.5/33)
getSortIndex (): Index 100% (1/1)95%  (198/208)94%  (48/51)
getGroupSortedIndex (): Index 100% (1/1)96%  (48/50)92%  (12/13)
queryFlat (int, ResultTarget, long): void 100% (1/1)97%  (120/124)94%  (27.4/29)
isGroupSortedIndex (TableFilter, Index): boolean 100% (1/1)98%  (85/87)95%  (18/19)
init (): void 100% (1/1)99%  (276/278)98%  (62/63)
Select (Session): void 100% (1/1)100% (10/10)100% (4/4)
addCondition (Expression): void 100% (1/1)100% (17/17)100% (4/4)
addGroupSortedRow (Value [], int, ResultTarget): void 100% (1/1)100% (67/67)100% (13/13)
addTableFilter (TableFilter, boolean): void 100% (1/1)100% (13/13)100% (4/4)
allowGlobalConditions (): boolean 100% (1/1)100% (13/13)100% (3/3)
createLocalResult (LocalResult): LocalResult 100% (1/1)100% (14/14)100% (1/1)
expandColumnList (): void 100% (1/1)100% (162/162)100% (36/36)
fireBeforeSelectTriggers (): void 100% (1/1)100% (25/25)100% (4/4)
getColumnCount (): int 100% (1/1)100% (3/3)100% (1/1)
getCost (): double 100% (1/1)100% (3/3)100% (1/1)
getCurrentGroup (): HashMap 100% (1/1)100% (3/3)100% (1/1)
getCurrentGroupRowId (): int 100% (1/1)100% (3/3)100% (1/1)
getExpressions (): ArrayList 100% (1/1)100% (3/3)100% (1/1)
getGroupByExpressionCount (): int 100% (1/1)100% (29/29)100% (7/7)
getSortOrder (): SortOrder 100% (1/1)100% (3/3)100% (1/1)
getTables (): HashSet 100% (1/1)100% (21/21)100% (5/5)
getTopTableFilter (): TableFilter 100% (1/1)100% (3/3)100% (1/1)
getType (): int 100% (1/1)100% (2/2)100% (1/1)
isCacheable (): boolean 100% (1/1)100% (7/7)100% (1/1)
isHavingNullOrFalse (Value []): boolean 100% (1/1)100% (22/22)100% (7/7)
isQuickAggregateQuery (): boolean 100% (1/1)100% (3/3)100% (1/1)
isReadOnly (): boolean 100% (1/1)100% (4/4)100% (1/1)
isSortColumnImplicit (TableFilter, Column): boolean 100% (1/1)100% (42/42)100% (10/10)
keepOnlyDistinct (Value [], int): Value [] 100% (1/1)100% (19/19)100% (5/5)
mapColumns (ColumnResolver, int): void 100% (1/1)100% (25/25)100% (6/6)
preparePlan (): double 100% (1/1)100% (56/56)100% (10/10)
queryDistinct (ResultTarget, long): void 100% (1/1)100% (113/113)100% (28/28)
queryGroup (int, LocalResult): void 100% (1/1)100% (244/244)100% (53/53)
queryGroupSorted (int, ResultTarget): void 100% (1/1)100% (125/125)100% (29/29)
queryMeta (): ResultInterface 100% (1/1)100% (14/14)100% (3/3)
queryQuick (int, ResultTarget): void 100% (1/1)100% (27/27)100% (6/6)
setEvaluatable (TableFilter, boolean): void 100% (1/1)100% (25/25)100% (6/6)
setExpressions (ArrayList): void 100% (1/1)100% (4/4)100% (2/2)
setForUpdate (boolean): void 100% (1/1)100% (18/18)100% (4/4)
setGroupBy (ArrayList): void 100% (1/1)100% (4/4)100% (2/2)
setGroupQuery (): void 100% (1/1)100% (4/4)100% (2/2)
setHaving (Expression): void 100% (1/1)100% (4/4)100% (2/2)
setOrder (ArrayList): void 100% (1/1)100% (4/4)100% (2/2)

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 */
6package org.h2.command.dml;
7 
8import java.util.ArrayList;
9import java.util.Arrays;
10import java.util.HashMap;
11import java.util.HashSet;
12 
13import org.h2.api.ErrorCode;
14import org.h2.api.Trigger;
15import org.h2.command.CommandInterface;
16import org.h2.engine.Constants;
17import org.h2.engine.Database;
18import org.h2.engine.Session;
19import org.h2.engine.SysProperties;
20import org.h2.expression.Comparison;
21import org.h2.expression.ConditionAndOr;
22import org.h2.expression.Expression;
23import org.h2.expression.ExpressionColumn;
24import org.h2.expression.ExpressionVisitor;
25import org.h2.expression.Parameter;
26import org.h2.expression.Wildcard;
27import org.h2.index.Cursor;
28import org.h2.index.Index;
29import org.h2.index.IndexCondition;
30import org.h2.index.IndexType;
31import org.h2.message.DbException;
32import org.h2.result.LocalResult;
33import org.h2.result.ResultInterface;
34import org.h2.result.ResultTarget;
35import org.h2.result.Row;
36import org.h2.result.SearchRow;
37import org.h2.result.SortOrder;
38import org.h2.table.Column;
39import org.h2.table.ColumnResolver;
40import org.h2.table.IndexColumn;
41import org.h2.table.Table;
42import org.h2.table.TableFilter;
43import org.h2.util.New;
44import org.h2.util.StatementBuilder;
45import org.h2.util.StringUtils;
46import org.h2.util.ValueHashMap;
47import org.h2.value.Value;
48import org.h2.value.ValueArray;
49import 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 */
64public 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}

[all classes][org.h2.command.dml]
EMMA 2.0.5312 (C) Vladimir Roubtsov