File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\result\LocalResult.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.result;
8
9 import java.sql.ResultSet;
10 import java.sql.SQLException;
11 import java.util.ArrayList;
12 import org.h2.engine.Database;
13 import org.h2.engine.Session;
14 import org.h2.expression.Expression;
15 import org.h2.message.DbException;
16 import org.h2.util.New;
17 import org.h2.util.ValueHashMap;
18 import org.h2.value.DataType;
19 import org.h2.value.Value;
20 import org.h2.value.ValueArray;
21
22 /**
23 * A local result set contains all row data of a result set.
24 * This is the object generated by engine,
25 * and it is also used directly by the ResultSet class in the embedded mode.
26 * If the result does not fit in memory, it is written to a temporary file.
27 */
28 public class LocalResult implements ResultInterface, ResultTarget {
29
30 private int maxMemoryRows;
31 private Session session;
32 private int visibleColumnCount;
33 private Expression[] expressions;
34 private int rowId, rowCount;
35 private ArrayList<Value[]> rows;
36 private SortOrder sort;
37 private ValueHashMap<Value[]> distinctRows;
38 private Value[] currentRow;
39 private int offset;
40 private int limit = -1;
41 private ResultExternal external;
42 private int diskOffset;
43 private boolean distinct;
44 private boolean randomAccess;
45 private boolean closed;
46
47 /**
48 * Construct a local result object.
49 */
50 public LocalResult() {
51 // nothing to do
52 }
53
54 /**
55 * Construct a local result object.
56 *
57 * @param session the session
58 * @param expressions the expression array
59 * @param visibleColumnCount the number of visible columns
60 */
61 public LocalResult(Session session, Expression[] expressions, int visibleColumnCount) {
62 this.session = session;
63 if (session == null) {
64 this.maxMemoryRows = Integer.MAX_VALUE;
65 } else {
66 this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
67 }
68 rows = New.arrayList();
69 this.visibleColumnCount = visibleColumnCount;
70 rowId = -1;
71 this.expressions = expressions;
72 }
73
74 /**
75 * Construct a local result set by reading all data from a regular result set.
76 *
77 * @param session the session
78 * @param rs the result set
79 * @param maxrows the maximum number of rows to read (0 for no limit)
80 * @return the local result set
81 */
82 public static LocalResult read(Session session, ResultSet rs, int maxrows) {
83 Expression[] cols = Expression.getExpressionColumns(session, rs);
84 int columnCount = cols.length;
85 LocalResult result = new LocalResult(session, cols, columnCount);
86 try {
87 for (int i = 0; (maxrows == 0 || i < maxrows) && rs.next(); i++) {
88 Value[] list = new Value[columnCount];
89 for (int j = 0; j < columnCount; j++) {
90 int type = result.getColumnType(j);
91 list[j] = DataType.readValue(session, rs, j + 1, type);
92 }
93 result.addRow(list);
94 }
95 } catch (SQLException e) {
96 throw DbException.convert(e);
97 }
98 result.done();
99 return result;
100 }
101
102 /**
103 * Create a shallow copy of the result set. The data and a temporary table
104 * (if there is any) is not copied.
105 *
106 * @param targetSession the session of the copy
107 * @return the copy
108 */
109 public LocalResult createShallowCopy(Session targetSession) {
110 if (external == null && (rows == null || rows.size() < rowCount)) {
111 return null;
112 }
113 ResultExternal e2 = null;
114 if (external != null) {
115 e2 = external.createShallowCopy();
116 if (e2 == null) {
117 return null;
118 }
119 }
120 LocalResult copy = new LocalResult();
121 copy.maxMemoryRows = this.maxMemoryRows;
122 copy.session = targetSession;
123 copy.visibleColumnCount = this.visibleColumnCount;
124 copy.expressions = this.expressions;
125 copy.rowId = -1;
126 copy.rowCount = this.rowCount;
127 copy.rows = this.rows;
128 copy.sort = this.sort;
129 copy.distinctRows = this.distinctRows;
130 copy.distinct = distinct;
131 copy.randomAccess = randomAccess;
132 copy.currentRow = null;
133 copy.offset = 0;
134 copy.limit = -1;
135 copy.external = e2;
136 copy.diskOffset = this.diskOffset;
137 return copy;
138 }
139
140 /**
141 * Set the sort order.
142 *
143 * @param sort the sort order
144 */
145 public void setSortOrder(SortOrder sort) {
146 this.sort = sort;
147 }
148
149 /**
150 * Remove duplicate rows.
151 */
152 public void setDistinct() {
153 distinct = true;
154 distinctRows = ValueHashMap.newInstance();
155 }
156
157 /**
158 * Random access is required (containsDistinct).
159 */
160 public void setRandomAccess() {
161 this.randomAccess = true;
162 }
163
164 /**
165 * Remove the row from the result set if it exists.
166 *
167 * @param values the row
168 */
169 public void removeDistinct(Value[] values) {
170 if (!distinct) {
171 DbException.throwInternalError();
172 }
173 if (distinctRows != null) {
174 ValueArray array = ValueArray.get(values);
175 distinctRows.remove(array);
176 rowCount = distinctRows.size();
177 } else {
178 rowCount = external.removeRow(values);
179 }
180 }
181
182 /**
183 * Check if this result set contains the given row.
184 *
185 * @param values the row
186 * @return true if the row exists
187 */
188 public boolean containsDistinct(Value[] values) {
189 if (external != null) {
190 return external.contains(values);
191 }
192 if (distinctRows == null) {
193 distinctRows = ValueHashMap.newInstance();
194 for (Value[] row : rows) {
195 if (row.length > visibleColumnCount) {
196 Value[] r2 = new Value[visibleColumnCount];
197 System.arraycopy(row, 0, r2, 0, visibleColumnCount);
198 row = r2;
199 }
200 ValueArray array = ValueArray.get(row);
201 distinctRows.put(array, row);
202 }
203 }
204 ValueArray array = ValueArray.get(values);
205 return distinctRows.get(array) != null;
206 }
207
208 public void reset() {
209 rowId = -1;
210 if (external != null) {
211 external.reset();
212 if (diskOffset > 0) {
213 for (int i = 0; i < diskOffset; i++) {
214 external.next();
215 }
216 }
217 }
218 }
219
220 public Value[] currentRow() {
221 return currentRow;
222 }
223
224 public boolean next() {
225 if (rowId < rowCount) {
226 rowId++;
227 if (rowId < rowCount) {
228 if (external != null) {
229 currentRow = external.next();
230 } else {
231 currentRow = rows.get(rowId);
232 }
233 return true;
234 }
235 currentRow = null;
236 }
237 return false;
238 }
239
240 public int getRowId() {
241 return rowId;
242 }
243
244 /**
245 * Add a row to this object.
246 *
247 * @param values the row to add
248 */
249 public void addRow(Value[] values) {
250 if (distinct) {
251 if (distinctRows != null) {
252 ValueArray array = ValueArray.get(values);
253 distinctRows.put(array, values);
254 rowCount = distinctRows.size();
255 Database db = session.getDatabase();
256 if (rowCount > db.getSettings().maxMemoryRowsDistinct && db.isPersistent() && !db.isReadOnly()) {
257 external = new ResultTempTable(session, sort);
258 rowCount = external.addRows(distinctRows.values());
259 distinctRows = null;
260 }
261 } else {
262 rowCount = external.addRow(values);
263 }
264 return;
265 }
266 rows.add(values);
267 rowCount++;
268 if (rows.size() > maxMemoryRows && session.getDatabase().isPersistent()) {
269 if (external == null) {
270 if (randomAccess) {
271 Database db = session.getDatabase();
272 if (!db.isReadOnly()) {
273 external = new ResultTempTable(session, sort);
274 }
275 } else {
276 external = new ResultDiskBuffer(session, sort, values.length);
277 }
278 }
279 addRowsToDisk();
280 }
281 }
282
283 private void addRowsToDisk() {
284 rowCount = external.addRows(rows);
285 rows.clear();
286 }
287
288 public int getVisibleColumnCount() {
289 return visibleColumnCount;
290 }
291
292 /**
293 * This method is called after all rows have been added.
294 */
295 public void done() {
296 if (distinct) {
297 if (distinctRows != null) {
298 rows = distinctRows.values();
299 } else {
300 if (external != null && sort != null) {
301 // external sort
302 ResultExternal temp = external;
303 external = null;
304 temp.reset();
305 rows = New.arrayList();
306 // TODO use offset directly if possible
307 while (true) {
308 Value[] list = temp.next();
309 if (list == null) {
310 break;
311 }
312 if (external == null) {
313 if (randomAccess) {
314 Database db = session.getDatabase();
315 if (!db.isReadOnly()) {
316 external = new ResultTempTable(session, sort);
317 }
318 } else {
319 external = new ResultDiskBuffer(session, sort, list.length);
320 }
321 }
322 rows.add(list);
323 if (rows.size() > maxMemoryRows) {
324 rowCount = external.addRows(rows);
325 rows.clear();
326 }
327 }
328 temp.close();
329 // the remaining data in rows is written in the following lines
330 }
331 }
332 }
333 if (external != null) {
334 addRowsToDisk();
335 external.done();
336 } else {
337 if (sort != null) {
338 sort.sort(rows);
339 }
340 }
341 applyOffset();
342 applyLimit();
343 reset();
344 }
345
346 public int getRowCount() {
347 return rowCount;
348 }
349
350 /**
351 * Set the number of rows that this result will return at the maximum.
352 *
353 * @param limit the limit (-1 means no limit, 0 means no rows)
354 */
355 public void setLimit(int limit) {
356 this.limit = limit;
357 }
358
359 private void applyLimit() {
360 if (limit < 0) {
361 return;
362 }
363 if (external == null) {
364 if (rows.size() > limit) {
365 rows = New.arrayList(rows.subList(0, limit));
366 rowCount = limit;
367 }
368 } else {
369 if (limit < rowCount) {
370 rowCount = limit;
371 }
372 }
373 }
374
375 public boolean needToClose() {
376 return external != null;
377 }
378
379 public void close() {
380 if (external != null) {
381 external.close();
382 external = null;
383 closed = true;
384 }
385 }
386
387 public String getAlias(int i) {
388 return expressions[i].getAlias();
389 }
390
391 public String getTableName(int i) {
392 return expressions[i].getTableName();
393 }
394
395 public String getSchemaName(int i) {
396 return expressions[i].getSchemaName();
397 }
398
399 public int getDisplaySize(int i) {
400 return expressions[i].getDisplaySize();
401 }
402
403 public String getColumnName(int i) {
404 return expressions[i].getColumnName();
405 }
406
407 public int getColumnType(int i) {
408 return expressions[i].getType();
409 }
410
411 public long getColumnPrecision(int i) {
412 return expressions[i].getPrecision();
413 }
414
415 public int getNullable(int i) {
416 return expressions[i].getNullable();
417 }
418
419 public boolean isAutoIncrement(int i) {
420 return expressions[i].isAutoIncrement();
421 }
422
423 public int getColumnScale(int i) {
424 return expressions[i].getScale();
425 }
426
427 /**
428 * Set the offset of the first row to return.
429 *
430 * @param offset the offset
431 */
432 public void setOffset(int offset) {
433 this.offset = offset;
434 }
435
436 private void applyOffset() {
437 if (offset <= 0) {
438 return;
439 }
440 if (external == null) {
441 if (offset >= rows.size()) {
442 rows.clear();
443 rowCount = 0;
444 } else {
445 // avoid copying the whole array for each row
446 int remove = Math.min(offset, rows.size());
447 rows = New.arrayList(rows.subList(remove, rows.size()));
448 rowCount -= remove;
449 }
450 } else {
451 if (offset >= rowCount) {
452 rowCount = 0;
453 } else {
454 diskOffset = offset;
455 rowCount -= offset;
456 }
457 }
458 }
459
460 public String toString() {
461 return super.toString() + " columns: " + visibleColumnCount + " rows: " + rowCount + " pos: " + rowId;
462 }
463
464 /**
465 * Check if this result set is closed.
466 *
467 * @return true if it is
468 */
469 public boolean isClosed() {
470 return closed;
471 }
472
473 public int getFetchSize() {
474 return 0;
475 }
476
477 public void setFetchSize(int fetchSize) {
478 // ignore
479 }
480
481 }
482