001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: * Free SoftwareFoundation, Inc.
023: * 59 Temple Place, Suite 330
024: * Boston, MA 02111-1307 USA
025: *
026: * @author Scott Ferguson
027: */
028:
029: package com.caucho.db.sql;
030:
031: import com.caucho.db.ResultSetImpl;
032: import com.caucho.db.store.BlobInputStream;
033: import com.caucho.db.store.Store;
034: import com.caucho.db.table.Column;
035: import com.caucho.db.table.TableIterator;
036: import com.caucho.sql.SQLExceptionWrapper;
037: import com.caucho.util.CharBuffer;
038: import com.caucho.util.FreeList;
039: import com.caucho.util.L10N;
040: import com.caucho.util.QDate;
041: import com.caucho.vfs.ReadStream;
042: import com.caucho.vfs.TempBuffer;
043: import com.caucho.vfs.TempStream;
044: import com.caucho.vfs.WriteStream;
045:
046: import java.io.IOException;
047: import java.sql.SQLException;
048:
049: public class SelectResultSetImpl extends ResultSetImpl {
050: private static final L10N L = new L10N(SelectResultSetImpl.class);
051:
052: private static final FreeList<SelectResultSetImpl> _freeList = new FreeList<SelectResultSetImpl>(
053: 16);
054:
055: private final WriteStream _ws;
056: private final TempBuffer _buf;
057: private final ReadStream _rs;
058: private final byte[] _buffer;
059: private final CharBuffer _cb;
060: private final TempStream _ts;
061:
062: private static QDate _date = new QDate();
063:
064: private Expr[] _exprs;
065: private int[] _types = new int[32];
066: private int[] _offsets = new int[32];
067: private int[] _lengths = new int[32];
068: private Store[] _stores = new Store[32];
069:
070: private TableIterator[] _rows;
071:
072: private int _lastColumn;
073:
074: private SelectResultSetImpl() {
075: _ws = new WriteStream();
076: _ws.setReuseBuffer(true);
077: _ts = new TempStream();
078: _rs = new ReadStream();
079: _rs.setReuseBuffer(true);
080: _buf = TempBuffer.allocate();
081: _buffer = _buf.getBuffer();
082: _cb = new CharBuffer();
083:
084: _rows = new TableIterator[0];
085: }
086:
087: public static SelectResultSetImpl create(Expr[] exprs) {
088: SelectResultSetImpl rs = _freeList.allocate();
089:
090: if (rs == null)
091: rs = new SelectResultSetImpl();
092:
093: rs.init(exprs);
094:
095: return rs;
096: }
097:
098: TableIterator[] initRows(FromItem[] fromItems) {
099: if (_rows.length < fromItems.length)
100: _rows = new TableIterator[fromItems.length];
101:
102: for (int i = 0; i < fromItems.length; i++) {
103: if (_rows[i] == null)
104: _rows[i] = new TableIterator();
105: _rows[i].init(fromItems[i].getTable());
106: }
107:
108: return _rows;
109: }
110:
111: private void init(Expr[] exprs) {
112: _exprs = exprs;
113:
114: if (_offsets.length < _exprs.length) {
115: _offsets = new int[exprs.length];
116: _lengths = new int[exprs.length];
117: _types = new int[exprs.length];
118: _stores = new Store[exprs.length];
119: }
120:
121: for (int i = 0; i < exprs.length; i++) {
122: _stores[i] = exprs[i].getTable();
123: }
124: }
125:
126: void initRead() throws IOException {
127: _ts.openRead(_rs);
128: }
129:
130: WriteStream getWriteStream() {
131: _ts.openWrite();
132: _ws.init(_ts);
133:
134: return _ws;
135: }
136:
137: public boolean next() throws SQLException {
138: try {
139: ReadStream rs = _rs;
140:
141: _lastColumn = 0;
142:
143: int hasData = rs.read();
144: if (hasData <= 0)
145: return false;
146:
147: int length = 0;
148: int fields = _exprs.length;
149:
150: byte[] buffer = _buffer;
151: for (int i = 0; i < fields; i++) {
152: int type = rs.read();
153:
154: int sublen = 0;
155:
156: switch (type) {
157: case Column.NONE:
158: sublen = -1;
159: break;
160:
161: case Column.VARCHAR:
162: int l0 = rs.read();
163: int l1 = rs.read();
164: int l2 = rs.read();
165: int l3 = rs.read();
166:
167: sublen = ((l0 << 24) + (l1 << 16) + (l2 << 8) + (l3));
168: break;
169:
170: case Column.INT:
171: sublen = 4;
172: break;
173: case Column.LONG:
174: case Column.DOUBLE:
175: case Column.DATE:
176: sublen = 8;
177: break;
178:
179: case Column.BLOB:
180: sublen = 128;
181: break;
182:
183: default:
184: throw new SQLException("Unknown column: " + type);
185: }
186:
187: _types[i] = type;
188: _offsets[i] = length;
189: _lengths[i] = sublen;
190:
191: if (sublen > 0) {
192: rs.read(buffer, length, sublen);
193:
194: length += sublen;
195: }
196: }
197:
198: return true;
199: } catch (IOException e) {
200: throw new SQLExceptionWrapper(e);
201: }
202: }
203:
204: /**
205: * Returns the column index with the given name.
206: */
207: public int findColumnIndex(String name) throws SQLException {
208: for (int i = 0; i < _exprs.length; i++) {
209: if (_exprs[i].getName().equals(name))
210: return i + 1;
211: }
212:
213: throw new SQLException(L
214: .l("column `{0}' does not exist.", name));
215: }
216:
217: /**
218: * Returns the string value of the given index.
219: */
220: public String getString(int index) throws SQLException {
221: _lastColumn = index;
222:
223: byte[] buffer = _buffer;
224: int offset = _offsets[index];
225: int length = _lengths[index];
226:
227: switch (_types[index]) {
228: case Column.NONE:
229: return null;
230:
231: case Column.INT: {
232: int value = (((buffer[offset] & 0xff) << 24)
233: + ((buffer[offset + 1] & 0xff) << 16)
234: + ((buffer[offset + 2] & 0xff) << 8) + ((buffer[offset + 3] & 0xff)));
235:
236: return String.valueOf(value);
237: }
238:
239: case Column.LONG: {
240: long value = (((buffer[offset + 0] & 0xffL) << 56)
241: + ((buffer[offset + 1] & 0xffL) << 48)
242: + ((buffer[offset + 2] & 0xffL) << 40)
243: + ((buffer[offset + 3] & 0xffL) << 32)
244: + ((buffer[offset + 4] & 0xffL) << 24)
245: + ((buffer[offset + 5] & 0xffL) << 16)
246: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
247: return String.valueOf(value);
248: }
249:
250: case Column.DOUBLE: {
251: long value = (((buffer[offset + 0] & 0xffL) << 56)
252: + ((buffer[offset + 1] & 0xffL) << 48)
253: + ((buffer[offset + 2] & 0xffL) << 40)
254: + ((buffer[offset + 3] & 0xffL) << 32)
255: + ((buffer[offset + 4] & 0xffL) << 24)
256: + ((buffer[offset + 5] & 0xffL) << 16)
257: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
258: return String.valueOf(Double.longBitsToDouble(value));
259: }
260:
261: case Column.DATE: {
262: long value = (((buffer[offset + 0] & 0xffL) << 56)
263: + ((buffer[offset + 1] & 0xffL) << 48)
264: + ((buffer[offset + 2] & 0xffL) << 40)
265: + ((buffer[offset + 3] & 0xffL) << 32)
266: + ((buffer[offset + 4] & 0xffL) << 24)
267: + ((buffer[offset + 5] & 0xffL) << 16)
268: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
269: return QDate.formatGMT(value);
270: }
271:
272: case Column.VARCHAR:
273: return getStringValue(index);
274:
275: case Column.BLOB:
276: return getBlobString(index);
277:
278: default:
279: return null;
280: }
281: }
282:
283: /**
284: * Returns the string value for the result set.
285: */
286: private String getStringValue(int index) throws SQLException {
287: _lastColumn = index;
288:
289: int length = _lengths[index];
290: int offset = _offsets[index];
291:
292: if (length < 0)
293: return null;
294:
295: CharBuffer cb = _cb;
296: cb.clear();
297:
298: byte[] buffer = _buffer;
299: for (; length > 0; length--) {
300: cb.append((char) buffer[offset++]);
301: }
302:
303: return cb.toString();
304: }
305:
306: /**
307: * Returns the string value for the result set.
308: */
309: private String getBlobString(int index) throws SQLException {
310: _lastColumn = index;
311:
312: int offset = _offsets[index];
313:
314: CharBuffer cb = _cb;
315: cb.clear();
316:
317: BlobInputStream is = null;
318: try {
319: is = new BlobInputStream(_stores[index], _buffer, offset);
320:
321: int ch;
322: while ((ch = is.read()) >= 0) {
323: if (ch < 0x80)
324: cb.append((char) ch);
325: }
326: } catch (IOException e) {
327: throw new SQLExceptionWrapper(e);
328: }
329:
330: return cb.toString();
331: }
332:
333: public int getInt(int index) throws SQLException {
334: _lastColumn = index;
335:
336: byte[] buffer = _buffer;
337: int offset = _offsets[index];
338: int length = _lengths[index];
339:
340: switch (_types[index]) {
341: case Column.NONE:
342: return 0;
343:
344: case Column.INT:
345: return (((buffer[offset + 0] & 0xff) << 24)
346: + ((buffer[offset + 1] & 0xff) << 16)
347: + ((buffer[offset + 2] & 0xff) << 8) + ((buffer[offset + 3] & 0xff)));
348:
349: case Column.LONG: {
350: long value = (((buffer[offset + 0] & 0xffL) << 56)
351: + ((buffer[offset + 1] & 0xffL) << 48)
352: + ((buffer[offset + 2] & 0xffL) << 40)
353: + ((buffer[offset + 3] & 0xffL) << 32)
354: + ((buffer[offset + 4] & 0xffL) << 24)
355: + ((buffer[offset + 5] & 0xffL) << 16)
356: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
357: return (int) value;
358: }
359:
360: case Column.DOUBLE: {
361: long value = (((buffer[offset + 0] & 0xffL) << 56)
362: + ((buffer[offset + 1] & 0xffL) << 48)
363: + ((buffer[offset + 2] & 0xffL) << 40)
364: + ((buffer[offset + 3] & 0xffL) << 32)
365: + ((buffer[offset + 4] & 0xffL) << 24)
366: + ((buffer[offset + 5] & 0xffL) << 16)
367: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
368: return (int) Double.longBitsToDouble(value);
369: }
370:
371: case Column.VARCHAR:
372: return Integer.parseInt(getString(index));
373:
374: default:
375: return 0;
376: }
377: }
378:
379: public long getLong(int index) throws SQLException {
380: _lastColumn = index;
381:
382: byte[] buffer = _buffer;
383: int offset = _offsets[index];
384: int length = _lengths[index];
385:
386: switch (_types[index]) {
387: case Column.NONE:
388: return 0;
389:
390: case Column.INT:
391: return (((buffer[offset] & 0xff) << 24)
392: + ((buffer[offset + 1] & 0xff) << 16)
393: + ((buffer[offset + 2] & 0xff) << 8) + ((buffer[offset + 3] & 0xff)));
394:
395: case Column.LONG:
396: case Column.DATE: {
397: long value = (((buffer[offset + 0] & 0xffL) << 56)
398: + ((buffer[offset + 1] & 0xffL) << 48)
399: + ((buffer[offset + 2] & 0xffL) << 40)
400: + ((buffer[offset + 3] & 0xffL) << 32)
401: + ((buffer[offset + 4] & 0xffL) << 24)
402: + ((buffer[offset + 5] & 0xffL) << 16)
403: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
404: return value;
405: }
406:
407: case Column.DOUBLE: {
408: long value = (((buffer[offset + 0] & 0xffL) << 56)
409: + ((buffer[offset + 1] & 0xffL) << 48)
410: + ((buffer[offset + 2] & 0xffL) << 40)
411: + ((buffer[offset + 3] & 0xffL) << 32)
412: + ((buffer[offset + 4] & 0xffL) << 24)
413: + ((buffer[offset + 5] & 0xffL) << 16)
414: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
415: return (long) Double.longBitsToDouble(value);
416: }
417:
418: case Column.VARCHAR:
419: return Long.parseLong(getString(index));
420:
421: default:
422: return 0;
423: }
424: }
425:
426: /**
427: * Returns a double value from this column.
428: */
429: public double getDouble(int index) throws SQLException {
430: _lastColumn = index;
431:
432: byte[] buffer = _buffer;
433: int offset = _offsets[index];
434: int length = _lengths[index];
435:
436: switch (_types[index]) {
437: case Column.NONE:
438: return 0;
439:
440: case Column.INT:
441: return (((buffer[offset + 0] & 0xff) << 24)
442: + ((buffer[offset + 1] & 0xff) << 16)
443: + ((buffer[offset + 2] & 0xff) << 8) + ((buffer[offset + 3] & 0xff)));
444:
445: case Column.LONG: {
446: long value = (((buffer[offset + 0] & 0xffL) << 56)
447: + ((buffer[offset + 1] & 0xffL) << 48)
448: + ((buffer[offset + 2] & 0xffL) << 40)
449: + ((buffer[offset + 3] & 0xffL) << 32)
450: + ((buffer[offset + 4] & 0xffL) << 24)
451: + ((buffer[offset + 5] & 0xffL) << 16)
452: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
453: return value;
454: }
455:
456: case Column.DOUBLE: {
457: long value = (((buffer[offset + 0] & 0xffL) << 56)
458: + ((buffer[offset + 1] & 0xffL) << 48)
459: + ((buffer[offset + 2] & 0xffL) << 40)
460: + ((buffer[offset + 3] & 0xffL) << 32)
461: + ((buffer[offset + 4] & 0xffL) << 24)
462: + ((buffer[offset + 5] & 0xffL) << 16)
463: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
464: return Double.longBitsToDouble(value);
465: }
466:
467: case Column.VARCHAR:
468: return Double.parseDouble(getString(index));
469:
470: default:
471: return 0;
472: }
473: }
474:
475: public long getDate(int index) throws SQLException {
476: _lastColumn = index;
477:
478: byte[] buffer = _buffer;
479: int offset = _offsets[index];
480: int length = _lengths[index];
481:
482: switch (_types[index]) {
483: case Column.NONE:
484: return 0;
485:
486: case Column.LONG:
487: case Column.DATE: {
488: long value = (((buffer[offset + 0] & 0xffL) << 56)
489: + ((buffer[offset + 1] & 0xffL) << 48)
490: + ((buffer[offset + 2] & 0xffL) << 40)
491: + ((buffer[offset + 3] & 0xffL) << 32)
492: + ((buffer[offset + 4] & 0xffL) << 24)
493: + ((buffer[offset + 5] & 0xffL) << 16)
494: + ((buffer[offset + 6] & 0xffL) << 8) + ((buffer[offset + 7] & 0xffL)));
495: return value;
496: }
497:
498: case Column.VARCHAR:
499: case Column.BLOB: {
500: String value = getString(index);
501:
502: if (value == null)
503: return 0;
504:
505: synchronized (_date) {
506: try {
507: return _date.parseDate(value);
508: } catch (Exception e) {
509: throw new SQLExceptionWrapper(e);
510: }
511: }
512: }
513:
514: default:
515: throw new SQLException("unknown type:" + _types[index]);
516: }
517: }
518:
519: /**
520: * Returns true if the last column read was null.
521: */
522: public boolean wasNull() {
523: if (_lastColumn < 0)
524: return false;
525: else
526: return _lengths[_lastColumn] < 0;
527: }
528:
529: public void close() {
530: _freeList.free(this);
531: }
532: }
|