/* Copyright (c) 2001-2019, 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.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException;
import java.sql.Clob;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;

import org.hsqldb.HsqlException;
import org.hsqldb.SessionInterface;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.types.ClobDataID;
import org.hsqldb.types.ClobInputStream;

A wrapper for HSQLDB ClobData objects. Instances of this class are returned by calls to ResultSet methods.
Author:Fred Toussi (fredt@users dot sourceforge.net)
Version:2.3.5
Since: JDK 1.2, HSQLDB 1.9.0
/** * A wrapper for HSQLDB ClobData objects. * * Instances of this class are returned by calls to ResultSet methods. * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.3.5 * @since JDK 1.2, HSQLDB 1.9.0 */
public class JDBCClobClient implements Clob {
Retrieves the CLOB value designated by this Clob object as an ascii stream.
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:a java.io.InputStream object containing the CLOB data
/** * 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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized InputStream getAsciiStream() throws SQLException { checkClosed(); return new InputStream() { private final byte[] oneChar = new byte[1]; private boolean m_closed; // better size than 8192 for network connections. private CharBuffer m_charBuffer = (CharBuffer) CharBuffer.allocate(64 * 1024).flip(); private ByteBuffer m_byteBuffer = ByteBuffer.allocate(1024); private Charset m_charset = charsetForName("US-ASCII"); private CharsetEncoder m_encoder = m_charset.newEncoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); private Reader m_reader = clob.getCharacterStream(session); public int read() throws IOException { if (isEOF()) { return -1; } synchronized (oneChar) { int charsRead = read(oneChar, 0, 1); return charsRead == 1 ? oneChar[0] : -1; } } public int read(byte[] b, int off, int len) throws IOException { checkClosed(); if (isEOF()) { return -1; } final CharBuffer cb = m_charBuffer; // int charsRead; int bytesRead; if (cb.remaining() == 0) { cb.clear(); charsRead = m_reader.read(cb); cb.flip(); if (charsRead < 0) { setEOF(); return -1; } else if (charsRead == 0) { return 0; } } final ByteBuffer bb = (m_byteBuffer.capacity() < len) ? ByteBuffer.allocate(len) : m_byteBuffer; // Since ASCII is single-byte, restrict encoder character consumption // to at most 'len' characters' to produce at most len ASCII // characters int cbLimit = cb.limit(); int cbPosition = cb.position(); cb.limit(cbPosition + len); bb.clear(); int bbPosition = bb.position(); CoderResult result = m_encoder.encode(cb, bb, false); if (bbPosition == bb.position() && result.isUnderflow()) { // surrogate character time cb.limit(cb.limit() + 1); m_encoder.encode(cb, bb, false); } // Restore the old limit so the buffer gets topped up // when required. cb.limit(cbLimit); bb.flip(); bytesRead = bb.limit(); if (bytesRead == 0) { setEOF(); return -1; } m_byteBuffer = bb; bb.get(b, off, bytesRead); return bytesRead; } public void close() throws IOException { boolean isClosed = m_closed; if (!isClosed) { m_closed = true; m_charBuffer = null; m_charset = null; m_encoder = null; try { m_reader.close(); } catch (Exception ex) {} } } private boolean isEOF() { final Reader reader = m_reader; return (reader == null); } private void setEOF() { final Reader reader = m_reader; if (reader != null) { try { reader.close(); } catch (IOException iOException) {} } m_reader = null; } private void checkClosed() throws IOException { if (JDBCClobClient.this.isClosed()) { try { this.close(); } catch (Exception ex) {} } if (m_closed) { throw new IOException("The stream is closed."); } } }; }
Retrieves the CLOB value designated by this Clob object as a java.io.Reader object (or as a stream of characters).
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:a java.io.Reader object containing the CLOB data
/** * 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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized Reader getCharacterStream() throws SQLException { checkClosed(); return new ClobInputStream(session, clob, 0, length()); }
Retrieves a copy of the specified substring in the CLOB value designated by this Clob object.
Params:
  • pos – the first character of the substring to be extracted. The first character is at position 1.
  • length – the number of consecutive characters to be copied
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:a String that is the specified substring in the CLOB value designated by this Clob object
/** * Retrieves a copy of the specified substring in the <code>CLOB</code> * value designated by this <code>Clob</code> object. * * @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 * @return a <code>String</code> that is the specified substring in the * <code>CLOB</code> value designated by this <code>Clob</code> object * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized String getSubString(long pos, int length) throws SQLException { checkClosed(); if (!isInLimits(Long.MAX_VALUE, pos - 1, length)) { throw JDBCUtil.outOfRangeArgument(); } try { return clob.getSubString(session, pos - 1, length); } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } }
Retrieves the number of characters in the CLOB value designated by this Clob object.
Throws:
  • SQLException – if there is an error accessing the length of the CLOB value
Returns:length of the CLOB in characters
/** * 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 * @throws SQLException if there is an error accessing the length of the * <code>CLOB</code> value */
public synchronized long length() throws SQLException { checkClosed(); try { return clob.length(session); } catch (HsqlException 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.
Params:
  • searchstr – the substring for which to search
  • start – the position at which to begin searching; the first position is 1
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:the position at which the substring appears or -1 if it is not present; the first position is 1
/** * 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. * * @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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized long position(String searchstr, long start) throws SQLException { checkClosed(); if (!isInLimits(Long.MAX_VALUE, start - 1, 0)) { throw JDBCUtil.outOfRangeArgument(); } try { return clob.position(session, searchstr, start - 1); } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } }
Retrieves the character position at which the specified Clob object searchstr appears in this Clob object.
Params:
  • searchstr – the Clob object for which to search
  • start – the position at which to begin searching; the first position is 1
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:the position at which the Clob object appears or -1 if it is not present; the first position is 1
/** * Retrieves the character position at which the specified * <code>Clob</code> object <code>searchstr</code> appears in this * <code>Clob</code> object. * * @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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized long position(Clob searchstr, long start) throws SQLException { checkClosed(); if (!isInLimits(Long.MAX_VALUE, start - 1, 0)) { throw JDBCUtil.outOfRangeArgument(); } if (searchstr instanceof JDBCClobClient) { ClobDataID searchClob = ((JDBCClobClient) searchstr).clob; try { return clob.position(session, searchClob, start - 1); } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } } return position(searchstr.getSubString(1, (int) searchstr.length()), start); }
Retrieves a stream to be used to write Ascii characters to the CLOB value that this Clob object represents, starting at position pos.
Params:
  • pos – the position at which to start writing to this CLOB object
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:the stream to which ASCII encoded characters can be written
/** * 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>. * * @param pos the position at which to start writing to this * <code>CLOB</code> object * @return the stream to which ASCII encoded characters can be written * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized OutputStream setAsciiStream(final long pos) throws SQLException { checkClosed(); if (pos < 1) { throw JDBCUtil.outOfRangeArgument("pos: " + pos); } if (!isWritable) { throw JDBCUtil.notUpdatableColumn(); } startUpdate(); return new OutputStream() { private long m_position = pos - 1; private Charset m_charset = charsetForName("US-ASCII"); private CharsetDecoder m_decoder = m_charset.newDecoder().onMalformedInput( CodingErrorAction.REPLACE).onUnmappableCharacter( CodingErrorAction.REPLACE); private CharBuffer m_charBuffer = CharBuffer.allocate(64 * 1024); private ByteBuffer m_byteBuffer = ByteBuffer.allocate(1024); private final byte[] oneByte = new byte[1]; private boolean m_closed; public void write(int b) throws IOException { synchronized (oneByte) { oneByte[0] = (byte) b; this.write(oneByte, 0, 1); } } public void write(byte[] b, int off, int len) throws IOException { checkClosed(); final ByteBuffer bb = (m_byteBuffer.capacity() < len) ? ByteBuffer.allocate(len) : m_byteBuffer; if (m_charBuffer.remaining() < len) { flush0(); } final CharBuffer cb = m_charBuffer.capacity() < len ? CharBuffer.allocate(len) : m_charBuffer; bb.clear(); bb.put(b, off, len); bb.flip(); m_decoder.decode(bb, cb, false); if (cb.remaining() == 0) { flush(); } } public void flush() throws IOException { checkClosed(); flush0(); } public void close() throws IOException { if (!m_closed) { try { flush0(); } finally { m_closed = true; m_byteBuffer = null; m_charBuffer = null; m_charset = null; m_decoder = null; } } } private void checkClosed() throws IOException { if (JDBCClobClient.this.isClosed()) { try { close(); } catch (Exception ex) {} } if (m_closed) { throw new IOException("The stream is closed."); } } private void flush0() throws IOException { final CharBuffer cb = m_charBuffer; cb.flip(); final char[] chars = new char[cb.length()]; cb.get(chars); cb.clear(); try { clob.setChars(session, m_position, chars, 0, chars.length); } catch (Exception e) { throw new IOException(e.toString()); } m_position += chars.length; } }; }
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.
Params:
  • pos – the position at which to start writing to the CLOB value
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:a stream to which Unicode encoded characters can be written
/** * 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>. * * @param pos the position at which to start writing to the * <code>CLOB</code> value * @return a stream to which Unicode encoded characters can be written * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized Writer setCharacterStream(final long pos) throws SQLException { checkClosed(); if (pos < 1) { throw JDBCUtil.outOfRangeArgument("pos: " + pos); } if (!isWritable) { throw JDBCUtil.notUpdatableColumn(); } startUpdate(); return new Writer() { private long m_clobPosition = pos - 1; private boolean m_closed; public void write(char[] cbuf, int off, int len) throws IOException { checkClosed(); clob.setChars(session, m_clobPosition, cbuf, off, len); m_clobPosition += len; } public void flush() throws IOException { // no-op } @Override public void close() throws IOException { m_closed = true; } private void checkClosed() throws IOException { if (m_closed || JDBCClobClient.this.isClosed()) { throw new IOException("The stream is closed"); } } }; }
Writes the given Java String to the CLOB value that this Clob object designates at the position pos.
Params:
  • pos – the position at which to start writing to the CLOB value that this Clob object represents
  • str – the string to be written to the CLOB value that this Clob designates
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:the number of characters written
/** * 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>. * * @param pos the position at which to start writing to the * <code>CLOB</code> value that this <code>Clob</code> object * represents * @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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized int setString(long pos, String str) throws SQLException { return setString(pos, str, 0, str.length()); }
Writes len characters of str, starting at character offset, to the CLOB value that this Clob represents.
Params:
  • pos – the position at which to start writing to this CLOB object
  • str – the string to be written to the CLOB value that this Clob object represents
  • offset – the offset into str to start reading the characters to be written
  • len – the number of characters to be written
Throws:
  • SQLException – if there is an error accessing the CLOB value
Returns:the number of characters written
/** * 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. * * @param pos the position at which to start writing to this * <code>CLOB</code> object * @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 * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized int setString(long pos, String str, int offset, int len) throws SQLException { checkClosed(); if (!isInLimits(str.length(), offset, len)) { throw JDBCUtil.outOfRangeArgument(); } if (pos < 1) { throw JDBCUtil.outOfRangeArgument("pos: " + pos); } if (!isWritable) { throw JDBCUtil.notUpdatableColumn(); } startUpdate(); str = str.substring(offset, offset + len); try { clob.setString(session, pos - 1, str); return len; } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } }
Truncates the CLOB value that this Clob designates to have a length of len characters.
Params:
  • len – the length, in bytes, to which the CLOB value should be truncated
Throws:
  • SQLException – if there is an error accessing the CLOB value
/** * Truncates the <code>CLOB</code> value that this <code>Clob</code> * designates to have a length of <code>len</code> characters. * * @param len the length, in bytes, to which the <code>CLOB</code> value * should be truncated * @throws SQLException if there is an error accessing the * <code>CLOB</code> value */
public synchronized void truncate(long len) throws SQLException { checkClosed(); if (len < 0) { throw JDBCUtil.outOfRangeArgument("len: " + len); } try { clob.truncate(session, len); } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } } //------------------------- 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:
Since:JDK 1.6, HSQLDB 2.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 SQLFeatureNotSupportedException if the JDBC driver does not support * this method * @since JDK 1.6, HSQLDB 2.0 */
public synchronized void free() throws SQLException { isClosed = true; clob = null; session = null; }
Returns a Reader object that contains a partial Clob value, starting with the character specified by pos, which is length characters in length.
Params:
  • pos – the offset to the first character of the partial value to be retrieved. The first character in the Clob is at position 1.
  • length – the length in characters of the partial value to be retrieved.
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
  • SQLFeatureNotSupportedException – if the JDBC driver does not support this method
Returns:Reader through which the partial Clob value can be read.
Since:JDK 1.6, HSQLDB 2.0
/** * 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 SQLFeatureNotSupportedException if the JDBC driver does not support * this method * @since JDK 1.6, HSQLDB 2.0 */
public synchronized Reader getCharacterStream(long pos, long length) throws SQLException { checkClosed(); if (!isInLimits(this.length(), pos - 1, length)) { throw JDBCUtil.outOfRangeArgument(); } return new ClobInputStream(session, clob, pos - 1, length); } char[] getChars(long position, int length) throws SQLException { try { return clob.getChars(session, position - 1, length); } catch (HsqlException e) { throw JDBCUtil.sqlException(e); } } // ClobDataID originalClob; ClobDataID clob; SessionInterface session; int colIndex; private boolean isClosed; private boolean isWritable; JDBCResultSet resultSet; public JDBCClobClient(SessionInterface session, ClobDataID clob) { this.session = session; this.clob = clob; } public ClobDataID getClob() { return clob; } public synchronized boolean isClosed() { return isClosed; } public synchronized void setWritable(JDBCResultSet result, int index) { isWritable = true; resultSet = result; colIndex = index; } public synchronized void clearUpdates() { if (originalClob != null) { clob = originalClob; originalClob = null; } } private void startUpdate() throws SQLException { if (originalClob != null) { return; } originalClob = clob; clob = (ClobDataID) clob.duplicate(session); resultSet.startUpdate(colIndex + 1); resultSet.preparedStatement.parameterValues[colIndex] = clob; resultSet.preparedStatement.parameterSet[colIndex] = Boolean.TRUE; } private void checkClosed() throws SQLException { if (isClosed) { throw JDBCUtil.sqlException(ErrorCode.X_0F502); } } static boolean isInLimits(long fullLength, long pos, long len) { return fullLength >= 0 && pos >= 0 && len >= 0 && pos <= fullLength - len; } protected static Charset charsetForName(final String charsetName) throws SQLException { String csn = charsetName; if (csn == null) { csn = Charset.defaultCharset().name(); } try { if (Charset.isSupported(csn)) { return Charset.forName(csn); } } catch (IllegalCharsetNameException x) {} throw JDBCUtil.sqlException(new UnsupportedEncodingException(csn)); } }