/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.jdbc; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.math.BigDecimal; import java.net.URL; import java.nio.charset.Charset; import java.sql.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.TreeMap; import java.util.regex.Matcher; import javax.sql.rowset.serial.SerialArray; import org.teiid.client.RequestMessage; import org.teiid.client.RequestMessage.ResultsMode; import org.teiid.client.RequestMessage.StatementType; import org.teiid.client.metadata.MetadataResult; import org.teiid.client.util.ResultsFuture; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.types.ArrayImpl; import org.teiid.core.types.BlobImpl; import org.teiid.core.types.ClobImpl; import org.teiid.core.types.DataTypeManager; import org.teiid.core.types.InputStreamFactory; import org.teiid.core.types.JDBCSQLTypeInfo; import org.teiid.core.types.Streamable; import org.teiid.core.util.MultiArrayOutputStream; import org.teiid.core.util.ReaderInputStream; import org.teiid.core.util.SqlUtil; import org.teiid.core.util.TimestampWithTimezone; import org.teiid.netty.handler.codec.serialization.CompactObjectOutputStream; /** * <p> Instances of PreparedStatement contain a SQL statement that has already been * compiled. The SQL statement contained in a PreparedStatement object may have * one or more IN parameters. An IN parameter is a parameter whose value is not * specified when a SQL statement is created. Instead the statement has a placeholder * for each IN parameter.</p> * <p> The MMPreparedStatement object wraps the server's PreparedStatement object. * The methods in this class are used to set the IN parameters on a server's * preparedstatement object.</p> */ public class PreparedStatementImpl extends StatementImpl implements TeiidPreparedStatement { // sql, which this prepared statement is operating on protected String prepareSql; //map that holds parameter index to values for prepared statements private Map<Integer, Object> parameterMap; TreeMap<String, Integer> paramsByName; //a list of map that holds parameter index to values for prepared statements protected List<List<Object>> batchParameterList; // metadata private MetadataResult metadataResults; private ResultSetMetaData metadata; private ParameterMetaDataImpl parameterMetaData; private Calendar serverCalendar; private Object command; private boolean autoGeneratedKeys; /** * <p>MMPreparedStatement constructor. * @param Driver's connection object. * @param String object representing the prepared statement */ PreparedStatementImpl(ConnectionImpl connection, String sql, int resultSetType, int resultSetConcurrency) throws SQLException { super(connection, resultSetType, resultSetConcurrency); if (sql == null) { throw new TeiidSQLException(JDBCPlugin.Util.getString("MMPreparedStatement.Err_prep_sql")); //$NON-NLS-1$ } this.prepareSql = sql; TimeZone timezone = connection.getServerConnection().getLogonResult().getTimeZone(); if (timezone != null && !timezone.hasSameRules(getDefaultCalendar().getTimeZone())) { this.serverCalendar = Calendar.getInstance(timezone); } } public void setAutoGeneratedKeys(boolean getAutoGeneratedKeys) { this.autoGeneratedKeys = getAutoGeneratedKeys; } /** * <p>Adds a set of parameters to this PreparedStatement object's list of commands * to be sent to the database for execution as a batch. * @throws SQLException if there is an error */ public void addBatch() throws SQLException { checkStatement(); if(batchParameterList == null){ batchParameterList = new ArrayList<List<Object>>(); } batchParameterList.add(getParameterValues()); } /** * Makes the set of commands in the current batch empty. * * @throws SQLException if a database access error occurs or the * driver does not support batch statements */ public void clearBatch() throws SQLException { if (batchParameterList != null ) { batchParameterList.clear(); } } /** * <p>Clears the values set for the PreparedStatement object's IN parameters and * releases the resources used by those values. In general, parameter values * remain in force for repeated use of statement. * @throws SQLException if there is an error while clearing params */ public void clearParameters() throws SQLException { checkStatement(); //clear the parameters list on servers prepared statement object if(parameterMap != null){ parameterMap.clear(); } } @Override public boolean execute(String sql) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public void submitExecute(String sql, StatementCallback callback, RequestOptions options) throws TeiidSQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public ResultSet executeQuery(String sql) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public int executeUpdate(String sql) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public boolean execute(String sql, int[] columnIndexes) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public boolean execute(String sql, String[] columnNames) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public int executeUpdate(String sql, String[] columnNames) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public void addBatch(String sql) throws SQLException { String msg = JDBCPlugin.Util.getString("JDBC.Method_not_supported"); //$NON-NLS-1$ throw new TeiidSQLException(msg); } @Override public void submitExecute(StatementCallback callback, RequestOptions options) throws SQLException { NonBlockingRowProcessor processor = new NonBlockingRowProcessor(this, callback); submitExecute(ResultsMode.EITHER, options).addCompletionListener(processor); } public ResultsFuture<Boolean> submitExecute(ResultsMode mode, RequestOptions options) throws SQLException { return executeSql(new String[] {this.prepareSql}, false, mode, false, options, autoGeneratedKeys); } @Override public boolean execute() throws SQLException { executeSql(new String[] {this.prepareSql}, false, ResultsMode.EITHER, true, null, autoGeneratedKeys); return hasResultSet(); } @Override public int[] executeBatch() throws SQLException { if (batchParameterList == null || batchParameterList.isEmpty()) { return new int[0]; } try{ //check to see if we need to split large batches int[] allUpdateCounts = null; if (batchParameterList.size() > 256 && !this.getConnection().getServerConnection().isLocal()) { MultiArrayOutputStream maos = new MultiArrayOutputStream(1 << 13); try { CompactObjectOutputStream coos = new CompactObjectOutputStream(maos); List<List<Object>> copy = new ArrayList<List<Object>>(this.batchParameterList); int start = 0; for (int i = 0; i < copy.size(); i++) { coos.writeObject(copy.get(i)); coos.flush(); //if we have have over .5 mb, then send that as a single message //TODO: we could use max message size from the server if (maos.getCount() > (1 << 19) && i + 1 < copy.size()) { this.batchParameterList = copy.subList(start, i + 1); start = i + 1; executeSql(new String[] {this.prepareSql}, true, ResultsMode.UPDATECOUNT, true, null); if (allUpdateCounts == null) { allUpdateCounts = this.updateCounts; } else { allUpdateCounts = concatArrays(allUpdateCounts, this.updateCounts); } coos.reset(); maos.reset(0); } } if (allUpdateCounts != null) { if (start < copy.size()) { this.batchParameterList = copy.subList(start, copy.size()); executeSql(new String[] {this.prepareSql}, true, ResultsMode.UPDATECOUNT, true, null); allUpdateCounts = concatArrays(allUpdateCounts, this.updateCounts); } this.updateCounts = allUpdateCounts; } } catch (IOException e) { TeiidSQLException ex = TeiidSQLException.create(e); throw new BatchUpdateException(e.getMessage(), ex.getSQLState(), ex.getErrorCode(), allUpdateCounts==null?new int[0]:allUpdateCounts, e); } catch (BatchUpdateException e) { if (allUpdateCounts == null) { throw e; } allUpdateCounts = concatArrays(allUpdateCounts, e.getUpdateCounts()); throw new BatchUpdateException(e.getMessage(), e.getSQLState(), e.getErrorCode(), allUpdateCounts, e); } } if (allUpdateCounts == null) { executeSql(new String[] {this.prepareSql}, true, ResultsMode.UPDATECOUNT, true, null); } }finally{ batchParameterList.clear(); } return this.updateCounts; } static int[] concatArrays(int[] array1, int[] array2) { int length = array1.length; array1 = Arrays.copyOf(array1, length + array2.length); System.arraycopy(array2, 0, array1, length, array2.length); return array1; } @Override public ResultSetImpl executeQuery() throws SQLException { executeSql(new String[] {this.prepareSql}, false, ResultsMode.RESULTSET, true, null, autoGeneratedKeys); return resultSet; } @Override public int executeUpdate() throws SQLException { executeSql(new String[] {this.prepareSql}, false, ResultsMode.UPDATECOUNT, true, null, autoGeneratedKeys); if (this.updateCounts == null) { return 0; } if (this.updateCounts.length == 0) { return 0; } return this.updateCounts[0]; } @Override protected RequestMessage createRequestMessage(String[] commands, boolean isBatchedCommand, ResultsMode resultsMode) { RequestMessage message = super.createRequestMessage(commands, false, resultsMode); message.setStatementType(StatementType.PREPARED); message.setParameterValues(isBatchedCommand?getParameterValuesList(): getParameterValues()); message.setBatchedUpdate(isBatchedCommand); message.setCommand(this.command); return message; } public ResultSetMetaData getMetaData() throws SQLException { // check if the statement is open checkStatement(); if(metadata == null) { if (updateCounts != null) { return null; } else if(resultSet != null) { metadata = resultSet.getMetaData(); } else { Matcher matcher = StatementImpl.SHOW_STATEMENT.matcher(prepareSql); if (matcher.matches()) { this.executeShow(matcher); metadata = this.resultSet.getMetaData(); this.resultSet = null; return metadata; } if (getMetadataResults().getColumnMetadata() == null || getMetadataResults().getColumnMetadata().length == 0) { return null; } MetadataProvider provider = new MetadataProvider(getMetadataResults().getColumnMetadata()); metadata = new ResultSetMetaDataImpl(provider, this.getExecutionProperty(ExecutionProperties.JDBC4COLUMNNAMEANDLABELSEMANTICS)); } } return metadata; } private MetadataResult getMetadataResults() throws SQLException { if (metadataResults == null) { if (StatementImpl.SET_STATEMENT.matcher(prepareSql).matches() || StatementImpl.TRANSACTION_STATEMENT.matcher(prepareSql).matches() || StatementImpl.SHOW_STATEMENT.matcher(prepareSql).matches() || StatementImpl.SET_CHARACTERISTIC_STATEMENT.matcher(prepareSql).matches()) { metadataResults = new MetadataResult(); } else { try { metadataResults = this.getDQP().getMetadata(this.currentRequestID, prepareSql, Boolean.valueOf(getExecutionProperty(ExecutionProperties.ANSI_QUOTED_IDENTIFIERS)).booleanValue()); } catch (TeiidComponentException e) { throw TeiidSQLException.create(e); } catch (TeiidProcessingException e) { throw TeiidSQLException.create(e); } } } return metadataResults; } public void setAsciiStream(int parameterIndex, java.io.InputStream in, int length) throws SQLException { setAsciiStream(parameterIndex, in); } public void setBigDecimal (int parameterIndex, java.math.BigDecimal value) throws SQLException { setObject(parameterIndex, value); } public void setBinaryStream(int parameterIndex, java.io.InputStream in, int length) throws SQLException { setBlob(parameterIndex, in); } public void setBlob (int parameterIndex, Blob x) throws SQLException { setObject(parameterIndex, x); } public void setBoolean (int parameterIndex, boolean value) throws SQLException { setObject(parameterIndex, Boolean.valueOf(value)); } public void setByte(int parameterIndex, byte value) throws SQLException { setObject(parameterIndex, Byte.valueOf(value)); } public void setBytes(int parameterIndex, byte bytes[]) throws SQLException { setObject(parameterIndex, bytes); } public void setCharacterStream (int parameterIndex, java.io.Reader reader, int length) throws SQLException { setCharacterStream(parameterIndex, reader); } public void setClob (int parameterIndex, Clob x) throws SQLException { setObject(parameterIndex, x); } public void setDate(int parameterIndex, java.sql.Date value) throws SQLException { setDate(parameterIndex, value, null); } public void setDate(int parameterIndex, java.sql.Date x ,java.util.Calendar cal) throws SQLException { setDate(Integer.valueOf(parameterIndex), x, cal); } void setDate(Object parameterIndex, java.sql.Date x ,java.util.Calendar cal) throws SQLException { if (cal == null || x == null) { setObject(parameterIndex, x); return; } // set the parameter on the stored procedure setObject(parameterIndex, TimestampWithTimezone.createDate(x, cal.getTimeZone(), getDefaultCalendar())); } public void setDouble(int parameterIndex, double value) throws SQLException { setObject(parameterIndex, new Double(value)); } public void setFloat(int parameterIndex, float value) throws SQLException { setObject(parameterIndex, new Float(value)); } public void setInt(int parameterIndex, int value) throws SQLException { setObject(parameterIndex, Integer.valueOf(value)); } public void setLong(int parameterIndex, long value) throws SQLException { setObject(parameterIndex, Long.valueOf(value)); } public void setNull(int parameterIndex, int jdbcType) throws SQLException { setObject(parameterIndex, null); } public void setNull(int parameterIndex, int jdbcType, String typeName) throws SQLException { setObject(parameterIndex, null); } public void setObject (int parameterIndex, Object value, int targetJdbcType, int scale) throws SQLException { setObject(Integer.valueOf(parameterIndex), value, targetJdbcType, scale); } void setObject (Object parameterIndex, Object value, int targetJdbcType, int scale) throws SQLException { if(value == null) { setObject(parameterIndex, null); return; } if(targetJdbcType != Types.DECIMAL || targetJdbcType != Types.NUMERIC) { setObject(parameterIndex, value, targetJdbcType); // Decimal and NUMERIC types correspond to java.math.BigDecimal } else { // transform the object to a BigDecimal BigDecimal bigDecimalObject = DataTypeTransformer.getBigDecimal(value); // set scale on the BigDecimal setObject(parameterIndex, bigDecimalObject.setScale(scale)); } } public void setObject(int parameterIndex, Object value, int targetJdbcType) throws SQLException { setObject(Integer.valueOf(parameterIndex), value, targetJdbcType); } void setObject(Object parameterIndex, Object value, int targetJdbcType) throws SQLException { Object targetObject = value; if(value == null) { setObject(parameterIndex, null); return; } // get the java class name for the given JDBC type String typeName = JDBCSQLTypeInfo.getTypeName(targetJdbcType); int typeCode = DataTypeManager.getTypeCode(DataTypeManager.getDataTypeClass(typeName)); // transform the value to the target datatype switch (typeCode) { case DataTypeManager.DefaultTypeCodes.STRING: targetObject = DataTypeTransformer.getString(value); break; case DataTypeManager.DefaultTypeCodes.CHAR: targetObject = DataTypeTransformer.getCharacter(value); break; case DataTypeManager.DefaultTypeCodes.INTEGER: targetObject = DataTypeTransformer.getInteger(value); break; case DataTypeManager.DefaultTypeCodes.BYTE: targetObject = DataTypeTransformer.getByte(value); break; case DataTypeManager.DefaultTypeCodes.SHORT: targetObject = DataTypeTransformer.getShort(value); break; case DataTypeManager.DefaultTypeCodes.LONG: targetObject = DataTypeTransformer.getLong(value); break; case DataTypeManager.DefaultTypeCodes.FLOAT: targetObject = DataTypeTransformer.getFloat(value); break; case DataTypeManager.DefaultTypeCodes.DOUBLE: targetObject = DataTypeTransformer.getDouble(value); break; case DataTypeManager.DefaultTypeCodes.BOOLEAN: targetObject = DataTypeTransformer.getBoolean(value); break; case DataTypeManager.DefaultTypeCodes.BIGDECIMAL: targetObject = DataTypeTransformer.getBigDecimal(value); break; case DataTypeManager.DefaultTypeCodes.TIMESTAMP: targetObject = DataTypeTransformer.getTimestamp(value); break; case DataTypeManager.DefaultTypeCodes.DATE: targetObject = DataTypeTransformer.getDate(value); break; case DataTypeManager.DefaultTypeCodes.TIME: targetObject = DataTypeTransformer.getTime(value); break; case DataTypeManager.DefaultTypeCodes.BLOB: targetObject = DataTypeTransformer.getBlob(value); break; case DataTypeManager.DefaultTypeCodes.CLOB: targetObject = DataTypeTransformer.getClob(value); break; case DataTypeManager.DefaultTypeCodes.XML: targetObject = DataTypeTransformer.getSQLXML(value); break; case DataTypeManager.DefaultTypeCodes.VARBINARY: targetObject = DataTypeTransformer.getBytes(value); break; } setObject(parameterIndex, targetObject); } public void setObject(int parameterIndex, Object value) throws SQLException { setObject(Integer.valueOf(parameterIndex), value); } void setObject(Object parameterIndex, Object value) throws SQLException { if (parameterIndex instanceof String) { String s = (String)parameterIndex; if (paramsByName == null) { paramsByName = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER); ParameterMetaDataImpl pmdi = getParameterMetaData(); for (int i = 1; i <= pmdi.getParameterCount(); i++) { String name = pmdi.getParameterName(i); paramsByName.put(name, i); } } parameterIndex = paramsByName.get(s); if (parameterIndex == null) { throw new TeiidSQLException(JDBCPlugin.Util.getString("MMCallableStatement.Param_not_found", s)); //$NON-NLS-1$ } } if ((Integer)parameterIndex < 1) { throw new TeiidSQLException(JDBCPlugin.Util.getString("MMPreparedStatement.Invalid_param_index")); //$NON-NLS-1$ } if(parameterMap == null){ parameterMap = new TreeMap<Integer, Object>(); } if (serverCalendar != null && value instanceof java.util.Date) { value = TimestampWithTimezone.create((java.util.Date)value, getDefaultCalendar().getTimeZone(), serverCalendar, value.getClass()); } parameterMap.put((Integer)parameterIndex, value); } public void setShort(int parameterIndex, short value) throws SQLException { setObject(parameterIndex, Short.valueOf(value)); } public void setString(int parameterIndex, String value) throws SQLException { setObject(parameterIndex, value); } public void setTime(int parameterIndex, java.sql.Time value) throws SQLException { setTime(parameterIndex, value, null); } public void setTime(int parameterIndex, java.sql.Time x, java.util.Calendar cal) throws SQLException { setTime(Integer.valueOf(parameterIndex), x, cal); } void setTime(Object parameterIndex, java.sql.Time x, java.util.Calendar cal) throws SQLException { if (cal == null || x == null) { setObject(parameterIndex, x); return; } // set the parameter on the stored procedure setObject(parameterIndex, TimestampWithTimezone.createTime(x, cal.getTimeZone(), getDefaultCalendar())); } public void setTimestamp(int parameterIndex, java.sql.Timestamp value) throws SQLException { setTimestamp(parameterIndex, value, null); } public void setTimestamp(int parameterIndex, java.sql.Timestamp x, java.util.Calendar cal) throws SQLException { setTimestamp(Integer.valueOf(parameterIndex), x, cal); } void setTimestamp(Object parameterIndex, java.sql.Timestamp x, java.util.Calendar cal) throws SQLException { if (cal == null || x == null) { setObject(parameterIndex, x); return; } // set the parameter on the stored procedure setObject(parameterIndex, TimestampWithTimezone.createTimestamp(x, cal.getTimeZone(), getDefaultCalendar())); } public void setURL(int parameterIndex, URL x) throws SQLException { setObject(parameterIndex, x); } List<List<Object>> getParameterValuesList() { if(batchParameterList == null || batchParameterList.isEmpty()){ return Collections.emptyList(); } return new ArrayList<List<Object>>(batchParameterList); } List<Object> getParameterValues() { if(parameterMap == null || parameterMap.isEmpty()){ return Collections.emptyList(); } return new ArrayList<Object>(parameterMap.values()); } public ParameterMetaDataImpl getParameterMetaData() throws SQLException { if (parameterMetaData == null) { //TODO: some of the base implementation of ResultSetMetadata could be on the MetadataProvider this.parameterMetaData = new ParameterMetaDataImpl(new ResultSetMetaDataImpl(new MetadataProvider(getMetadataResults().getParameterMetadata()), this.getExecutionProperty(ExecutionProperties.JDBC4COLUMNNAMEANDLABELSEMANTICS))); } return parameterMetaData; } /** * Exposed for unit testing */ void setServerCalendar(Calendar serverCalendar) { this.serverCalendar = serverCalendar; } public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { setObject(parameterIndex, xmlObject); } public void setArray(int parameterIndex, Array x) throws SQLException { if (x instanceof ArrayImpl) { setObject(parameterIndex, x); } else { setObject(parameterIndex, new SerialArray(x)); } } @Override public void setAsciiStream(int parameterIndex, final InputStream x) throws SQLException { setAsciiStream(Integer.valueOf(parameterIndex), x); } void setAsciiStream(Object parameterIndex, final InputStream x) throws SQLException { if (x == null) { this.setObject(parameterIndex, null); return; } this.setObject(parameterIndex, new ClobImpl(new InputStreamFactory() { @Override public InputStream getInputStream() throws IOException { return x; } }, -1)); } public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { setAsciiStream(parameterIndex, x); } public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { setBlob(parameterIndex, x); } public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { setBinaryStream(parameterIndex, x); } public void setBlob(int parameterIndex, final InputStream inputStream) throws SQLException { setBlob(Integer.valueOf(parameterIndex), inputStream); } void setBlob(Object parameterIndex, final InputStream inputStream) throws SQLException { if (inputStream == null) { this.setObject(parameterIndex, null); return; } this.setObject(parameterIndex, new BlobImpl(new InputStreamFactory() { @Override public InputStream getInputStream() throws IOException { return inputStream; } })); } public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { setBlob(parameterIndex, inputStream); } public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { setClob(parameterIndex, reader); } public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { setCharacterStream(parameterIndex, reader); } public void setClob(int parameterIndex, final Reader reader) throws SQLException { setClob(Integer.valueOf(parameterIndex), reader); } void setClob(Object parameterIndex, final Reader reader) throws SQLException { if (reader == null) { this.setObject(parameterIndex, null); return; } this.setObject(parameterIndex, new ClobImpl(new InputStreamFactory() { @Override public InputStream getInputStream() throws IOException { return new ReaderInputStream(reader, Charset.forName(Streamable.ENCODING)); } }, -1)); } public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { setClob(parameterIndex, reader); } public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { setClob(parameterIndex, value); } public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { setCharacterStream(parameterIndex, value); } public void setNClob(int parameterIndex, NClob value) throws SQLException { setObject(parameterIndex, value); } public void setNClob(int parameterIndex, Reader reader) throws SQLException { setClob(parameterIndex, reader); } public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { setClob(parameterIndex, reader); } public void setNString(int parameterIndex, String value) throws SQLException { setObject(parameterIndex, value); } public void setRef(int parameterIndex, Ref x) throws SQLException { throw SqlUtil.createFeatureNotSupportedException(); } public void setRowId(int parameterIndex, RowId x) throws SQLException { throw SqlUtil.createFeatureNotSupportedException(); } public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { throw SqlUtil.createFeatureNotSupportedException(); } public void setCommand(Object command) { this.command = command; } }