1 | /* |
2 | |
3 | Derby - Class org.apache.derby.client.net.NetCursor |
4 | |
5 | Copyright (c) 2001, 2005 The Apache Software Foundation or its licensors, where applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.client.net; |
22 | |
23 | import org.apache.derby.client.am.Agent; |
24 | import org.apache.derby.client.am.Blob; |
25 | import org.apache.derby.client.am.Clob; |
26 | import org.apache.derby.client.am.DisconnectException; |
27 | import org.apache.derby.client.am.SignedBinary; |
28 | import org.apache.derby.client.am.SqlException; |
29 | import org.apache.derby.client.am.ClientMessageId; |
30 | import org.apache.derby.client.am.SqlWarning; |
31 | import org.apache.derby.client.am.Types; |
32 | import org.apache.derby.client.am.SqlCode; |
33 | import org.apache.derby.shared.common.reference.SQLState; |
34 | import org.apache.derby.shared.common.sanity.SanityManager; |
35 | |
36 | public class NetCursor extends org.apache.derby.client.am.Cursor { |
37 | |
38 | NetResultSet netResultSet_; |
39 | NetAgent netAgent_; |
40 | |
41 | Typdef qrydscTypdef_; |
42 | |
43 | int targetSqlamForTypdef_; |
44 | |
45 | |
46 | // override column meta data |
47 | int numMddOverrides_; |
48 | int maximumRowSize_; |
49 | boolean blocking_; // if true, multiple rows may be "blocked" in a single reply |
50 | |
51 | // Raw fdoca column meta data. |
52 | int[] typeToUseForComputingDataLength_; |
53 | boolean[] isGraphic_; |
54 | |
55 | // key = column position, value = index into extdtaData_ |
56 | java.util.HashMap extdtaPositions_; |
57 | java.util.ArrayList extdtaData_; // queue to hold EXTDTA data that hasn't been correlated to its column # |
58 | |
59 | |
60 | boolean rtnextrow_ = true; |
61 | |
62 | /** Flag indicating whether the result set on the server is |
63 | * implicitly closed when end-of-data is received. */ |
64 | private boolean qryclsimpEnabled_; |
65 | |
66 | //-----------------------------constants-------------------------------------- |
67 | |
68 | //---------------------constructors/finalizer--------------------------------- |
69 | |
70 | NetCursor(NetAgent netAgent) { |
71 | super(netAgent); |
72 | netAgent_ = netAgent; |
73 | numMddOverrides_ = 0; |
74 | maximumRowSize_ = 0; |
75 | extdtaPositions_ = new java.util.HashMap(); |
76 | extdtaData_ = new java.util.ArrayList(); |
77 | } |
78 | |
79 | NetCursor(NetAgent netAgent, |
80 | int qryprctyp) //protocolType, CodePoint.FIXROWPRC | CodePoint.LMTBLKPRC |
81 | { |
82 | this(netAgent); |
83 | if (qryprctyp == CodePoint.FIXROWPRC) { |
84 | blocking_ = false; |
85 | } else if (qryprctyp == CodePoint.LMTBLKPRC) { |
86 | blocking_ = true; |
87 | } |
88 | } |
89 | //-----------------------------parsing the data buffer------------------------ |
90 | |
91 | /** |
92 | * Calculate the column offsets for a row. |
93 | * <p> |
94 | * Pseudo-code: |
95 | * <ol> |
96 | * <li>parse thru the current row in dataBuffer computing column |
97 | * offsets</li> |
98 | * <li>if (we hit the super.lastValidBytePosition, ie. encounter |
99 | * partial row) |
100 | * <ol> |
101 | * <li>shift partial row bytes to beginning of dataBuffer |
102 | * (this.shiftPartialRowToBeginning())</li> |
103 | * <li>reset current row position (also done by |
104 | * this.shiftPartialRowToBeginning())</li> |
105 | * <li>send and recv continue-query into commBuffer |
106 | * (rs.flowContinueQuery())</li> |
107 | * <li>parse commBuffer up to QRYDTA |
108 | * (rs.flowContinueQuery())</li> |
109 | * <li>copy query data from reply's commBuffer to our |
110 | * dataBuffer (this.copyQrydta())</li> |
111 | * </ol> |
112 | * </ol> |
113 | * |
114 | * @param rowIndex row index |
115 | * @param allowServerFetch if true, allow fetching more data from |
116 | * server |
117 | * @return <code>true</code> if the current row position is a |
118 | * valid row position. |
119 | * @exception SqlException |
120 | * @exception DisconnectException |
121 | */ |
122 | protected |
123 | boolean calculateColumnOffsetsForRow_(int rowIndex, |
124 | boolean allowServerFetch) |
125 | throws SqlException, DisconnectException |
126 | { |
127 | int daNullIndicator = CodePoint.NULLDATA; |
128 | int colNullIndicator = CodePoint.NULLDATA; |
129 | int length; |
130 | |
131 | int[] columnDataPosition = null; |
132 | int[] columnDataComputedLength = null; |
133 | boolean[] columnDataIsNull = null; |
134 | boolean receivedDeleteHoleWarning = false; |
135 | boolean receivedRowUpdatedWarning = false; |
136 | |
137 | if ((position_ == lastValidBytePosition_) && |
138 | (netResultSet_ != null) && (netResultSet_.scrollable_)) { |
139 | return false; |
140 | } |
141 | |
142 | if (hasLobs_) { |
143 | extdtaPositions_.clear(); // reset positions for this row |
144 | } |
145 | |
146 | NetSqlca[] netSqlca = this.parseSQLCARD(qrydscTypdef_); |
147 | |
148 | if (netSqlca != null) { |
149 | for (int i=0;i<netSqlca.length; i++) { |
150 | int sqlcode = netSqlca[i].getSqlCode(); |
151 | if (sqlcode < 0) { |
152 | throw new SqlException(netAgent_.logWriter_, |
153 | netSqlca[i]); |
154 | } else { |
155 | if (sqlcode == SqlCode.END_OF_DATA.getCode()) { |
156 | setAllRowsReceivedFromServer(true); |
157 | if (netResultSet_ != null && |
158 | netSqlca[i].containsSqlcax()) { |
159 | netResultSet_.setRowCountEvent( |
160 | netSqlca[i].getRowCount( |
161 | qrydscTypdef_)); |
162 | } |
163 | } else if (netResultSet_ != null && sqlcode > 0) { |
164 | String sqlState = netSqlca[i].getSqlState(); |
165 | if (!sqlState.equals(SQLState.ROW_DELETED) && |
166 | !sqlState.equals(SQLState.ROW_UPDATED)) { |
167 | netResultSet_.accumulateWarning( |
168 | new SqlWarning(agent_.logWriter_, |
169 | netSqlca[i])); |
170 | } else { |
171 | receivedDeleteHoleWarning |
172 | |= sqlState.equals(SQLState.ROW_DELETED); |
173 | receivedRowUpdatedWarning |
174 | |= sqlState.equals(SQLState.ROW_UPDATED); |
175 | } |
176 | } |
177 | } |
178 | } |
179 | } |
180 | |
181 | setIsUpdataDeleteHole(rowIndex, receivedDeleteHoleWarning); |
182 | setIsRowUpdated(receivedRowUpdatedWarning); |
183 | |
184 | // If we don't have at least one byte in the buffer for the DA null indicator, |
185 | // then we need to send a CNTQRY request to fetch the next block of data. |
186 | // Read the DA null indicator. |
187 | daNullIndicator = readFdocaOneByte(); |
188 | |
189 | // In the case for held cursors, the +100 comes back as part of the QRYDTA, and as |
190 | // we are parsing through the row that contains the SQLCA with +100, we mark the |
191 | // nextRowPosition_ which is the lastValidBytePosition_, but we don't mark the |
192 | // currentRowPosition_ until the next time next() is called causing the check |
193 | // cursor_.currentRowPositionIsEqualToNextRowPosition () to fail in getRow() and thus |
194 | // not returning 0 when it should. So we need to mark the current row position immediately |
195 | // in order for getRow() to be able to pick it up. |
196 | |
197 | // markNextRowPosition() is called again once this method returns, but it is ok |
198 | // since it's only resetting nextRowPosition_ to position_ and position_ will |
199 | // not change again from this point. |
200 | |
201 | if (allRowsReceivedFromServer() && |
202 | (position_ == lastValidBytePosition_)) { |
203 | markNextRowPosition(); |
204 | makeNextRowPositionCurrent(); |
205 | return false; |
206 | } |
207 | |
208 | // If data flows.... |
209 | if (daNullIndicator == 0x0) { |
210 | |
211 | if (SanityManager.DEBUG && receivedDeleteHoleWarning) { |
212 | SanityManager.THROWASSERT("Delete hole warning received: nulldata expected"); |
213 | } |
214 | incrementRowsReadEvent(); |
215 | |
216 | // netResultSet_ is null if this method is invoked from Lob.position() |
217 | // If row has exceeded the size of the ArrayList, new up a new int[] and add it to the |
218 | // ArrayList, otherwise just reuse the int[]. |
219 | if (netResultSet_ != null && netResultSet_.scrollable_) { |
220 | columnDataPosition = allocateColumnDataPositionArray(rowIndex); |
221 | columnDataComputedLength = allocateColumnDataComputedLengthArray(rowIndex); |
222 | columnDataIsNull = allocateColumnDataIsNullArray(rowIndex); |
223 | // Since we are no longer setting the int[]'s to null for a delete/update hole, we need |
224 | // another way of keeping track of the delete/update holes. |
225 | setIsUpdataDeleteHole(rowIndex, false); |
226 | } else { |
227 | // Use the arrays defined on the Cursor for forward-only cursors. |
228 | // can they ever be null |
229 | if (columnDataPosition_ == null || columnDataComputedLength_ == null || isNull_ == null) { |
230 | allocateColumnOffsetAndLengthArrays(); |
231 | } |
232 | columnDataPosition = columnDataPosition_; |
233 | columnDataComputedLength = columnDataComputedLength_; |
234 | columnDataIsNull = isNull_; |
235 | } |
236 | |
237 | // Loop through the columns |
238 | for (int index = 0; index < columns_; index++) { |
239 | // If column is nullable, read the 1-byte null indicator. |
240 | if (nullable_[index]) |
241 | // Need to pass the column index so all previously calculated offsets can be |
242 | // readjusted if the query block splits on a column null indicator. |
243 | |
244 | // null indicators from FD:OCA data |
245 | // 0 to 127: a data value will flow. |
246 | // -1 to -128: no data value will flow. |
247 | { |
248 | colNullIndicator = readFdocaOneByte(index); |
249 | } |
250 | |
251 | // If non-null column data |
252 | if (!nullable_[index] || (colNullIndicator >= 0 && colNullIndicator <= 127)) { |
253 | |
254 | // Set the isNull indicator to false |
255 | columnDataIsNull[index] = false; |
256 | |
257 | switch (typeToUseForComputingDataLength_[index]) { |
258 | // for fixed length data |
259 | case Typdef.FIXEDLENGTH: |
260 | columnDataPosition[index] = position_; |
261 | if (isGraphic_[index]) { |
262 | columnDataComputedLength[index] = skipFdocaBytes(fdocaLength_[index] * 2, index); |
263 | } else { |
264 | columnDataComputedLength[index] = skipFdocaBytes(fdocaLength_[index], index); |
265 | } |
266 | break; |
267 | |
268 | // for variable character string and variable byte string, |
269 | // there are 2-byte of length in front of the data |
270 | case Typdef.TWOBYTELENGTH: |
271 | columnDataPosition[index] = position_; |
272 | length = readFdocaTwoByteLength(index); |
273 | // skip length + the 2-byte length field |
274 | if (isGraphic_[index]) { |
275 | columnDataComputedLength[index] = skipFdocaBytes(length * 2, index) + 2; |
276 | } else { |
277 | columnDataComputedLength[index] = skipFdocaBytes(length, index) + 2; |
278 | } |
279 | break; |
280 | |
281 | // For decimal columns, determine the precision, scale, and the representation |
282 | case Typdef.DECIMALLENGTH: |
283 | columnDataPosition[index] = position_; |
284 | columnDataComputedLength[index] = skipFdocaBytes(getDecimalLength(index), index); |
285 | break; |
286 | |
287 | case Typdef.LOBLENGTH: |
288 | columnDataPosition[index] = position_; |
289 | columnDataComputedLength[index] = this.skipFdocaBytes(fdocaLength_[index] & 0x7fff, index); |
290 | break; |
291 | |
292 | // for short variable character string and short variable byte string, |
293 | // there is a 1-byte length in front of the data |
294 | case Typdef.ONEBYTELENGTH: |
295 | columnDataPosition[index] = position_; |
296 | length = readFdocaOneByte(index); |
297 | // skip length + the 1-byte length field |
298 | if (isGraphic_[index]) { |
299 | columnDataComputedLength[index] = skipFdocaBytes(length * 2, index) + 1; |
300 | } else { |
301 | columnDataComputedLength[index] = skipFdocaBytes(length, index) + 1; |
302 | } |
303 | break; |
304 | |
305 | default: |
306 | columnDataPosition[index] = position_; |
307 | if (isGraphic_[index]) { |
308 | columnDataComputedLength[index] = skipFdocaBytes(fdocaLength_[index] * 2, index); |
309 | } else { |
310 | columnDataComputedLength[index] = skipFdocaBytes(fdocaLength_[index], index); |
311 | } |
312 | break; |
313 | } |
314 | } else if ((colNullIndicator & 0x80) == 0x80) { |
315 | // Null data. Set the isNull indicator to true. |
316 | columnDataIsNull[index] = true; |
317 | } |
318 | } |
319 | |
320 | // set column offsets for the current row. |
321 | columnDataPosition_ = columnDataPosition; |
322 | columnDataComputedLength_ = columnDataComputedLength; |
323 | isNull_ = columnDataIsNull; |
324 | |
325 | if (!allRowsReceivedFromServer()) { |
326 | calculateLobColumnPositionsForRow(); |
327 | // Flow another CNTQRY if we are blocking, are using rtnextrow, and expect |
328 | // non-trivial EXTDTAs for forward only cursors. Note we do not support |
329 | // EXTDTA retrieval for scrollable cursors. |
330 | // if qryrowset was sent on excsqlstt for a sp call, which is only the case |
331 | if (blocking_ && rtnextrow_ && |
332 | !netResultSet_.scrollable_ && |
333 | !extdtaPositions_.isEmpty()) { |
334 | if (allowServerFetch) { |
335 | netResultSet_.flowFetch(); |
336 | } else { |
337 | return false; |
338 | } |
339 | } |
340 | } |
341 | } else { |
342 | if (netResultSet_ != null && netResultSet_.scrollable_) { |
343 | if (receivedDeleteHoleWarning) { |
344 | setIsUpdataDeleteHole(rowIndex, true); |
345 | } else { |
346 | if (SanityManager.DEBUG) { |
347 | // Invariant: for SUR, we introduced the warning |
348 | // in addition to null data. |
349 | SanityManager |
350 | .THROWASSERT("Delete hole warning expected"); |
351 | } |
352 | } |
353 | } |
354 | } |
355 | |
356 | // If blocking protocol is used, we could have already received an ENDQRYRM, |
357 | // which sets allRowsReceivedFromServer_ to true. It's safe to assume that all of |
358 | // our QRYDTA's have been successfully copied to the dataBuffer. And even though |
359 | // the flag for allRowsReceivedFromServer_ is set, we still want to continue to parse through |
360 | // the data in the dataBuffer. |
361 | // But in the case where fixed row protocol is used, |
362 | if (!blocking_ && allRowsReceivedFromServer() && |
363 | daNullIndicator == 0xFF) { |
364 | return false; |
365 | } else { |
366 | return true; |
367 | } |
368 | } |
369 | |
370 | /** |
371 | * Scan the data buffer to see if end of data (SQL state 02000) |
372 | * has been received. This method should only be called when the |
373 | * cursor is being closed since the pointer to the current row can |
374 | * be modified. |
375 | * |
376 | * @exception SqlException |
377 | */ |
378 | void scanDataBufferForEndOfData() throws SqlException { |
379 | while (!allRowsReceivedFromServer() && |
380 | (position_ != lastValidBytePosition_)) { |
381 | stepNext(false); |
382 | } |
383 | } |
384 | |
385 | protected boolean isDataBufferNull() { |
386 | if (dataBuffer_ == null) { |
387 | return true; |
388 | } else { |
389 | return false; |
390 | } |
391 | } |
392 | |
393 | protected void allocateDataBuffer() { |
394 | int length; |
395 | if (maximumRowSize_ > DssConstants.MAX_DSS_LEN) { |
396 | length = maximumRowSize_; |
397 | } else { |
398 | length = DssConstants.MAX_DSS_LEN; |
399 | } |
400 | |
401 | dataBuffer_ = new byte[length]; |
402 | position_ = 0; |
403 | lastValidBytePosition_ = 0; |
404 | } |
405 | |
406 | protected void allocateDataBuffer(int length) { |
407 | dataBuffer_ = new byte[length]; |
408 | } |
409 | |
410 | |
411 | private int readFdocaInt() throws org.apache.derby.client.am.DisconnectException, SqlException { |
412 | if ((position_ + 4) > lastValidBytePosition_) { |
413 | // Check for ENDQRYRM, throw SqlException if already received one. |
414 | checkAndThrowReceivedEndqryrm(); |
415 | |
416 | // Send CNTQRY to complete the row/rowset. |
417 | int lastValidByteBeforeFetch = completeSplitRow(); |
418 | |
419 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
420 | // throw a SqlException for the ENDQRYRM. |
421 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
422 | } |
423 | |
424 | int i = SignedBinary.getInt(dataBuffer_, position_); |
425 | position_ += 4; |
426 | return i; |
427 | } |
428 | |
429 | // Reads 8-bytes from the dataBuffer from the current position. |
430 | // If position is already at the end of the buffer, send CNTQRY to get more |
431 | // data. |
432 | private long readFdocaLong() throws |
433 | org.apache.derby.client.am.DisconnectException, SqlException { |
434 | if ((position_ + 8) > lastValidBytePosition_) { |
435 | // Check for ENDQRYRM, throw SqlException if already received one. |
436 | checkAndThrowReceivedEndqryrm(); |
437 | |
438 | // Send CNTQRY to complete the row/rowset. |
439 | int lastValidByteBeforeFetch = completeSplitRow(); |
440 | |
441 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was |
442 | // received, throw a SqlException for the ENDQRYRM. |
443 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
444 | } |
445 | |
446 | long i = SignedBinary.getLong(dataBuffer_, position_); |
447 | position_ += 8; |
448 | return i; |
449 | } |
450 | |
451 | // Reads 1-byte from the dataBuffer from the current position. |
452 | // If position is already at the end of the buffer, send CNTQRY to get more data. |
453 | private int readFdocaOneByte() throws org.apache.derby.client.am.DisconnectException, SqlException { |
454 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
455 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
456 | if (position_ == lastValidBytePosition_) { |
457 | // Check for ENDQRYRM, throw SqlException if already received one. |
458 | checkAndThrowReceivedEndqryrm(); |
459 | |
460 | // Send CNTQRY to complete the row/rowset. |
461 | int lastValidByteBeforeFetch = completeSplitRow(); |
462 | |
463 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
464 | // throw a SqlException for the ENDQRYRM. |
465 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
466 | } |
467 | return dataBuffer_[position_++] & 0xff; |
468 | } |
469 | |
470 | // Reads 1-byte from the dataBuffer from the current position. |
471 | // If position is already at the end of the buffer, send CNTQRY to get more data. |
472 | private int readFdocaOneByte(int index) throws org.apache.derby.client.am.DisconnectException, SqlException { |
473 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
474 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
475 | if (position_ == lastValidBytePosition_) { |
476 | // Check for ENDQRYRM, throw SqlException if already received one. |
477 | checkAndThrowReceivedEndqryrm(); |
478 | |
479 | // Send CNTQRY to complete the row/rowset. |
480 | int lastValidByteBeforeFetch = completeSplitRow(index); |
481 | |
482 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
483 | // throw a SqlException for the ENDQRYRM. |
484 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
485 | } |
486 | return dataBuffer_[position_++] & 0xff; |
487 | } |
488 | |
489 | // Reads <i>length</i> number of bytes from the dataBuffer starting from the |
490 | // current position. Returns a new byte array which contains the bytes read. |
491 | // If current position plus length goes past the lastValidBytePosition, send |
492 | // CNTQRY to get more data. |
493 | private byte[] readFdocaBytes(int length) throws org.apache.derby.client.am.DisconnectException, SqlException { |
494 | byte[] b = new byte[length]; |
495 | ; |
496 | |
497 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
498 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
499 | if ((position_ + length) > lastValidBytePosition_) { |
500 | // Check for ENDQRYRM, throw SqlException if already received one. |
501 | checkAndThrowReceivedEndqryrm(); |
502 | |
503 | // Send CNTQRY to complete the row/rowset. |
504 | int lastValidByteBeforeFetch = completeSplitRow(); |
505 | |
506 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
507 | // throw a SqlException for the ENDQRYRM. |
508 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
509 | } |
510 | |
511 | for (int i = 0; i < length; i++) { |
512 | b[i] = dataBuffer_[position_++]; |
513 | } |
514 | |
515 | return b; |
516 | } |
517 | |
518 | // Reads 2-bytes from the dataBuffer starting from the current position, and |
519 | // returns an integer constructed from the 2-bytes. If current position plus |
520 | // 2 bytes goes past the lastValidBytePosition, send CNTQRY to get more data. |
521 | private int readFdocaTwoByteLength() throws org.apache.derby.client.am.DisconnectException, SqlException { |
522 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
523 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
524 | if ((position_ + 2) > lastValidBytePosition_) { |
525 | // Check for ENDQRYRM, throw SqlException if already received one. |
526 | checkAndThrowReceivedEndqryrm(); |
527 | |
528 | // Send CNTQRY to complete the row/rowset. |
529 | int lastValidByteBeforeFetch = completeSplitRow(); |
530 | |
531 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
532 | // throw a SqlException for the ENDQRYRM. |
533 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
534 | } |
535 | |
536 | return |
537 | ((dataBuffer_[position_++] & 0xff) << 8) + |
538 | ((dataBuffer_[position_++] & 0xff) << 0); |
539 | } |
540 | |
541 | private int readFdocaTwoByteLength(int index) throws org.apache.derby.client.am.DisconnectException, SqlException { |
542 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
543 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
544 | if ((position_ + 2) > lastValidBytePosition_) { |
545 | // Check for ENDQRYRM, throw SqlException if already received one. |
546 | checkAndThrowReceivedEndqryrm(); |
547 | |
548 | // Send CNTQRY to complete the row/rowset. |
549 | int lastValidByteBeforeFetch = completeSplitRow(index); |
550 | |
551 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
552 | // throw a SqlException for the ENDQRYRM. |
553 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
554 | } |
555 | |
556 | return |
557 | ((dataBuffer_[position_++] & 0xff) << 8) + |
558 | ((dataBuffer_[position_++] & 0xff) << 0); |
559 | } |
560 | |
561 | // Check if position plus length goes past the lastValidBytePosition. |
562 | // If so, send CNTQRY to get more data. |
563 | // length - number of bytes to skip |
564 | // returns the number of bytes skipped |
565 | private int skipFdocaBytes(int length) throws org.apache.derby.client.am.DisconnectException, SqlException { |
566 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
567 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
568 | if ((position_ + length) > lastValidBytePosition_) { |
569 | // Check for ENDQRYRM, throw SqlException if already received one. |
570 | checkAndThrowReceivedEndqryrm(); |
571 | |
572 | // Send CNTQRY to complete the row/rowset. |
573 | int lastValidByteBeforeFetch = completeSplitRow(); |
574 | |
575 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
576 | // throw a SqlException for the ENDQRYRM. |
577 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
578 | } |
579 | position_ += length; |
580 | return length; |
581 | } |
582 | |
583 | private int skipFdocaBytes(int length, int index) throws org.apache.derby.client.am.DisconnectException, SqlException { |
584 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
585 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
586 | if ((position_ + length) > lastValidBytePosition_) { |
587 | // Check for ENDQRYRM, throw SqlException if already received one. |
588 | checkAndThrowReceivedEndqryrm(); |
589 | |
590 | // Send CNTQRY to complete the row/rowset. |
591 | int lastValidByteBeforeFetch = completeSplitRow(index); |
592 | |
593 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
594 | // throw a SqlException for the ENDQRYRM. |
595 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
596 | } |
597 | |
598 | position_ += length; |
599 | return length; |
600 | } |
601 | |
602 | // Shift partial row bytes to beginning of dataBuffer, |
603 | // and resets current row position, and lastValidBytePosition. |
604 | // When we shift partial row, we'll have to recalculate column offsets |
605 | // up to this column. |
606 | private void shiftPartialRowToBeginning() { |
607 | // Get the length to shift from the beginning of the partial row. |
608 | int length = lastValidBytePosition_ - currentRowPosition_; |
609 | |
610 | // shift the data in the dataBufferStream |
611 | dataBufferStream_.reset(); |
612 | if (dataBuffer_ != null) { |
613 | dataBufferStream_.write(dataBuffer_, currentRowPosition_, length); |
614 | } |
615 | |
616 | for (int i = 0; i < length; i++) { |
617 | dataBuffer_[i] = dataBuffer_[currentRowPosition_ + i]; |
618 | } |
619 | |
620 | position_ = length - (lastValidBytePosition_ - position_); |
621 | lastValidBytePosition_ = length; |
622 | } |
623 | |
624 | private void adjustColumnOffsetsForColumnsPreviouslyCalculated(int index) { |
625 | for (int j = 0; j <= index; j++) { |
626 | columnDataPosition_[j] -= currentRowPosition_; |
627 | } |
628 | } |
629 | |
630 | private void resetCurrentRowPosition() { |
631 | currentRowPosition_ = 0; |
632 | } |
633 | |
634 | // Calculates the column index for Lob objects constructed from EXTDTA data. |
635 | // Describe information isn't sufficient because we have to check |
636 | // for trivial values (nulls or zero-length) and exclude them. |
637 | void calculateLobColumnPositionsForRow() { |
638 | int currentPosition = 0; |
639 | |
640 | for (int i = 0; i < columns_; i++) { |
641 | if (isNonTrivialDataLob(i)) |
642 | // key = column position, data = index to corresponding data in extdtaData_ |
643 | // ASSERT: the server always returns the EXTDTA objects in ascending order |
644 | { |
645 | extdtaPositions_.put(new Integer(i + 1), new Integer(currentPosition++)); |
646 | } |
647 | } |
648 | } |
649 | |
650 | // prereq: the base data for the cursor has been processed for offsets and lengths |
651 | boolean isNonTrivialDataLob(int index) { |
652 | long length = 0L; |
653 | |
654 | if (isNull_[index] || |
655 | (jdbcTypes_[index] != Types.BLOB && |
656 | jdbcTypes_[index] != Types.CLOB)) { |
657 | return false; |
658 | } |
659 | |
660 | int position = columnDataPosition_[index]; |
661 | |
662 | // if the high-order bit is set, length is unknown -> set value to x'FF..FF' |
663 | if (((dataBuffer_[position]) & 0x80) == 0x80) { |
664 | length = -1; |
665 | } else { |
666 | |
667 | byte[] lengthBytes = new byte[columnDataComputedLength_[index]]; |
668 | byte[] longBytes = new byte[8]; |
669 | |
670 | System.arraycopy(dataBuffer_, |
671 | position, |
672 | lengthBytes, |
673 | 0, |
674 | columnDataComputedLength_[index]); |
675 | |
676 | // right-justify for BIG ENDIAN |
677 | int j = 0; |
678 | for (int i = 8 - columnDataComputedLength_[index]; i < 8; i++) { |
679 | longBytes[i] = lengthBytes[j]; |
680 | j++; |
681 | } |
682 | length = SignedBinary.getLong(longBytes, 0); |
683 | } |
684 | return (length != 0L) ? true : false; |
685 | } |
686 | |
687 | protected void clearLobData_() { |
688 | extdtaData_.clear(); |
689 | extdtaPositions_.clear(); |
690 | } |
691 | |
692 | // SQLCARD : FDOCA EARLY ROW |
693 | // SQL Communications Area Row Description |
694 | // |
695 | // FORMAT FOR ALL SQLAM LEVELS |
696 | // SQLCAGRP; GROUP LID 0x54; ELEMENT TAKEN 0(all); REP FACTOR 1 |
697 | NetSqlca[] parseSQLCARD(Typdef typdef) throws org.apache.derby.client.am.DisconnectException, SqlException { |
698 | return parseSQLCAGRP(typdef); |
699 | } |
700 | |
701 | // SQLCAGRP : FDOCA EARLY GROUP |
702 | // SQL Communcations Area Group Description |
703 | // |
704 | // FORMAT FOR SQLAM <= 6 |
705 | // SQLCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
706 | // SQLSTATE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5 |
707 | // SQLERRPROC; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8 |
708 | // SQLCAXGRP; PROTOCOL TYPE N-GDA; ENVLID 0x52; Length Override 0 |
709 | // |
710 | // FORMAT FOR SQLAM >= 7 |
711 | // SQLCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
712 | // SQLSTATE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5 |
713 | // SQLERRPROC; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8 |
714 | // SQLCAXGRP; PROTOCOL TYPE N-GDA; ENVLID 0x52; Length Override 0 |
715 | // SQLDIAGGRP; PROTOCOL TYPE N-GDA; ENVLID 0x56; Length Override 0 |
716 | private NetSqlca[] parseSQLCAGRP(Typdef typdef) throws org.apache.derby.client.am.DisconnectException, SqlException { |
717 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
718 | return null; |
719 | } |
720 | int sqlcode = readFdocaInt(); |
721 | byte[] sqlstate = readFdocaBytes(5); |
722 | byte[] sqlerrproc = readFdocaBytes(8); |
723 | NetSqlca netSqlca = new NetSqlca(netAgent_.netConnection_, sqlcode, sqlstate, sqlerrproc); |
724 | |
725 | parseSQLCAXGRP(typdef, netSqlca); |
726 | |
727 | NetSqlca[] sqlCa = parseSQLDIAGGRP(); |
728 | |
729 | NetSqlca[] ret_val; |
730 | if (sqlCa != null) { |
731 | ret_val = new NetSqlca[sqlCa.length + 1]; |
732 | System.arraycopy(sqlCa, 0, ret_val, 1, sqlCa.length); |
733 | } else { |
734 | ret_val = new NetSqlca[1]; |
735 | } |
736 | ret_val[0] = netSqlca; |
737 | |
738 | return ret_val; |
739 | } |
740 | |
741 | // SQLCAXGRP : EARLY FDOCA GROUP |
742 | // SQL Communications Area Exceptions Group Description |
743 | // |
744 | // FORMAT FOR SQLAM <= 6 |
745 | // SQLRDBNME; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 18 |
746 | // SQLERRD1; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
747 | // SQLERRD2; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
748 | // SQLERRD3; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
749 | // SQLERRD4; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
750 | // SQLERRD5; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
751 | // SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
752 | // SQLWARN0; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
753 | // SQLWARN1; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
754 | // SQLWARN2; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
755 | // SQLWARN3; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
756 | // SQLWARN4; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
757 | // SQLWARN5; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
758 | // SQLWARN6; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
759 | // SQLWARN7; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
760 | // SQLWARN8; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
761 | // SQLWARN9; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
762 | // SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
763 | // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70 |
764 | // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70 |
765 | // |
766 | // FORMAT FOR SQLAM >= 7 |
767 | // SQLERRD1; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
768 | // SQLERRD2; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
769 | // SQLERRD3; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
770 | // SQLERRD4; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
771 | // SQLERRD5; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
772 | // SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
773 | // SQLWARN0; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
774 | // SQLWARN1; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
775 | // SQLWARN2; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
776 | // SQLWARN3; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
777 | // SQLWARN4; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
778 | // SQLWARN5; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
779 | // SQLWARN6; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
780 | // SQLWARN7; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
781 | // SQLWARN8; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
782 | // SQLWARN9; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
783 | // SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
784 | // SQLRDBNAME; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255 |
785 | // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70 |
786 | // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70 |
787 | private void parseSQLCAXGRP(Typdef typdef, NetSqlca netSqlca) throws DisconnectException, SqlException { |
788 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
789 | netSqlca.setContainsSqlcax(false); |
790 | return; |
791 | } |
792 | |
793 | |
794 | // SQLERRD1 to SQLERRD6; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
795 | int[] sqlerrd = new int[6]; |
796 | for (int i = 0; i < sqlerrd.length; i++) { |
797 | sqlerrd[i] = readFdocaInt(); |
798 | } |
799 | |
800 | // SQLWARN0 to SQLWARNA; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 1 |
801 | byte[] sqlwarn = readFdocaBytes(11); |
802 | |
803 | // skip over the rdbnam for now |
804 | // SQLRDBNAME; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255 |
805 | parseVCS(typdef); |
806 | |
807 | // SQLERRMSG_m; PROTOCOL TYPE VCM; ENVLID 0x3E; Length Override 70 |
808 | // SQLERRMSG_s; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 70 |
809 | int varcharLength = readFdocaTwoByteLength(); // mixed length |
810 | byte[] sqlerrmc = null; |
811 | int sqlerrmcCcsid = 0; |
812 | if (varcharLength != 0) { // if mixed |
813 | sqlerrmc = readFdocaBytes(varcharLength); // read mixed bytes |
814 | sqlerrmcCcsid = typdef.getCcsidMbc(); |
815 | skipFdocaBytes(2); // skip single length |
816 | } else { |
817 | varcharLength = readFdocaTwoByteLength(); // read single length |
818 | sqlerrmc = readFdocaBytes(varcharLength); // read single bytes |
819 | sqlerrmcCcsid = typdef.getCcsidSbc(); |
820 | } |
821 | |
822 | netSqlca.setSqlerrd(sqlerrd); |
823 | netSqlca.setSqlwarnBytes(sqlwarn); |
824 | netSqlca.setSqlerrmcBytes(sqlerrmc, sqlerrmcCcsid); |
825 | } |
826 | |
827 | // SQLDIAGGRP : FDOCA EARLY GROUP |
828 | private NetSqlca[] parseSQLDIAGGRP() throws DisconnectException, SqlException { |
829 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
830 | return null; |
831 | } |
832 | |
833 | parseSQLDIAGSTT(); |
834 | NetSqlca[] sqlca = parseSQLDIAGCI(); |
835 | parseSQLDIAGCN(); |
836 | |
837 | return sqlca; |
838 | } |
839 | |
840 | // SQL Diagnostics Statement Group Description - Identity 0xD3 |
841 | // NULLDATA will be received for now |
842 | private void parseSQLDIAGSTT() throws DisconnectException, SqlException { |
843 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
844 | return; |
845 | } |
846 | |
847 | // The server should send NULLDATA |
848 | netAgent_.accumulateChainBreakingReadExceptionAndThrow( |
849 | new DisconnectException(netAgent_, |
850 | new ClientMessageId(SQLState.DRDA_COMMAND_NOT_IMPLEMENTED), |
851 | "parseSQLDIAGSTT")); |
852 | } |
853 | |
854 | // SQL Diagnostics Condition Information Array - Identity 0xF5 |
855 | // SQLNUMROW; ROW LID 0x68; ELEMENT TAKEN 0(all); REP FACTOR 1 |
856 | // SQLDCIROW; ROW LID 0xE5; ELEMENT TAKEN 0(all); REP FACTOR 0(all) |
857 | private NetSqlca[] parseSQLDIAGCI() |
858 | throws DisconnectException, SqlException { |
859 | int num = readFdocaTwoByteLength(); // SQLNUMGRP - SQLNUMROW |
860 | NetSqlca[] ret_val = null; |
861 | if (num != 0) { |
862 | ret_val = new NetSqlca[num]; |
863 | } |
864 | |
865 | for (int i = 0; i < num; i++) { |
866 | ret_val[i] = parseSQLDCROW(); |
867 | } |
868 | return ret_val; |
869 | } |
870 | |
871 | // SQL Diagnostics Connection Array - Identity 0xF6 |
872 | // NULLDATA will be received for now |
873 | private void parseSQLDIAGCN() throws DisconnectException, SqlException { |
874 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
875 | return; |
876 | } |
877 | |
878 | // The server should send NULLDATA |
879 | netAgent_.accumulateChainBreakingReadExceptionAndThrow( |
880 | new DisconnectException(netAgent_, |
881 | new ClientMessageId(SQLState.DRDA_COMMAND_NOT_IMPLEMENTED), |
882 | "parseSQLDIAGCN")); |
883 | } |
884 | |
885 | // SQL Diagnostics Condition Group Description |
886 | // |
887 | // SQLDCCODE; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
888 | // SQLDCSTATE; PROTOCOL TYPE FCS; ENVLID Ox30; Lengeh Override 5 |
889 | // SQLDCREASON; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
890 | // SQLDCLINEN; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
891 | // SQLDCROWN; PROTOCOL TYPE I8; ENVLID 0x16; Lengeh Override 8 |
892 | // SQLDCER01; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
893 | // SQLDCER02; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
894 | // SQLDCER03; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
895 | // SQLDCER04; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
896 | // SQLDCPART; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
897 | // SQLDCPPOP; PROTOCOL TYPE I4; ENVLID 0x02; Length Override 4 |
898 | // SQLDCMSGID; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 10 |
899 | // SQLDCMDE; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 8 |
900 | // SQLDCPMOD; PROTOCOL TYPE FCS; ENVLID 0x30; Length Override 5 |
901 | // SQLDCRDB; PROTOCOL TYPE VCS; ENVLID 0x32; Length Override 255 |
902 | // SQLDCTOKS; PROTOCOL TYPE N-RLO; ENVLID 0xF7; Length Override 0 |
903 | // SQLDCMSG_m; PROTOCOL TYPE NVMC; ENVLID 0x3F; Length Override 32672 |
904 | // SQLDCMSG_S; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 32672 |
905 | // SQLDCCOLN_m; PROTOCOL TYPE NVCM ; ENVLID 0x3F; Length Override 255 |
906 | // SQLDCCOLN_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255 |
907 | // SQLDCCURN_m; PROTOCOL TYPE NVCM; ENVLID 0x3F; Length Override 255 |
908 | // SQLDCCURN_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255 |
909 | // SQLDCPNAM_m; PROTOCOL TYPE NVCM; ENVLID 0x3F; Length Override 255 |
910 | // SQLDCPNAM_s; PROTOCOL TYPE NVCS; ENVLID 0x33; Length Override 255 |
911 | // SQLDCXGRP; PROTOCOL TYPE N-GDA; ENVLID 0xD3; Length Override 1 |
912 | private NetSqlca parseSQLDCGRP() |
913 | throws DisconnectException, SqlException { |
914 | |
915 | int sqldcCode = readFdocaInt(); // SQLCODE |
916 | String sqldcState = readFdocaString(5, |
917 | netAgent_.targetTypdef_.getCcsidSbcEncoding()); // SQLSTATE |
918 | int sqldcReason = readFdocaInt(); // REASON_CODE |
919 | |
920 | skipFdocaBytes(12); // LINE_NUMBER + ROW_NUMBER |
921 | |
922 | NetSqlca sqlca = new NetSqlca(netAgent_.netConnection_, |
923 | sqldcCode, |
924 | sqldcState, |
925 | (byte[]) null); |
926 | |
927 | skipFdocaBytes(49); // SQLDCER01-04 + SQLDCPART + SQLDCPPOP + SQLDCMSGID |
928 | // SQLDCMDE + SQLDCPMOD + RDBNAME |
929 | parseSQLDCTOKS(); // MESSAGE_TOKENS |
930 | |
931 | String sqldcMsg = parseVCS(qrydscTypdef_); // MESSAGE_TEXT |
932 | |
933 | if (sqldcMsg != null) { |
934 | sqlca.setSqlerrmcBytes(sqldcMsg.getBytes(), |
935 | netAgent_.targetTypdef_.getByteOrder()); |
936 | } |
937 | |
938 | skipFdocaBytes(12); // COLUMN_NAME + PARAMETER_NAME + EXTENDED_NAMES |
939 | |
940 | parseSQLDCXGRP(); // SQLDCXGRP |
941 | return sqlca; |
942 | } |
943 | |
944 | // SQL Diagnostics Condition Row - Identity 0xE5 |
945 | // SQLDCGRP; GROUP LID 0xD5; ELEMENT TAKEN 0(all); REP FACTOR 1 |
946 | private NetSqlca parseSQLDCROW() throws DisconnectException, SqlException { |
947 | return parseSQLDCGRP(); |
948 | } |
949 | |
950 | // SQL Diagnostics Condition Token Array - Identity 0xF7 |
951 | // NULLDATA will be received for now |
952 | void parseSQLDCTOKS() throws DisconnectException, SqlException { |
953 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
954 | return; |
955 | } |
956 | |
957 | // The server should send NULLDATA |
958 | netAgent_.accumulateChainBreakingReadExceptionAndThrow( |
959 | new DisconnectException(netAgent_, |
960 | new ClientMessageId(SQLState.DRDA_COMMAND_NOT_IMPLEMENTED), |
961 | "parseSQLDCTOKS")); |
962 | } |
963 | |
964 | // SQL Diagnostics Extended Names Group Description - Identity 0xD5 |
965 | // NULLDATA will be received for now |
966 | private void parseSQLDCXGRP() throws DisconnectException, SqlException { |
967 | if (readFdocaOneByte() == CodePoint.NULLDATA) { |
968 | return; |
969 | } |
970 | |
971 | // The server should send NULLDATA |
972 | netAgent_.accumulateChainBreakingReadExceptionAndThrow( |
973 | new DisconnectException(netAgent_, |
974 | new ClientMessageId(SQLState.DRDA_COMMAND_NOT_IMPLEMENTED), |
975 | "parseSQLDCXGRP")); |
976 | } |
977 | |
978 | private String parseVCS(Typdef typdefInEffect) throws DisconnectException, SqlException { |
979 | return readFdocaString(readFdocaTwoByteLength(), |
980 | typdefInEffect.getCcsidSbcEncoding()); |
981 | } |
982 | |
983 | // This is not used for column data. |
984 | private String readFdocaString(int length, String encoding) throws DisconnectException, SqlException { |
985 | if (length == 0) { |
986 | return null; |
987 | } |
988 | |
989 | // For singleton select, the complete row always comes back, even if multiple query blocks are required, |
990 | // so there is no need to drive a flowFetch (continue query) request for singleton select. |
991 | if ((position_ + length) > lastValidBytePosition_) { |
992 | // Check for ENDQRYRM, throw SqlException if already received one. |
993 | checkAndThrowReceivedEndqryrm(); |
994 | |
995 | // Send CNTQRY to complete the row/rowset. |
996 | int lastValidByteBeforeFetch = completeSplitRow(); |
997 | |
998 | // if lastValidBytePosition_ has not changed, and an ENDQRYRM was received, |
999 | // throw a SqlException for the ENDQRYRM. |
1000 | checkAndThrowReceivedEndqryrm(lastValidByteBeforeFetch); |
1001 | } |
1002 | |
1003 | String s = null; |
1004 | |
1005 | try { |
1006 | s = new String(dataBuffer_, position_, length, encoding); |
1007 | } catch (java.io.UnsupportedEncodingException e) { |
1008 | netAgent_.accumulateChainBreakingReadExceptionAndThrow( |
1009 | new org.apache.derby.client.am.DisconnectException( |
1010 | netAgent_, |
1011 | new ClientMessageId(SQLState.NET_ENCODING_NOT_SUPPORTED), |
1012 | e)); |
1013 | } |
1014 | |
1015 | position_ += length; |
1016 | return s; |
1017 | } |
1018 | |
1019 | void allocateColumnOffsetAndLengthArrays() { |
1020 | columnDataPosition_ = new int[columns_]; |
1021 | columnDataComputedLength_ = new int[columns_]; |
1022 | isNull_ = new boolean[columns_]; |
1023 | } |
1024 | |
1025 | void setBlocking(int queryProtocolType) { |
1026 | if (queryProtocolType == CodePoint.LMTBLKPRC) { |
1027 | blocking_ = true; |
1028 | } else { |
1029 | blocking_ = false; |
1030 | } |
1031 | } |
1032 | |
1033 | protected byte[] findExtdtaData(int column) { |
1034 | byte[] data = null; |
1035 | |
1036 | // locate the EXTDTA bytes, if any |
1037 | Integer key = new Integer(column); |
1038 | |
1039 | if (extdtaPositions_.containsKey(key)) { |
1040 | // found, get the data |
1041 | int extdtaQueuePosition = ((Integer) extdtaPositions_.get(key)).intValue(); |
1042 | data = (byte[]) (extdtaData_.get(extdtaQueuePosition)); |
1043 | } |
1044 | |
1045 | return data; |
1046 | } |
1047 | |
1048 | public Blob getBlobColumn_(int column, Agent agent) throws SqlException { |
1049 | int index = column - 1; |
1050 | int dataOffset; |
1051 | byte[] data; |
1052 | Blob blob = null; |
1053 | |
1054 | // locate the EXTDTA bytes, if any |
1055 | data = findExtdtaData(column); |
1056 | |
1057 | if (data != null) { |
1058 | // data found |
1059 | // set data offset based on the presence of a null indicator |
1060 | if (!nullable_[index]) { |
1061 | dataOffset = 0; |
1062 | } else { |
1063 | dataOffset = 1; |
1064 | } |
1065 | |
1066 | blob = new Blob(data, agent, dataOffset); |
1067 | } else { |
1068 | blob = new Blob(new byte[0], agent, 0); |
1069 | } |
1070 | |
1071 | return blob; |
1072 | } |
1073 | |
1074 | |
1075 | public Clob getClobColumn_(int column, Agent agent) throws SqlException { |
1076 | int index = column - 1; |
1077 | int dataOffset; |
1078 | byte[] data; |
1079 | Clob clob = null; |
1080 | |
1081 | // locate the EXTDTA bytes, if any |
1082 | data = findExtdtaData(column); |
1083 | |
1084 | if (data != null) { |
1085 | // data found |
1086 | // set data offset based on the presence of a null indicator |
1087 | if (!nullable_[index]) { |
1088 | dataOffset = 0; |
1089 | } else { |
1090 | dataOffset = 1; |
1091 | } |
1092 | clob = new Clob(agent, data, charsetName_[index], dataOffset); |
1093 | } else { |
1094 | // the locator is not valid, it is a zero-length LOB |
1095 | clob = new Clob(agent, ""); |
1096 | } |
1097 | |
1098 | return clob; |
1099 | } |
1100 | |
1101 | public byte[] getClobBytes_(int column, int[] dataOffset /*output*/) throws SqlException { |
1102 | int index = column - 1; |
1103 | byte[] data = null; |
1104 | |
1105 | // locate the EXTDTA bytes, if any |
1106 | data = findExtdtaData(column); |
1107 | |
1108 | if (data != null) { |
1109 | // data found |
1110 | // set data offset based on the presence of a null indicator |
1111 | if (!nullable_[index]) { |
1112 | dataOffset[0] = 0; |
1113 | } else { |
1114 | dataOffset[0] = 1; |
1115 | } |
1116 | } |
1117 | |
1118 | return data; |
1119 | } |
1120 | |
1121 | // this is really an event-callback from NetStatementReply.parseSQLDTARDarray() |
1122 | void initializeColumnInfoArrays(Typdef typdef, |
1123 | int columnCount, int targetSqlamForTypdef) throws DisconnectException { |
1124 | qrydscTypdef_ = typdef; |
1125 | |
1126 | // Allocate arrays to hold the descriptor information. |
1127 | setNumberOfColumns(columnCount); |
1128 | fdocaLength_ = new int[columnCount]; |
1129 | isGraphic_ = new boolean[columnCount]; |
1130 | typeToUseForComputingDataLength_ = new int[columnCount]; |
1131 | targetSqlamForTypdef_ = targetSqlamForTypdef; |
1132 | } |
1133 | |
1134 | |
1135 | int ensureSpaceForDataBuffer(int ddmLength) { |
1136 | if (dataBuffer_ == null) { |
1137 | allocateDataBuffer(); |
1138 | } |
1139 | //super.resultSet.cursor.clearColumnDataOffsetsCache(); |
1140 | // Need to know how many bytes to ask from the Reply object, |
1141 | // and handle the case where buffer is not big enough for all the bytes. |
1142 | // Get the length in front of the code point first. |
1143 | |
1144 | int bytesAvailableInDataBuffer = dataBuffer_.length - lastValidBytePosition_; |
1145 | |
1146 | // Make sure the buffer has at least ddmLength amount of room left. |
1147 | // If not, expand the buffer before calling the getQrydtaData() method. |
1148 | if (bytesAvailableInDataBuffer < ddmLength) { |
1149 | |
1150 | // Get a new buffer that is twice the size of the current buffer. |
1151 | // Copy the contents from the old buffer to the new buffer. |
1152 | int newBufferSize = 2 * dataBuffer_.length; |
1153 | |
1154 | while (newBufferSize < ddmLength) { |
1155 | newBufferSize = 2 * newBufferSize; |
1156 | } |
1157 | |
1158 | byte[] tempBuffer = new byte[newBufferSize]; |
1159 | |
1160 | System.arraycopy(dataBuffer_, |
1161 | 0, |
1162 | tempBuffer, |
1163 | 0, |
1164 | lastValidBytePosition_); |
1165 | |
1166 | // Make the new buffer the dataBuffer. |
1167 | dataBuffer_ = tempBuffer; |
1168 | |
1169 | // Recalculate bytesAvailableInDataBuffer |
1170 | bytesAvailableInDataBuffer = dataBuffer_.length - lastValidBytePosition_; |
1171 | } |
1172 | return bytesAvailableInDataBuffer; |
1173 | } |
1174 | |
1175 | protected void getMoreData_() throws SqlException { |
1176 | // reset the dataBuffer_ before getting more data if cursor is foward-only. |
1177 | // getMoreData() is only called in Cursor.next() when current position is |
1178 | // equal to lastValidBytePosition_. |
1179 | if (netResultSet_.resultSetType_ == java.sql.ResultSet.TYPE_FORWARD_ONLY) { |
1180 | resetDataBuffer(); |
1181 | } |
1182 | netResultSet_.flowFetch(); |
1183 | } |
1184 | |
1185 | public void nullDataForGC() // memory leak fix |
1186 | { |
1187 | super.nullDataForGC(); |
1188 | qrydscTypdef_ = null; |
1189 | typeToUseForComputingDataLength_ = null; |
1190 | isGraphic_ = null; |
1191 | |
1192 | if (extdtaPositions_ != null) { |
1193 | extdtaPositions_.clear(); |
1194 | } |
1195 | extdtaPositions_ = null; |
1196 | |
1197 | if (extdtaData_ != null) { |
1198 | extdtaData_.clear(); |
1199 | } |
1200 | extdtaData_ = null; |
1201 | } |
1202 | |
1203 | // It is possible for the driver to have received an QRYDTA(with incomplete row)+ENDQRYRM+SQLCARD. |
1204 | // This means some error has occurred on the server and the server is terminating the query. |
1205 | // Before sending a CNTQRY to retrieve the rest of the split row, check if an ENDQRYRM has already |
1206 | // been received. If so, do not send CNTQRY because the cursor is already closed on the server. |
1207 | // Instead, throw a SqlException. Since we did not receive a complete row, it is not safe to |
1208 | // allow the application to continue to access the ResultSet, so we close it. |
1209 | private void checkAndThrowReceivedEndqryrm() throws SqlException { |
1210 | // If we are in a split row, and before sending CNTQRY, check whether an ENDQRYRM |
1211 | // has been received. |
1212 | if (!netResultSet_.openOnServer_) { |
1213 | SqlException sqlException = null; |
1214 | int sqlcode = org.apache.derby.client.am.Utils.getSqlcodeFromSqlca(netResultSet_.queryTerminatingSqlca_); |
1215 | if (sqlcode < 0) { |
1216 | sqlException = new SqlException(agent_.logWriter_, netResultSet_.queryTerminatingSqlca_); |
1217 | } else { |
1218 | sqlException = new SqlException(agent_.logWriter_, |
1219 | new ClientMessageId(SQLState.NET_QUERY_PROCESSING_TERMINATED)); |
1220 | } |
1221 | try { |
1222 | netResultSet_.closeX(); // the auto commit logic is in closeX() |
1223 | } catch (SqlException e) { |
1224 | sqlException.setNextException(e); |
1225 | } |
1226 | throw sqlException; |
1227 | } |
1228 | } |
1229 | |
1230 | private void checkAndThrowReceivedEndqryrm(int lastValidBytePositionBeforeFetch) throws SqlException { |
1231 | // if we have received more data in the dataBuffer_, just return. |
1232 | if (lastValidBytePosition_ > lastValidBytePositionBeforeFetch) { |
1233 | return; |
1234 | } |
1235 | checkAndThrowReceivedEndqryrm(); |
1236 | } |
1237 | |
1238 | private int completeSplitRow() throws DisconnectException, SqlException { |
1239 | int lastValidBytePositionBeforeFetch = 0; |
1240 | if (netResultSet_ != null && netResultSet_.scrollable_) { |
1241 | lastValidBytePositionBeforeFetch = lastValidBytePosition_; |
1242 | netResultSet_.flowFetchToCompleteRowset(); |
1243 | } else { |
1244 | // Shift partial row to the beginning of the dataBuffer |
1245 | shiftPartialRowToBeginning(); |
1246 | resetCurrentRowPosition(); |
1247 | lastValidBytePositionBeforeFetch = lastValidBytePosition_; |
1248 | netResultSet_.flowFetch(); |
1249 | } |
1250 | return lastValidBytePositionBeforeFetch; |
1251 | } |
1252 | |
1253 | private int completeSplitRow(int index) throws DisconnectException, SqlException { |
1254 | int lastValidBytePositionBeforeFetch = 0; |
1255 | if (netResultSet_ != null && netResultSet_.scrollable_) { |
1256 | lastValidBytePositionBeforeFetch = lastValidBytePosition_; |
1257 | netResultSet_.flowFetchToCompleteRowset(); |
1258 | } else { |
1259 | // Shift partial row to the beginning of the dataBuffer |
1260 | shiftPartialRowToBeginning(); |
1261 | adjustColumnOffsetsForColumnsPreviouslyCalculated(index); |
1262 | resetCurrentRowPosition(); |
1263 | lastValidBytePositionBeforeFetch = lastValidBytePosition_; |
1264 | netResultSet_.flowFetch(); |
1265 | } |
1266 | return lastValidBytePositionBeforeFetch; |
1267 | } |
1268 | |
1269 | private int[] allocateColumnDataPositionArray(int row) { |
1270 | int[] columnDataPosition; |
1271 | if (columnDataPositionCache_.size() == row) { |
1272 | columnDataPosition = new int[columns_]; |
1273 | columnDataPositionCache_.add(columnDataPosition); |
1274 | } else { |
1275 | columnDataPosition = (int[]) columnDataPositionCache_.get(row); |
1276 | } |
1277 | return columnDataPosition; |
1278 | } |
1279 | |
1280 | private int[] allocateColumnDataComputedLengthArray(int row) { |
1281 | int[] columnDataComputedLength; |
1282 | if (columnDataLengthCache_.size() == row) { |
1283 | columnDataComputedLength = new int[columns_]; |
1284 | columnDataLengthCache_.add(columnDataComputedLength); |
1285 | } else { |
1286 | columnDataComputedLength = (int[]) columnDataLengthCache_.get(row); |
1287 | } |
1288 | return columnDataComputedLength; |
1289 | } |
1290 | |
1291 | private boolean[] allocateColumnDataIsNullArray(int row) { |
1292 | boolean[] columnDataIsNull; |
1293 | if (columnDataIsNullCache_.size() <= row) { |
1294 | columnDataIsNull = new boolean[columns_]; |
1295 | columnDataIsNullCache_.add(columnDataIsNull); |
1296 | } else { |
1297 | columnDataIsNull = (boolean[]) columnDataIsNullCache_.get(row); |
1298 | } |
1299 | return columnDataIsNull; |
1300 | } |
1301 | |
1302 | protected int getDecimalLength(int index) { |
1303 | return (((fdocaLength_[index] >> 8) & 0xff) + 2) / 2; |
1304 | } |
1305 | |
1306 | /** |
1307 | * Set the value of value of allRowsReceivedFromServer_. |
1308 | * |
1309 | * @param b a <code>boolean</code> value indicating whether all |
1310 | * rows are received from the server |
1311 | */ |
1312 | public final void setAllRowsReceivedFromServer(boolean b) { |
1313 | if (b && qryclsimpEnabled_) { |
1314 | netResultSet_.markClosedOnServer(); |
1315 | } |
1316 | super.setAllRowsReceivedFromServer(b); |
1317 | } |
1318 | |
1319 | /** |
1320 | * Set a flag indicating whether QRYCLSIMP is enabled. |
1321 | * |
1322 | * @param flag true if QRYCLSIMP is enabled |
1323 | */ |
1324 | final void setQryclsimpEnabled(boolean flag) { |
1325 | qryclsimpEnabled_ = flag; |
1326 | } |
1327 | |
1328 | /** |
1329 | * Check whether QRYCLSIMP is enabled on this cursor. |
1330 | * |
1331 | * @return true if QRYCLSIMP is enabled |
1332 | */ |
1333 | final boolean getQryclsimpEnabled() { |
1334 | return qryclsimpEnabled_; |
1335 | } |
1336 | } |