001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.result;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.SysProperties;
011: import org.h2.engine.Constants;
012: import org.h2.engine.Database;
013: import org.h2.engine.Session;
014: import org.h2.store.DataPage;
015: import org.h2.store.FileStore;
016: import org.h2.util.Cache;
017: import org.h2.util.CacheObject;
018: import org.h2.util.ObjectArray;
019: import org.h2.value.Value;
020: import org.h2.value.ValueLob;
021:
022: /**
023: * A list of rows. If the list grows too large, it is buffered to disk
024: * automatically.
025: */
026: public class RowList {
027: private final Session session;
028: private ObjectArray list = new ObjectArray();
029: private int size;
030: private int index, listIndex;
031: private FileStore file;
032: private DataPage rowBuff;
033: private Cache cache;
034: private ObjectArray lobs;
035: private int memory, maxMemory;
036: private boolean written;
037: private boolean readUncached;
038:
039: public RowList(Session session) {
040: this .session = session;
041: if (SysProperties.DEFAULT_MAX_OPERATION_MEMORY > 0
042: && session.getDatabase().isPersistent()) {
043: maxMemory = session.getDatabase().getMaxOperationMemory();
044: }
045: }
046:
047: private void writeRow(DataPage buff, Row r) throws SQLException {
048: buff.checkCapacity(1 + buff.getIntLen() * 6);
049: buff.writeByte((byte) 1);
050: buff.writeInt(r.getMemorySize());
051: buff.writeInt(r.getColumnCount());
052: buff.writeInt(r.getPos());
053: buff.writeInt(r.getDeleted() ? 1 : 0);
054: buff.writeInt(r.getSessionId());
055: buff.writeInt(r.getStorageId());
056: for (int i = 0; i < r.getColumnCount(); i++) {
057: Value v = r.getValue(i);
058: if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) {
059: // need to keep a reference to temporary lobs,
060: // otherwise the temp file is deleted
061: ValueLob lob = (ValueLob) v;
062: if (lob.getSmall() == null && lob.getTableId() == 0) {
063: if (lobs == null) {
064: lobs = new ObjectArray();
065: }
066: lobs.add(lob);
067: }
068: }
069: buff.checkCapacity(buff.getValueLen(v));
070: buff.writeValue(v);
071: }
072: }
073:
074: private void writeAllRows() throws SQLException {
075: if (file == null) {
076: Database db = session.getDatabase();
077: this .cache = db.getDataFile().getCache();
078: String fileName = db.createTempFile();
079: file = db.openFile(fileName, "rw", false);
080: file.autoDelete();
081: file.seek(FileStore.HEADER_LENGTH);
082: rowBuff = DataPage.create(db,
083: Constants.DEFAULT_DATA_PAGE_SIZE);
084: file.seek(FileStore.HEADER_LENGTH);
085: }
086: DataPage buff = rowBuff;
087: initBuffer(buff);
088: for (int i = 0; i < list.size(); i++) {
089: if (i > 0 && buff.length() > Constants.IO_BUFFER_SIZE) {
090: flushBuffer(buff);
091: initBuffer(buff);
092: }
093: Row r = (Row) list.get(i);
094: writeRow(buff, r);
095: }
096: flushBuffer(buff);
097: list.clear();
098: memory = 0;
099: }
100:
101: private void initBuffer(DataPage buff) {
102: buff.reset();
103: buff.writeInt(0);
104: }
105:
106: private void flushBuffer(DataPage buff) throws SQLException {
107: buff.checkCapacity(1);
108: buff.writeByte((byte) 0);
109: buff.fillAligned();
110: buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
111: buff.updateChecksum();
112: file.write(buff.getBytes(), 0, buff.length());
113: }
114:
115: public void add(Row r) throws SQLException {
116: list.add(r);
117: memory += r.getMemorySize();
118: if (maxMemory > 0 && memory > maxMemory) {
119: writeAllRows();
120: }
121: size++;
122: }
123:
124: public void reset() throws SQLException {
125: index = 0;
126: if (file != null) {
127: listIndex = 0;
128: if (!written) {
129: writeAllRows();
130: written = true;
131: }
132: list.clear();
133: file.seek(FileStore.HEADER_LENGTH);
134: }
135: }
136:
137: public boolean hasNext() {
138: return index < size;
139: }
140:
141: private Row readRow(DataPage buff) throws SQLException {
142: if (buff.readByte() == 0) {
143: return null;
144: }
145: int memory = buff.readInt();
146: int columnCount = buff.readInt();
147: int pos = buff.readInt();
148: if (readUncached) {
149: pos = 0;
150: }
151: boolean deleted = buff.readInt() == 1;
152: int sessionId = buff.readInt();
153: int storageId = buff.readInt();
154: Value[] values = new Value[columnCount];
155: for (int i = 0; i < columnCount; i++) {
156: Value v = buff.readValue();
157: if (v.isLinked()) {
158: ValueLob lob = (ValueLob) v;
159: // the table id is 0 if it was linked when writing
160: // a temporary entry
161: if (lob.getTableId() == 0) {
162: session.unlinkAtCommit(v);
163: }
164: }
165: values[i] = v;
166: }
167: if (pos != 0) {
168: CacheObject found = cache.find(pos);
169: if (found != null) {
170: return (Row) found;
171: }
172: }
173: Row row = new Row(values, memory);
174: row.setPos(pos);
175: row.setDeleted(deleted);
176: row.setSessionId(sessionId);
177: row.setStorageId(storageId);
178: return row;
179: }
180:
181: public Row next() throws SQLException {
182: Row r;
183: if (file == null) {
184: r = (Row) list.get(index++);
185: } else {
186: if (listIndex >= list.size()) {
187: list.clear();
188: listIndex = 0;
189: DataPage buff = rowBuff;
190: buff.reset();
191: int min = Constants.FILE_BLOCK_SIZE;
192: file.readFully(buff.getBytes(), 0, min);
193: int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
194: buff.checkCapacity(len);
195: if (len - min > 0) {
196: file.readFully(buff.getBytes(), min, len - min);
197: }
198: buff.check(len);
199: for (int i = 0;; i++) {
200: r = readRow(buff);
201: if (r == null) {
202: break;
203: }
204: list.add(r);
205: }
206: }
207: index++;
208: r = (Row) list.get(listIndex++);
209: }
210: return r;
211: }
212:
213: public int size() {
214: return size;
215: }
216:
217: public void invalidateCache() {
218: readUncached = true;
219: }
220:
221: public void close() {
222: if (file != null) {
223: file.closeAndDeleteSilently();
224: file = null;
225: rowBuff = null;
226: }
227: }
228:
229: }
|