0001: /*
0002:
0003: Derby - Class org.apache.derby.client.net.NetCursor
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to You under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.client.net;
0023:
0024: import org.apache.derby.client.am.Agent;
0025: import org.apache.derby.client.am.Blob;
0026: import org.apache.derby.client.am.Clob;
0027: import org.apache.derby.client.am.DisconnectException;
0028: import org.apache.derby.client.am.SignedBinary;
0029: import org.apache.derby.client.am.SqlException;
0030: import org.apache.derby.client.am.ClientMessageId;
0031: import org.apache.derby.client.am.SqlWarning;
0032: import org.apache.derby.client.am.Types;
0033: import org.apache.derby.client.am.SqlCode;
0034: import org.apache.derby.shared.common.reference.SQLState;
0035: import org.apache.derby.shared.common.sanity.SanityManager;
0036:
0037: public class NetCursor extends org.apache.derby.client.am.Cursor {
0038:
0039: NetResultSet netResultSet_;
0040: NetAgent netAgent_;
0041:
0042: Typdef qrydscTypdef_;
0043:
0044: int targetSqlamForTypdef_;
0045:
0046: // override column meta data
0047: int numMddOverrides_;
0048: int maximumRowSize_;
0049: boolean blocking_; // if true, multiple rows may be "blocked" in a single reply
0050:
0051: // Raw fdoca column meta data.
0052: int[] typeToUseForComputingDataLength_;
0053: boolean[] isGraphic_;
0054:
0055: // key = column position, value = index into extdtaData_
0056: java.util.HashMap extdtaPositions_;
0057: java.util.ArrayList extdtaData_; // queue to hold EXTDTA data that hasn't been correlated to its column #
0058:
0059: boolean rtnextrow_ = true;
0060:
0061: /** Flag indicating whether the result set on the server is
0062: * implicitly closed when end-of-data is received. */
0063: private boolean qryclsimpEnabled_;
0064:
0065: //-----------------------------constants--------------------------------------
0066:
0067: //---------------------constructors/finalizer---------------------------------
0068:
0069: NetCursor(NetAgent netAgent) {
0070: super (netAgent);
0071: netAgent_ = netAgent;
0072: numMddOverrides_ = 0;
0073: maximumRowSize_ = 0;
0074: extdtaPositions_ = new java.util.HashMap();
0075: extdtaData_ = new java.util.ArrayList();
0076: }
0077:
0078: NetCursor(NetAgent netAgent, int qryprctyp) //protocolType, CodePoint.FIXROWPRC | CodePoint.LMTBLKPRC
0079: {
0080: this (netAgent);
0081: if (qryprctyp == CodePoint.FIXROWPRC) {
0082: blocking_ = false;
0083: } else if (qryprctyp == CodePoint.LMTBLKPRC) {
0084: blocking_ = true;
0085: }
0086: }
0087:
0088: //-----------------------------parsing the data buffer------------------------
0089:
0090: /**
0091: * Calculate the column offsets for a row.
0092: * <p>
0093: * Pseudo-code:
0094: * <ol>
0095: * <li>parse thru the current row in dataBuffer computing column
0096: * offsets</li>
0097: * <li>if (we hit the super.lastValidBytePosition, ie. encounter
0098: * partial row)
0099: * <ol>
0100: * <li>shift partial row bytes to beginning of dataBuffer
0101: * (this.shiftPartialRowToBeginning())</li>
0102: * <li>reset current row position (also done by
0103: * this.shiftPartialRowToBeginning())</li>
0104: * <li>send and recv continue-query into commBuffer
0105: * (rs.flowContinueQuery())</li>
0106: * <li>parse commBuffer up to QRYDTA
0107: * (rs.flowContinueQuery())</li>
0108: * <li>copy query data from reply's commBuffer to our
0109: * dataBuffer (this.copyQrydta())</li>
0110: * </ol>
0111: * </ol>
0112: *
0113: * @param rowIndex row index
0114: * @param allowServerFetch if true, allow fetching more data from
0115: * server
0116: * @return <code>true</code> if the current row position is a
0117: * valid row position.
0118: * @exception SqlException
0119: * @exception DisconnectException
0120: */
0121: protected boolean calculateColumnOffsetsForRow_(int rowIndex,
0122: boolean allowServerFetch) throws SqlException,
0123: DisconnectException {
0124: int daNullIndicator = CodePoint.NULLDATA;
0125: int colNullIndicator = CodePoint.NULLDATA;
0126: int length;
0127:
0128: int[] columnDataPosition = null;
0129: int[] columnDataComputedLength = null;
0130: boolean[] columnDataIsNull = null;
0131: boolean receivedDeleteHoleWarning = false;
0132: boolean receivedRowUpdatedWarning = false;
0133:
0134: if ((position_ == lastValidBytePosition_)
0135: && (netResultSet_ != null)
0136: && (netResultSet_.scrollable_)) {
0137: return false;
0138: }
0139:
0140: if (hasLobs_) {
0141: extdtaPositions_.clear(); // reset positions for this row
0142: }
0143:
0144: NetSqlca[] netSqlca = this .parseSQLCARD(qrydscTypdef_);
0145:
0146: if (netSqlca != null) {
0147: for (int i = 0; i < netSqlca.length; i++) {
0148: int sqlcode = netSqlca[i].getSqlCode();
0149: if (sqlcode < 0) {
0150: throw new SqlException(netAgent_.logWriter_,
0151: netSqlca[i]);
0152: } else {
0153: if (sqlcode == SqlCode.END_OF_DATA.getCode()) {
0154: setAllRowsReceivedFromServer(true);
0155: if (netResultSet_ != null
0156: && netSqlca[i].containsSqlcax()) {
0157: netResultSet_.setRowCountEvent(netSqlca[i]
0158: .getRowCount(qrydscTypdef_));
0159: }
0160: } else if (netResultSet_ != null && sqlcode > 0) {
0161: String sqlState = netSqlca[i].getSqlState();
0162: if (!sqlState.equals(SQLState.ROW_DELETED)
0163: && !sqlState
0164: .equals(SQLState.ROW_UPDATED)) {
0165: netResultSet_
0166: .accumulateWarning(new SqlWarning(
0167: agent_.logWriter_,
0168: netSqlca[i]));
0169: } else {
0170: receivedDeleteHoleWarning |= sqlState
0171: .equals(SQLState.ROW_DELETED);
0172: receivedRowUpdatedWarning |= sqlState
0173: .equals(SQLState.ROW_UPDATED);
0174: }
0175: }
0176: }
0177: }
0178: }
0179:
0180: setIsUpdataDeleteHole(rowIndex, receivedDeleteHoleWarning);
0181: setIsRowUpdated(receivedRowUpdatedWarning);
0182:
0183: // If we don't have at least one byte in the buffer for the DA null indicator,
0184: // then we need to send a CNTQRY request to fetch the next block of data.
0185: // Read the DA null indicator.
0186: daNullIndicator = readFdocaOneByte();
0187:
0188: // In the case for held cursors, the +100 comes back as part of the QRYDTA, and as
0189: // we are parsing through the row that contains the SQLCA with +100, we mark the
0190: // nextRowPosition_ which is the lastValidBytePosition_, but we don't mark the
0191: // currentRowPosition_ until the next time next() is called causing the check
0192: // cursor_.currentRowPositionIsEqualToNextRowPosition () to fail in getRow() and thus
0193: // not returning 0 when it should. So we need to mark the current row position immediately
0194: // in order for getRow() to be able to pick it up.
0195:
0196: // markNextRowPosition() is called again once this method returns, but it is ok
0197: // since it's only resetting nextRowPosition_ to position_ and position_ will
0198: // not change again from this point.
0199:
0200: if (allRowsReceivedFromServer()
0201: && (position_ == lastValidBytePosition_)) {
0202: markNextRowPosition();
0203: makeNextRowPositionCurrent();
0204: return false;
0205: }
0206:
0207: // If data flows....
0208: if (daNullIndicator == 0x0) {
0209:
0210: if (SanityManager.DEBUG && receivedDeleteHoleWarning) {
0211: SanityManager
0212: .THROWASSERT("Delete hole warning received: nulldata expected");
0213: }
0214: incrementRowsReadEvent();
0215:
0216: // netResultSet_ is null if this method is invoked from Lob.position()
0217: // If row has exceeded the size of the ArrayList, new up a new int[] and add it to the
0218: // ArrayList, otherwise just reuse the int[].
0219: if (netResultSet_ != null && netResultSet_.scrollable_) {
0220: columnDataPosition = allocateColumnDataPositionArray(rowIndex);
0221: columnDataComputedLength = allocateColumnDataComputedLengthArray(rowIndex);
0222: columnDataIsNull = allocateColumnDataIsNullArray(rowIndex);
0223: // Since we are no longer setting the int[]'s to null for a delete/update hole, we need
0224: // another way of keeping track of the delete/update holes.
0225: setIsUpdataDeleteHole(rowIndex, false);
0226: } else {
0227: // Use the arrays defined on the Cursor for forward-only cursors.
0228: // can they ever be null
0229: if (columnDataPosition_ == null
0230: || columnDataComputedLength_ == null
0231: || isNull_ == null) {
0232: allocateColumnOffsetAndLengthArrays();
0233: }
0234: columnDataPosition = columnDataPosition_;
0235: columnDataComputedLength = columnDataComputedLength_;
0236: columnDataIsNull = isNull_;
0237: }
0238:
0239: // Loop through the columns
0240: for (int index = 0; index < columns_; index++) {
0241: // If column is nullable, read the 1-byte null indicator.
0242: if (nullable_[index])
0243: // Need to pass the column index so all previously calculated offsets can be
0244: // readjusted if the query block splits on a column null indicator.
0245:
0246: // null indicators from FD:OCA data
0247: // 0 to 127: a data value will flow.
0248: // -1 to -128: no data value will flow.
0249: {
0250: colNullIndicator = readFdocaOneByte(index);
0251: }
0252:
0253: // If non-null column data
0254: if (!nullable_[index]
0255: || (colNullIndicator >= 0 && colNullIndicator <= 127)) {
0256:
0257: // Set the isNull indicator to false
0258: columnDataIsNull[index] = false;
0259:
0260: switch (typeToUseForComputingDataLength_[index]) {
0261: // for fixed length data
0262: case Typdef.FIXEDLENGTH:
0263: columnDataPosition[index] = position_;
0264: if (isGraphic_[index]) {
0265: columnDataComputedLength[index] = skipFdocaBytes(
0266: fdocaLength_[index] * 2, index);
0267: } else {
0268: columnDataComputedLength[index] = skipFdocaBytes(
0269: fdocaLength_[index], index);
0270: }
0271: break;
0272:
0273: // for variable character string and variable byte string,
0274: // there are 2-byte of length in front of the data
0275: case Typdef.TWOBYTELENGTH:
0276: columnDataPosition[index] = position_;
0277: length = readFdocaTwoByteLength(index);
0278: // skip length + the 2-byte length field
0279: if (isGraphic_[index]) {
0280: columnDataComputedLength[index] = skipFdocaBytes(
0281: length * 2, index) + 2;
0282: } else {
0283: columnDataComputedLength[index] = skipFdocaBytes(
0284: length, index) + 2;
0285: }
0286: break;
0287:
0288: // For decimal columns, determine the precision, scale, and the representation
0289: case Typdef.DECIMALLENGTH:
0290: columnDataPosition[index] = position_;
0291: columnDataComputedLength[index] = skipFdocaBytes(
0292: getDecimalLength(index), index);
0293: break;
0294:
0295: case Typdef.LOBLENGTH:
0296: columnDataPosition[index] = position_;
0297: columnDataComputedLength[index] = this
0298: .skipFdocaBytes(
0299: fdocaLength_[index] & 0x7fff,
0300: index);
0301: break;
0302:
0303: // for short variable character string and short variable byte string,
0304: // there is a 1-byte length in front of the data
0305: case Typdef.ONEBYTELENGTH:
0306: columnDataPosition[index] = position_;
0307: length = readFdocaOneByte(index);
0308: // skip length + the 1-byte length field
0309: if (isGraphic_[index]) {
0310: columnDataComputedLength[index] = skipFdocaBytes(
0311: length * 2, index) + 1;
0312: } else {
0313: columnDataComputedLength[index] = skipFdocaBytes(
0314: length, index) + 1;
0315: }
0316: break;
0317:
0318: default:
0319: columnDataPosition[index] = position_;
0320: if (isGraphic_[index]) {
0321: columnDataComputedLength[index] = skipFdocaBytes(
0322: fdocaLength_[index] * 2, index);
0323: } else {
0324: columnDataComputedLength[index] = skipFdocaBytes(
0325: fdocaLength_[index], index);
0326: }
0327: break;
0328: }
0329: } else if ((colNullIndicator & 0x80) == 0x80) {
0330: // Null data. Set the isNull indicator to true.
0331: columnDataIsNull[index] = true;
0332: }
0333: }
0334:
0335: // set column offsets for the current row.
0336: columnDataPosition_ = columnDataPosition;
0337: columnDataComputedLength_ = columnDataComputedLength;
0338: isNull_ = columnDataIsNull;
0339:
0340: if (!allRowsReceivedFromServer()) {
0341: calculateLobColumnPositionsForRow();
0342: // Flow another CNTQRY if we are blocking, are using rtnextrow, and expect
0343: // non-trivial EXTDTAs for forward only cursors. Note we do not support
0344: // EXTDTA retrieval for scrollable cursors.
0345: // if qryrowset was sent on excsqlstt for a sp call, which is only the case
0346: if (blocking_ && rtnextrow_
0347: && !netResultSet_.scrollable_
0348: && !extdtaPositions_.isEmpty()) {
0349: if (allowServerFetch) {
0350: netResultSet_.flowFetch();
0351: } else {
0352: return false;
0353: }
0354: }
0355: }
0356: } else {
0357: if (netResultSet_ != null && netResultSet_.scrollable_) {
0358: if (receivedDeleteHoleWarning) {
0359: setIsUpdataDeleteHole(rowIndex, true);
0360: } else {
0361: if (SanityManager.DEBUG) {
0362: // Invariant: for SUR, we introduced the warning
0363: // in addition to null data.
0364: SanityManager
0365: .THROWASSERT("Delete hole warning expected");
0366: }
0367: }
0368: }
0369: }
0370:
0371: // If blocking protocol is used, we could have already received an ENDQRYRM,
0372: // which sets allRowsReceivedFromServer_ to true. It's safe to assume that all of
0373: // our QRYDTA's have been successfully copied to the dataBuffer. And even though
0374: // the flag for allRowsReceivedFromServer_ is set, we still want to continue to parse through
0375: // the data in the dataBuffer.
0376: // But in the case where fixed row protocol is used,
0377: if (!blocking_ && allRowsReceivedFromServer()
0378: && daNullIndicator == 0xFF) {
0379: return false;
0380: } else {
0381: return true;
0382: }
0383: }
0384:
0385: /**
0386: * Scan the data buffer to see if end of data (SQL state 02000)
0387: * has been received. This method should only be called when the
0388: * cursor is being closed since the pointer to the current row can
0389: * be modified.
0390: *
0391: * @exception SqlException
0392: */
0393: void scanDataBufferForEndOfData() throws SqlException {
0394: while (!allRowsReceivedFromServer()
0395: && (position_ != lastValidBytePosition_)) {
0396: stepNext(false);
0397: }
0398: }
0399:
0400: protected boolean isDataBufferNull() {
0401: if (dataBuffer_ == null) {
0402: return true;
0403: } else {
0404: return false;
0405: }
0406: }
0407:
0408: protected void allocateDataBuffer() {
0409: int length;
0410: if (maximumRowSize_ > DssConstants.MAX_DSS_LEN) {
0411: length = maximumRowSize_;
0412: } else {
0413: length = DssConstants.MAX_DSS_LEN;
0414: }
0415:
0416: dataBuffer_ = new byte[length];
0417: position_ = 0;
0418: lastValidBytePosition_ = 0;
0419: }
0420:
0421: protected void allocateDataBuffer(int length) {
0422: dataBuffer_ = new byte[length];
0423: }
0424:
0425: private int readFdocaInt()
0426: throws org.apache.derby.client.am.DisconnectException,
0427: SqlException {
0428: if ((position_ + 4) > lastValidBytePosition_) {
0429: // Check for ENDQRYRM, throw SqlException if already received one.
0430: checkAndThrowReceivedEndqryrm();
0431:
0432: // Send CNTQRY to complete the row/rowset.
0433: int lastValidByteBeforeFetch = completeSplitRow();
0434:
0435: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0436: // throw a SqlException for the ENDQRYRM.
0437: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0438: }
0439:
0440: int i = SignedBinary.getInt(dataBuffer_, position_);
0441: position_ += 4;
0442: return i;
0443: }
0444:
0445: // Reads 8-bytes from the dataBuffer from the current position.
0446: // If position is already at the end of the buffer, send CNTQRY to get more
0447: // data.
0448: private long readFdocaLong()
0449: throws org.apache.derby.client.am.DisconnectException,
0450: SqlException {
0451: if ((position_ + 8) > lastValidBytePosition_) {
0452: // Check for ENDQRYRM, throw SqlException if already received one.
0453: checkAndThrowReceivedEndqryrm();
0454:
0455: // Send CNTQRY to complete the row/rowset.
0456: int lastValidByteBeforeFetch = completeSplitRow();
0457:
0458: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was
0459: // received, throw a SqlException for the ENDQRYRM.
0460: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0461: }
0462:
0463: long i = SignedBinary.getLong(dataBuffer_, position_);
0464: position_ += 8;
0465: return i;
0466: }
0467:
0468: // Reads 1-byte from the dataBuffer from the current position.
0469: // If position is already at the end of the buffer, send CNTQRY to get more data.
0470: private int readFdocaOneByte()
0471: throws org.apache.derby.client.am.DisconnectException,
0472: SqlException {
0473: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0474: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0475: if (position_ == lastValidBytePosition_) {
0476: // Check for ENDQRYRM, throw SqlException if already received one.
0477: checkAndThrowReceivedEndqryrm();
0478:
0479: // Send CNTQRY to complete the row/rowset.
0480: int lastValidByteBeforeFetch = completeSplitRow();
0481:
0482: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0483: // throw a SqlException for the ENDQRYRM.
0484: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0485: }
0486: return dataBuffer_[position_++] & 0xff;
0487: }
0488:
0489: // Reads 1-byte from the dataBuffer from the current position.
0490: // If position is already at the end of the buffer, send CNTQRY to get more data.
0491: private int readFdocaOneByte(int index)
0492: throws org.apache.derby.client.am.DisconnectException,
0493: SqlException {
0494: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0495: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0496: if (position_ == lastValidBytePosition_) {
0497: // Check for ENDQRYRM, throw SqlException if already received one.
0498: checkAndThrowReceivedEndqryrm();
0499:
0500: // Send CNTQRY to complete the row/rowset.
0501: int lastValidByteBeforeFetch = completeSplitRow(index);
0502:
0503: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0504: // throw a SqlException for the ENDQRYRM.
0505: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0506: }
0507: return dataBuffer_[position_++] & 0xff;
0508: }
0509:
0510: // Reads <i>length</i> number of bytes from the dataBuffer starting from the
0511: // current position. Returns a new byte array which contains the bytes read.
0512: // If current position plus length goes past the lastValidBytePosition, send
0513: // CNTQRY to get more data.
0514: private byte[] readFdocaBytes(int length)
0515: throws org.apache.derby.client.am.DisconnectException,
0516: SqlException {
0517: byte[] b = new byte[length];
0518: ;
0519:
0520: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0521: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0522: if ((position_ + length) > lastValidBytePosition_) {
0523: // Check for ENDQRYRM, throw SqlException if already received one.
0524: checkAndThrowReceivedEndqryrm();
0525:
0526: // Send CNTQRY to complete the row/rowset.
0527: int lastValidByteBeforeFetch = completeSplitRow();
0528:
0529: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0530: // throw a SqlException for the ENDQRYRM.
0531: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0532: }
0533:
0534: for (int i = 0; i < length; i++) {
0535: b[i] = dataBuffer_[position_++];
0536: }
0537:
0538: return b;
0539: }
0540:
0541: // Reads 2-bytes from the dataBuffer starting from the current position, and
0542: // returns an integer constructed from the 2-bytes. If current position plus
0543: // 2 bytes goes past the lastValidBytePosition, send CNTQRY to get more data.
0544: private int readFdocaTwoByteLength()
0545: throws org.apache.derby.client.am.DisconnectException,
0546: SqlException {
0547: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0548: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0549: if ((position_ + 2) > lastValidBytePosition_) {
0550: // Check for ENDQRYRM, throw SqlException if already received one.
0551: checkAndThrowReceivedEndqryrm();
0552:
0553: // Send CNTQRY to complete the row/rowset.
0554: int lastValidByteBeforeFetch = completeSplitRow();
0555:
0556: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0557: // throw a SqlException for the ENDQRYRM.
0558: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0559: }
0560:
0561: return ((dataBuffer_[position_++] & 0xff) << 8)
0562: + ((dataBuffer_[position_++] & 0xff) << 0);
0563: }
0564:
0565: private int readFdocaTwoByteLength(int index)
0566: throws org.apache.derby.client.am.DisconnectException,
0567: SqlException {
0568: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0569: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0570: if ((position_ + 2) > lastValidBytePosition_) {
0571: // Check for ENDQRYRM, throw SqlException if already received one.
0572: checkAndThrowReceivedEndqryrm();
0573:
0574: // Send CNTQRY to complete the row/rowset.
0575: int lastValidByteBeforeFetch = completeSplitRow(index);
0576:
0577: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0578: // throw a SqlException for the ENDQRYRM.
0579: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0580: }
0581:
0582: return ((dataBuffer_[position_++] & 0xff) << 8)
0583: + ((dataBuffer_[position_++] & 0xff) << 0);
0584: }
0585:
0586: // Check if position plus length goes past the lastValidBytePosition.
0587: // If so, send CNTQRY to get more data.
0588: // length - number of bytes to skip
0589: // returns the number of bytes skipped
0590: private int skipFdocaBytes(int length)
0591: throws org.apache.derby.client.am.DisconnectException,
0592: SqlException {
0593: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0594: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0595: if ((position_ + length) > lastValidBytePosition_) {
0596: // Check for ENDQRYRM, throw SqlException if already received one.
0597: checkAndThrowReceivedEndqryrm();
0598:
0599: // Send CNTQRY to complete the row/rowset.
0600: int lastValidByteBeforeFetch = completeSplitRow();
0601:
0602: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0603: // throw a SqlException for the ENDQRYRM.
0604: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0605: }
0606: position_ += length;
0607: return length;
0608: }
0609:
0610: private int skipFdocaBytes(int length, int index)
0611: throws org.apache.derby.client.am.DisconnectException,
0612: SqlException {
0613: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
0614: // so there is no need to drive a flowFetch (continue query) request for singleton select.
0615: if ((position_ + length) > lastValidBytePosition_) {
0616: // Check for ENDQRYRM, throw SqlException if already received one.
0617: checkAndThrowReceivedEndqryrm();
0618:
0619: // Send CNTQRY to complete the row/rowset.
0620: int lastValidByteBeforeFetch = completeSplitRow(index);
0621:
0622: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
0623: // throw a SqlException for the ENDQRYRM.
0624: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
0625: }
0626:
0627: position_ += length;
0628: return length;
0629: }
0630:
0631: // Shift partial row bytes to beginning of dataBuffer,
0632: // and resets current row position, and lastValidBytePosition.
0633: // When we shift partial row, we'll have to recalculate column offsets
0634: // up to this column.
0635: private void shiftPartialRowToBeginning() {
0636: // Get the length to shift from the beginning of the partial row.
0637: int length = lastValidBytePosition_ - currentRowPosition_;
0638:
0639: // shift the data in the dataBufferStream
0640: dataBufferStream_.reset();
0641: if (dataBuffer_ != null) {
0642: dataBufferStream_.write(dataBuffer_, currentRowPosition_,
0643: length);
0644: }
0645:
0646: for (int i = 0; i < length; i++) {
0647: dataBuffer_[i] = dataBuffer_[currentRowPosition_ + i];
0648: }
0649:
0650: position_ = length - (lastValidBytePosition_ - position_);
0651: lastValidBytePosition_ = length;
0652: }
0653:
0654: private void adjustColumnOffsetsForColumnsPreviouslyCalculated(
0655: int index) {
0656: for (int j = 0; j <= index; j++) {
0657: columnDataPosition_[j] -= currentRowPosition_;
0658: }
0659: }
0660:
0661: private void resetCurrentRowPosition() {
0662: currentRowPosition_ = 0;
0663: }
0664:
0665: // Calculates the column index for Lob objects constructed from EXTDTA data.
0666: // Describe information isn't sufficient because we have to check
0667: // for trivial values (nulls or zero-length) and exclude them.
0668: void calculateLobColumnPositionsForRow() {
0669: int currentPosition = 0;
0670:
0671: for (int i = 0; i < columns_; i++) {
0672: if (isNonTrivialDataLob(i))
0673: // key = column position, data = index to corresponding data in extdtaData_
0674: // ASSERT: the server always returns the EXTDTA objects in ascending order
0675: {
0676: extdtaPositions_.put(new Integer(i + 1), new Integer(
0677: currentPosition++));
0678: }
0679: }
0680: }
0681:
0682: // prereq: the base data for the cursor has been processed for offsets and lengths
0683: boolean isNonTrivialDataLob(int index) {
0684: long length = 0L;
0685:
0686: if (isNull_[index]
0687: || (jdbcTypes_[index] != Types.BLOB && jdbcTypes_[index] != Types.CLOB)) {
0688: return false;
0689: }
0690:
0691: int position = columnDataPosition_[index];
0692:
0693: // if the high-order bit is set, length is unknown -> set value to x'FF..FF'
0694: if (((dataBuffer_[position]) & 0x80) == 0x80) {
0695: length = -1;
0696: } else {
0697:
0698: byte[] lengthBytes = new byte[columnDataComputedLength_[index]];
0699: byte[] longBytes = new byte[8];
0700:
0701: System.arraycopy(dataBuffer_, position, lengthBytes, 0,
0702: columnDataComputedLength_[index]);
0703:
0704: // right-justify for BIG ENDIAN
0705: int j = 0;
0706: for (int i = 8 - columnDataComputedLength_[index]; i < 8; i++) {
0707: longBytes[i] = lengthBytes[j];
0708: j++;
0709: }
0710: length = SignedBinary.getLong(longBytes, 0);
0711: }
0712: return (length != 0L) ? true : false;
0713: }
0714:
0715: protected void clearLobData_() {
0716: extdtaData_.clear();
0717: extdtaPositions_.clear();
0718: }
0719:
0720: // SQLCARD : FDOCA EARLY ROW
0721: // SQL Communications Area Row Description
0722: //
0723: // FORMAT FOR ALL SQLAM LEVELS
0724: // SQLCAGRP; GROUP LID 0x54; ELEMENT TAKEN 0(all); REP FACTOR 1
0725: NetSqlca[] parseSQLCARD(Typdef typdef)
0726: throws org.apache.derby.client.am.DisconnectException,
0727: SqlException {
0728: return parseSQLCAGRP(typdef);
0729: }
0730:
0731: // SQLCAGRP : FDOCA EARLY GROUP
0732: // SQL Communcations Area Group Description
0733: //
0734: // FORMAT FOR SQLAM <= 6
0735: // SQLCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0736: // SQLSTATE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5
0737: // SQLERRPROC; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8
0738: // SQLCAXGRP; PROTOCOL TYPE N-GDA; ENVLID 0x52; Length Override 0
0739: //
0740: // FORMAT FOR SQLAM >= 7
0741: // SQLCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0742: // SQLSTATE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5
0743: // SQLERRPROC; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8
0744: // SQLCAXGRP; PROTOCOL TYPE N-GDA; ENVLID 0x52; Length Override 0
0745: // SQLDIAGGRP; PROTOCOL TYPE N-GDA; ENVLID 0x56; Length Override 0
0746: private NetSqlca[] parseSQLCAGRP(Typdef typdef)
0747: throws org.apache.derby.client.am.DisconnectException,
0748: SqlException {
0749: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0750: return null;
0751: }
0752: int sqlcode = readFdocaInt();
0753: byte[] sqlstate = readFdocaBytes(5);
0754: byte[] sqlerrproc = readFdocaBytes(8);
0755: NetSqlca netSqlca = new NetSqlca(netAgent_.netConnection_,
0756: sqlcode, sqlstate, sqlerrproc);
0757:
0758: parseSQLCAXGRP(typdef, netSqlca);
0759:
0760: NetSqlca[] sqlCa = parseSQLDIAGGRP();
0761:
0762: NetSqlca[] ret_val;
0763: if (sqlCa != null) {
0764: ret_val = new NetSqlca[sqlCa.length + 1];
0765: System.arraycopy(sqlCa, 0, ret_val, 1, sqlCa.length);
0766: } else {
0767: ret_val = new NetSqlca[1];
0768: }
0769: ret_val[0] = netSqlca;
0770:
0771: return ret_val;
0772: }
0773:
0774: // SQLCAXGRP : EARLY FDOCA GROUP
0775: // SQL Communications Area Exceptions Group Description
0776: //
0777: // FORMAT FOR SQLAM <= 6
0778: // SQLRDBNME; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 18
0779: // SQLERRD1; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0780: // SQLERRD2; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0781: // SQLERRD3; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0782: // SQLERRD4; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0783: // SQLERRD5; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0784: // SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0785: // SQLWARN0; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0786: // SQLWARN1; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0787: // SQLWARN2; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0788: // SQLWARN3; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0789: // SQLWARN4; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0790: // SQLWARN5; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0791: // SQLWARN6; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0792: // SQLWARN7; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0793: // SQLWARN8; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0794: // SQLWARN9; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0795: // SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0796: // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70
0797: // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70
0798: //
0799: // FORMAT FOR SQLAM >= 7
0800: // SQLERRD1; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0801: // SQLERRD2; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0802: // SQLERRD3; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0803: // SQLERRD4; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0804: // SQLERRD5; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0805: // SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0806: // SQLWARN0; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0807: // SQLWARN1; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0808: // SQLWARN2; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0809: // SQLWARN3; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0810: // SQLWARN4; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0811: // SQLWARN5; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0812: // SQLWARN6; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0813: // SQLWARN7; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0814: // SQLWARN8; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0815: // SQLWARN9; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0816: // SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0817: // SQLRDBNAME; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255
0818: // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70
0819: // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70
0820: private void parseSQLCAXGRP(Typdef typdef, NetSqlca netSqlca)
0821: throws DisconnectException, SqlException {
0822: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0823: netSqlca.setContainsSqlcax(false);
0824: return;
0825: }
0826:
0827: // SQLERRD1 to SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0828: int[] sqlerrd = new int[6];
0829: for (int i = 0; i < sqlerrd.length; i++) {
0830: sqlerrd[i] = readFdocaInt();
0831: }
0832:
0833: // SQLWARN0 to SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1
0834: byte[] sqlwarn = readFdocaBytes(11);
0835:
0836: // skip over the rdbnam for now
0837: // SQLRDBNAME; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255
0838: parseVCS(typdef);
0839:
0840: // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70
0841: // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70
0842: int varcharLength = readFdocaTwoByteLength(); // mixed length
0843: byte[] sqlerrmc = null;
0844: int sqlerrmcCcsid = 0;
0845: if (varcharLength != 0) { // if mixed
0846: sqlerrmc = readFdocaBytes(varcharLength); // read mixed bytes
0847: sqlerrmcCcsid = typdef.getCcsidMbc();
0848: skipFdocaBytes(2); // skip single length
0849: } else {
0850: varcharLength = readFdocaTwoByteLength(); // read single length
0851: sqlerrmc = readFdocaBytes(varcharLength); // read single bytes
0852: sqlerrmcCcsid = typdef.getCcsidSbc();
0853: }
0854:
0855: netSqlca.setSqlerrd(sqlerrd);
0856: netSqlca.setSqlwarnBytes(sqlwarn);
0857: netSqlca.setSqlerrmcBytes(sqlerrmc, sqlerrmcCcsid);
0858: }
0859:
0860: // SQLDIAGGRP : FDOCA EARLY GROUP
0861: private NetSqlca[] parseSQLDIAGGRP() throws DisconnectException,
0862: SqlException {
0863: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0864: return null;
0865: }
0866:
0867: parseSQLDIAGSTT();
0868: NetSqlca[] sqlca = parseSQLDIAGCI();
0869: parseSQLDIAGCN();
0870:
0871: return sqlca;
0872: }
0873:
0874: // SQL Diagnostics Statement Group Description - Identity 0xD3
0875: // NULLDATA will be received for now
0876: private void parseSQLDIAGSTT() throws DisconnectException,
0877: SqlException {
0878: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0879: return;
0880: }
0881:
0882: // The server should send NULLDATA
0883: netAgent_
0884: .accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(
0885: netAgent_, new ClientMessageId(
0886: SQLState.DRDA_COMMAND_NOT_IMPLEMENTED),
0887: "parseSQLDIAGSTT"));
0888: }
0889:
0890: // SQL Diagnostics Condition Information Array - Identity 0xF5
0891: // SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1
0892: // SQLDCIROW; ROW LID 0xE5; ELEMENT TAKEN 0(all); REP FACTOR 0(all)
0893: private NetSqlca[] parseSQLDIAGCI() throws DisconnectException,
0894: SqlException {
0895: int num = readFdocaTwoByteLength(); // SQLNUMGRP - SQLNUMROW
0896: NetSqlca[] ret_val = null;
0897: if (num != 0) {
0898: ret_val = new NetSqlca[num];
0899: }
0900:
0901: for (int i = 0; i < num; i++) {
0902: ret_val[i] = parseSQLDCROW();
0903: }
0904: return ret_val;
0905: }
0906:
0907: // SQL Diagnostics Connection Array - Identity 0xF6
0908: // NULLDATA will be received for now
0909: private void parseSQLDIAGCN() throws DisconnectException,
0910: SqlException {
0911: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0912: return;
0913: }
0914:
0915: // The server should send NULLDATA
0916: netAgent_
0917: .accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(
0918: netAgent_, new ClientMessageId(
0919: SQLState.DRDA_COMMAND_NOT_IMPLEMENTED),
0920: "parseSQLDIAGCN"));
0921: }
0922:
0923: // SQL Diagnostics Condition Group Description
0924: //
0925: // SQLDCCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0926: // SQLDCSTATE; PROTOCOL TYPE FCS; ENVLID Ox30; Lengeh Override 5
0927: // SQLDCREASON; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0928: // SQLDCLINEN; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0929: // SQLDCROWN; PROTOCOL TYPE I8; ENVLID 0x16; Lengeh Override 8
0930: // SQLDCER01; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0931: // SQLDCER02; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0932: // SQLDCER03; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0933: // SQLDCER04; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0934: // SQLDCPART; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0935: // SQLDCPPOP; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4
0936: // SQLDCMSGID; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 10
0937: // SQLDCMDE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8
0938: // SQLDCPMOD; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5
0939: // SQLDCRDB; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255
0940: // SQLDCTOKS; PROTOCOL TYPE N-RLO; ENVLID 0xF7; Length Override 0
0941: // SQLDCMSG_m; PROTOCOL TYPE NVMC; ENVLID 0x3F; Length Override 32672
0942: // SQLDCMSG_S; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 32672
0943: // SQLDCCOLN_m; PROTOCOL TYPE NVCM ; ENVLID 0x3F; Length Override 255
0944: // SQLDCCOLN_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255
0945: // SQLDCCURN_m; PROTOCOL TYPE NVCM; ENVLID 0x3F; Length Override 255
0946: // SQLDCCURN_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255
0947: // SQLDCPNAM_m; PROTOCOL TYPE NVCM; ENVLID 0x3F; Length Override 255
0948: // SQLDCPNAM_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255
0949: // SQLDCXGRP; PROTOCOL TYPE N-GDA; ENVLID 0xD3; Length Override 1
0950: private NetSqlca parseSQLDCGRP() throws DisconnectException,
0951: SqlException {
0952:
0953: int sqldcCode = readFdocaInt(); // SQLCODE
0954: String sqldcState = readFdocaString(5, netAgent_.targetTypdef_
0955: .getCcsidSbcEncoding()); // SQLSTATE
0956: int sqldcReason = readFdocaInt(); // REASON_CODE
0957:
0958: skipFdocaBytes(12); // LINE_NUMBER + ROW_NUMBER
0959:
0960: NetSqlca sqlca = new NetSqlca(netAgent_.netConnection_,
0961: sqldcCode, sqldcState, (byte[]) null);
0962:
0963: skipFdocaBytes(49); // SQLDCER01-04 + SQLDCPART + SQLDCPPOP + SQLDCMSGID
0964: // SQLDCMDE + SQLDCPMOD + RDBNAME
0965: parseSQLDCTOKS(); // MESSAGE_TOKENS
0966:
0967: String sqldcMsg = parseVCS(qrydscTypdef_); // MESSAGE_TEXT
0968:
0969: if (sqldcMsg != null) {
0970: sqlca.setSqlerrmcBytes(sqldcMsg.getBytes(),
0971: netAgent_.targetTypdef_.getByteOrder());
0972: }
0973:
0974: skipFdocaBytes(12); // COLUMN_NAME + PARAMETER_NAME + EXTENDED_NAMES
0975:
0976: parseSQLDCXGRP(); // SQLDCXGRP
0977: return sqlca;
0978: }
0979:
0980: // SQL Diagnostics Condition Row - Identity 0xE5
0981: // SQLDCGRP; GROUP LID 0xD5; ELEMENT TAKEN 0(all); REP FACTOR 1
0982: private NetSqlca parseSQLDCROW() throws DisconnectException,
0983: SqlException {
0984: return parseSQLDCGRP();
0985: }
0986:
0987: // SQL Diagnostics Condition Token Array - Identity 0xF7
0988: // NULLDATA will be received for now
0989: void parseSQLDCTOKS() throws DisconnectException, SqlException {
0990: if (readFdocaOneByte() == CodePoint.NULLDATA) {
0991: return;
0992: }
0993:
0994: // The server should send NULLDATA
0995: netAgent_
0996: .accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(
0997: netAgent_, new ClientMessageId(
0998: SQLState.DRDA_COMMAND_NOT_IMPLEMENTED),
0999: "parseSQLDCTOKS"));
1000: }
1001:
1002: // SQL Diagnostics Extended Names Group Description - Identity 0xD5
1003: // NULLDATA will be received for now
1004: private void parseSQLDCXGRP() throws DisconnectException,
1005: SqlException {
1006: if (readFdocaOneByte() == CodePoint.NULLDATA) {
1007: return;
1008: }
1009:
1010: // The server should send NULLDATA
1011: netAgent_
1012: .accumulateChainBreakingReadExceptionAndThrow(new DisconnectException(
1013: netAgent_, new ClientMessageId(
1014: SQLState.DRDA_COMMAND_NOT_IMPLEMENTED),
1015: "parseSQLDCXGRP"));
1016: }
1017:
1018: private String parseVCS(Typdef typdefInEffect)
1019: throws DisconnectException, SqlException {
1020: return readFdocaString(readFdocaTwoByteLength(), typdefInEffect
1021: .getCcsidSbcEncoding());
1022: }
1023:
1024: // This is not used for column data.
1025: private String readFdocaString(int length, String encoding)
1026: throws DisconnectException, SqlException {
1027: if (length == 0) {
1028: return null;
1029: }
1030:
1031: // For singleton select, the complete row always comes back, even if multiple query blocks are required,
1032: // so there is no need to drive a flowFetch (continue query) request for singleton select.
1033: if ((position_ + length) > lastValidBytePosition_) {
1034: // Check for ENDQRYRM, throw SqlException if already received one.
1035: checkAndThrowReceivedEndqryrm();
1036:
1037: // Send CNTQRY to complete the row/rowset.
1038: int lastValidByteBeforeFetch = completeSplitRow();
1039:
1040: // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received,
1041: // throw a SqlException for the ENDQRYRM.
1042: checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch);
1043: }
1044:
1045: String s = null;
1046:
1047: try {
1048: s = new String(dataBuffer_, position_, length, encoding);
1049: } catch (java.io.UnsupportedEncodingException e) {
1050: netAgent_
1051: .accumulateChainBreakingReadExceptionAndThrow(new org.apache.derby.client.am.DisconnectException(
1052: netAgent_,
1053: new ClientMessageId(
1054: SQLState.NET_ENCODING_NOT_SUPPORTED),
1055: e));
1056: }
1057:
1058: position_ += length;
1059: return s;
1060: }
1061:
1062: void allocateColumnOffsetAndLengthArrays() {
1063: columnDataPosition_ = new int[columns_];
1064: columnDataComputedLength_ = new int[columns_];
1065: isNull_ = new boolean[columns_];
1066: }
1067:
1068: void setBlocking(int queryProtocolType) {
1069: if (queryProtocolType == CodePoint.LMTBLKPRC) {
1070: blocking_ = true;
1071: } else {
1072: blocking_ = false;
1073: }
1074: }
1075:
1076: protected byte[] findExtdtaData(int column) {
1077: byte[] data = null;
1078:
1079: // locate the EXTDTA bytes, if any
1080: Integer key = new Integer(column);
1081:
1082: if (extdtaPositions_.containsKey(key)) {
1083: // found, get the data
1084: int extdtaQueuePosition = ((Integer) extdtaPositions_
1085: .get(key)).intValue();
1086: data = (byte[]) (extdtaData_.get(extdtaQueuePosition));
1087: }
1088:
1089: return data;
1090: }
1091:
1092: public Blob getBlobColumn_(int column, Agent agent)
1093: throws SqlException {
1094: int index = column - 1;
1095: int dataOffset;
1096: byte[] data;
1097: Blob blob = null;
1098:
1099: // locate the EXTDTA bytes, if any
1100: data = findExtdtaData(column);
1101:
1102: if (data != null) {
1103: // data found
1104: // set data offset based on the presence of a null indicator
1105: if (!nullable_[index]) {
1106: dataOffset = 0;
1107: } else {
1108: dataOffset = 1;
1109: }
1110:
1111: blob = new Blob(data, agent, dataOffset);
1112: } else {
1113: blob = new Blob(new byte[0], agent, 0);
1114: }
1115:
1116: return blob;
1117: }
1118:
1119: public Clob getClobColumn_(int column, Agent agent)
1120: throws SqlException {
1121: int index = column - 1;
1122: int dataOffset;
1123: byte[] data;
1124: Clob clob = null;
1125:
1126: // locate the EXTDTA bytes, if any
1127: data = findExtdtaData(column);
1128:
1129: if (data != null) {
1130: // data found
1131: // set data offset based on the presence of a null indicator
1132: if (!nullable_[index]) {
1133: dataOffset = 0;
1134: } else {
1135: dataOffset = 1;
1136: }
1137: clob = new Clob(agent, data, charsetName_[index],
1138: dataOffset);
1139: } else {
1140: // the locator is not valid, it is a zero-length LOB
1141: clob = new Clob(agent, "");
1142: }
1143:
1144: return clob;
1145: }
1146:
1147: public byte[] getClobBytes_(int column, int[] dataOffset /*output*/)
1148: throws SqlException {
1149: int index = column - 1;
1150: byte[] data = null;
1151:
1152: // locate the EXTDTA bytes, if any
1153: data = findExtdtaData(column);
1154:
1155: if (data != null) {
1156: // data found
1157: // set data offset based on the presence of a null indicator
1158: if (!nullable_[index]) {
1159: dataOffset[0] = 0;
1160: } else {
1161: dataOffset[0] = 1;
1162: }
1163: }
1164:
1165: return data;
1166: }
1167:
1168: // this is really an event-callback from NetStatementReply.parseSQLDTARDarray()
1169: void initializeColumnInfoArrays(Typdef typdef, int columnCount,
1170: int targetSqlamForTypdef) throws DisconnectException {
1171: qrydscTypdef_ = typdef;
1172:
1173: // Allocate arrays to hold the descriptor information.
1174: setNumberOfColumns(columnCount);
1175: fdocaLength_ = new int[columnCount];
1176: isGraphic_ = new boolean[columnCount];
1177: typeToUseForComputingDataLength_ = new int[columnCount];
1178: targetSqlamForTypdef_ = targetSqlamForTypdef;
1179: }
1180:
1181: int ensureSpaceForDataBuffer(int ddmLength) {
1182: if (dataBuffer_ == null) {
1183: allocateDataBuffer();
1184: }
1185: //super.resultSet.cursor.clearColumnDataOffsetsCache();
1186: // Need to know how many bytes to ask from the Reply object,
1187: // and handle the case where buffer is not big enough for all the bytes.
1188: // Get the length in front of the code point first.
1189:
1190: int bytesAvailableInDataBuffer = dataBuffer_.length
1191: - lastValidBytePosition_;
1192:
1193: // Make sure the buffer has at least ddmLength amount of room left.
1194: // If not, expand the buffer before calling the getQrydtaData() method.
1195: if (bytesAvailableInDataBuffer < ddmLength) {
1196:
1197: // Get a new buffer that is twice the size of the current buffer.
1198: // Copy the contents from the old buffer to the new buffer.
1199: int newBufferSize = 2 * dataBuffer_.length;
1200:
1201: while (newBufferSize < ddmLength) {
1202: newBufferSize = 2 * newBufferSize;
1203: }
1204:
1205: byte[] tempBuffer = new byte[newBufferSize];
1206:
1207: System.arraycopy(dataBuffer_, 0, tempBuffer, 0,
1208: lastValidBytePosition_);
1209:
1210: // Make the new buffer the dataBuffer.
1211: dataBuffer_ = tempBuffer;
1212:
1213: // Recalculate bytesAvailableInDataBuffer
1214: bytesAvailableInDataBuffer = dataBuffer_.length
1215: - lastValidBytePosition_;
1216: }
1217: return bytesAvailableInDataBuffer;
1218: }
1219:
1220: protected void getMoreData_() throws SqlException {
1221: // reset the dataBuffer_ before getting more data if cursor is foward-only.
1222: // getMoreData() is only called in Cursor.next() when current position is
1223: // equal to lastValidBytePosition_.
1224: if (netResultSet_.resultSetType_ == java.sql.ResultSet.TYPE_FORWARD_ONLY) {
1225: resetDataBuffer();
1226: }
1227: netResultSet_.flowFetch();
1228: }
1229:
1230: public void nullDataForGC() // memory leak fix
1231: {
1232: super .nullDataForGC();
1233: qrydscTypdef_ = null;
1234: typeToUseForComputingDataLength_ = null;
1235: isGraphic_ = null;
1236:
1237: if (extdtaPositions_ != null) {
1238: extdtaPositions_.clear();
1239: }
1240: extdtaPositions_ = null;
1241:
1242: if (extdtaData_ != null) {
1243: extdtaData_.clear();
1244: }
1245: extdtaData_ = null;
1246: }
1247:
1248: // It is possible for the driver to have received an QRYDTA(with incomplete row)+ENDQRYRM+SQLCARD.
1249: // This means some error has occurred on the server and the server is terminating the query.
1250: // Before sending a CNTQRY to retrieve the rest of the split row, check if an ENDQRYRM has already
1251: // been received. If so, do not send CNTQRY because the cursor is already closed on the server.
1252: // Instead, throw a SqlException. Since we did not receive a complete row, it is not safe to
1253: // allow the application to continue to access the ResultSet, so we close it.
1254: private void checkAndThrowReceivedEndqryrm() throws SqlException {
1255: // If we are in a split row, and before sending CNTQRY, check whether an ENDQRYRM
1256: // has been received.
1257: if (!netResultSet_.openOnServer_) {
1258: SqlException sqlException = null;
1259: int sqlcode = org.apache.derby.client.am.Utils
1260: .getSqlcodeFromSqlca(netResultSet_.queryTerminatingSqlca_);
1261: if (sqlcode < 0) {
1262: sqlException = new SqlException(agent_.logWriter_,
1263: netResultSet_.queryTerminatingSqlca_);
1264: } else {
1265: sqlException = new SqlException(
1266: agent_.logWriter_,
1267: new ClientMessageId(
1268: SQLState.NET_QUERY_PROCESSING_TERMINATED));
1269: }
1270: try {
1271: netResultSet_.closeX(); // the auto commit logic is in closeX()
1272: } catch (SqlException e) {
1273: sqlException.setNextException(e);
1274: }
1275: throw sqlException;
1276: }
1277: }
1278:
1279: private void checkAndThrowReceivedEndqryrm(
1280: int lastValidBytePositionBeforeFetch) throws SqlException {
1281: // if we have received more data in the dataBuffer_, just return.
1282: if (lastValidBytePosition_ > lastValidBytePositionBeforeFetch) {
1283: return;
1284: }
1285: checkAndThrowReceivedEndqryrm();
1286: }
1287:
1288: private int completeSplitRow() throws DisconnectException,
1289: SqlException {
1290: int lastValidBytePositionBeforeFetch = 0;
1291: if (netResultSet_ != null && netResultSet_.scrollable_) {
1292: lastValidBytePositionBeforeFetch = lastValidBytePosition_;
1293: netResultSet_.flowFetchToCompleteRowset();
1294: } else {
1295: // Shift partial row to the beginning of the dataBuffer
1296: shiftPartialRowToBeginning();
1297: resetCurrentRowPosition();
1298: lastValidBytePositionBeforeFetch = lastValidBytePosition_;
1299: netResultSet_.flowFetch();
1300: }
1301: return lastValidBytePositionBeforeFetch;
1302: }
1303:
1304: private int completeSplitRow(int index) throws DisconnectException,
1305: SqlException {
1306: int lastValidBytePositionBeforeFetch = 0;
1307: if (netResultSet_ != null && netResultSet_.scrollable_) {
1308: lastValidBytePositionBeforeFetch = lastValidBytePosition_;
1309: netResultSet_.flowFetchToCompleteRowset();
1310: } else {
1311: // Shift partial row to the beginning of the dataBuffer
1312: shiftPartialRowToBeginning();
1313: adjustColumnOffsetsForColumnsPreviouslyCalculated(index);
1314: resetCurrentRowPosition();
1315: lastValidBytePositionBeforeFetch = lastValidBytePosition_;
1316: netResultSet_.flowFetch();
1317: }
1318: return lastValidBytePositionBeforeFetch;
1319: }
1320:
1321: private int[] allocateColumnDataPositionArray(int row) {
1322: int[] columnDataPosition;
1323: if (columnDataPositionCache_.size() == row) {
1324: columnDataPosition = new int[columns_];
1325: columnDataPositionCache_.add(columnDataPosition);
1326: } else {
1327: columnDataPosition = (int[]) columnDataPositionCache_
1328: .get(row);
1329: }
1330: return columnDataPosition;
1331: }
1332:
1333: private int[] allocateColumnDataComputedLengthArray(int row) {
1334: int[] columnDataComputedLength;
1335: if (columnDataLengthCache_.size() == row) {
1336: columnDataComputedLength = new int[columns_];
1337: columnDataLengthCache_.add(columnDataComputedLength);
1338: } else {
1339: columnDataComputedLength = (int[]) columnDataLengthCache_
1340: .get(row);
1341: }
1342: return columnDataComputedLength;
1343: }
1344:
1345: private boolean[] allocateColumnDataIsNullArray(int row) {
1346: boolean[] columnDataIsNull;
1347: if (columnDataIsNullCache_.size() <= row) {
1348: columnDataIsNull = new boolean[columns_];
1349: columnDataIsNullCache_.add(columnDataIsNull);
1350: } else {
1351: columnDataIsNull = (boolean[]) columnDataIsNullCache_
1352: .get(row);
1353: }
1354: return columnDataIsNull;
1355: }
1356:
1357: protected int getDecimalLength(int index) {
1358: return (((fdocaLength_[index] >> 8) & 0xff) + 2) / 2;
1359: }
1360:
1361: /**
1362: * Set the value of value of allRowsReceivedFromServer_.
1363: *
1364: * @param b a <code>boolean</code> value indicating whether all
1365: * rows are received from the server
1366: */
1367: public final void setAllRowsReceivedFromServer(boolean b) {
1368: if (b && qryclsimpEnabled_) {
1369: netResultSet_.markClosedOnServer();
1370: }
1371: super .setAllRowsReceivedFromServer(b);
1372: }
1373:
1374: /**
1375: * Set a flag indicating whether QRYCLSIMP is enabled.
1376: *
1377: * @param flag true if QRYCLSIMP is enabled
1378: */
1379: final void setQryclsimpEnabled(boolean flag) {
1380: qryclsimpEnabled_ = flag;
1381: }
1382:
1383: /**
1384: * Check whether QRYCLSIMP is enabled on this cursor.
1385: *
1386: * @return true if QRYCLSIMP is enabled
1387: */
1388: final boolean getQryclsimpEnabled() {
1389: return qryclsimpEnabled_;
1390: }
1391: }
|