org.hsqldb.jdbc.JDBCClob Maven / Gradle / Ivy
Show all versions of sqltool Show documentation
/* Copyright (c) 2001-2021, 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.sql.Clob;
import java.sql.SQLException;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.FrameworkLogger;
import org.hsqldb.lib.java.JavaSystem;
/* $Id: JDBCClob.java 6266 2021-01-25 16:08:06Z 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 CLOB
type.
* An SQL CLOB
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 Clob
object using an SQL
* locator(CLOB)
, which means that a Clob
object
* contains a logical pointer to the SQL CLOB
data rather than
* the data itself. A Clob
object is valid for the duration
* of the transaction in which it was created.
* The Clob
interface provides methods for getting the
* length of an SQL CLOB
(Character Large Object) value,
* for materializing a CLOB
value on the client, and for
* searching for a substring or CLOB
object within a
* CLOB
value.
* Methods in the interfaces {@link java.sql.ResultSet},
* {@link java.sql.CallableStatement}, and {@link java.sql.PreparedStatement}, such as
* getClob
and setClob
allow a programmer to
* access an SQL CLOB
value. In addition, this interface
* has methods for updating a CLOB
value.
*
* All methods on the Clob
interface must be fully implemented if the
* JDBC driver supports the data type.
*
*
*
* HSQLDB-Specific Information:
*
* 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.
*
* Starting with 2.0, the HSQLDB driver fully supports both local and remote
* SQL CLOB data implementations, meaning that an HSQLDB Clob object may
* 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.
*
*
*
*
* @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
* @version 2.5.0
* @since JDK 1.2, HSQLDB 1.7.2
*/
public class JDBCClob implements Clob {
private static final FrameworkLogger LOG =
FrameworkLogger.getLog(JDBCClob.class);
/**
* Retrieves the number of characters
* in the CLOB
value
* designated by this Clob
object.
*
* @return length of the CLOB
in characters
* @exception SQLException if there is an error accessing the
* length of the CLOB
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 CLOB
value
* designated by this Clob
object.
* The substring begins at position
* pos
and has up to length
consecutive
* characters.
*
*
*
* HSQLDB-Specific Information:
*
* 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.
*
* Note
*
* This method uses {@link java.lang.String#substring(int, int)}.
*
* 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 may or may not 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.
*
* 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).
*
*
*
* @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 String
that is the specified substring in
* the CLOB
value designated by this Clob
object
* @exception SQLException if there is an error accessing the
* CLOB
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 CLOB
value designated by this Clob
* object as a java.io.Reader
object (or as a stream of
* characters).
*
* @return a java.io.Reader
object containing the
* CLOB
data
* @exception SQLException if there is an error accessing the
* CLOB
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 CLOB
value designated by this Clob
* object as an ASCII stream.
*
* @return a java.io.InputStream
object containing the
* CLOB
data
* @exception SQLException if there is an error accessing the
* CLOB
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(JavaSystem.CS_US_ASCII));
} catch (Throwable e) {
throw JDBCUtil.sqlException(e);
}
}
/**
* Retrieves the character position at which the specified substring
* searchstr
appears in the SQL CLOB
value
* represented by this Clob
object. The search
* begins at position start
.
*
* @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
* CLOB
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
* Clob
object searchstr
appears in this
* Clob
object. The search begins at position
* start
.
*
* @param searchstr the Clob
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 Clob
object appears
* or -1 if it is not present; the first position is 1
* @exception SQLException if there is an error accessing the
* CLOB
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 String
to the CLOB
* value that this Clob
object designates at the position
* pos
. The string will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing the given string, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
*
*
* HSQLDB-Specific Information:
*
* Starting with HSQLDB 2.0 this feature is supported.
*
* 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.
*
* Implementation Notes:
*
* 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().
*
* 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.
*
* When the value specified for pos
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.
*
*
*
*
*
* @param pos the position at which to start writing to the CLOB
* value that this Clob
object represents;
* The first position is 1
* @param str the string to be written to the CLOB
* value that this Clob
designates
* @return the number of characters written
* @exception SQLException if there is an error accessing the
* CLOB
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
*/
public int setString(long pos, String str) throws SQLException {
return setString(pos, str, 0, str == null ? 0
: str.length());
}
/**
* Writes len
characters of str
, starting
* at character offset
, to the CLOB
value
* that this Clob
represents. The string will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing the given string, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
*
*
* HSQLDB-Specific Information:
*
* Starting with HSQLDB 2.0 this feature is supported.
*
* 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.
*
* Implementation Notes:
*
* If the value specified for pos
* is greater than the length of the CLOB
value, then
* the CLOB
value is extended in length to accept the
* written characters and the undefined region up to pos
is
* filled with space (' ') characters.
*
* 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().
*
* 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.
*
*
*
*
* @param pos the position at which to start writing to this
* CLOB
object; The first position is 1
* @param str the string to be written to the CLOB
* value that this Clob
object represents
* @param offset the offset into str
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
* CLOB
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
*/
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
* CLOB
value that this Clob
object represents,
* starting at position pos
. Characters written to the stream
* will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing characters to the stream, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater than the length of the CLOB
value, then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
*
*
* HSQLDB-Specific Information:
*
* Starting with HSQLDB 2.0 this feature is supported.
*
* 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.
*
* Implementation Notes:
*
* The data written to the stream does not appear in this
* Clob until the stream is closed.
*
* When the stream is closed, if the value specified for pos
* is greater than the length of the CLOB
value, then
* the CLOB
value is extended in length to accept the
* written characters and the undefined region up to pos
is
* filled with space (' ') characters.
*
* 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().
*
* 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.
*
*
*
*
* @param pos the position at which to start writing to this
* CLOB
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
* CLOB
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
*/
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,
JavaSystem.CS_US_ASCII);
JDBCClob.this.setString(pos, str);
} catch (Throwable e) {
throw JavaSystem.toIOException(e);
}
}
};
}
/**
* Retrieves a stream to be used to write a stream of Unicode characters
* to the CLOB
value that this Clob
object
* represents, at position pos
. Characters written to the stream
* will overwrite the existing characters
* in the Clob
object starting at the position
* pos
. If the end of the Clob
value is reached
* while writing characters to the stream, then the length of the Clob
* value will be increased to accommodate the extra characters.
*
* Note: If the value specified for pos
* is greater then the length+1 of the CLOB
value then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
*
*
* HSQLDB-Specific Information:
*
* Starting with HSQLDB 2.0 this feature is supported.
*
* 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.
*
* Implementation Notes:
*
* The data written to the stream does not appear in this
* Clob until the stream is closed.
*
* When the stream is closed, if the value specified for pos
* is greater than the length of the CLOB
value, then
* the CLOB
value is extended in length to accept the
* written characters and the undefined region up to pos
is
* filled with space (' ') characters.
*
* 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().
*
* 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.
*
*
*
*
* @param pos the position at which to start writing to the
* CLOB
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
* CLOB
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
*/
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 CLOB
value that this Clob
* designates to have a length of len
* characters.
*
* Note: If the value specified for len
* is greater than the length of the CLOB
value, then the
* behavior is undefined. Some JDBC drivers may throw a
* SQLException
while other drivers may support this
* operation.
*
*
*
* HSQLDB-Specific Information:
*
* Starting with HSQLDB 2.0 this feature is fully supported.
*
* 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.
*
* Implementation Notes:
*
* HSQLDB throws an SQLException if the specified len
is greater
* than the value returned by {@link #length() length}.
*
*
*
*
* @param len the length, in characters, to which the CLOB
value
* should be truncated
* @exception SQLException if there is an error accessing the
* CLOB
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
*/
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 Clob
object and releases the resources the resources
* that it holds. The object is invalid once the free
method
* is called.
*
* After free
has been called, any attempt to invoke a
* method other than free
will result in a SQLException
* being thrown. If free
is called multiple times, the subsequent
* calls to free
are treated as a no-op.
*
* @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 Reader
object that contains a partial Clob
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 Reader
through which the partial Clob
value can be read.
* @throws SQLException if pos is less than 1 or if pos is greater than the number of
* characters in the Clob
or if pos + length is greater than the number of
* characters in the Clob
*
* @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.
*
* 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
* CLOB
object; The first position is 1
* @param sb the buffer to be written to the CLOB
* value that this Clob
object represents
* @param offset the offset into sb
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
* CLOB
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;
}
}