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.io.IOException;
009: import java.sql.SQLException;
010:
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.SessionRemote;
013: import org.h2.message.Message;
014: import org.h2.message.Trace;
015: import org.h2.util.ObjectArray;
016: import org.h2.value.Transfer;
017: import org.h2.value.Value;
018:
019: /**
020: * The client side part of a result set that is kept on the server.
021: * In many cases, the complete data is kept on the client side,
022: * but for large results only a subset is in-memory.
023: */
024: public class ResultRemote implements ResultInterface {
025:
026: private final int fetchSize;
027: private SessionRemote session;
028: private Transfer transfer;
029: private int id;
030: private ResultColumn[] columns;
031: private Value[] currentRow;
032: private int rowId, rowCount, rowOffset;
033: private ObjectArray result;
034: private ObjectArray lobValues;
035:
036: public boolean isUpdateCount() {
037: return false;
038: }
039:
040: public int getUpdateCount() {
041: return 0;
042: }
043:
044: public ResultRemote(SessionRemote session, Transfer transfer,
045: int id, int columnCount, int fetchSize) throws IOException,
046: SQLException {
047: this .session = session;
048: this .transfer = transfer;
049: this .id = id;
050: this .columns = new ResultColumn[columnCount];
051: rowCount = transfer.readInt();
052: for (int i = 0; i < columnCount; i++) {
053: columns[i] = new ResultColumn(transfer);
054: }
055: rowId = -1;
056: result = new ObjectArray();
057: this .fetchSize = fetchSize;
058: fetchRows(false);
059: }
060:
061: public String getAlias(int i) {
062: return columns[i].alias;
063: }
064:
065: public String getSchemaName(int i) {
066: return columns[i].schemaName;
067: }
068:
069: public String getTableName(int i) {
070: return columns[i].tableName;
071: }
072:
073: public String getColumnName(int i) {
074: return columns[i].columnName;
075: }
076:
077: public int getColumnType(int i) {
078: return columns[i].columnType;
079: }
080:
081: public long getColumnPrecision(int i) {
082: return columns[i].precision;
083: }
084:
085: public int getColumnScale(int i) {
086: return columns[i].scale;
087: }
088:
089: public int getDisplaySize(int i) {
090: return columns[i].displaySize;
091: }
092:
093: public boolean isAutoIncrement(int i) {
094: return columns[i].autoIncrement;
095: }
096:
097: public int getNullable(int i) {
098: return columns[i].nullable;
099: }
100:
101: public void reset() throws SQLException {
102: rowId = -1;
103: currentRow = null;
104: if (session == null) {
105: return;
106: }
107: synchronized (session) {
108: session.checkClosed();
109: try {
110: session.traceOperation("RESULT_RESET", id);
111: transfer.writeInt(SessionRemote.RESULT_RESET).writeInt(
112: id).flush();
113: } catch (IOException e) {
114: throw Message.convertIOException(e, null);
115: }
116: }
117: }
118:
119: public Value[] currentRow() {
120: return currentRow;
121: }
122:
123: public boolean next() throws SQLException {
124: if (rowId < rowCount) {
125: rowId++;
126: remapIfOld();
127: if (rowId < rowCount) {
128: if (rowId - rowOffset >= result.size()) {
129: fetchRows(true);
130: }
131: currentRow = (Value[]) result.get(rowId - rowOffset);
132: return true;
133: }
134: currentRow = null;
135: }
136: return false;
137: }
138:
139: public int getRowId() {
140: return rowId;
141: }
142:
143: public int getVisibleColumnCount() {
144: return columns.length;
145: }
146:
147: public int getRowCount() {
148: return rowCount;
149: }
150:
151: private void sendClose() {
152: if (session == null) {
153: return;
154: }
155: // TODO result sets: no reset possible for larger remote result sets
156: synchronized (session) {
157: try {
158: session.traceOperation("RESULT_CLOSE", id);
159: transfer.writeInt(SessionRemote.RESULT_CLOSE).writeInt(
160: id);
161: } catch (IOException e) {
162: session.getTrace().error("close", e);
163: } finally {
164: transfer = null;
165: session = null;
166: }
167: }
168: }
169:
170: public void close() {
171: if (session == null) {
172: return;
173: }
174: result = null;
175: Trace trace = session.getTrace();
176: sendClose();
177: if (lobValues != null) {
178: for (int i = 0; i < lobValues.size(); i++) {
179: Value v = (Value) lobValues.get(i);
180: try {
181: v.close();
182: } catch (SQLException e) {
183: trace.error("delete lob " + v.getSQL(), e);
184: }
185: }
186: lobValues = null;
187: }
188: }
189:
190: private void remapIfOld() throws SQLException {
191: if (session == null) {
192: return;
193: }
194: try {
195: if (id <= session.getCurrentId()
196: - SysProperties.SERVER_CACHED_OBJECTS / 2) {
197: // object is too old - we need to map it to a new id
198: int newId = session.getNextId();
199: session.traceOperation("CHANGE_ID", id);
200: transfer.writeInt(SessionRemote.CHANGE_ID).writeInt(id)
201: .writeInt(newId);
202: id = newId;
203: // TODO remote result set: very old result sets may be
204: // already removed on the server (theoretically) - how to
205: // solve this?
206: }
207: } catch (IOException e) {
208: throw Message.convertIOException(e, null);
209: }
210: }
211:
212: private void fetchRows(boolean sendFetch) throws SQLException {
213: synchronized (session) {
214: session.checkClosed();
215: try {
216: rowOffset += result.size();
217: result.clear();
218: int fetch = Math.min(fetchSize, rowCount - rowOffset);
219: if (sendFetch) {
220: session.traceOperation("RESULT_FETCH_ROWS", id);
221: transfer.writeInt(SessionRemote.RESULT_FETCH_ROWS)
222: .writeInt(id).writeInt(fetch);
223: session.done(transfer);
224: }
225: for (int r = 0; r < fetch; r++) {
226: boolean row = transfer.readBoolean();
227: if (!row) {
228: break;
229: }
230: int len = columns.length;
231: Value[] values = new Value[len];
232: for (int i = 0; i < len; i++) {
233: Value v = transfer.readValue();
234: values[i] = v;
235: if (v.isFileBased()) {
236: if (lobValues == null) {
237: lobValues = new ObjectArray();
238: }
239: lobValues.add(v);
240: }
241: }
242: result.add(values);
243: }
244: if (rowOffset + result.size() >= rowCount) {
245: sendClose();
246: }
247: } catch (IOException e) {
248: throw Message.convertIOException(e, null);
249: }
250: }
251: }
252:
253: public String toString() {
254: return "columns: " + columns.length + " rows: " + rowCount
255: + " pos: " + rowId;
256: }
257:
258: }
|