1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.jdbc.EmbedClob |
4 | |
5 | Copyright 2000, 2004 The Apache Software Foundation or its licensors, as 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 | |
22 | package org.apache.derby.impl.jdbc; |
23 | |
24 | import org.apache.derby.iapi.reference.SQLState; |
25 | import org.apache.derby.iapi.error.StandardException; |
26 | import org.apache.derby.iapi.services.sanity.SanityManager; |
27 | import org.apache.derby.iapi.types.DataValueDescriptor; |
28 | import org.apache.derby.iapi.types.Resetable; |
29 | import org.apache.derby.impl.jdbc.ConnectionChild; |
30 | import org.apache.derby.impl.jdbc.EmbedConnection; |
31 | import org.apache.derby.impl.jdbc.Util; |
32 | import org.apache.derby.impl.jdbc.UTF8Reader; |
33 | import org.apache.derby.impl.jdbc.ReaderToAscii; |
34 | |
35 | import java.io.InputStream; |
36 | import java.io.InputStreamReader; |
37 | import java.io.StringReader; |
38 | import java.io.Reader; |
39 | import java.io.IOException; |
40 | import java.io.EOFException; |
41 | import java.sql.SQLException; |
42 | import java.sql.Clob; |
43 | |
44 | /** |
45 | Implements java.sql.Clob (see the JDBC 2.0 spec). |
46 | A clob sits on top of a CHAR, VARCHAR or LONG VARCHAR column. |
47 | If its data is small (less than 1 page) it is a byte array taken from |
48 | the SQLChar class. If it is large (more than 1 page) it is a long column |
49 | in the database. The long column is accessed as a stream, and is implemented |
50 | in store as an OverflowInputStream. The Resetable interface allows sending |
51 | messages to that stream to initialize itself (reopen its container and |
52 | lock the corresponding row) and to reset itself to the beginning. |
53 | |
54 | NOTE: In the case that the data is large, it is represented as a stream. |
55 | This stream can be returned to the user in the getAsciiStream() method. |
56 | This means that we have limited control over the state of the stream, |
57 | since the user can read bytes from it at any time. Thus all methods |
58 | here reset the stream to the beginning before doing any work. |
59 | CAVEAT: The methods may not behave correctly if a user sets up |
60 | multiple threads and sucks data from the stream (returned from |
61 | getAsciiStream()) at the same time as calling the Clob methods. |
62 | |
63 | <P><B>Supports</B> |
64 | <UL> |
65 | <LI> JSR169 - no subsetting for java.sql.Clob |
66 | <LI> JDBC 2.0 |
67 | <LI> JDBC 3.0 - no new dependencies on new JDBC 3.0 or JDK 1.4 classes, |
68 | new update methods can safely be added into implementation. |
69 | </UL> |
70 | */ |
71 | final class EmbedClob extends ConnectionChild implements Clob |
72 | { |
73 | // clob is either a string or stream |
74 | private boolean isString; |
75 | private InputStream myStream; |
76 | private String myString; |
77 | |
78 | //This boolean variable indicates whether the Clob object has |
79 | //been invalidated by calling free() on it |
80 | private boolean isValid = true; |
81 | |
82 | /** |
83 | * This constructor is used to create a empty Clob object. It is used by the |
84 | * Connection interface method createClob(). |
85 | * |
86 | * @param clobString A String object containing the data to be stores in the |
87 | * Clob. |
88 | * |
89 | * @param con The Connection object associated with this EmbedClob object. |
90 | * |
91 | */ |
92 | |
93 | EmbedClob(String clobString,EmbedConnection con) { |
94 | super(con); |
95 | myString = clobString; |
96 | isString = true; |
97 | } |
98 | |
99 | /* |
100 | This constructor should only be called by EmbedResultSet.getClob |
101 | */ |
102 | protected EmbedClob(DataValueDescriptor dvd, EmbedConnection con) |
103 | throws StandardException |
104 | { |
105 | super(con); |
106 | // if the underlying column is null, ResultSet.getClob will return null, |
107 | // never should get this far |
108 | if (SanityManager.DEBUG) |
109 | SanityManager.ASSERT(!dvd.isNull(), "clob is created on top of a null column"); |
110 | |
111 | myStream = dvd.getStream(); |
112 | if (myStream == null) |
113 | { |
114 | isString = true; |
115 | myString = dvd.getString(); |
116 | if (SanityManager.DEBUG) |
117 | SanityManager.ASSERT(myString != null,"clob has a null value underneath"); |
118 | } |
119 | else |
120 | { |
121 | /* |
122 | We are expecting this stream to be a FormatIdInputStream with an |
123 | OverflowInputStream inside. FormatIdInputStream implements |
124 | Resetable, as does OverflowInputStream. This should be the case |
125 | when retrieving data from a long column. However, SQLChar, which is |
126 | the class implementing the getStream() method for dvd.getStream(), |
127 | does not guarantee this for us. In particular, the logging system |
128 | (see StoredPage.logColumn) calls setStream with an argument that |
129 | is sometimes a RememberBytesInputStream on a SQLChar object |
130 | (e.g. see test repStreaming.sql). However, such a SQLChar |
131 | object is going to the log buffer, NOT back to the user, so it |
132 | should not break the ASSERT below. |
133 | */ |
134 | if (SanityManager.DEBUG) |
135 | SanityManager.ASSERT(myStream instanceof Resetable); |
136 | |
137 | try { |
138 | ((Resetable) myStream).initStream(); |
139 | } catch (StandardException se) { |
140 | if (se.getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) { |
141 | throw StandardException |
142 | .newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT); |
143 | } |
144 | } |
145 | } |
146 | } |
147 | |
148 | |
149 | /** |
150 | * Returns the number of characters |
151 | * in the <code>CLOB</code> value |
152 | * designated by this <code>Clob</code> object. |
153 | * @return length of the <code>CLOB</code> in characters |
154 | * @exception SQLException if there is an error accessing the |
155 | * length of the <code>CLOB</code> |
156 | */ |
157 | |
158 | public long length() throws SQLException |
159 | { |
160 | //call checkValidity to exit by throwing a SQLException if |
161 | //the Clob object has been freed by calling free() on it |
162 | checkValidity(); |
163 | // if we have a string, not a stream |
164 | if (isString) |
165 | return myString.length(); |
166 | |
167 | |
168 | Object synchronization = getConnectionSynchronization(); |
169 | synchronized (synchronization) |
170 | { |
171 | Reader clobReader = null; |
172 | setupContextStack(); |
173 | try { |
174 | |
175 | clobReader = getCharacterStream(); |
176 | long clobLength = 0; |
177 | for (;;) |
178 | { |
179 | long size = clobReader.skip(32 * 1024); |
180 | if (size == -1) |
181 | break; |
182 | clobLength += size; |
183 | } |
184 | clobReader.close(); |
185 | clobReader = null; |
186 | |
187 | return clobLength; |
188 | } |
189 | catch (Throwable t) |
190 | { |
191 | throw noStateChangeLOB(t); |
192 | } |
193 | finally |
194 | { |
195 | if (clobReader != null) { |
196 | try { |
197 | clobReader.close(); |
198 | } catch (IOException ioe) { |
199 | } |
200 | } |
201 | restoreContextStack(); |
202 | } |
203 | } |
204 | } |
205 | |
206 | /** |
207 | * Returns a copy of the specified substring |
208 | * in the <code>CLOB</code> value |
209 | * designated by this <code>Clob</code> object. |
210 | * The substring begins at position |
211 | * <code>pos</code> and has up to <code>length</code> consecutive |
212 | * characters. |
213 | * @param pos the first character of the substring to be extracted. |
214 | * The first character is at position 1. |
215 | * @param length the number of consecutive characters to be copied |
216 | * @return a <code>String</code> that is the specified substring in |
217 | * the <code>CLOB</code> value designated by this <code>Clob</code> object |
218 | * @exception SQLException if there is an error accessing the |
219 | * <code>CLOB</code> |
220 | |
221 | NOTE: return the empty string if pos is too large |
222 | */ |
223 | |
224 | public String getSubString(long pos, int length) throws SQLException |
225 | { |
226 | //call checkValidity to exit by throwing a SQLException if |
227 | //the Clob object has been freed by calling free() on it |
228 | checkValidity(); |
229 | |
230 | if (pos < 1) |
231 | throw Util.generateCsSQLException( |
232 | SQLState.BLOB_BAD_POSITION, new Long(pos)); |
233 | if (length <= 0) |
234 | throw Util.generateCsSQLException( |
235 | SQLState.BLOB_NONPOSITIVE_LENGTH, new Integer(length)); |
236 | |
237 | // if we have a string, not a stream |
238 | if (isString) |
239 | { |
240 | int sLength = myString.length(); |
241 | if (sLength < pos) |
242 | throw Util.generateCsSQLException( |
243 | SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos)); |
244 | int endIndex = ((int) pos) + length - 1; |
245 | // cannot go over length of string, or we get an exception |
246 | return myString.substring(((int) pos) - 1, (sLength > endIndex ? endIndex : sLength)); |
247 | } |
248 | |
249 | Object synchronization = getConnectionSynchronization(); |
250 | synchronized (synchronization) |
251 | { |
252 | setupContextStack(); |
253 | |
254 | UTF8Reader clobReader = null; |
255 | try { |
256 | |
257 | clobReader = getCharacterStreamAtPos(pos, synchronization); |
258 | if (clobReader == null) |
259 | throw StandardException.newException(SQLState.BLOB_POSITION_TOO_LARGE, new Long(pos)); |
260 | |
261 | StringBuffer sb = new StringBuffer(length); |
262 | int remainToRead = length; |
263 | while (remainToRead > 0) { |
264 | |
265 | int read = clobReader.readInto(sb, remainToRead); |
266 | if (read == -1) |
267 | break; |
268 | |
269 | remainToRead -= read; |
270 | } |
271 | clobReader.close(); |
272 | clobReader = null; |
273 | |
274 | return sb.toString(); |
275 | } |
276 | catch (Throwable t) |
277 | { |
278 | throw noStateChangeLOB(t); |
279 | } |
280 | finally |
281 | { |
282 | if (clobReader != null) |
283 | clobReader.close(); |
284 | restoreContextStack(); |
285 | } |
286 | } |
287 | } |
288 | |
289 | |
290 | /** |
291 | * Gets the <code>Clob</code> contents as a Unicode stream. |
292 | * @return a Unicode stream containing the <code>CLOB</code> data |
293 | * @exception SQLException if there is an error accessing the |
294 | * <code>CLOB</code> |
295 | */ |
296 | |
297 | public java.io.Reader getCharacterStream() throws SQLException |
298 | { |
299 | //call checkValidity to exit by throwing a SQLException if |
300 | //the Clob object has been freed by calling free() on it |
301 | checkValidity(); |
302 | |
303 | // if we have a string, not a stream |
304 | if (isString) |
305 | { |
306 | return new StringReader(myString); |
307 | } |
308 | |
309 | |
310 | Object synchronization = getConnectionSynchronization(); |
311 | synchronized (synchronization) |
312 | { |
313 | setupContextStack(); |
314 | |
315 | try { |
316 | return getCharacterStreamAtPos(1, synchronization); |
317 | } |
318 | catch (Throwable t) |
319 | { |
320 | throw noStateChangeLOB(t); |
321 | } |
322 | finally |
323 | { |
324 | restoreContextStack(); |
325 | } |
326 | } |
327 | } |
328 | |
329 | |
330 | /** |
331 | * Gets the <code>CLOB</code> value designated by this <code>Clob</code> |
332 | * object as a stream of Ascii bytes. |
333 | * @return an ascii stream containing the <code>CLOB</code> data |
334 | * @exception SQLException if there is an error accessing the |
335 | * <code>CLOB</code> value |
336 | */ |
337 | |
338 | public java.io.InputStream getAsciiStream() throws SQLException |
339 | { |
340 | //call checkValidity to exit by throwing a SQLException if |
341 | //the Clob object has been freed by calling free() on it |
342 | checkValidity(); |
343 | return new ReaderToAscii(getCharacterStream()); |
344 | } |
345 | |
346 | private UTF8Reader getCharacterStreamAtPos(long position, Object synchronization) |
347 | throws IOException, StandardException |
348 | { |
349 | ((Resetable)myStream).resetStream(); |
350 | UTF8Reader clobReader = new UTF8Reader(myStream, 0, this, synchronization); |
351 | |
352 | // skip to the correct position (pos is one based) |
353 | long remainToSkip = position - 1; |
354 | while (remainToSkip > 0) { |
355 | long skipBy = clobReader.skip(remainToSkip); |
356 | if (skipBy == -1) |
357 | return null; |
358 | |
359 | remainToSkip -= skipBy; |
360 | } |
361 | |
362 | return clobReader; |
363 | } |
364 | |
365 | |
366 | /** |
367 | * Determines the character position at which the specified substring |
368 | * <code>searchstr</code> appears in the <code>CLOB</code>. The search |
369 | * begins at position <code>start</code>. |
370 | * @param searchStr the substring for which to search |
371 | * @param start the position at which to begin searching; the first position |
372 | * is 1 |
373 | * @return the position at which the substring appears, else -1; the first |
374 | * position is 1 |
375 | * @exception SQLException if there is an error accessing the |
376 | * <code>CLOB</code> value |
377 | */ |
378 | public long position(String searchStr, long start) |
379 | throws SQLException |
380 | { |
381 | //call checkValidity to exit by throwing a SQLException if |
382 | //the Clob object has been freed by calling free() on it |
383 | checkValidity(); |
384 | |
385 | boolean pushStack = false; |
386 | try |
387 | { |
388 | if (start < 1) |
389 | throw StandardException.newException( |
390 | SQLState.BLOB_BAD_POSITION, new Long(start)); |
391 | if (searchStr == null) |
392 | throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR); |
393 | if (searchStr == "") |
394 | return start; // match DB2's SQL LOCATE function |
395 | |
396 | // if we have a string, not a stream |
397 | if (isString) |
398 | { |
399 | // avoid truncation errors in the cast of start to an int. |
400 | if (start > myString.length()) |
401 | return -1; |
402 | |
403 | int result = myString.indexOf(searchStr, (int) start-1); |
404 | return result < 0 ? -1 : result + 1; |
405 | } |
406 | else // we have a stream |
407 | { |
408 | Object synchronization = getConnectionSynchronization(); |
409 | synchronized (synchronization) |
410 | { |
411 | pushStack = !getEmbedConnection().isClosed(); |
412 | if (pushStack) |
413 | setupContextStack(); |
414 | |
415 | char[] tmpClob = new char[256]; |
416 | int patternLength = searchStr.length(); |
417 | |
418 | restartPattern: |
419 | for (;;) { |
420 | |
421 | //System.out.println("RESET " + start); |
422 | UTF8Reader clobReader = getCharacterStreamAtPos(start, synchronization); |
423 | if (clobReader == null) |
424 | return -1; |
425 | |
426 | |
427 | |
428 | // start of any match of the complete pattern. |
429 | |
430 | int patternIndex = 0; |
431 | char[] tmpPattern = null; |
432 | boolean needPattern = true; |
433 | |
434 | // how many characters of the patter segment we have matched |
435 | int matchCount = 0; |
436 | |
437 | long currentPosition = start; |
438 | int clobOffset = -1; |
439 | int read = -1; |
440 | |
441 | // absolute position of a possible match |
442 | long matchPosition = -1; |
443 | |
444 | |
445 | // absolute position of the next possible match |
446 | long nextBestMatchPosition = -1; |
447 | //System.out.println("restartPattern: " + start); |
448 | |
449 | |
450 | search: |
451 | for (;;) |
452 | { |
453 | //System.out.println("search: " + needPattern + " -- " + clobOffset); |
454 | if (needPattern) { |
455 | |
456 | String tmpPatternS; |
457 | if ((patternLength - patternIndex) > 256) |
458 | tmpPatternS = searchStr.substring(patternIndex, 256); |
459 | else |
460 | tmpPatternS = searchStr; |
461 | |
462 | tmpPattern = tmpPatternS.toCharArray(); |
463 | needPattern = false; |
464 | matchCount = 0; |
465 | |
466 | } |
467 | |
468 | if (clobOffset == -1) { |
469 | |
470 | read = clobReader.read(tmpClob, 0, tmpClob.length); |
471 | //System.out.println("MORE DATA " + read); |
472 | if (read == -1) |
473 | return -1; |
474 | |
475 | if (read == 0) |
476 | continue search; |
477 | |
478 | clobOffset = 0; |
479 | } |
480 | |
481 | |
482 | // find matches within our two temp arrays. |
483 | compareArrays: |
484 | for (; clobOffset < read; clobOffset++) { |
485 | |
486 | //System.out.println("compareArrays " + clobOffset); |
487 | |
488 | char clobC = tmpClob[clobOffset]; |
489 | |
490 | |
491 | if (clobC == tmpPattern[matchCount]) |
492 | { |
493 | if (matchPosition == -1) { |
494 | matchPosition = currentPosition + clobOffset; |
495 | } |
496 | |
497 | matchCount++; |
498 | |
499 | // have we matched the entire pattern segment |
500 | if (matchCount == tmpPattern.length) |
501 | { |
502 | // move onto the next segment. |
503 | patternIndex += tmpPattern.length; |
504 | if (patternIndex == patternLength) { |
505 | // complete match !! |
506 | clobReader.close(); |
507 | //System.out.println("COMPLETE@" + matchPosition); |
508 | return matchPosition; |
509 | } |
510 | |
511 | needPattern = true; |
512 | continue search; |
513 | |
514 | } |
515 | |
516 | if (clobC == tmpPattern[0]) { |
517 | |
518 | // save the next best start position. |
519 | |
520 | // must be the first character of the actual pattern |
521 | if (patternIndex == 0) { |
522 | |
523 | // must not be just a repeat of the match of the first character |
524 | if (matchCount != 1) { |
525 | |
526 | // must not have a previous next best. |
527 | |
528 | if (nextBestMatchPosition == -1) { |
529 | nextBestMatchPosition = currentPosition + clobOffset; |
530 | } |
531 | |
532 | } |
533 | |
534 | } |
535 | } |
536 | |
537 | continue compareArrays; |
538 | } |
539 | else |
540 | { |
541 | // not a match |
542 | // |
543 | // |
544 | if (matchPosition != -1) { |
545 | // failed after we matched some amount of the pattern |
546 | matchPosition = -1; |
547 | |
548 | // See if we found a next best match |
549 | if (nextBestMatchPosition == -1) |
550 | { |
551 | // NO - just continue on, re-starting at this character |
552 | |
553 | if (patternIndex != 0) { |
554 | needPattern = true; |
555 | continue search; |
556 | } |
557 | } |
558 | else if (nextBestMatchPosition >= currentPosition) |
559 | { |
560 | // restart in the current array |
561 | clobOffset = (int) (nextBestMatchPosition - currentPosition); |
562 | nextBestMatchPosition = -1; |
563 | |
564 | if (patternIndex != 0) { |
565 | needPattern = true; |
566 | continue search; |
567 | } |
568 | } |
569 | else |
570 | { |
571 | clobReader.close(); |
572 | start = nextBestMatchPosition; |
573 | continue restartPattern; |
574 | } |
575 | |
576 | clobOffset--; // since the continue will increment it |
577 | matchCount = 0; |
578 | continue compareArrays; |
579 | } |
580 | |
581 | // no current match, just continue |
582 | } |
583 | } |
584 | |
585 | currentPosition += read; |
586 | |
587 | // indicates we need to read more data |
588 | clobOffset = -1; |
589 | } |
590 | } |
591 | } |
592 | } |
593 | } |
594 | catch (Throwable t) |
595 | { |
596 | throw noStateChangeLOB(t); |
597 | } |
598 | finally |
599 | { |
600 | if (pushStack) |
601 | restoreContextStack(); |
602 | } |
603 | |
604 | } |
605 | |
606 | |
607 | /** |
608 | * Determines the character position at which the specified |
609 | * <code>Clob</code> object <code>searchstr</code> appears in this |
610 | * <code>Clob</code> object. The search begins at position |
611 | * <code>start</code>. |
612 | * @param searchClob the <code>Clob</code> object for which to search |
613 | * @param start the position at which to begin searching; the first |
614 | * position is 1 |
615 | * @return the position at which the <code>Clob</code> object appears, |
616 | * else -1; the first position is 1 |
617 | * @exception SQLException if there is an error accessing the |
618 | * <code>CLOB</code> value |
619 | */ |
620 | |
621 | public long position(Clob searchClob, long start) |
622 | throws SQLException |
623 | { |
624 | //call checkValidity to exit by throwing a SQLException if |
625 | //the Clob object has been freed by calling free() on it |
626 | checkValidity(); |
627 | |
628 | boolean pushStack = false; |
629 | try |
630 | { |
631 | if (start < 1) |
632 | throw StandardException.newException( |
633 | SQLState.BLOB_BAD_POSITION, new Long(start)); |
634 | if (searchClob == null) |
635 | throw StandardException.newException(SQLState.BLOB_NULL_PATTERN_OR_SEARCH_STR); |
636 | |
637 | synchronized (getConnectionSynchronization()) |
638 | { |
639 | char[] subPatternChar = new char[256]; |
640 | |
641 | boolean seenOneCharacter = false; |
642 | |
643 | //System.out.println("BEGIN CLOB SEARCH @ " + start); |
644 | |
645 | restartScan: |
646 | for (;;) { |
647 | |
648 | long firstPosition = -1; |
649 | |
650 | Reader patternReader = searchClob.getCharacterStream(); |
651 | |
652 | //System.out.println("RESTART CLOB SEARCH @ " + start); |
653 | |
654 | try { |
655 | |
656 | for (;;) { |
657 | |
658 | int read = patternReader.read(subPatternChar, 0, subPatternChar.length); |
659 | if (read == -1) { |
660 | //empty pattern |
661 | if (!seenOneCharacter) |
662 | return start; // matches DB2 SQL LOCATE function |
663 | |
664 | return firstPosition; |
665 | } |
666 | if (read == 0) { |
667 | //System.out.println("STUCK IN READ 0 HELL"); |
668 | continue; |
669 | } |
670 | |
671 | seenOneCharacter = true; |
672 | |
673 | String subPattern = new String(subPatternChar, 0, read); |
674 | //System.out.println("START CLOB SEARCH @ " + start + " -- " + subPattern); |
675 | long position = position(subPattern, start); |
676 | //System.out.println("DONE SUB CLOB SEARCH @ " + start + " -- " + position); |
677 | if (position == -1) { |
678 | // never seen any match |
679 | if (firstPosition == -1) |
680 | return -1; |
681 | |
682 | start = firstPosition + 1; |
683 | continue restartScan; |
684 | } |
685 | |
686 | if (firstPosition == -1) |
687 | firstPosition = position; |
688 | else if (position != start) { |
689 | // must match at the first character of the segment |
690 | start = firstPosition + 1; |
691 | continue restartScan; |
692 | } |
693 | |
694 | // read is the length of the subPattern string |
695 | start = position + read; |
696 | } |
697 | } finally { |
698 | patternReader.close(); |
699 | } |
700 | } |
701 | } |
702 | } |
703 | catch (Throwable t) |
704 | { |
705 | throw noStateChangeLOB(t); |
706 | } |
707 | finally |
708 | { |
709 | if (pushStack) |
710 | restoreContextStack(); |
711 | } |
712 | |
713 | } |
714 | |
715 | |
716 | /* |
717 | If we have a stream, release the resources associated with it. |
718 | */ |
719 | protected void finalize() |
720 | { |
721 | // System.out.println("finalizer called"); |
722 | if (!isString) |
723 | ((Resetable)myStream).closeStream(); |
724 | } |
725 | |
726 | |
727 | /** |
728 | Following methods are for the new JDBC 3.0 methods in java.sql.Clob |
729 | (see the JDBC 3.0 spec). We have the JDBC 3.0 methods in Local20 |
730 | package, so we don't have to have a new class in Local30. |
731 | The new JDBC 3.0 methods don't make use of any new JDBC3.0 classes and |
732 | so this will work fine in jdbc2.0 configuration. |
733 | */ |
734 | |
735 | ///////////////////////////////////////////////////////////////////////// |
736 | // |
737 | // JDBC 3.0 - New public methods |
738 | // |
739 | ///////////////////////////////////////////////////////////////////////// |
740 | |
741 | /** |
742 | * JDBC 3.0 |
743 | * |
744 | * Writes the given Java String to the CLOB value that this Clob object designates |
745 | * at the position pos. |
746 | * |
747 | * @param pos - the position at which to start writing to the CLOB value that |
748 | * this Clob object represents |
749 | * @return the number of characters written |
750 | * @exception SQLException Feature not implemented for now. |
751 | */ |
752 | public int setString(long pos, String parameterName) |
753 | throws SQLException |
754 | { |
755 | throw Util.notImplemented(); |
756 | } |
757 | |
758 | /** |
759 | * JDBC 3.0 |
760 | * |
761 | * Writes len characters of str, starting at character offset, to the CLOB value |
762 | * that this Clob represents. |
763 | * |
764 | * @param pos - the position at which to start writing to this Clob object |
765 | * @param str - the string to be written to the CLOB value that this Clob designates |
766 | * @param offset - the offset into str to start reading the characters to be written |
767 | * @param len - the number of characters to be written |
768 | * @return the number of characters written |
769 | * @exception SQLException Feature not implemented for now. |
770 | */ |
771 | public int setString(long pos, String str, int offset, int len) |
772 | throws SQLException |
773 | { |
774 | throw Util.notImplemented(); |
775 | } |
776 | |
777 | /** |
778 | * JDBC 3.0 |
779 | * |
780 | * Retrieves a stream to be used to write Ascii characters to the CLOB |
781 | * value that this Clob object represents, starting at position pos. |
782 | * |
783 | * @param pos - the position at which to start writing to this Clob object |
784 | * @return the stream to which ASCII encoded characters can be written |
785 | * @exception SQLException Feature not implemented for now. |
786 | */ |
787 | public java.io.OutputStream setAsciiStream(long pos) |
788 | throws SQLException |
789 | { |
790 | throw Util.notImplemented(); |
791 | } |
792 | |
793 | /** |
794 | * JDBC 3.0 |
795 | * |
796 | * Retrieves a stream to be used to write a stream of Unicode characters to the |
797 | * CLOB value that this Clob object represents, starting at position pos. |
798 | * |
799 | * @param pos - the position at which to start writing to this Clob object |
800 | * @return the stream to which Unicode encoded characters can be written |
801 | * @exception SQLException Feature not implemented for now. |
802 | */ |
803 | public java.io.Writer setCharacterStream(long pos) |
804 | throws SQLException |
805 | { |
806 | throw Util.notImplemented(); |
807 | } |
808 | |
809 | /** |
810 | * JDBC 3.0 |
811 | * |
812 | * Truncates the CLOB value that this Clob designates to have a length of len characters |
813 | * |
814 | * @param len - the length, in bytes, to which the CLOB value that this Blob |
815 | * value should be truncated |
816 | * @exception SQLException Feature not implemented for now. |
817 | */ |
818 | public void truncate(long len) |
819 | throws SQLException |
820 | { |
821 | throw Util.notImplemented(); |
822 | } |
823 | |
824 | ///////////////////////////////////////////////////////////////////////// |
825 | // |
826 | // JDBC 4.0 - New public methods |
827 | // |
828 | ///////////////////////////////////////////////////////////////////////// |
829 | /** |
830 | * This method frees the <code>Clob</code> object and releases the resources the resources |
831 | * that it holds. The object is invalid once the <code>free</code> method |
832 | * is called. If <code>free</code> is called multiple times, the |
833 | * subsequent calls to <code>free</code> are treated as a no-op. |
834 | * |
835 | * @throws SQLException if an error occurs releasing |
836 | * the Clob's resources |
837 | */ |
838 | public void free() |
839 | throws SQLException { |
840 | //calling free() on a already freed object is treated as a no-op |
841 | if (!isValid) return; |
842 | |
843 | //now that free has been called the Clob object is no longer |
844 | //valid |
845 | isValid = false; |
846 | |
847 | if (!isString) |
848 | ((Resetable)myStream).closeStream(); |
849 | else |
850 | myString = null; |
851 | } |
852 | |
853 | public java.io.Reader getCharacterStream(long pos, long length) |
854 | throws SQLException { |
855 | throw Util.notImplemented(); |
856 | } |
857 | |
858 | /* |
859 | ** |
860 | */ |
861 | |
862 | static SQLException noStateChangeLOB(Throwable t) { |
863 | if (t instanceof StandardException) |
864 | { |
865 | // container closed means the blob or clob was accessed after commit |
866 | if (((StandardException) t).getMessageId().equals(SQLState.DATA_CONTAINER_CLOSED)) |
867 | { |
868 | t = StandardException.newException(SQLState.BLOB_ACCESSED_AFTER_COMMIT); |
869 | } |
870 | } |
871 | return org.apache.derby.impl.jdbc.EmbedResultSet.noStateChangeException(t); |
872 | } |
873 | |
874 | /* |
875 | * Checks is isValid is true. If it is not true throws |
876 | * a SQLException stating that a method has been called on |
877 | * an invalid LOB object |
878 | * |
879 | * throws SQLException if isValid is not true. |
880 | */ |
881 | private void checkValidity() throws SQLException{ |
882 | if(!isValid) |
883 | throw newSQLException(SQLState.LOB_OBJECT_INVALID); |
884 | } |
885 | } |