0001: /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
0002: * All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * Redistributions of source code must retain the above copyright notice, this
0008: * list of conditions and the following disclaimer.
0009: *
0010: * Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * Neither the name of the Hypersonic SQL Group nor the names of its
0015: * contributors may be used to endorse or promote products derived from this
0016: * software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0021: * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
0022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: *
0030: * This software consists of voluntary contributions made by many individuals
0031: * on behalf of the Hypersonic SQL Group.
0032: *
0033: *
0034: * For work added by the HSQL Development Group:
0035: *
0036: * Copyright (c) 2001-2005, The HSQL Development Group
0037: * All rights reserved.
0038: *
0039: * Redistribution and use in source and binary forms, with or without
0040: * modification, are permitted provided that the following conditions are met:
0041: *
0042: * Redistributions of source code must retain the above copyright notice, this
0043: * list of conditions and the following disclaimer.
0044: *
0045: * Redistributions in binary form must reproduce the above copyright notice,
0046: * this list of conditions and the following disclaimer in the documentation
0047: * and/or other materials provided with the distribution.
0048: *
0049: * Neither the name of the HSQL Development Group nor the names of its
0050: * contributors may be used to endorse or promote products derived from this
0051: * software without specific prior written permission.
0052: *
0053: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0054: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0055: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0056: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
0057: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0058: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0059: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
0060: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0061: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0062: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0063: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0064: */
0065:
0066: package org.hsqldb;
0067:
0068: import java.io.DataInput;
0069: import java.io.IOException;
0070: import java.io.OutputStream;
0071: import java.util.NoSuchElementException;
0072:
0073: import org.hsqldb.lib.Iterator;
0074: import org.hsqldb.rowio.RowInputBinary;
0075: import org.hsqldb.rowio.RowOutputBinary;
0076:
0077: // fredt@users 20020130 - patch 1.7.0 by fredt
0078: // to ensure consistency of r.rTail r.iSize in all operations
0079: // methods for set operations moved here from Select.java
0080: // tony_lai@users 20020820 - patch 595073 - duplicated exception msg
0081: // fredt@users 20030801 - patch 1.7.2 - separate metadata and polymophic serialisation
0082: // boucherb@users 200307/8 - various, in support of fred's work over the same time period
0083:
0084: /**
0085: * The primary unit of comunication between Connection, Server and Session
0086: * objects.
0087: *
0088: * An HSQLDB Result object encapsulates all requests (such as to alter or
0089: * query session settings, to allocate and execute statements, etc.) and all
0090: * responses (such as exception indications, update counts, result sets and
0091: * result set metadata). It also implements the HSQL wire protocol for
0092: * comunicating all such requests and responses across the network.
0093: *
0094: * Extensively rewritten and extended in successive versions of HSQLDB.
0095: *
0096: * @author Thomas Mueller (Hypersonic SQL Group)
0097: * @version 1.8.0
0098: * @since Hypersonic SQL
0099: */
0100: public class Result {
0101:
0102: // record list
0103: public Record rRoot;
0104: private Record rTail;
0105: private int size;
0106:
0107: // transient - number of significant columns
0108: private int significantColumns;
0109:
0110: // type of result
0111: public int mode;
0112:
0113: // boolean isMulti;
0114: // database ID
0115: int databaseID;
0116:
0117: // session ID
0118: int sessionID;
0119:
0120: // user / password or error strings
0121: String mainString;
0122: String subString;
0123:
0124: // database name
0125: String subSubString;
0126:
0127: // the exception if this is an error
0128: private Throwable exception;
0129:
0130: // prepared statement id / error vendor code
0131: int statementID;
0132:
0133: // max rows (out) or update count (in)
0134: int updateCount;
0135: public ResultMetaData metaData;
0136:
0137: /** A Result object's metadata */
0138: public static class ResultMetaData {
0139:
0140: // always resolved
0141: public String[] colLabels;
0142: public String[] tableNames;
0143: public String[] colNames;
0144: public boolean[] isLabelQuoted;
0145: public int[] colTypes;
0146: public int[] colSizes;
0147: public int[] colScales;
0148:
0149: // extra attrs, sometimes resolved
0150: public String[] catalogNames;
0151: public String[] schemaNames;
0152: public int[] colNullable;
0153: public boolean[] isIdentity;
0154: public boolean[] isWritable;
0155: public int[] paramMode;
0156:
0157: // It's possible to do better than java.lang.Object
0158: // for type OTHER if the expression generating the value
0159: // is of type FUNCTION. This applies to result set columns
0160: // whose value is the result of a SQL function call and
0161: // especially to the arguments and return value of a CALL
0162: public String[] classNames;
0163: boolean isParameterDescription;
0164:
0165: ResultMetaData() {
0166: }
0167:
0168: ResultMetaData(int n) {
0169: prepareData(n);
0170: }
0171:
0172: /**
0173: * Method declaration
0174: *
0175: * @param columns
0176: */
0177: private void prepareData(int columns) {
0178:
0179: colLabels = new String[columns];
0180: tableNames = new String[columns];
0181: colNames = new String[columns];
0182: isLabelQuoted = new boolean[columns];
0183: colTypes = new int[columns];
0184: colSizes = new int[columns];
0185: colScales = new int[columns];
0186: catalogNames = new String[columns];
0187: schemaNames = new String[columns];
0188: colNullable = new int[columns];
0189: isIdentity = new boolean[columns];
0190: isWritable = new boolean[columns];
0191: classNames = new String[columns];
0192: }
0193:
0194: public int[] getParameterTypes() {
0195: return colTypes;
0196: }
0197:
0198: boolean isTableColumn(int i) {
0199: return tableNames[i] != null && tableNames[i].length() > 0
0200: && colNames[i] != null && colNames[i].length() > 0;
0201: }
0202:
0203: private void decodeTableColumnAttrs(int in, int i) {
0204:
0205: colNullable[i] = in & 0x0000000f;
0206: isIdentity[i] = (in & 0x00000010) != 0;
0207: isWritable[i] = (in & 0x00000020) != 0;
0208: }
0209:
0210: private void writeTableColumnAttrs(RowOutputBinary out, int i)
0211: throws IOException, HsqlException {
0212:
0213: // HSQLDB also ignores precision and scale for all types except
0214: // XXXCHAR, for which it may (or may not) perform some trimming/padding.
0215: // All in all, it's currently meaningless (indeed misleading) to
0216: // transmit and report the values, as the data typically will
0217: // not be constrained accordingly.
0218: // switch(colType[i]) {
0219: // // As early as SQL 92, these are allowed to have a scale.
0220: // // However, DatabaseCommandInterpreter.processCreateColumn
0221: // // does not currently handle this correctly and will assign
0222: // // a precision instead of a scale if TIME(s) or TIMESTAMP(s)
0223: // // is specified
0224: // case Types.TIME :
0225: // case Types.TIMESTAMP :
0226: // out.writeIntData(colScale[i]);
0227: // break;
0228: // case Types.DECIMAL :
0229: // case Types.NUMERIC : {
0230: // out.writeIntData(colScale[i]);
0231: // } // fall through
0232: // // Apparently, SQL 92 specifies that FLOAT can have
0233: // // a declared precision, which is typically the number of
0234: // // bits (not binary digits). In any case, this is somewhat
0235: // // meaningless under HSQLDB/Java, in that we use java.lang.Double
0236: // // to represent SQL FLOAT
0237: // case Types.FLOAT :
0238: // // It's legal to declare precision for these, although HSQLDB
0239: // // currently does not use it to constrain values
0240: // case Types.BINARY :
0241: // case Types.VARBINARY :
0242: // case Types.LONGVARBINARY :
0243: // // possibly, but not universally acted upon (trimmming/padding)
0244: // case Types.CHAR :
0245: // case Types.VARCHAR :
0246: // case Types.LONGVARCHAR : {
0247: // out.writeIntData(colSize[i]);
0248: // }
0249: // }
0250: out.writeIntData(encodeTableColumnAttrs(i));
0251: out.writeString(catalogNames[i] == null ? ""
0252: : catalogNames[i]);
0253: out.writeString(schemaNames[i] == null ? ""
0254: : schemaNames[i]);
0255: }
0256:
0257: private int encodeTableColumnAttrs(int i) {
0258:
0259: int out = colNullable[i]; // always between 0x00 and 0x02
0260:
0261: if (isIdentity[i]) {
0262: out |= 0x00000010;
0263: }
0264:
0265: if (isWritable[i]) {
0266: out |= 0x00000020;
0267: }
0268:
0269: return out;
0270: }
0271:
0272: private void readTableColumnAttrs(RowInputBinary in, int i)
0273: throws IOException, HsqlException {
0274:
0275: decodeTableColumnAttrs(in.readIntData(), i);
0276:
0277: catalogNames[i] = in.readString();
0278: schemaNames[i] = in.readString();
0279: }
0280:
0281: ResultMetaData(RowInputBinary in, int mode)
0282: throws HsqlException, IOException {
0283:
0284: int l = in.readIntData();
0285:
0286: prepareData(l);
0287:
0288: if (mode == ResultConstants.PARAM_META_DATA) {
0289: isParameterDescription = true;
0290: paramMode = new int[l];
0291: }
0292:
0293: for (int i = 0; i < l; i++) {
0294: colTypes[i] = in.readType();
0295:
0296: // fredt - 1.8.0 added
0297: colSizes[i] = in.readIntData();
0298: colScales[i] = in.readIntData();
0299: colLabels[i] = in.readString();
0300: tableNames[i] = in.readString();
0301: colNames[i] = in.readString();
0302: classNames[i] = in.readString();
0303:
0304: if (isTableColumn(i)) {
0305: readTableColumnAttrs(in, i);
0306: }
0307:
0308: if (mode == ResultConstants.PARAM_META_DATA) {
0309: paramMode[i] = in.readIntData();
0310: }
0311: }
0312: }
0313:
0314: void write(RowOutputBinary out, int colCount)
0315: throws HsqlException, IOException {
0316:
0317: out.writeIntData(colCount);
0318:
0319: for (int i = 0; i < colCount; i++) {
0320: out.writeType(colTypes[i]);
0321:
0322: // fredt - 1.8.0 added
0323: out.writeIntData(colSizes[i]);
0324: out.writeIntData(colScales[i]);
0325: out.writeString(colLabels[i] == null ? ""
0326: : colLabels[i]);
0327: out.writeString(tableNames[i] == null ? ""
0328: : tableNames[i]);
0329: out.writeString(colNames[i] == null ? "" : colNames[i]);
0330: out.writeString(classNames[i] == null ? ""
0331: : classNames[i]);
0332:
0333: if (isTableColumn(i)) {
0334: writeTableColumnAttrs(out, i);
0335: }
0336:
0337: if (isParameterDescription) {
0338: out.writeIntData(paramMode[i]);
0339: }
0340: }
0341: }
0342: }
0343:
0344: /**
0345: * General constructor
0346: */
0347: public Result(int type) {
0348:
0349: mode = type;
0350:
0351: /*
0352: if (type == ResultConstants.MULTI) {
0353: isMulti = true;
0354: }
0355: */
0356: if (type == ResultConstants.DATA
0357: || type == ResultConstants.PARAM_META_DATA
0358: || type == ResultConstants.SQLEXECUTE
0359: || type == ResultConstants.SETSESSIONATTR) {
0360: metaData = new ResultMetaData();
0361: }
0362: }
0363:
0364: Result(ResultMetaData md) {
0365:
0366: mode = ResultConstants.DATA;
0367: significantColumns = md.colTypes.length;
0368: metaData = md;
0369: }
0370:
0371: // fredt@users 20020221 - patch 513005 by sqlbob@users (RMP)
0372:
0373: /**
0374: * Constructor for errors
0375: *
0376: * @param error error message
0377: * @param state sql state
0378: * @param code vendor code
0379: */
0380: Result(String error, String state, int code) {
0381:
0382: mode = ResultConstants.ERROR;
0383: mainString = error;
0384: subString = state;
0385: statementID = code;
0386: subSubString = "";
0387: }
0388:
0389: /**
0390: * Only used with DATA and PARAM_META_DATA results
0391: *
0392: * @param columns
0393: */
0394: Result(int type, int columns) {
0395:
0396: metaData = new ResultMetaData();
0397:
0398: metaData.prepareData(columns);
0399:
0400: if (type == ResultConstants.PARAM_META_DATA) {
0401: metaData.isParameterDescription = true;
0402: metaData.paramMode = new int[columns];
0403: }
0404:
0405: mode = type;
0406: significantColumns = columns;
0407: }
0408:
0409: /**
0410: * For BATCHEXECUTE and BATCHEXECDIRECT
0411: */
0412: public Result(int type, int[] types, int id) {
0413:
0414: mode = type;
0415: metaData = new ResultMetaData();
0416: metaData.colTypes = types;
0417: significantColumns = types.length;
0418: statementID = id;
0419: }
0420:
0421: /**
0422: * Constructor declaration
0423: *
0424: * @param in
0425: * @exception HsqlException Description of the Exception
0426: */
0427: Result(RowInputBinary in) throws HsqlException {
0428:
0429: try {
0430: mode = in.readIntData();
0431:
0432: if (mode == ResultConstants.MULTI) {
0433: readMultiResult(in);
0434:
0435: return;
0436: }
0437:
0438: databaseID = in.readIntData();
0439: sessionID = in.readIntData();
0440:
0441: switch (mode) {
0442:
0443: case ResultConstants.GETSESSIONATTR:
0444: case ResultConstants.SQLDISCONNECT:
0445: case ResultConstants.SQLSTARTTRAN:
0446: case ResultConstants.HSQLRESETSESSION:
0447: break;
0448:
0449: case ResultConstants.SQLPREPARE:
0450: setStatementType(in.readIntData());
0451:
0452: mainString = in.readString();
0453: break;
0454:
0455: case ResultConstants.PREPARE_ACK:
0456: case ResultConstants.SQLFREESTMT:
0457: statementID = in.readIntData();
0458: break;
0459:
0460: case ResultConstants.SQLEXECDIRECT:
0461: updateCount = in.readIntData();
0462: statementID = in.readIntData();
0463: mainString = in.readString();
0464: break;
0465:
0466: case ResultConstants.ERROR:
0467: case ResultConstants.SQLCONNECT:
0468: mainString = in.readString();
0469: subString = in.readString();
0470: subSubString = in.readString();
0471: statementID = in.readIntData();
0472:
0473: // throw Trace.getError(string, code);
0474: break;
0475:
0476: case ResultConstants.UPDATECOUNT:
0477: updateCount = in.readIntData();
0478: break;
0479:
0480: case ResultConstants.SQLENDTRAN: {
0481: int type = in.readIntData();
0482:
0483: setEndTranType(type); // endtran type
0484:
0485: switch (type) {
0486:
0487: case ResultConstants.SAVEPOINT_NAME_RELEASE:
0488: case ResultConstants.SAVEPOINT_NAME_ROLLBACK:
0489: mainString = in.readString(); // savepoint name
0490: }
0491:
0492: break;
0493: }
0494: case ResultConstants.BATCHEXECUTE:
0495: case ResultConstants.BATCHEXECDIRECT:
0496: case ResultConstants.SQLEXECUTE:
0497: case ResultConstants.SETSESSIONATTR: {
0498: updateCount = in.readIntData();
0499: statementID = in.readIntData();
0500:
0501: int l = in.readIntData();
0502:
0503: metaData = new ResultMetaData(l);
0504: significantColumns = l;
0505:
0506: for (int i = 0; i < l; i++) {
0507: metaData.colTypes[i] = in.readType();
0508: }
0509:
0510: int count = in.readIntData();
0511:
0512: while (count-- > 0) {
0513: add(in.readData(metaData.colTypes));
0514: }
0515:
0516: break;
0517: }
0518: case ResultConstants.DATA:
0519: case ResultConstants.PARAM_META_DATA: {
0520: metaData = new ResultMetaData(in, mode);
0521: significantColumns = metaData.colLabels.length;
0522:
0523: int count = in.readIntData();
0524:
0525: while (count-- > 0) {
0526: add(in.readData(metaData.colTypes));
0527: }
0528:
0529: break;
0530: }
0531: case ResultConstants.SQLSETCONNECTATTR: {
0532: int type = in.readIntData(); // attr type
0533:
0534: setConnectionAttrType(type);
0535:
0536: switch (type) {
0537:
0538: case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
0539: mainString = in.readString(); // savepoint name
0540:
0541: // case ResultConstants.SQL_ATTR_AUTO_IPD :
0542: // - always true
0543: // default: throw - case never happens
0544: }
0545:
0546: break;
0547: }
0548: default:
0549: throw new HsqlException(Trace.getMessage(
0550: Trace.Result_Result, true,
0551: new Object[] { new Integer(mode) }), null, 0);
0552: }
0553: } catch (IOException e) {
0554: throw Trace.error(Trace.TRANSFER_CORRUPTED);
0555: }
0556: }
0557:
0558: static Result newSingleColumnResult(String colName, int colType) {
0559:
0560: Result result = new Result(ResultConstants.DATA, 1);
0561:
0562: result.metaData.colNames[0] = colName;
0563: result.metaData.colLabels[0] = colName;
0564: result.metaData.tableNames[0] = "";
0565: result.metaData.colTypes[0] = colType;
0566:
0567: return result;
0568: }
0569:
0570: static Result newPrepareResponse(int csid, Result rsmd, Result pmd) {
0571:
0572: Result out;
0573: Result pack;
0574:
0575: out = new Result(ResultConstants.MULTI);
0576:
0577: // out.isMulti = true;
0578: pack = new Result(ResultConstants.PREPARE_ACK);
0579: pack.statementID = csid;
0580:
0581: out.add(new Object[] { pack });
0582: out.add(new Object[] { rsmd });
0583: out.add(new Object[] { pmd });
0584:
0585: return out;
0586: }
0587:
0588: static Result newParameterDescriptionResult(int len) {
0589:
0590: Result r = new Result(ResultConstants.PARAM_META_DATA, len);
0591:
0592: r.metaData.isParameterDescription = true;
0593: r.metaData.paramMode = new int[len];
0594:
0595: return r;
0596: }
0597:
0598: public static Result newFreeStmtRequest(int statementID) {
0599:
0600: Result r = new Result(ResultConstants.SQLFREESTMT);
0601:
0602: r.statementID = statementID;
0603:
0604: return r;
0605: }
0606:
0607: static Result newExecuteDirectRequest(String sql) {
0608:
0609: Result out;
0610:
0611: out = new Result(ResultConstants.SQLEXECDIRECT);
0612:
0613: out.setMainString(sql);
0614:
0615: return out;
0616: }
0617:
0618: public static Result newReleaseSavepointRequest(String name) {
0619:
0620: Result out;
0621:
0622: out = new Result(ResultConstants.SQLENDTRAN);
0623:
0624: out.setMainString(name);
0625: out.setEndTranType(ResultConstants.SAVEPOINT_NAME_RELEASE);
0626:
0627: return out;
0628: }
0629:
0630: public static Result newRollbackToSavepointRequest(String name) {
0631:
0632: Result out;
0633:
0634: out = new Result(ResultConstants.SQLENDTRAN);
0635:
0636: out.setMainString(name);
0637: out.setEndTranType(ResultConstants.SAVEPOINT_NAME_ROLLBACK);
0638:
0639: return out;
0640: }
0641:
0642: public static Result newSetSavepointRequest(String name) {
0643:
0644: Result out;
0645:
0646: out = new Result(ResultConstants.SQLSETCONNECTATTR);
0647:
0648: out
0649: .setConnectionAttrType(ResultConstants.SQL_ATTR_SAVEPOINT_NAME);
0650: out.setMainString(name);
0651:
0652: return out;
0653: }
0654:
0655: /**
0656: * Method declaration
0657: *
0658: * @return
0659: */
0660: public int getSize() {
0661: return size;
0662: }
0663:
0664: /**
0665: * Method declaration
0666: *
0667: * @param columns
0668: */
0669: void setColumnCount(int columns) {
0670: significantColumns = columns;
0671: }
0672:
0673: /**
0674: * Method declaration
0675: *
0676: * @return
0677: */
0678: public int getColumnCount() {
0679: return significantColumns;
0680: }
0681:
0682: /**
0683: * Append Result argument to this.
0684: *
0685: * @param a
0686: */
0687: void append(Result a) {
0688:
0689: if (a.rRoot == null) {
0690: return;
0691: }
0692:
0693: if (rRoot == null) {
0694: rRoot = a.rRoot;
0695: } else {
0696: rTail.next = a.rRoot;
0697: }
0698:
0699: rTail = a.rTail;
0700: size += a.size;
0701: }
0702:
0703: void addAll(Result r) {
0704:
0705: if (r == null) {
0706: return;
0707: }
0708:
0709: Record from = r.rRoot;
0710:
0711: while (from != null) {
0712: add(from.data);
0713:
0714: from = from.next;
0715: }
0716: }
0717:
0718: public void clear() {
0719:
0720: rRoot = null;
0721: rTail = null;
0722: size = 0;
0723: }
0724:
0725: public boolean isEmpty() {
0726: return rRoot == null;
0727: }
0728:
0729: /**
0730: * Method declaration
0731: *
0732: * @param a
0733: */
0734: void setRows(Result a) {
0735:
0736: if (a == null) {
0737: rRoot = null;
0738: rTail = null;
0739: size = 0;
0740: } else {
0741: rRoot = a.rRoot;
0742: rTail = a.rTail;
0743: size = a.size;
0744: }
0745: }
0746:
0747: /**
0748: * Method declaration
0749: *
0750: * @param d
0751: */
0752: public void add(Object[] d) {
0753:
0754: Record r = new Record();
0755:
0756: r.data = d;
0757:
0758: if (rRoot == null) {
0759: rRoot = r;
0760: } else {
0761: rTail.next = r;
0762: }
0763:
0764: rTail = r;
0765:
0766: size++;
0767: }
0768:
0769: /**
0770: * Method declaration
0771: *
0772: * @param limitstart number of records to discard at the head
0773: * @param limitcount number of records to keep, all the rest if 0
0774: */
0775:
0776: // fredt@users 20020130 - patch 1.7.0 by fredt
0777: // rewritten and moved from Select.java
0778: void trimResult(int limitstart, int limitcount) {
0779:
0780: Record n = rRoot;
0781:
0782: if (n == null) {
0783: return;
0784: }
0785:
0786: if (limitstart >= size) {
0787: size = 0;
0788: rRoot = rTail = null;
0789:
0790: return;
0791: }
0792:
0793: size -= limitstart;
0794:
0795: for (int i = 0; i < limitstart; i++) {
0796: n = n.next;
0797:
0798: if (n == null) {
0799:
0800: // if iSize is consistent this block will never be reached
0801: size = 0;
0802: rRoot = rTail = n;
0803:
0804: return;
0805: }
0806: }
0807:
0808: rRoot = n;
0809:
0810: if (limitcount == 0 || limitcount >= size) {
0811: return;
0812: }
0813:
0814: for (int i = 1; i < limitcount; i++) {
0815: n = n.next;
0816:
0817: if (n == null) {
0818:
0819: // if iSize is consistent this block will never be reached
0820: return;
0821: }
0822: }
0823:
0824: size = limitcount;
0825: n.next = null;
0826: rTail = n;
0827: }
0828:
0829: /**
0830: * Removes duplicate rows on the basis of comparing the singificant
0831: * columns of the rows in the result.
0832: *
0833: * @throws HsqlException
0834: */
0835: void removeDuplicates(Session session) throws HsqlException {
0836: removeDuplicates(session, significantColumns);
0837: }
0838:
0839: /**
0840: * Removes duplicate rows on the basis of comparing the first columnCount
0841: * columns of rows in the result.
0842: *
0843: * @throws HsqlException
0844: */
0845:
0846: // fredt@users 20020130 - patch 1.7.0 by fredt
0847: // to ensure consistency of r.rTail r.iSize in all set operations
0848: void removeDuplicates(Session session, int columnCount)
0849: throws HsqlException {
0850:
0851: if (rRoot == null) {
0852: return;
0853: }
0854:
0855: int[] order = new int[columnCount];
0856: int[] way = new int[columnCount];
0857:
0858: for (int i = 0; i < columnCount; i++) {
0859: order[i] = i;
0860: way[i] = 1;
0861: }
0862:
0863: sortResult(session, order, way);
0864:
0865: Record n = rRoot;
0866:
0867: for (;;) {
0868: Record next = n.next;
0869:
0870: if (next == null) {
0871: break;
0872: }
0873:
0874: if (compareRecord(session, n.data, next.data, columnCount) == 0) {
0875: n.next = next.next;
0876:
0877: size--;
0878: } else {
0879: n = next;
0880: }
0881: }
0882:
0883: rTail = n;
0884: }
0885:
0886: /**
0887: * Removes duplicates then removes the contents of the second result
0888: * from this one base on first columnCount of the rows in each result.
0889: *
0890: * @param minus
0891: * @throws HsqlException
0892: */
0893: void removeSecond(Session session, Result minus, int columnCount)
0894: throws HsqlException {
0895:
0896: removeDuplicates(session, columnCount);
0897: minus.removeDuplicates(session, columnCount);
0898:
0899: Record n = rRoot;
0900: Record last = rRoot;
0901: boolean rootr = true; // checking rootrecord
0902: Record n2 = minus.rRoot;
0903: int i = 0;
0904:
0905: while (n != null && n2 != null) {
0906: i = compareRecord(session, n.data, n2.data, columnCount);
0907:
0908: if (i == 0) {
0909: if (rootr) {
0910: rRoot = last = n.next;
0911: } else {
0912: last.next = n.next;
0913: }
0914:
0915: n = n.next;
0916:
0917: size--;
0918: } else if (i > 0) { // r > minus
0919: n2 = n2.next;
0920: } else { // r < minus
0921: last = n;
0922: rootr = false;
0923: n = n.next;
0924: }
0925: }
0926:
0927: for (; n != null;) {
0928: last = n;
0929: n = n.next;
0930: }
0931:
0932: rTail = last;
0933: }
0934:
0935: /**
0936: * Removes all duplicate rows then removes all rows that are not shared
0937: * between this and the other result, based on comparing the first
0938: * columnCount columns of each result.
0939: *
0940: * @param r2
0941: * @throws HsqlException
0942: */
0943: void removeDifferent(Session session, Result r2, int columnCount)
0944: throws HsqlException {
0945:
0946: removeDuplicates(session, columnCount);
0947: r2.removeDuplicates(session, columnCount);
0948:
0949: Record n = rRoot;
0950: Record last = rRoot;
0951: boolean rootr = true; // checking rootrecord
0952: Record n2 = r2.rRoot;
0953: int i = 0;
0954:
0955: size = 0;
0956:
0957: while (n != null && n2 != null) {
0958: i = compareRecord(session, n.data, n2.data, columnCount);
0959:
0960: if (i == 0) { // same rows
0961: if (rootr) {
0962: rRoot = n; // make this the first record
0963: } else {
0964: last.next = n; // this is next record in resultset
0965: }
0966:
0967: rootr = false;
0968: last = n; // this is last record in resultset
0969: n = n.next;
0970: n2 = n2.next;
0971:
0972: size++;
0973: } else if (i > 0) { // r > r2
0974: n2 = n2.next;
0975: } else { // r < r2
0976: n = n.next;
0977: }
0978: }
0979:
0980: if (rootr) { // if no lines in resultset
0981: rRoot = null; // then return null
0982: last = null;
0983: } else {
0984: last.next = null; // else end resultset
0985: }
0986:
0987: rTail = last;
0988: }
0989:
0990: /**
0991: * Method declaration
0992: *
0993: * @param order
0994: * @param way
0995: * @throws HsqlException
0996: */
0997: void sortResult(Session session, final int[] order, final int[] way)
0998: throws HsqlException {
0999:
1000: if (rRoot == null || rRoot.next == null) {
1001: return;
1002: }
1003:
1004: Record source0, source1;
1005: Record[] target = new Record[2];
1006: Record[] targetlast = new Record[2];
1007: int dest = 0;
1008: Record n = rRoot;
1009:
1010: while (n != null) {
1011: Record next = n.next;
1012:
1013: n.next = target[dest];
1014: target[dest] = n;
1015: n = next;
1016: dest ^= 1;
1017: }
1018:
1019: for (int blocksize = 1; target[1] != null; blocksize <<= 1) {
1020: source0 = target[0];
1021: source1 = target[1];
1022: target[0] = target[1] = targetlast[0] = targetlast[1] = null;
1023:
1024: for (dest = 0; source0 != null; dest ^= 1) {
1025: int n0 = blocksize, n1 = blocksize;
1026:
1027: while (true) {
1028: if (n0 == 0 || source0 == null) {
1029: if (n1 == 0 || source1 == null) {
1030: break;
1031: }
1032:
1033: n = source1;
1034: source1 = source1.next;
1035:
1036: n1--;
1037: } else if (n1 == 0 || source1 == null) {
1038: n = source0;
1039: source0 = source0.next;
1040:
1041: n0--;
1042: } else if (compareRecord(session, source0.data,
1043: source1.data, order, way) > 0) {
1044: n = source1;
1045: source1 = source1.next;
1046:
1047: n1--;
1048: } else {
1049: n = source0;
1050: source0 = source0.next;
1051:
1052: n0--;
1053: }
1054:
1055: if (target[dest] == null) {
1056: target[dest] = n;
1057: } else {
1058: targetlast[dest].next = n;
1059: }
1060:
1061: targetlast[dest] = n;
1062: n.next = null;
1063: }
1064: }
1065: }
1066:
1067: rRoot = target[0];
1068: rTail = targetlast[0];
1069: }
1070:
1071: /**
1072: * Method declaration
1073: *
1074: * @param a
1075: * @param b
1076: * @param order
1077: * @param way
1078: * @return -1, 0, +1
1079: * @throws HsqlException
1080: */
1081: private int compareRecord(Session session, Object[] a,
1082: final Object[] b, final int[] order, int[] way)
1083: throws HsqlException {
1084:
1085: int i = Column.compare(session.database.collation, a[order[0]],
1086: b[order[0]], metaData.colTypes[order[0]]);
1087:
1088: if (i == 0) {
1089: for (int j = 1; j < order.length; j++) {
1090: i = Column.compare(session.database.collation,
1091: a[order[j]], b[order[j]],
1092: metaData.colTypes[order[j]]);
1093:
1094: if (i != 0) {
1095: return i * way[j];
1096: }
1097: }
1098: }
1099:
1100: return i * way[0];
1101: }
1102:
1103: /**
1104: * Method declaration
1105: *
1106: * @param a
1107: * @param b
1108: * @param len
1109: * @return -1, 0, +1
1110: * @throws HsqlException
1111: */
1112: private int compareRecord(Session session, Object[] a, Object[] b,
1113: int len) throws HsqlException {
1114:
1115: for (int j = 0; j < len; j++) {
1116: int i = Column.compare(session.database.collation, a[j],
1117: b[j], metaData.colTypes[j]);
1118:
1119: if (i != 0) {
1120: return i;
1121: }
1122: }
1123:
1124: return 0;
1125: }
1126:
1127: /**
1128: * Result structure used for set/get session attributes
1129: */
1130: static Result newSessionAttributesResult() {
1131:
1132: Result r = new Result(ResultConstants.DATA, 7);
1133:
1134: r.metaData.colNames = r.metaData.colLabels = r.metaData.tableNames = new String[] {
1135: "", "", "", "", "", "", "" };
1136: r.metaData.colTypes = new int[] { Types.VARCHAR, Types.VARCHAR,
1137: Types.INTEGER, Types.INTEGER, Types.BOOLEAN,
1138: Types.BOOLEAN, Types.BOOLEAN };
1139:
1140: return r;
1141: }
1142:
1143: void write(RowOutputBinary out) throws IOException, HsqlException {
1144:
1145: if (mode == ResultConstants.MULTI) {
1146: writeMulti(out);
1147:
1148: return;
1149: }
1150:
1151: int startPos = out.size();
1152:
1153: out.writeSize(0);
1154: out.writeIntData(mode);
1155: out.writeIntData(databaseID);
1156: out.writeIntData(sessionID);
1157:
1158: switch (mode) {
1159:
1160: case ResultConstants.GETSESSIONATTR:
1161: case ResultConstants.SQLDISCONNECT:
1162: case ResultConstants.SQLSTARTTRAN:
1163: case ResultConstants.HSQLRESETSESSION:
1164: break;
1165:
1166: case ResultConstants.SQLPREPARE:
1167:
1168: // Allows the engine side to fast-fail prepare of non-CALL
1169: // statement against a CallableStatement object and CALL
1170: // statement against PreparedStatement.
1171: //
1172: // May be useful in the future for other things
1173: out.writeIntData(getStatementType());
1174: out.writeString(mainString);
1175: break;
1176:
1177: case ResultConstants.PREPARE_ACK:
1178: case ResultConstants.SQLFREESTMT:
1179: out.writeIntData(statementID);
1180: break;
1181:
1182: case ResultConstants.SQLEXECDIRECT:
1183: out.writeIntData(updateCount);
1184: out.writeIntData(statementID); // currently unused
1185: out.writeString(mainString);
1186: break;
1187:
1188: case ResultConstants.ERROR:
1189: case ResultConstants.SQLCONNECT:
1190: out.writeString(mainString);
1191: out.writeString(subString);
1192: out.writeString(subSubString);
1193: out.writeIntData(statementID);
1194: break;
1195:
1196: case ResultConstants.UPDATECOUNT:
1197: out.writeIntData(updateCount);
1198: break;
1199:
1200: case ResultConstants.SQLENDTRAN: {
1201: int type = getEndTranType();
1202:
1203: out.writeIntData(type); // endtran type
1204:
1205: switch (type) {
1206:
1207: case ResultConstants.SAVEPOINT_NAME_RELEASE:
1208: case ResultConstants.SAVEPOINT_NAME_ROLLBACK:
1209: out.writeString(mainString); // savepoint name
1210:
1211: // default; // do nothing
1212: }
1213:
1214: break;
1215: }
1216: case ResultConstants.BATCHEXECUTE:
1217: case ResultConstants.BATCHEXECDIRECT:
1218: case ResultConstants.SQLEXECUTE:
1219: case ResultConstants.SETSESSIONATTR: {
1220: out.writeIntData(updateCount);
1221: out.writeIntData(statementID);
1222:
1223: int l = significantColumns;
1224:
1225: out.writeIntData(l);
1226:
1227: for (int i = 0; i < l; i++) {
1228: out.writeType(metaData.colTypes[i]);
1229: }
1230:
1231: out.writeIntData(size);
1232:
1233: Record n = rRoot;
1234:
1235: while (n != null) {
1236: out.writeData(l, metaData.colTypes, n.data, null, null);
1237:
1238: n = n.next;
1239: }
1240:
1241: break;
1242: }
1243: case ResultConstants.DATA:
1244: case ResultConstants.PARAM_META_DATA: {
1245: metaData.write(out, significantColumns);
1246: out.writeIntData(size);
1247:
1248: Record n = rRoot;
1249:
1250: while (n != null) {
1251: out.writeData(significantColumns, metaData.colTypes,
1252: n.data, null, null);
1253:
1254: n = n.next;
1255: }
1256:
1257: break;
1258: }
1259: case ResultConstants.SQLSETCONNECTATTR: {
1260: int type = getConnectionAttrType();
1261:
1262: out.writeIntData(type); // attr type
1263:
1264: switch (type) {
1265:
1266: case ResultConstants.SQL_ATTR_SAVEPOINT_NAME:
1267: out.writeString(mainString); // savepoint name
1268:
1269: // case ResultConstants.SQL_ATTR_AUTO_IPD // always true
1270: // default: // throw, but case never happens
1271: }
1272:
1273: break;
1274: }
1275: default:
1276: throw new HsqlException(Trace.getMessage(
1277: Trace.Result_Result, true,
1278: new Object[] { new Integer(mode) }), null, 0);
1279: }
1280:
1281: out.writeIntData(out.size(), startPos);
1282: }
1283:
1284: void readMultiResult(RowInputBinary in) throws HsqlException,
1285: IOException {
1286:
1287: mode = ResultConstants.MULTI;
1288: databaseID = in.readIntData();
1289: sessionID = in.readIntData();
1290:
1291: int count = in.readIntData();
1292:
1293: for (int i = 0; i < count; i++) {
1294:
1295: // Currently required for the outer result, but can simply
1296: // be ignored for sub-results
1297: in.readIntData();
1298: add(new Object[] { new Result(in) });
1299: }
1300: }
1301:
1302: private void writeMulti(RowOutputBinary out) throws IOException,
1303: HsqlException {
1304:
1305: int startPos = out.size();
1306:
1307: out.writeSize(0);
1308: out.writeIntData(mode);
1309: out.writeIntData(databaseID);
1310: out.writeIntData(sessionID);
1311: out.writeIntData(size);
1312:
1313: Record n = rRoot;
1314:
1315: while (n != null) {
1316: ((Result) n.data[0]).write(out);
1317:
1318: n = n.next;
1319: }
1320:
1321: out.writeIntData(out.size(), startPos);
1322: }
1323:
1324: /**
1325: * Convenience method for writing, shared by Server side.
1326: */
1327: public static void write(Result r, RowOutputBinary rowout,
1328: OutputStream dataout) throws IOException, HsqlException {
1329:
1330: rowout.reset();
1331: r.write(rowout);
1332: dataout.write(rowout.getOutputStream().getBuffer(), 0, rowout
1333: .getOutputStream().size());
1334: dataout.flush();
1335: }
1336:
1337: /**
1338: * Convenience method for reading, shared by Server side.
1339: */
1340: public static Result read(RowInputBinary rowin, DataInput datain)
1341: throws IOException, HsqlException {
1342:
1343: int length = datain.readInt();
1344:
1345: rowin.resetRow(0, length);
1346:
1347: byte[] byteArray = rowin.getBuffer();
1348: int offset = 4;
1349:
1350: datain.readFully(byteArray, offset, length - offset);
1351:
1352: return new Result(rowin);
1353: }
1354:
1355: /** @todo fredt - move the messages to Trace.java */
1356: public Result(Throwable t, String statement) {
1357:
1358: mode = ResultConstants.ERROR;
1359: exception = t;
1360:
1361: if (t instanceof HsqlException) {
1362: HsqlException he = (HsqlException) t;
1363:
1364: subString = he.getSQLState();
1365: mainString = he.getMessage();
1366:
1367: if (statement != null) {
1368: mainString += " in statement [" + statement + "]";
1369: }
1370:
1371: statementID = he.getErrorCode();
1372: } else if (t instanceof OutOfMemoryError) {
1373:
1374: // At this point, we've nothing to lose by doing this
1375: System.gc();
1376:
1377: subString = "S1000";
1378: mainString = "out of memory";
1379: statementID = Trace.OUT_OF_MEMORY;
1380: } else {
1381: subString = "S1000";
1382: mainString = Trace.getMessage(Trace.GENERAL_ERROR) + " "
1383: + t;
1384:
1385: if (statement != null) {
1386: mainString += " in statement [" + statement + "]";
1387: }
1388:
1389: statementID = Trace.GENERAL_ERROR;
1390: }
1391:
1392: subSubString = "";
1393: }
1394:
1395: public Throwable getException() {
1396: return exception;
1397: }
1398:
1399: public int getStatementID() {
1400: return statementID;
1401: }
1402:
1403: void setStatementID(int id) {
1404: statementID = id;
1405: }
1406:
1407: public String getMainString() {
1408: return mainString;
1409: }
1410:
1411: public void setMainString(String sql) {
1412: mainString = sql;
1413: }
1414:
1415: public String getSubString() {
1416: return subString;
1417: }
1418:
1419: public void setMaxRows(int count) {
1420: updateCount = count;
1421: }
1422:
1423: public int getUpdateCount() {
1424: return updateCount;
1425: }
1426:
1427: int getConnectionAttrType() {
1428: return updateCount;
1429: }
1430:
1431: void setConnectionAttrType(int type) {
1432: updateCount = type;
1433: }
1434:
1435: int getEndTranType() {
1436: return updateCount;
1437: }
1438:
1439: void setEndTranType(int type) {
1440: updateCount = type;
1441: }
1442:
1443: /** @todo fred - check this repurposing */
1444: public int[] getUpdateCounts() {
1445: return metaData.colTypes;
1446: }
1447:
1448: Object[] getParameterData() {
1449: return (rRoot == null) ? null : rRoot.data;
1450: }
1451:
1452: public void setParameterData(Object[] data) {
1453:
1454: if (rRoot == null) {
1455: rRoot = new Record();
1456: }
1457:
1458: rRoot.data = data;
1459: rRoot.next = null;
1460: rTail = rRoot;
1461: size = 1;
1462: }
1463:
1464: public void setResultType(int type) {
1465: mode = type;
1466: }
1467:
1468: public void setStatementType(int type) {
1469: updateCount = type;
1470: }
1471:
1472: public int getStatementType() {
1473: return updateCount;
1474: }
1475:
1476: public int getType() {
1477: return mode;
1478: }
1479:
1480: public boolean isData() {
1481: return mode == ResultConstants.DATA;
1482: }
1483:
1484: public boolean isError() {
1485: return mode == ResultConstants.ERROR;
1486: }
1487:
1488: public boolean isUpdateCount() {
1489: return mode == ResultConstants.UPDATECOUNT;
1490: }
1491:
1492: public Iterator iterator() {
1493: return new ResultIterator();
1494: }
1495:
1496: private class ResultIterator implements Iterator {
1497:
1498: boolean removed;
1499: int counter;
1500: Record current = rRoot;
1501: Record last;
1502:
1503: public boolean hasNext() {
1504: return counter < size;
1505: }
1506:
1507: public Object next() {
1508:
1509: if (hasNext()) {
1510: removed = false;
1511:
1512: if (counter != 0) {
1513: last = current;
1514: current = current.next;
1515: }
1516:
1517: counter++;
1518:
1519: return current.data;
1520: }
1521:
1522: throw new NoSuchElementException();
1523: }
1524:
1525: public int nextInt() {
1526: throw new NoSuchElementException();
1527: }
1528:
1529: public long nextLong() {
1530: throw new NoSuchElementException();
1531: }
1532:
1533: public void remove() {
1534:
1535: if (counter <= size && counter != 0 && !removed) {
1536: removed = true;
1537:
1538: if (current == rTail) {
1539: rTail = last;
1540: }
1541:
1542: if (current == rRoot) {
1543: current = rRoot = rRoot.next;
1544: } else {
1545: current = last;
1546: last = null;
1547: current.next = current.next.next;
1548: }
1549:
1550: size--;
1551: counter--;
1552:
1553: return;
1554: }
1555:
1556: throw new NoSuchElementException();
1557: }
1558: }
1559: }
|