File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\index\ViewIndex.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 org.h2.command.dml.Query;
11 import org.h2.command.dml.SelectUnion;
12 import org.h2.constant.ErrorCode;
13 import org.h2.engine.Constants;
14 import org.h2.engine.Session;
15 import org.h2.expression.Comparison;
16 import org.h2.expression.Parameter;
17 import org.h2.message.DbException;
18 import org.h2.result.LocalResult;
19 import org.h2.result.ResultInterface;
20 import org.h2.result.Row;
21 import org.h2.result.SearchRow;
22 import org.h2.table.Column;
23 import org.h2.table.IndexColumn;
24 import org.h2.table.TableView;
25 import org.h2.util.IntArray;
26 import org.h2.util.New;
27 import org.h2.util.SmallLRUCache;
28 import org.h2.util.SynchronizedVerifier;
29 import org.h2.util.Utils;
30 import org.h2.value.Value;
31
32 /**
33 * This object represents a virtual index for a query.
34 * Actually it only represents a prepared SELECT statement.
35 */
36 public class ViewIndex extends BaseIndex {
37
38 private final TableView view;
39 private final String querySQL;
40 private final ArrayList<Parameter> originalParameters;
41 private final SmallLRUCache<IntArray, CostElement> costCache =
42 SmallLRUCache.newInstance(Constants.VIEW_INDEX_CACHE_SIZE);
43 private boolean recursive;
44 private final int[] indexMasks;
45 private String planSQL;
46 private Query query;
47 private final Session createSession;
48
49 public ViewIndex(TableView view, String querySQL, ArrayList<Parameter> originalParameters, boolean recursive) {
50 initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
51 this.view = view;
52 this.querySQL = querySQL;
53 this.originalParameters = originalParameters;
54 this.recursive = recursive;
55 columns = new Column[0];
56 this.createSession = null;
57 this.indexMasks = null;
58 }
59
60 public ViewIndex(TableView view, ViewIndex index, Session session, int[] masks) {
61 initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
62 this.view = view;
63 this.querySQL = index.querySQL;
64 this.originalParameters = index.originalParameters;
65 this.recursive = index.recursive;
66 this.indexMasks = masks;
67 this.createSession = session;
68 columns = new Column[0];
69 if (!recursive) {
70 query = getQuery(session, masks);
71 planSQL = query.getPlanSQL();
72 }
73 }
74
75 public Session getSession() {
76 return createSession;
77 }
78
79 public String getPlanSQL() {
80 return planSQL;
81 }
82
83 public void close(Session session) {
84 // nothing to do
85 }
86
87 public void add(Session session, Row row) {
88 throw DbException.getUnsupportedException("VIEW");
89 }
90
91 public void remove(Session session, Row row) {
92 throw DbException.getUnsupportedException("VIEW");
93 }
94
95 /**
96 * A calculated cost value.
97 */
98 static class CostElement {
99
100 /**
101 * The time in milliseconds when this cost was calculated.
102 */
103 long evaluatedAt;
104
105 /**
106 * The cost.
107 */
108 double cost;
109 }
110
111 public synchronized double getCost(Session session, int[] masks) {
112 if (recursive) {
113 return 1000;
114 }
115 IntArray masksArray = new IntArray(masks == null ? Utils.EMPTY_INT_ARRAY : masks);
116 SynchronizedVerifier.check(costCache);
117 CostElement cachedCost = costCache.get(masksArray);
118 if (cachedCost != null) {
119 long time = System.currentTimeMillis();
120 if (time < cachedCost.evaluatedAt + Constants.VIEW_COST_CACHE_MAX_AGE) {
121 return cachedCost.cost;
122 }
123 }
124 Query q = (Query) session.prepare(querySQL, true);
125 if (masks != null) {
126 IntArray paramIndex = new IntArray();
127 for (int i = 0; i < masks.length; i++) {
128 int mask = masks[i];
129 if (mask == 0) {
130 continue;
131 }
132 paramIndex.add(i);
133 }
134 int len = paramIndex.size();
135 for (int i = 0; i < len; i++) {
136 int idx = paramIndex.get(i);
137 int mask = masks[idx];
138 int nextParamIndex = q.getParameters().size() + view.getParameterOffset();
139 if ((mask & IndexCondition.EQUALITY) != 0) {
140 Parameter param = new Parameter(nextParamIndex);
141 q.addGlobalCondition(param, idx, Comparison.EQUAL_NULL_SAFE);
142 } else {
143 if ((mask & IndexCondition.START) != 0) {
144 Parameter param = new Parameter(nextParamIndex);
145 q.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
146 }
147 if ((mask & IndexCondition.END) != 0) {
148 Parameter param = new Parameter(nextParamIndex);
149 q.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
150 }
151 }
152 }
153 String sql = q.getPlanSQL();
154 q = (Query) session.prepare(sql, true);
155 }
156 double cost = q.getCost();
157 cachedCost = new CostElement();
158 cachedCost.evaluatedAt = System.currentTimeMillis();
159 cachedCost.cost = cost;
160 costCache.put(masksArray, cachedCost);
161 return cost;
162 }
163
164 public Cursor find(Session session, SearchRow first, SearchRow last) {
165 if (recursive) {
166 ResultInterface recResult = view.getRecursiveResult();
167 if (recResult != null) {
168 recResult.reset();
169 return new ViewCursor(this, recResult, first, last);
170 }
171 if (query == null) {
172 query = (Query) createSession.prepare(querySQL, true);
173 planSQL = query.getPlanSQL();
174 }
175 if (!(query instanceof SelectUnion)) {
176 throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL");
177 }
178 SelectUnion union = (SelectUnion) query;
179 if (union.getUnionType() != SelectUnion.UNION_ALL) {
180 throw DbException.get(ErrorCode.SYNTAX_ERROR_2, "recursive queries without UNION ALL");
181 }
182 Query left = union.getLeft();
183 // to ensure the last result is not closed
184 left.disableCache();
185 LocalResult r = left.query(0);
186 LocalResult result = union.getEmptyResult();
187 while (r.next()) {
188 result.addRow(r.currentRow());
189 }
190 Query right = union.getRight();
191 r.reset();
192 view.setRecursiveResult(r);
193 // to ensure the last result is not closed
194 right.disableCache();
195 while (true) {
196 r = right.query(0);
197 if (r.getRowCount() == 0) {
198 break;
199 }
200 while (r.next()) {
201 result.addRow(r.currentRow());
202 }
203 r.reset();
204 view.setRecursiveResult(r);
205 }
206 view.setRecursiveResult(null);
207 result.done();
208 return new ViewCursor(this, result, first, last);
209 }
210 ArrayList<Parameter> paramList = query.getParameters();
211 if (originalParameters != null) {
212 for (int i = 0, size = originalParameters.size(); i < size; i++) {
213 Parameter orig = originalParameters.get(i);
214 int idx = orig.getIndex();
215 Value value = orig.getValue(session);
216 setParameter(paramList, idx, value);
217 }
218 }
219 int len;
220 if (first != null) {
221 len = first.getColumnCount();
222 } else if (last != null) {
223 len = last.getColumnCount();
224 } else {
225 len = 0;
226 }
227 int idx = originalParameters == null ? 0 : originalParameters.size();
228 idx += view.getParameterOffset();
229 for (int i = 0; i < len; i++) {
230 if (first != null) {
231 Value v = first.getValue(i);
232 if (v != null) {
233 int x = idx++;
234 setParameter(paramList, x, v);
235 }
236 }
237 // for equality, only one parameter is used (first == last)
238 if (last != null && indexMasks[i] != IndexCondition.EQUALITY) {
239 Value v = last.getValue(i);
240 if (v != null) {
241 int x = idx++;
242 setParameter(paramList, x, v);
243 }
244 }
245 }
246 ResultInterface result = query.query(0);
247 return new ViewCursor(this, result, first, last);
248 }
249
250 private static void setParameter(ArrayList<Parameter> paramList, int x, Value v) {
251 if (x >= paramList.size()) {
252 // the parameter may be optimized away as in
253 // select * from (select null as x) where x=1;
254 return;
255 }
256 Parameter param = paramList.get(x);
257 param.setValue(v);
258 }
259
260 private Query getQuery(Session session, int[] masks) {
261 Query q = (Query) session.prepare(querySQL, true);
262 if (masks == null) {
263 return q;
264 }
265 if (!q.allowGlobalConditions()) {
266 return q;
267 }
268 int firstIndexParam = originalParameters == null ? 0 : originalParameters.size();
269 firstIndexParam += view.getParameterOffset();
270 IntArray paramIndex = new IntArray();
271 int indexColumnCount = 0;
272 for (int i = 0; i < masks.length; i++) {
273 int mask = masks[i];
274 if (mask == 0) {
275 continue;
276 }
277 indexColumnCount++;
278 paramIndex.add(i);
279 if (Integer.bitCount(mask) > 1) {
280 // two parameters for range queries: >= x AND <= y
281 paramIndex.add(i);
282 }
283 }
284 int len = paramIndex.size();
285 ArrayList<Column> columnList = New.arrayList();
286 for (int i = 0; i < len;) {
287 int idx = paramIndex.get(i);
288 columnList.add(table.getColumn(idx));
289 int mask = masks[idx];
290 if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
291 Parameter param = new Parameter(firstIndexParam + i);
292 q.addGlobalCondition(param, idx, Comparison.EQUAL_NULL_SAFE);
293 i++;
294 }
295 if ((mask & IndexCondition.START) == IndexCondition.START) {
296 Parameter param = new Parameter(firstIndexParam + i);
297 q.addGlobalCondition(param, idx, Comparison.BIGGER_EQUAL);
298 i++;
299 }
300 if ((mask & IndexCondition.END) == IndexCondition.END) {
301 Parameter param = new Parameter(firstIndexParam + i);
302 q.addGlobalCondition(param, idx, Comparison.SMALLER_EQUAL);
303 i++;
304 }
305 }
306 columns = new Column[columnList.size()];
307 columnList.toArray(columns);
308
309 // reconstruct the index columns from the masks
310 this.indexColumns = new IndexColumn[indexColumnCount];
311 this.columnIds = new int[indexColumnCount];
312 for (int type = 0, indexColumnId = 0; type < 2; type++) {
313 for (int i = 0; i < masks.length; i++) {
314 int mask = masks[i];
315 if (mask == 0) {
316 continue;
317 }
318 if (type == 0) {
319 if ((mask & IndexCondition.EQUALITY) != IndexCondition.EQUALITY) {
320 // the first columns need to be equality conditions
321 continue;
322 }
323 } else {
324 if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
325 // then only range conditions
326 continue;
327 }
328 }
329 IndexColumn c = new IndexColumn();
330 c.column = table.getColumn(i);
331 indexColumns[indexColumnId] = c;
332 columnIds[indexColumnId] = c.column.getColumnId();
333 indexColumnId++;
334 }
335 }
336
337 String sql = q.getPlanSQL();
338 q = (Query) session.prepare(sql, true);
339 return q;
340 }
341
342 public void remove(Session session) {
343 throw DbException.getUnsupportedException("VIEW");
344 }
345
346 public void truncate(Session session) {
347 throw DbException.getUnsupportedException("VIEW");
348 }
349
350 public void checkRename() {
351 throw DbException.getUnsupportedException("VIEW");
352 }
353
354 public boolean needRebuild() {
355 return false;
356 }
357
358 public boolean canGetFirstOrLast() {
359 return false;
360 }
361
362 public Cursor findFirstOrLast(Session session, boolean first) {
363 throw DbException.getUnsupportedException("VIEW");
364 }
365
366 public void setRecursive(boolean value) {
367 this.recursive = value;
368 }
369
370 public long getRowCount(Session session) {
371 return 0;
372 }
373
374 public long getRowCountApproximation() {
375 return 0;
376 }
377
378 public boolean isRecursive() {
379 return recursive;
380 }
381
382 }
383