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

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.index;
8
9 import java.util.ArrayList;
10 import java.util.HashSet;
11 import org.h2.engine.Session;
12 import org.h2.expression.Comparison;
13 import org.h2.message.DbException;
14 import org.h2.result.ResultInterface;
15 import org.h2.result.Row;
16 import org.h2.result.SearchRow;
17 import org.h2.result.SortOrder;
18 import org.h2.table.Column;
19 import org.h2.table.IndexColumn;
20 import org.h2.table.Table;
21 import org.h2.table.TableFilter;
22 import org.h2.value.Value;
23 import org.h2.value.ValueNull;
24
25 /**
26 * The filter used to walk through an index. This class supports IN(..)
27 * and IN(SELECT ...) optimizations.
28 */
29 public class IndexCursor implements Cursor {
30
31 private Session session;
32 private final TableFilter tableFilter;
33 private Index index;
34 private Table table;
35 private IndexColumn[] indexColumns;
36 private boolean alwaysFalse;
37
38 private SearchRow start, end;
39 private Cursor cursor;
40 private Column inColumn;
41 private int inListIndex;
42 private Value[] inList;
43 private ResultInterface inResult;
44 private HashSet<Value> inResultTested;
45
46 public IndexCursor(TableFilter filter) {
47 this.tableFilter = filter;
48 }
49
50 public void setIndex(Index index) {
51 this.index = index;
52 this.table = index.getTable();
53 Column[] columns = table.getColumns();
54 indexColumns = new IndexColumn[columns.length];
55 IndexColumn[] idxCols = index.getIndexColumns();
56 if (idxCols != null) {
57 for (int i = 0, len = columns.length; i < len; i++) {
58 int idx = index.getColumnIndex(columns[i]);
59 if (idx >= 0) {
60 indexColumns[i] = idxCols[idx];
61 }
62 }
63 }
64 }
65
66 /**
67 * Re-evaluate the start and end values of the index search for rows.
68 *
69 * @param s the session
70 * @param indexConditions the index conditions
71 */
72 public void find(Session s, ArrayList<IndexCondition> indexConditions) {
73 this.session = s;
74 alwaysFalse = false;
75 start = end = null;
76 inList = null;
77 inColumn = null;
78 inResult = null;
79 inResultTested = null;
80 // don't use enhanced for loop to avoid creating objects
81 for (int i = 0, size = indexConditions.size(); i < size; i++) {
82 IndexCondition condition = indexConditions.get(i);
83 if (condition.isAlwaysFalse()) {
84 alwaysFalse = true;
85 break;
86 }
87 Column column = condition.getColumn();
88 if (condition.getCompareType() == Comparison.IN_LIST) {
89 if (start == null && end == null) {
90 if (canUseIndexForIn(column)) {
91 this.inColumn = column;
92 inList = condition.getCurrentValueList(s);
93 inListIndex = 0;
94 }
95 }
96 } else if (condition.getCompareType() == Comparison.IN_QUERY) {
97 if (start == null && end == null) {
98 if (canUseIndexForIn(column)) {
99 this.inColumn = column;
100 inResult = condition.getCurrentResult();
101 }
102 }
103 } else {
104 Value v = condition.getCurrentValue(s);
105 boolean isStart = condition.isStart();
106 boolean isEnd = condition.isEnd();
107 int id = column.getColumnId();
108 if (id >= 0) {
109 IndexColumn idxCol = indexColumns[id];
110 if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
111 // if the index column is sorted the other way, we swap end and start
112 // NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
113 boolean temp = isStart;
114 isStart = isEnd;
115 isEnd = temp;
116 }
117 }
118 if (isStart) {
119 start = getSearchRow(start, id, v, true);
120 }
121 if (isEnd) {
122 end = getSearchRow(end, id, v, false);
123 }
124 if (isStart || isEnd) {
125 // an X=? condition will produce less rows than
126 // an X IN(..) condition
127 inColumn = null;
128 inList = null;
129 inResult = null;
130 }
131 if (!session.getDatabase().getSettings().optimizeIsNull) {
132 if (isStart && isEnd) {
133 if (v == ValueNull.INSTANCE) {
134 // join on a column=NULL is always false
135 alwaysFalse = true;
136 }
137 }
138 }
139 }
140 }
141 if (inColumn != null) {
142 return;
143 }
144 if (!alwaysFalse) {
145 cursor = index.find(tableFilter, start, end);
146 }
147 }
148
149 private boolean canUseIndexForIn(Column column) {
150 if (inColumn != null) {
151 // only one IN(..) condition can be used at the same time
152 return false;
153 }
154 // The first column of the index must match this column,
155 // or it must be a VIEW index (where the column is null).
156 // Multiple IN conditions with views are not supported, see
157 // IndexCondition.getMask.
158 IndexColumn[] cols = index.getIndexColumns();
159 if (cols == null) {
160 return true;
161 }
162 IndexColumn idxCol = cols[0];
163 return idxCol == null || idxCol.column == column;
164 }
165
166 private SearchRow getSearchRow(SearchRow row, int id, Value v, boolean max) {
167 if (row == null) {
168 row = table.getTemplateRow();
169 } else {
170 v = getMax(row.getValue(id), v, max);
171 }
172 if (id < 0) {
173 row.setKey(v.getLong());
174 } else {
175 row.setValue(id, v);
176 }
177 return row;
178 }
179
180 private Value getMax(Value a, Value b, boolean bigger) {
181 if (a == null) {
182 return b;
183 } else if (b == null) {
184 return a;
185 }
186 if (session.getDatabase().getSettings().optimizeIsNull) {
187 // IS NULL must be checked later
188 if (a == ValueNull.INSTANCE) {
189 return b;
190 } else if (b == ValueNull.INSTANCE) {
191 return a;
192 }
193 }
194 int comp = a.compareTo(b, table.getDatabase().getCompareMode());
195 if (comp == 0) {
196 return a;
197 }
198 if (a == ValueNull.INSTANCE || b == ValueNull.INSTANCE) {
199 if (session.getDatabase().getSettings().optimizeIsNull) {
200 // column IS NULL AND column <op> <not null> is always false
201 return null;
202 }
203 }
204 if (!bigger) {
205 comp = -comp;
206 }
207 return comp > 0 ? a : b;
208 }
209
210 /**
211 * Check if the result is empty for sure.
212 *
213 * @return true if it is
214 */
215 public boolean isAlwaysFalse() {
216 return alwaysFalse;
217 }
218
219 public Row get() {
220 if (cursor == null) {
221 return null;
222 }
223 return cursor.get();
224 }
225
226 public SearchRow getSearchRow() {
227 return cursor.getSearchRow();
228 }
229
230 public boolean next() {
231 while (true) {
232 if (cursor == null) {
233 nextCursor();
234 if (cursor == null) {
235 return false;
236 }
237 }
238 if (cursor.next()) {
239 return true;
240 }
241 cursor = null;
242 }
243 }
244
245 private void nextCursor() {
246 if (inList != null) {
247 while (inListIndex < inList.length) {
248 Value v = inList[inListIndex++];
249 if (v != ValueNull.INSTANCE) {
250 find(v);
251 break;
252 }
253 }
254 } else if (inResult != null) {
255 while (inResult.next()) {
256 Value v = inResult.currentRow()[0];
257 if (v != ValueNull.INSTANCE) {
258 v = inColumn.convert(v);
259 if (inResultTested == null) {
260 inResultTested = new HashSet<Value>();
261 }
262 if (inResultTested.add(v)) {
263 find(v);
264 break;
265 }
266 }
267 }
268 }
269 }
270
271 private void find(Value v) {
272 v = inColumn.convert(v);
273 int id = inColumn.getColumnId();
274 if (start == null) {
275 start = table.getTemplateRow();
276 }
277 start.setValue(id, v);
278 cursor = index.find(tableFilter, start, start);
279 }
280
281 public boolean previous() {
282 throw DbException.throwInternalError();
283 }
284
285 }
286