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: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.db.sql;
031:
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.IntArray;
040: import com.caucho.util.L10N;
041: import com.caucho.util.QDate;
042: import com.caucho.vfs.TempBuffer;
043:
044: import java.io.IOException;
045: import java.sql.Blob;
046: import java.sql.Clob;
047: import java.sql.SQLException;
048:
049: public class SelectResult {
050: private static final L10N L = new L10N(SelectResult.class);
051:
052: private static final FreeList<SelectResult> _freeList = new FreeList<SelectResult>(
053: 32);
054:
055: private static final int SIZE = TempBuffer.SIZE;
056:
057: private static QDate _date = new QDate();
058:
059: private CharBuffer _cb = new CharBuffer();
060: private byte[] _blob = new byte[128];
061:
062: private Expr[] _exprs;
063: private Store[] _stores = new Store[32];
064:
065: private TableIterator[] _rows = new TableIterator[16];
066:
067: private Order _order;
068: private IntArray _orderIndex;
069:
070: private TempBuffer[] _tempBuffers = new TempBuffer[128];
071: private byte[][] _buffers = new byte[128][];
072: private int _length;
073: private int _rowCount;
074:
075: private int _row;
076:
077: private int _offset;
078: private int _rowOffset;
079: private int _columnOffset;
080: private int _column;
081:
082: private boolean _wasNull;
083:
084: private SelectResult() {
085: }
086:
087: public static SelectResult create(Expr[] exprs, Order order) {
088: SelectResult rs = _freeList.allocate();
089:
090: if (rs == null)
091: rs = new SelectResult();
092:
093: rs.init(exprs, order);
094:
095: return rs;
096: }
097:
098: /**
099: * Initialize the iterator.
100: */
101: TableIterator[] initRows(FromItem[] fromItems) {
102: if (_rows.length < fromItems.length)
103: _rows = new TableIterator[fromItems.length];
104:
105: for (int i = 0; i < fromItems.length; i++) {
106: if (_rows[i] == null)
107: _rows[i] = new TableIterator();
108: _rows[i].init(fromItems[i].getTable());
109: }
110:
111: return _rows;
112: }
113:
114: /**
115: * Initialize based on the exprs.
116: */
117: private void init(Expr[] exprs, Order order) {
118: _exprs = exprs;
119: _order = order;
120:
121: if (order != null)
122: _orderIndex = new IntArray();
123:
124: if (_stores.length < _exprs.length) {
125: _stores = new Store[exprs.length];
126: }
127:
128: for (int i = 0; i < exprs.length; i++)
129: _stores[i] = exprs[i].getTable();
130:
131: _length = 0;
132: _rowCount = 0;
133: }
134:
135: void initRead() throws SQLException {
136: if (_order != null)
137: _order.sort(this , _orderIndex);
138:
139: _row = -1;
140: _offset = 0;
141: _column = 0;
142: _rowOffset = 0;
143: _columnOffset = 0;
144: }
145:
146: /**
147: * Moves to the next row, returning true if the row has data.
148: */
149: public boolean next() throws SQLException {
150: if (++_row < _rowCount) {
151: if (_orderIndex != null) {
152: _offset = _orderIndex.get(_row);
153: } else if (_row != 0) {
154: _offset = _columnOffset;
155: skipColumns(_exprs.length - _column);
156: }
157:
158: _column = 0;
159: _rowOffset = _offset;
160: _columnOffset = _rowOffset;
161:
162: return true;
163: } else
164: return false;
165: }
166:
167: /**
168: * Returns the expressions.
169: */
170: public Expr[] getExprs() {
171: return _exprs;
172: }
173:
174: /**
175: * Returns the column index with the given name.
176: */
177: public int findColumnIndex(String name) throws SQLException {
178: for (int i = 0; i < _exprs.length; i++) {
179: if (_exprs[i].getName().equals(name))
180: return i + 1;
181: }
182:
183: throw new SQLException(L
184: .l("column `{0}' does not exist.", name));
185: }
186:
187: /**
188: * Returns the string value of the given index.
189: */
190: public String getString(int index) throws SQLException {
191: _wasNull = false;
192:
193: setColumn(index);
194:
195: int type = read();
196: switch (type) {
197: case Column.NONE:
198: _wasNull = true;
199: return null;
200:
201: case Column.INT: {
202: int value = ((read() << 24) + (read() << 16)
203: + (read() << 8) + (read()));
204:
205: return String.valueOf(value);
206: }
207:
208: case Column.LONG: {
209: long value = (((long) read() << 56) + ((long) read() << 48)
210: + ((long) read() << 40) + ((long) read() << 32)
211: + ((long) read() << 24) + ((long) read() << 16)
212: + ((long) read() << 8) + ((long) read()));
213:
214: return String.valueOf(value);
215: }
216:
217: case Column.DOUBLE: {
218: long value = (((long) read() << 56) + ((long) read() << 48)
219: + ((long) read() << 40) + ((long) read() << 32)
220: + ((long) read() << 24) + ((long) read() << 16)
221: + ((long) read() << 8) + ((long) read()));
222:
223: return String.valueOf(Double.longBitsToDouble(value));
224: }
225:
226: case Column.DATE: {
227: long value = (((long) read() << 56) + ((long) read() << 48)
228: + ((long) read() << 40) + ((long) read() << 32)
229: + ((long) read() << 24) + ((long) read() << 16)
230: + ((long) read() << 8) + ((long) read()));
231:
232: return QDate.formatISO8601(value);
233: }
234:
235: case Column.VARCHAR:
236: return readString();
237:
238: case Column.BLOB:
239: return readBlobString();
240:
241: default:
242: throw new RuntimeException("unknown column type:" + type
243: + " column:" + index);
244: }
245: }
246:
247: /**
248: * Returns the integer value of the column.
249: */
250: public int getInt(int index) throws SQLException {
251: _wasNull = false;
252: setColumn(index);
253:
254: int type = read();
255: switch (type) {
256: case Column.NONE:
257: _wasNull = true;
258: return 0;
259:
260: case Column.INT: {
261: int value = ((read() << 24) + (read() << 16)
262: + (read() << 8) + (read()));
263:
264: return value;
265: }
266:
267: case Column.LONG:
268: case Column.DATE: {
269: long value = (((long) read() << 56) + ((long) read() << 48)
270: + ((long) read() << 40) + ((long) read() << 32)
271: + ((long) read() << 24) + ((long) read() << 16)
272: + ((long) read() << 8) + ((long) read()));
273:
274: return (int) value;
275: }
276:
277: case Column.DOUBLE: {
278: long value = (((long) read() << 56) + ((long) read() << 48)
279: + ((long) read() << 40) + ((long) read() << 32)
280: + ((long) read() << 24) + ((long) read() << 16)
281: + ((long) read() << 8) + ((long) read()));
282:
283: return (int) Double.longBitsToDouble(value);
284: }
285:
286: case Column.VARCHAR:
287: return Integer.parseInt(readString());
288:
289: case Column.BLOB:
290: return Integer.parseInt(readBlobString());
291:
292: default:
293: return 0;
294: }
295: }
296:
297: /**
298: * Returns the long value of the column.
299: */
300: public long getLong(int index) throws SQLException {
301: _wasNull = false;
302: setColumn(index);
303:
304: int type = read();
305: switch (type) {
306: case Column.NONE:
307: _wasNull = true;
308: return 0;
309:
310: case Column.INT: {
311: int value = ((read() << 24) + (read() << 16)
312: + (read() << 8) + (read()));
313:
314: return value;
315: }
316:
317: case Column.LONG:
318: case Column.DATE: {
319: long value = (((long) read() << 56) + ((long) read() << 48)
320: + ((long) read() << 40) + ((long) read() << 32)
321: + ((long) read() << 24) + ((long) read() << 16)
322: + ((long) read() << 8) + ((long) read()));
323:
324: return value;
325: }
326:
327: case Column.DOUBLE: {
328: long value = (((long) read() << 56) + ((long) read() << 48)
329: + ((long) read() << 40) + ((long) read() << 32)
330: + ((long) read() << 24) + ((long) read() << 16)
331: + ((long) read() << 8) + ((long) read()));
332:
333: return (long) Double.longBitsToDouble(value);
334: }
335:
336: case Column.VARCHAR:
337: return Long.parseLong(readString());
338:
339: case Column.BLOB:
340: return Long.parseLong(readBlobString());
341:
342: default:
343: return 0;
344: }
345: }
346:
347: /**
348: * Returns a double value from this column.
349: */
350: public double getDouble(int index) throws SQLException {
351: _wasNull = false;
352: setColumn(index);
353:
354: int type = read();
355: switch (type) {
356: case Column.NONE:
357: _wasNull = true;
358: return 0;
359:
360: case Column.INT: {
361: int value = ((read() << 24) + (read() << 16)
362: + (read() << 8) + (read()));
363:
364: return value;
365: }
366:
367: case Column.LONG:
368: case Column.DATE: {
369: long value = (((long) read() << 56) + ((long) read() << 48)
370: + ((long) read() << 40) + ((long) read() << 32)
371: + ((long) read() << 24) + ((long) read() << 16)
372: + ((long) read() << 8) + ((long) read()));
373:
374: return value;
375: }
376:
377: case Column.DOUBLE: {
378: long value = (((long) read() << 56) + ((long) read() << 48)
379: + ((long) read() << 40) + ((long) read() << 32)
380: + ((long) read() << 24) + ((long) read() << 16)
381: + ((long) read() << 8) + ((long) read()));
382:
383: return Double.longBitsToDouble(value);
384: }
385:
386: case Column.VARCHAR:
387: return Double.parseDouble(readString());
388:
389: case Column.BLOB:
390: return Double.parseDouble(readBlobString());
391:
392: default:
393: return 0;
394: }
395: }
396:
397: public long getDate(int index) throws SQLException {
398: _wasNull = false;
399: setColumn(index);
400:
401: int type = read();
402: switch (type) {
403: case Column.NONE:
404: _wasNull = true;
405: return 0;
406:
407: case Column.LONG:
408: case Column.DATE: {
409: long value = (((long) read() << 56) + ((long) read() << 48)
410: + ((long) read() << 40) + ((long) read() << 32)
411: + ((long) read() << 24) + ((long) read() << 16)
412: + ((long) read() << 8) + ((long) read()));
413:
414: return value;
415: }
416:
417: case Column.VARCHAR: {
418: String value = readString();
419:
420: synchronized (_date) {
421: try {
422: return _date.parseDate(value);
423: } catch (Exception e) {
424: throw new SQLExceptionWrapper(e);
425: }
426: }
427: }
428:
429: case Column.BLOB: {
430: String value = readBlobString();
431:
432: synchronized (_date) {
433: try {
434: return _date.parseDate(value);
435: } catch (Exception e) {
436: throw new SQLExceptionWrapper(e);
437: }
438: }
439: }
440:
441: default:
442: throw new SQLException("unknown type: " + type);
443: }
444: }
445:
446: /**
447: * Returns the blob value of the given index.
448: */
449: public Blob getBlob(int index) throws SQLException {
450: _wasNull = false;
451:
452: setColumn(index);
453:
454: int type = read();
455: switch (type) {
456: case Column.NONE:
457: _wasNull = true;
458: return null;
459:
460: case Column.BLOB:
461: return getBlob();
462:
463: default:
464: throw new RuntimeException(
465: "column can't be retrieved as a blob:" + type
466: + " column:" + index);
467: }
468: }
469:
470: /**
471: * Returns the clob value of the given index.
472: */
473: public Clob getClob(int index) throws SQLException {
474: _wasNull = false;
475:
476: setColumn(index);
477:
478: int type = read();
479: switch (type) {
480: case Column.NONE:
481: _wasNull = true;
482: return null;
483:
484: case Column.BLOB:
485: return getClob();
486:
487: default:
488: throw new RuntimeException(
489: "column can't be retrieved as a clob:" + type
490: + " column:" + index);
491: }
492: }
493:
494: /**
495: * Returns true if the last column read was null.
496: */
497: public boolean wasNull() {
498: return _wasNull;
499: }
500:
501: /**
502: * Returns the string value for the result set.
503: */
504: private String readString() throws SQLException {
505: int length = ((read() << 24) + (read() << 16) + (read() << 8) + (read()));
506:
507: int len = length >> 1;
508:
509: CharBuffer cb = _cb;
510: cb.ensureCapacity(len);
511: char[] cBuf = cb.getBuffer();
512: int cLen = 0;
513:
514: for (; len > 0; len--) {
515: int ch1 = read();
516: int ch2 = read();
517:
518: cBuf[cLen++] = (char) (((ch1 & 0xff) << 8) + (ch2 & 0xff));
519: }
520:
521: return new String(cBuf, 0, cLen);
522: }
523:
524: /**
525: * Returns the blob value for the result set.
526: */
527: private Blob getBlob() throws SQLException {
528: BlobImpl blob = new BlobImpl();
529:
530: blob.setStore(_stores[_column]);
531:
532: byte[] inode = blob.getInode();
533:
534: read(inode, 0, 128);
535:
536: return blob;
537: }
538:
539: /**
540: * Returns the clob value for the result set.
541: */
542: private Clob getClob() throws SQLException {
543: ClobImpl clob = new ClobImpl();
544:
545: clob.setStore(_stores[_column]);
546:
547: byte[] inode = clob.getInode();
548:
549: read(inode, 0, 128);
550:
551: return clob;
552: }
553:
554: /**
555: * Returns the string value for the result set.
556: */
557: private String readBlobString() throws SQLException {
558: read(_blob, 0, 128);
559:
560: CharBuffer cb = _cb;
561: cb.clear();
562:
563: BlobInputStream is = null;
564: try {
565: is = new BlobInputStream(_stores[_column], _blob, 0);
566:
567: int ch;
568: while ((ch = is.read()) >= 0) {
569: if (ch < 0x80)
570: cb.append((char) ch);
571: }
572: } catch (IOException e) {
573: throw new SQLExceptionWrapper(e);
574: }
575:
576: return cb.toString();
577: }
578:
579: /**
580: * Set the column in the current row.
581: */
582: private void setColumn(int column) {
583: if (column < _column) {
584: _offset = _rowOffset;
585: skipColumns(column);
586: } else {
587: _offset = _columnOffset;
588: skipColumns(column - _column);
589: }
590:
591: _column = column;
592: _columnOffset = _offset;
593: }
594:
595: /**
596: * Set the column in the current row.
597: */
598: void setRow(int rowOffset) {
599: _rowOffset = rowOffset;
600: _offset = rowOffset;
601: _column = 0;
602: _columnOffset = rowOffset;
603: }
604:
605: /**
606: * Skips the specified number of columns.
607: */
608: private void skipColumns(int count) {
609: for (; count > 0; count--) {
610: int type = read();
611: int sublen;
612:
613: switch (type) {
614: case Column.NONE:
615: break;
616:
617: case Column.VARCHAR:
618: int l0 = read();
619: int l1 = read();
620: int l2 = read();
621: int l3 = read();
622:
623: sublen = ((l0 << 24) + (l1 << 16) + (l2 << 8) + (l3));
624:
625: _offset += sublen;
626: break;
627:
628: case Column.INT:
629: _offset += 4;
630: break;
631: case Column.LONG:
632: case Column.DOUBLE:
633: case Column.DATE:
634: _offset += 8;
635: break;
636:
637: case Column.BLOB:
638: _offset += 128;
639: break;
640:
641: default:
642: throw new RuntimeException("Unknown column: " + type);
643: }
644: }
645: }
646:
647: /**
648: * Starts a row
649: */
650: public void startRow() {
651: if (_orderIndex != null)
652: _orderIndex.add(_length);
653:
654: _rowCount++;
655: }
656:
657: /**
658: * Writes a null.
659: */
660: public void writeNull() {
661: write(Column.NONE);
662: }
663:
664: /**
665: * Writes a string.
666: */
667: public void writeString(String s) {
668: write(Column.VARCHAR);
669: int stringLength = s.length();
670: int length = 2 * stringLength;
671: write(length >> 24);
672: write(length >> 16);
673: write(length >> 8);
674: write(length);
675:
676: for (int i = 0; i < stringLength; i++) {
677: char ch = s.charAt(i);
678:
679: write(ch << 8);
680: write(ch);
681: }
682: }
683:
684: /**
685: * Writes a string.
686: */
687: public void writeString(byte[] buffer, int offset, int stringLength) {
688: int rLength = _length;
689:
690: int rOffset = rLength % SIZE;
691: int rBlockId = rLength / SIZE;
692:
693: if (_buffers[rBlockId] == null) {
694: TempBuffer tempBuffer = TempBuffer.allocate();
695: _tempBuffers[rBlockId] = tempBuffer;
696: _buffers[rBlockId] = tempBuffer.getBuffer();
697: }
698:
699: byte[] rBuffer = _buffers[rBlockId];
700: rBuffer[rOffset] = Column.VARCHAR;
701:
702: int length = 2 * stringLength;
703:
704: if (rOffset + 5 < rBuffer.length) {
705: rBuffer[rOffset + 1] = (byte) (length >> 24);
706: rBuffer[rOffset + 2] = (byte) (length >> 16);
707: rBuffer[rOffset + 3] = (byte) (length >> 8);
708: rBuffer[rOffset + 4] = (byte) length;
709:
710: if (rOffset + 5 + length < SIZE) {
711: System.arraycopy(buffer, offset, rBuffer, rOffset + 5,
712: length);
713:
714: _length = rLength + 5 + length;
715: } else {
716: _length = rLength + 5;
717: write(buffer, offset, length);
718: }
719: } else {
720: _length = rLength + 1;
721:
722: write(length >> 24);
723: write(length >> 16);
724: write(length >> 8);
725: write(length);
726:
727: write(buffer, offset, length);
728: }
729: }
730:
731: /**
732: * Writes a string.
733: */
734: public void writeBlock(int code, byte[] buffer, int offset,
735: int length) {
736: write(code);
737: write(buffer, offset, length);
738: }
739:
740: /**
741: * Writes a double.
742: */
743: public void writeDouble(double dValue) {
744: write(Column.DOUBLE);
745:
746: long value = Double.doubleToLongBits(dValue);
747:
748: write((int) (value >> 56));
749: write((int) (value >> 48));
750: write((int) (value >> 40));
751: write((int) (value >> 32));
752: write((int) (value >> 24));
753: write((int) (value >> 16));
754: write((int) (value >> 8));
755: write((int) value);
756: }
757:
758: /**
759: * Writes a long.
760: */
761: public void writeLong(long value) {
762: write(Column.LONG);
763: write((int) (value >> 56));
764: write((int) (value >> 48));
765: write((int) (value >> 40));
766: write((int) (value >> 32));
767: write((int) (value >> 24));
768: write((int) (value >> 16));
769: write((int) (value >> 8));
770: write((int) value);
771: }
772:
773: /**
774: * Writes a date.
775: */
776: public void writeDate(long value) {
777: write(Column.DATE);
778: write((int) (value >> 56));
779: write((int) (value >> 48));
780: write((int) (value >> 40));
781: write((int) (value >> 32));
782: write((int) (value >> 24));
783: write((int) (value >> 16));
784: write((int) (value >> 8));
785: write((int) value);
786: }
787:
788: /**
789: * Writes an long.
790: */
791: public void writeInt(int value) {
792: write(Column.INT);
793: write(value >> 24);
794: write(value >> 16);
795: write(value >> 8);
796: write(value);
797: }
798:
799: /**
800: * Writes a blob.
801: */
802: public void writeBlob(byte[] buffer, int offset) {
803: write(Column.BLOB);
804:
805: write(buffer, offset, 128);
806: }
807:
808: /**
809: * Seeks the specified offset.
810: */
811: private void seek(int offset) {
812: _offset = offset;
813: }
814:
815: /**
816: * Reads the next byte.
817: */
818: private int read() {
819: int offset = _offset;
820:
821: if (_length <= offset)
822: return -1;
823:
824: _offset = offset + 1;
825:
826: byte[] buf = _buffers[offset / SIZE];
827:
828: return buf[offset % SIZE] & 0xff;
829: }
830:
831: /**
832: * Reads the next byte.
833: */
834: private int read(byte[] buffer, int bufOffset, int bufLength) {
835: int offset = _offset;
836: int length = _length;
837: byte[][] buffers = _buffers;
838:
839: for (int i = bufLength; i > 0; i--) {
840: if (length <= offset) {
841: _offset = offset;
842: return -1;
843: }
844:
845: byte[] buf = buffers[offset / SIZE];
846:
847: buffer[bufOffset] = buf[offset % SIZE];
848:
849: offset++;
850: bufOffset++;
851: }
852:
853: _offset = offset;
854:
855: return bufLength;
856: }
857:
858: /**
859: * Writes the next byte.
860: */
861: public void write(int value) {
862: int length = _length;
863: int rOffset = length % SIZE;
864: int blockId = length / SIZE;
865:
866: byte[] buffer = _buffers[blockId];
867:
868: if (buffer == null) {
869: TempBuffer tempBuffer = TempBuffer.allocate();
870: _tempBuffers[blockId] = tempBuffer;
871: _buffers[blockId] = tempBuffer.getBuffer();
872:
873: buffer = _buffers[blockId];
874: }
875:
876: buffer[rOffset] = (byte) value;
877:
878: _length = length + 1;
879: }
880:
881: /**
882: * Writes a buffer
883: */
884: public void write(byte[] buffer, int offset, int length) {
885: int rLength = _length;
886:
887: while (length > 0) {
888: int rOffset = rLength % SIZE;
889:
890: int rBufferId = rLength / SIZE;
891:
892: if (rOffset == 0) {
893: TempBuffer tempBuffer = TempBuffer.allocate();
894: if (_tempBuffers.length <= rBufferId) {
895: int len = _tempBuffers.length;
896:
897: TempBuffer[] newTempBuffers = new TempBuffer[len + 32];
898: System.arraycopy(_tempBuffers, 0, newTempBuffers,
899: 0, len);
900: _tempBuffers = newTempBuffers;
901:
902: byte[][] newBuffers = new byte[len + 32][];
903: System.arraycopy(_buffers, 0, newBuffers, 0, len);
904: _buffers = newBuffers;
905:
906: }
907:
908: _tempBuffers[rBufferId] = tempBuffer;
909: _buffers[rBufferId] = tempBuffer.getBuffer();
910: }
911:
912: byte[] rBuffer = _buffers[rBufferId];
913:
914: int sublen = rBuffer.length - rOffset;
915:
916: if (length < sublen)
917: sublen = length;
918:
919: System.arraycopy(buffer, offset, rBuffer, rOffset, sublen);
920:
921: length -= sublen;
922: offset += sublen;
923: rLength += sublen;
924: }
925:
926: _length = rLength;
927: }
928:
929: public void close() {
930: for (int i = 0; i < _buffers.length; i++) {
931: TempBuffer buffer = _tempBuffers[i];
932:
933: if (buffer != null)
934: TempBuffer.free(buffer);
935:
936: _tempBuffers[i] = null;
937: _buffers[i] = null;
938: }
939:
940: _order = null;
941: _orderIndex = null;
942:
943: _freeList.free(this);
944: }
945: }
|