/* Copyright (c) 2001-2018, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.jdbc;
import java.io.
ByteArrayInputStream;
import java.io.
CharArrayReader;
import java.io.
Reader;
import java.io.
StringReader;
import java.io.
UnsupportedEncodingException;
import java.sql.
Clob;
import java.sql.
SQLException;
//#ifdef JAVA6
import java.sql.
SQLFeatureNotSupportedException;
//#endif JAVA6
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
FrameworkLogger;
import org.hsqldb.lib.java.
JavaSystem;
/* $Id: JDBCClob.java 5793 2018-01-06 13:12:38Z fredt $ */
// campbell-burnet@users 2004-03/04-xx - doc 1.7.2 - javadocs updated; methods put in
// correct (historical, interface
// declared) order
// campbell-burnet@users 2004-03/04-xx - patch 1.7.2 - null check for constructor (a
// null CLOB value is Java null,
// not a Clob object with null
// data);moderate thread safety;
// simplification; optimization
// of operations between jdbcClob
// instances
// campbell-burnet@users 2005-12-07 - patch 1.8.0.x - initial JDBC 4.0 support work
// campbell-burnet@users 2006-05-22 - doc 1.9.0 - full synch up to Mustang Build 84
// - patch 1.9.0 - setAsciiStream &
// setCharacterStream improvement
// patch 1.9.0
// - full synch up to Mustang b90
// - better bounds checking
/**
* The mapping in the Java™ programming language for the SQL <code>CLOB</code> type.
* An SQL <code>CLOB</code> is a built-in type
* that stores a Character Large Object as a column value in a row of
* a database table.
* By default drivers implement a <code>Clob</code> object using an SQL
* <code>locator(CLOB)</code>, which means that a <code>Clob</code> object
* contains a logical pointer to the SQL <code>CLOB</code> data rather than
* the data itself. A <code>Clob</code> object is valid for the duration
* of the transaction in which it was created.
* <P>The <code>Clob</code> interface provides methods for getting the
* length of an SQL <code>CLOB</code> (Character Large Object) value,
* for materializing a <code>CLOB</code> value on the client, and for
* searching for a substring or <code>CLOB</code> object within a
* <code>CLOB</code> value.
* Methods in the interfaces {@link java.sql.ResultSet},
* {@link java.sql.CallableStatement}, and {@link java.sql.PreparedStatement}, such as
* <code>getClob</code> and <code>setClob</code> allow a programmer to
* access an SQL <code>CLOB</code> value. In addition, this interface
* has methods for updating a <code>CLOB</code> value.
* <p>
* All methods on the <code>Clob</code> interface must be fully implemented if the
* JDBC driver supports the data type.
*
* <!-- start Release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Previous to 2.0, the HSQLDB driver did not implement Clob using an SQL
* locator(CLOB). That is, an HSQLDB Clob object did not contain a logical
* pointer to SQL CLOB data; rather it directly contained a representation of
* the data (a String). As a result, an HSQLDB Clob object was itself
* valid beyond the duration of the transaction in which is was created,
* although it did not necessarily represent a corresponding value
* on the database. Also, the interface methods for updating a CLOB value
* were unsupported, with the exception of the truncate method,
* in that it could be used to truncate the local value. <p>
*
* Starting with 2.0, the HSQLDB driver fully supports both local and remote
* SQL CLOB data implementations, meaning that an HSQLDB Clob object <em>may</em>
* contain a logical pointer to remote SQL CLOB data (see {@link JDBCClobClient
* JDBCClobClient}) or it may directly contain a local representation of the
* data (as implemented in this class). In particular, when the product is built
* under JDK 1.6+ and the Clob instance is constructed as a result of calling
* JDBCConnection.createClob(), then the resulting Clob instance is initially
* disconnected (is not bound to the transaction scope of the vending Connection
* object), the data is contained directly and all interface methods for
* updating the CLOB value are supported for local use until the first
* invocation of free(); otherwise, an HSQLDB Clob's implementation is
* determined at runtime by the driver, it is typically not valid beyond
* the duration of the transaction in which is was created, and there no
* standard way to query whether it represents a local or remote value.<p>
*
* </div>
* <!-- end release-specific documentation -->
*
* @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
* @version 2.4.0
* @since JDK 1.2, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public class
JDBCClob implements
Clob {
private static final
FrameworkLogger LOG
=
FrameworkLogger.
getLog(
JDBCClob.class);
/**
* Retrieves the number of characters
* in the <code>CLOB</code> value
* designated by this <code>Clob</code> object.
*
* @return length of the <code>CLOB</code> in characters
* @exception SQLException if there is an error accessing the
* length of the <code>CLOB</code> value
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2, HSQLDB 1.7.2
*/
public long
length() throws
SQLException {
return
getData().
length();
}
/**
* Retrieves a copy of the specified substring
* in the <code>CLOB</code> value
* designated by this <code>Clob</code> object.
* The substring begins at position
* <code>pos</code> and has up to <code>length</code> consecutive
* characters.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* The official specification above is ambiguous in that it does not
* precisely indicate the policy to be observed when
* {@code pos > this.length() - length}. One policy would be to retrieve
* the characters from {@code pos} to {@code this.length()}. Another would
* be to throw an exception. This class observes the second policy. <p>
*
* <b>Note</b><p>
*
* This method uses {@link java.lang.String#substring(int, int)}.
* <p>
* Depending on implementation (typically JDK 6 and earlier releases), the
* returned value may be sharing the underlying (and possibly much larger)
* character buffer. Depending on factors such as hardware acceleration for
* array copies, the average length and number of sub-strings taken, and so
* on, this <em>may or may not</em> result in faster operation and
* non-trivial memory savings. On the other hand, Oracle / OpenJDK 7, it
* was decided that the memory leak implications outweigh the benefits
* of buffer sharing for most use cases on modern hardware.
* <p>
* It is left up to any client of this method to determine if this is a
* potential factor relative to the target runtime and to decide how to
* handle space-time trade-offs (i.e. whether to make an isolated copy of
* the returned substring or risk that more memory remains allocated than
* is absolutely required).
* </div>
* <!-- end release-specific documentation -->
*
* @param pos the first character of the substring to be extracted.
* The first character is at position 1.
* @param length the number of consecutive characters to be copied;
* JDBC 4.1[ the value for length must be 0 or greater]
* @return a <code>String</code> that is the specified substring in
* the <code>CLOB</code> value designated by this <code>Clob</code> object
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value; if pos is less than 1 JDBC 4.1[or length is
* less than 0]
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2, HSQLDB 1.7.2
*/
public
String getSubString(final long
pos,
final int
length) throws
SQLException {
final
String data =
getData();
final int
dlen =
data.
length();
if (
pos ==
MIN_POS &&
length ==
dlen) {
return
data;
}
if (
pos <
MIN_POS ||
pos >
dlen) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
final long
index =
pos - 1;
if (
length < 0 ||
length >
dlen -
index) {
throw
JDBCUtil.
outOfRangeArgument("length: " +
length);
}
return
data.
substring((int)
index, (int)
index +
length);
}
/**
* Retrieves the <code>CLOB</code> value designated by this <code>Clob</code>
* object as a <code>java.io.Reader</code> object (or as a stream of
* characters).
*
* @return a <code>java.io.Reader</code> object containing the
* <code>CLOB</code> data
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #setCharacterStream
* @since JDK 1.2, HSQLDB 1.7.2
*/
public java.io.
Reader getCharacterStream() throws
SQLException {
return new
StringReader(
getData());
}
/**
* Retrieves the <code>CLOB</code> value designated by this <code>Clob</code>
* object as an ASCII stream.
*
* @return a <code>java.io.InputStream</code> object containing the
* <code>CLOB</code> data
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #setAsciiStream
* @since JDK 1.2, HSQLDB 1.7.2
*/
public java.io.
InputStream getAsciiStream() throws
SQLException {
try {
return new
ByteArrayInputStream(
getData().
getBytes("US-ASCII"));
} catch (
UnsupportedEncodingException e) {
LOG.
warning(
e.
getMessage(),
e);
return null;
}
}
/**
* Retrieves the character position at which the specified substring
* <code>searchstr</code> appears in the SQL <code>CLOB</code> value
* represented by this <code>Clob</code> object. The search
* begins at position <code>start</code>.
*
* @param searchstr the substring for which to search
* @param start the position at which to begin searching; the first position
* is 1
* @return the position at which the substring appears or -1 if it is not
* present; the first position is 1
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if start is less than 1
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2, HSQLDB 1.7.2
*/
public long
position(final
String searchstr,
long
start) throws
SQLException {
final
String data =
getData();
if (
start <
MIN_POS) {
throw
JDBCUtil.
outOfRangeArgument("start: " +
start);
}
if (
searchstr == null ||
start >
MAX_POS) {
return -1;
}
final int
position =
data.
indexOf(
searchstr, (int)
start - 1);
return (
position == -1) ? -1
:
position + 1;
}
/**
* Retrieves the character position at which the specified
* <code>Clob</code> object <code>searchstr</code> appears in this
* <code>Clob</code> object. The search begins at position
* <code>start</code>.
*
* @param searchstr the <code>Clob</code> object for which to search
* @param start the position at which to begin searching; the first
* position is 1
* @return the position at which the <code>Clob</code> object appears
* or -1 if it is not present; the first position is 1
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if start is less than 1
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.2, HSQLDB 1.7.2
*/
public long
position(final
Clob searchstr,
final long
start) throws
SQLException {
final
String data =
getData();
if (
start <
MIN_POS) {
throw
JDBCUtil.
outOfRangeArgument("start: " +
start);
}
if (
searchstr == null) {
return -1;
}
final long
dlen =
data.
length();
final long
sslen =
searchstr.
length();
final long
startIndex =
start-1;
// This is potentially much less expensive than materializing a large
// substring from some other vendor's CLOB. Indeed, we should probably
// do the comparison piecewise, using an in-memory buffer (or temp-files
// when available), if it is detected that the input CLOB is very long.
if (
startIndex >
dlen -
sslen) {
return -1;
}
// by now, we know sslen and startIndex are both < Integer.MAX_VALUE
String pattern;
if (
searchstr instanceof
JDBCClob) {
pattern = ((
JDBCClob)
searchstr).
getData();
} else {
pattern =
searchstr.
getSubString(1L, (int)
sslen);
}
final int
index =
data.
indexOf(
pattern, (int)
startIndex);
return (
index == -1) ? -1
:
index + 1;
}
//---------------------------- jdbc 3.0 -----------------------------------
/**
* Writes the given Java <code>String</code> to the <code>CLOB</code>
* value that this <code>Clob</code> object designates at the position
* <code>pos</code>. The string will overwrite the existing characters
* in the <code>Clob</code> object starting at the position
* <code>pos</code>. If the end of the <code>Clob</code> value is reached
* while writing the given string, then the length of the <code>Clob</code>
* value will be increased to accommodate the extra characters.
* <p>
* <b>Note:</b> If the value specified for <code>pos</code>
* is greater then the length+1 of the <code>CLOB</code> value then the
* behavior is undefined. Some JDBC drivers may throw a
* <code>SQLException</code> while other drivers may support this
* operation.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0 this feature is supported. <p>
*
* When built under JDK 1.6+ and the Clob instance is constructed as a
* result of calling JDBCConnection.createClob(), this operation affects
* only the client-side value; it has no effect upon a value stored in the
* database because JDBCConnection.createClob() constructs disconnected,
* initially empty Clob instances. To propagate the Clob value to a database
* in this case, it is required to supply the Clob instance to an updating
* or inserting setXXX method of a Prepared or Callable Statement, or to
* supply the Clob instance to an updateXXX method of an updateable
* ResultSet. <p>
*
* <b>Implementation Notes:</b><p>
*
* No attempt is made to ensure precise thread safety. Instead, volatile
* member field and local variable snapshot isolation semantics are
* implemented. This is expected to eliminate most issues related
* to race conditions, with the possible exception of concurrent
* invocation of free(). <p>
*
* In general, if an application may perform concurrent JDBCClob
* modifications and the integrity of the application depends on total order
* Clob modification semantics, then such operations should be synchronized
* on an appropriate monitor.<p>
*
* When the value specified for <code>pos</code> is greater then the
* length+1, then the CLOB value is extended in length to accept the
* written characters and the undefined region up to @{code pos} is filled
* with with space (' ') characters.
*
* </div>
* <!-- end release-specific documentation -->
*
* @param pos the position at which to start writing to the <code>CLOB</code>
* value that this <code>Clob</code> object represents;
* The first position is 1
* @param str the string to be written to the <code>CLOB</code>
* value that this <code>Clob</code> designates
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if pos is less than 1
*
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public int
setString(long
pos,
String str) throws
SQLException {
return
setString(
pos,
str, 0,
str == null ? 0 :
str.
length());
}
/**
* Writes <code>len</code> characters of <code>str</code>, starting
* at character <code>offset</code>, to the <code>CLOB</code> value
* that this <code>Clob</code> represents. The string will overwrite the existing characters
* in the <code>Clob</code> object starting at the position
* <code>pos</code>. If the end of the <code>Clob</code> value is reached
* while writing the given string, then the length of the <code>Clob</code>
* value will be increased to accommodate the extra characters.
* <p>
* <b>Note:</b> If the value specified for <code>pos</code>
* is greater then the length+1 of the <code>CLOB</code> value then the
* behavior is undefined. Some JDBC drivers may throw a
* <code>SQLException</code> while other drivers may support this
* operation.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0 this feature is supported. <p>
*
* When built under JDK 1.6+ and the Clob instance is constructed as a
* result of calling JDBCConnection.createClob(), this operation affects
* only the client-side value; it has no effect upon a value stored in a
* database because JDBCConnection.createClob() constructs disconnected,
* initially empty Clob instances. To propagate the Clob value to a database
* in this case, it is required to supply the Clob instance to an updating
* or inserting setXXX method of a Prepared or Callable Statement, or to
* supply the Clob instance to an updateXXX method of an updateable
* ResultSet. <p>
*
* <b>Implementation Notes:</b><p>
*
* If the value specified for <code>pos</code>
* is greater than the length of the <code>CLOB</code> value, then
* the <code>CLOB</code> value is extended in length to accept the
* written characters and the undefined region up to <code>pos</code> is
* filled with space (' ') characters.<p>
*
* No attempt is made to ensure precise thread safety. Instead, volatile
* member field and local variable snapshot isolation semantics are
* implemented. This is expected to eliminate most issues related
* to race conditions, with the possible exception of concurrent
* invocation of free(). <p>
*
* In general, if an application may perform concurrent JDBCClob
* modifications and the integrity of the application depends on total order
* Clob modification semantics, then such operations should be synchronized
* on an appropriate monitor.<p>
*
* </div>
* <!-- end release-specific documentation -->
*
* @param pos the position at which to start writing to this
* <code>CLOB</code> object; The first position is 1
* @param str the string to be written to the <code>CLOB</code>
* value that this <code>Clob</code> object represents
* @param offset the offset into <code>str</code> to start reading
* the characters to be written
* @param len the number of characters to be written
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if pos is less than 1
*
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public int
setString(final long
pos, final
String str, final int
offset,
final int
len) throws
SQLException {
checkReadonly();
final
String data =
getData();
if (
str == null) {
throw
JDBCUtil.
nullArgument("str");
}
final int
strlen =
str.
length();
final int
dlen =
data.
length();
final int
ipos = (int) (
pos - 1);
if (
offset == 0 &&
len ==
strlen &&
ipos == 0 &&
len >=
dlen) {
setData(
str);
return
len;
}
if (
offset < 0 ||
offset >
strlen) {
throw
JDBCUtil.
outOfRangeArgument("offset: " +
offset);
}
if (
len < 0 ||
len >
strlen -
offset) {
throw
JDBCUtil.
outOfRangeArgument("len: " +
len);
}
if (
pos <
MIN_POS || (
pos -
MIN_POS) > (
Integer.
MAX_VALUE -
len)) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
final long
endPos = (
pos +
len);
char[]
chars;
if (
pos >
dlen) {
// 1.) 'datachars' + '\32\32\32...' + substring
chars = new char[(int)
endPos - 1];
data.
getChars(0,
dlen,
chars, 0);
for(int
i =
dlen;
i <
ipos;
i++) {
chars[
i] = ' ';
}
str.
getChars(
offset,
offset +
len,
chars,
ipos);
} else if (
endPos >
dlen) {
// 2.) 'datach...' + substring
chars = new char[(int)
endPos - 1];
data.
getChars(0,
ipos,
chars, 0);
str.
getChars(
offset,
offset +
len,
chars,
ipos);
} else {
// 3.) 'dat' + substring + 'rs'
chars = new char[
dlen];
data.
getChars(0,
ipos,
chars, 0);
str.
getChars(
offset,
offset +
len,
chars,
ipos);
final int
dataOffset =
ipos +
len;
data.
getChars(
dataOffset,
dlen,
chars,
dataOffset);
}
setData(new
String(
chars));
return
len;
}
/**
* Retrieves a stream to be used to write ASCII characters to the
* <code>CLOB</code> value that this <code>Clob</code> object represents,
* starting at position <code>pos</code>. Characters written to the stream
* will overwrite the existing characters
* in the <code>Clob</code> object starting at the position
* <code>pos</code>. If the end of the <code>Clob</code> value is reached
* while writing characters to the stream, then the length of the <code>Clob</code>
* value will be increased to accommodate the extra characters.
* <p>
* <b>Note:</b> If the value specified for <code>pos</code>
* is greater than the length of the <code>CLOB</code> value, then the
* behavior is undefined. Some JDBC drivers may throw a
* <code>SQLException</code> while other drivers may support this
* operation.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0 this feature is supported. <p>
*
* When built under JDK 1.6+ and the Clob instance is constructed as a
* result of calling JDBCConnection.createClob(), this operation affects
* only the client-side value; it has no effect upon a value stored in a
* database because JDBCConnection.createClob() constructs disconnected,
* initially empty Clob instances. To propagate the Clob value to a database
* in this case, it is required to supply the Clob instance to an updating
* or inserting setXXX method of a Prepared or Callable Statement, or to
* supply the Clob instance to an updateXXX method of an updatable
* ResultSet. <p>
*
* <b>Implementation Notes:</b><p>
*
* The data written to the stream does not appear in this
* Clob until the stream is closed. <p>
*
* When the stream is closed, if the value specified for <code>pos</code>
* is greater than the length of the <code>CLOB</code> value, then
* the <code>CLOB</code> value is extended in length to accept the
* written characters and the undefined region up to <code>pos</code> is
* filled with space (' ') characters. <p>
*
* Also, no attempt is made to ensure precise thread safety. Instead,
* volatile member field and local variable snapshot isolation semantics
* are implemented. This is expected to eliminate most issues related
* to race conditions, with the possible exception of concurrent
* invocation of free(). <p>
*
* In general, if an application may perform concurrent JDBCClob
* modifications and the integrity of the application depends on total order
* Clob modification semantics, then such operations should be synchronized
* on an appropriate monitor.<p>
*
* </div>
* <!-- end release-specific documentation -->
*
* @param pos the position at which to start writing to this
* <code>CLOB</code> object; The first position is 1
* @return the stream to which ASCII encoded characters can be written
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if pos is less than 1
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #getAsciiStream
*
* @since JDK 1.4, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public java.io.
OutputStream setAsciiStream(
final long
pos) throws
SQLException {
checkReadonly();
checkClosed();
if (
pos <
MIN_POS ||
pos >
MAX_POS) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
return new java.io.
ByteArrayOutputStream() {
boolean
closed = false;
public synchronized void
close() throws java.io.
IOException {
if (
closed) {
return;
}
closed = true;
final byte[]
bytes = super.
buf;
final int
length = super.
count;
super.
buf = null;
super.
count = 0;
try {
final
String str = new
String(
bytes, 0,
length, "US-ASCII");
JDBCClob.this.
setString(
pos,
str);
} catch (
SQLException se) {
throw
JavaSystem.
toIOException(
se);
}
}
};
}
/**
* Retrieves a stream to be used to write a stream of Unicode characters
* to the <code>CLOB</code> value that this <code>Clob</code> object
* represents, at position <code>pos</code>. Characters written to the stream
* will overwrite the existing characters
* in the <code>Clob</code> object starting at the position
* <code>pos</code>. If the end of the <code>Clob</code> value is reached
* while writing characters to the stream, then the length of the <code>Clob</code>
* value will be increased to accommodate the extra characters.
* <p>
* <b>Note:</b> If the value specified for <code>pos</code>
* is greater then the length+1 of the <code>CLOB</code> value then the
* behavior is undefined. Some JDBC drivers may throw a
* <code>SQLException</code> while other drivers may support this
* operation.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0 this feature is supported. <p>
*
* When built under JDK 1.6+ and the Clob instance is constructed as a
* result of calling JDBCConnection.createClob(), this operation affects
* only the client-side value; it has no effect upon a value stored in a
* database because JDBCConnection.createClob() constructs disconnected,
* initially empty Clob instances. To propagate the Clob value to a database
* in this case, it is required to supply the Clob instance to an updating
* or inserting setXXX method of a Prepared or Callable Statement, or to
* supply the Clob instance to an updateXXX method of an updateable
* ResultSet. <p>
*
* <b>Implementation Notes:</b><p>
*
* The data written to the stream does not appear in this
* Clob until the stream is closed. <p>
*
* When the stream is closed, if the value specified for <code>pos</code>
* is greater than the length of the <code>CLOB</code> value, then
* the <code>CLOB</code> value is extended in length to accept the
* written characters and the undefined region up to <code>pos</code> is
* filled with space (' ') characters. <p>
*
* Also, no attempt is made to ensure precise thread safety. Instead,
* volatile member field and local variable snapshot isolation semantics
* are implemented. This is expected to eliminate most issues related
* to race conditions, with the possible exception of concurrent
* invocation of free(). <p>
*
* In general, if an application may perform concurrent JDBCClob
* modifications and the integrity of the application depends on
* total order Clob modification semantics, then such operations
* should be synchronized on an appropriate monitor.<p>
*
* </div>
* <!-- end release-specific documentation -->
*
* @param pos the position at which to start writing to the
* <code>CLOB</code> value; The first position is 1
*
* @return a stream to which Unicode encoded characters can be written
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if {@code pos} is less than 1
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see #getCharacterStream
*
* @since JDK 1.4, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public java.io.
Writer setCharacterStream(
final long
pos) throws
SQLException {
checkReadonly();
checkClosed();
if (
pos <
MIN_POS ||
pos >
MAX_POS) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
return new java.io.
StringWriter() {
private boolean
closed = false;
public synchronized void
close() throws java.io.
IOException {
if (
closed) {
return;
}
closed = true;
final
StringBuffer sb = super.getBuffer();
try {
JDBCClob.this.
setStringBuffer(
pos,
sb, 0,
sb.
length());
} catch (
SQLException se) {
throw
JavaSystem.
toIOException(
se);
} finally {
sb.
setLength(0);
sb.
trimToSize();
}
}
};
}
/**
* Truncates the <code>CLOB</code> value that this <code>Clob</code>
* designates to have a length of <code>len</code>
* characters.
* <p>
* <b>Note:</b> If the value specified for <code>len</code>
* is greater than the length of the <code>CLOB</code> value, then the
* behavior is undefined. Some JDBC drivers may throw a
* <code>SQLException</code> while other drivers may support this
* operation.
*
* <!-- start release-specific documentation -->
* <div class="ReleaseSpecificDocumentation">
* <h3>HSQLDB-Specific Information:</h3> <p>
*
* Starting with HSQLDB 2.0 this feature is fully supported. <p>
*
* When built under JDK 1.6+ and the Clob instance is constructed as a
* result of calling JDBCConnection.createClob(), this operation affects
* only the client-side value; it has no effect upon a value stored in a
* database because JDBCConnection.createClob() constructs disconnected,
* initially empty Blob instances. To propagate the truncated Clob value to
* a database in this case, it is required to supply the Clob instance to
* an updating or inserting setXXX method of a Prepared or Callable
* Statement, or to supply the Blob instance to an updateXXX method of an
* updateable ResultSet. <p>
*
* <b>Implementation Notes:</b> <p>
*
* HSQLDB throws an SQLException if the specified <tt>len</tt> is greater
* than the value returned by {@link #length() length}. <p>
*
* </div>
* <!-- end release-specific documentation -->
*
* @param len the length, in characters, to which the <code>CLOB</code> value
* should be truncated
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if len is less than 0
*
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.4, HSQLDB 1.7.2
* @revised JDK 1.6, HSQLDB 2.0
*/
public void
truncate(final long
len) throws
SQLException {
checkReadonly();
final
String data =
getData();
final long
dlen =
data.
length();
if (
len ==
dlen) {
return;
}
if (
len < 0 ||
len >
dlen) {
throw
JDBCUtil.
outOfRangeArgument("len: " +
len);
}
setData(
data.
substring(0, (int)
len));
}
//------------------------- JDBC 4.0 -----------------------------------
/**
* This method frees the <code>Clob</code> object and releases the resources the resources
* that it holds. The object is invalid once the <code>free</code> method
* is called.
* <p>
* After <code>free</code> has been called, any attempt to invoke a
* method other than <code>free</code> will result in a <code>SQLException</code>
* being thrown. If <code>free</code> is called multiple times, the subsequent
* calls to <code>free</code> are treated as a no-op.
* <p>
* @throws SQLException if an error occurs releasing
* the Clob's resources
*
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6, HSQLDB 2.0
*/
public synchronized void
free() throws
SQLException {
m_closed = true;
m_data = null;
}
/**
* Returns a <code>Reader</code> object that contains a partial <code>Clob</code> value, starting
* with the character specified by pos, which is length characters in length.
*
* @param pos the offset to the first character of the partial value to
* be retrieved. The first character in the Clob is at position 1.
* @param length the length in characters of the partial value to be retrieved.
* @return <code>Reader</code> through which the partial <code>Clob</code> value can be read.
* @throws SQLException if pos is less than 1 or if pos is greater than the number of
* characters in the <code>Clob</code> or if pos + length is greater than the number of
* characters in the <code>Clob</code>
*
* @exception java.sql.SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @since JDK 1.6, HSQLDB 2.0
*/
public
Reader getCharacterStream(long
pos,
long
length) throws
SQLException {
if (
length >
Integer.
MAX_VALUE) {
throw
JDBCUtil.
outOfRangeArgument("length: " +
length);
}
final
String data =
getData();
final int
dlen =
data.
length();
if (
pos ==
MIN_POS &&
length ==
dlen) {
return new
StringReader(
data);
}
if (
pos <
MIN_POS ||
pos >
dlen) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
final long
startIndex =
pos - 1;
if (
length < 0 ||
length >
dlen -
startIndex) {
throw
JDBCUtil.
outOfRangeArgument("length: " +
length);
}
final int
endIndex = (int) (
startIndex +
length); // exclusive
final char[]
chars = new char[(int)
length];
data.
getChars((int)
startIndex,
endIndex,
chars, 0);
return new
CharArrayReader(
chars);
}
// ---------------------- internal implementation --------------------------
private static final long
MIN_POS = 1L;
private static final long
MAX_POS = 1L + (long)
Integer.
MAX_VALUE;
private boolean
m_closed;
private
String m_data;
private final boolean
m_createdByConnection;
/**
* Constructs a new, read-only JDBCClob object wrapping the given character
* sequence. <p>
*
* This constructor is used internally to retrieve result set values as
* Clob objects, yet it must be public to allow access from other packages.
* As such (in the interest of efficiency) this object maintains a reference
* to the given String object rather than making a copy and so it is
* gently suggested (in the interest of effective memory management) that
* external clients using this constructor either take pause to consider
* the implications or at least take care to provide a String object whose
* internal character buffer is not much larger than required to represent
* the value.
*
* @param data the character sequence representing the Clob value
* @throws SQLException if the argument is null
*/
public
JDBCClob(final
String data) throws
SQLException {
if (
data == null) {
throw
JDBCUtil.
nullArgument();
}
m_data =
data;
m_createdByConnection = false;
}
/**
* Constructs a new, empty (zero-length), read/write JDBCClob object.
*/
protected
JDBCClob() {
m_data = "";
m_createdByConnection = true;
}
protected void
checkReadonly() throws
SQLException {
if (!
m_createdByConnection) {
throw
JDBCUtil.
sqlException(
ErrorCode.
X_25006, "Clob is read-only");
}
}
protected synchronized void
checkClosed() throws
SQLException {
if (
m_closed) {
throw
JDBCUtil.
sqlException(
ErrorCode.
X_07501);
}
}
synchronized
String getData() throws
SQLException {
checkClosed();
return
m_data;
}
private synchronized void
setData(
String data) throws
SQLException {
checkClosed();
m_data =
data;
}
/**
* Behaviour is identical to {@link #setString(long, java.lang.String, int, int)}.
*
* @param pos the position at which to start writing to this
* <code>CLOB</code> object; The first position is 1
* @param sb the buffer to be written to the <code>CLOB</code>
* value that this <code>Clob</code> object represents
* @param offset the offset into <code>sb</code> to start reading
* the characters to be written
* @param len the number of characters to be written
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* <code>CLOB</code> value or if pos is less than 1
*/
public int
setStringBuffer(final long
pos, final
StringBuffer sb,
final int
offset, final int
len) throws
SQLException {
checkReadonly();
String data =
getData();
if (
sb == null) {
throw
JDBCUtil.
nullArgument("sb");
}
final int
strlen =
sb.
length();
final int
dlen =
data.
length();
final int
ipos = (int) (
pos - 1);
if (
offset == 0 &&
len ==
strlen &&
ipos == 0 &&
len >=
dlen) {
setData(
sb.
toString());
return
len;
}
if (
offset < 0 ||
offset >
strlen) {
throw
JDBCUtil.
outOfRangeArgument("offset: " +
offset);
}
if (
len >
strlen -
offset) {
throw
JDBCUtil.
outOfRangeArgument("len: " +
len);
}
if (
pos <
MIN_POS || (
pos -
MIN_POS) > (
Integer.
MAX_VALUE -
len)) {
throw
JDBCUtil.
outOfRangeArgument("pos: " +
pos);
}
final long
endPos = (
pos +
len);
char[]
chars;
if (
pos >
dlen) {
// 1.) 'datachars' + '\32\32\32...' + substring
chars = new char[(int)
endPos - 1];
data.
getChars(0,
dlen,
chars, 0);
for(int
i =
dlen;
i <
ipos;
i++) {
chars[
i] = ' ';
}
sb.
getChars(
offset,
offset +
len,
chars,
ipos);
} else if (
endPos >
dlen) {
// 2.) 'datach...' + substring
chars = new char[(int)
endPos - 1];
data.
getChars(0,
ipos,
chars, 0);
sb.
getChars(
offset,
offset +
len,
chars,
ipos);
} else {
// 3.) 'dat' + substring + 'rs'
chars = new char[
dlen];
data.
getChars(0,
ipos,
chars, 0);
sb.
getChars(
offset,
offset +
len,
chars,
ipos);
final int
dataOffset =
ipos +
len;
data.
getChars(
dataOffset,
dlen,
chars,
dataOffset);
}
setData(new
String(
chars));
return
len;
}
}