/*
Derby - Class org.apache.derby.impl.jdbc.EmbedPreparedStatement
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.impl.jdbc;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.types.VariableSizeDataValue;
import org.apache.derby.iapi.sql.PreparedStatement;
import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.ParameterValueSet;
import org.apache.derby.iapi.sql.ResultDescription;
import org.apache.derby.iapi.types.DataTypeDescriptor;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.RawToBinaryFormatStream;
import org.apache.derby.iapi.types.ReaderToUTF8Stream;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.Vector;
/*
We would import these, but have name-overlap
import java.sql.PreparedStatement;
import java.sql.ResultSet;
*/
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Date;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Clob;
import java.sql.Blob;
import java.io.InputStream;
import java.io.Reader;
import java.sql.Array;
import java.sql.NClob;
import java.sql.ParameterMetaData;
import java.sql.Ref;
import java.sql.RowId;
import java.sql.SQLXML;
import java.sql.Types;
import org.apache.derby.iapi.jdbc.BrokeredConnectionControl;
import org.apache.derby.iapi.jdbc.EnginePreparedStatement;
import org.apache.derby.iapi.services.loader.GeneratedClass;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.types.StringDataValue;
import org.apache.derby.iapi.util.InterruptStatus;
/**
* EmbedPreparedStatement is a local JDBC statement.
* It supports JDBC 4.1.
*/
public class EmbedPreparedStatement extends EmbedStatement
implements EnginePreparedStatement
{
//Moving jdbc2.0 batch related code in this class because callableStatement in jdbc 20 needs
//this code too and it doesn't derive from prepared statement in jdbc 20 in our implementation.
protected ResultSetMetaData rMetaData;
//bug 4579-If the prepared statement was revalidated after doing getMetaData(), we
//should get the metadata info again on next getMetaData(). We store the generated
//class name in following variable during getMetaData() call. If it differs from the
//current generated class name, then that indicates a refetch of metadata is required.
private String gcDuringGetMetaData;
protected PreparedStatement preparedStatement;
private Activation activation;
/**
* Tells which header format to use when writing CLOBs into the store.
*
* This is lazily set if we need it, and there are currently only two
* valid header formats to choose between.
* @see #usePreTenFiveHdrFormat()
*/
private Boolean usePreTenFiveHdrFormat;
private BrokeredConnectionControl bcc=null;
/**
Constructor assumes caller will setup context stack
and restore it.
*/
public EmbedPreparedStatement (EmbedConnection conn, String sql, boolean forMetaData,
int resultSetType, int resultSetConcurrency,
int resultSetHoldability,
int autoGeneratedKeys,
int[] columnIndexes,
String[] columnNames)
throws SQLException {
super(conn, forMetaData, resultSetType, resultSetConcurrency, resultSetHoldability);
// PreparedStatement is poolable by default
isPoolable = true;
// if the sql string is null, raise an error
if (sql == null)
throw newSQLException(SQLState.NULL_SQL_TEXT);
// set up the SQLText in EmbedStatement
SQLText = sql;
try {
preparedStatement = lcc.prepareInternalStatement
(lcc.getDefaultSchema(), sql,
resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY,
forMetaData);
addWarning(preparedStatement.getCompileTimeWarnings());
activation = preparedStatement.getActivation(lcc,
resultSetType == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE);
checkRequiresCallableStatement(activation);
//bug 4838 - save the auto-generated key information in activation. keeping this
//information in lcc will not work work as it can be tampered by a nested trasaction
if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS)
activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames);
InterruptStatus.restoreIntrFlagIfSeen(lcc);
} catch (Throwable t) {
throw handleException(t);
}
}
/**
JDBC states that a Statement is closed when garbage collected.
@exception Throwable Allows any exception to be thrown during finalize
*/
protected void finalize() throws Throwable {
super.finalize();
/*
** We mark the activation as not being used and
** that is it. We rely on the connection to sweep
** through the activations to find the ones that
** aren't in use, and to close them. We cannot
** do a activation.close() here because there are
** synchronized methods under close that cannot
** be called during finalization.
*/
if (activation != null)
{
activation.markUnused();
}
}
/*
* Statement interface
we override all Statement methods that take a SQL
string as they must thrown an exception in a PreparedStatement.
See the JDBC 3.0 spec.
*/
public final boolean execute(String sql) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String)");
}
public final boolean execute(String sql, int autoGenKeys) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, int)");
}
public final boolean execute(String sql, int[] columnIndexes) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, int[])");
}
public final boolean execute(String sql, String[] columnNames) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "execute(String, String[])");
}
public final java.sql.ResultSet executeQuery(String sql) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeQuery(String)");
}
public final int executeUpdate(String sql) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String)");
}
public final int executeUpdate(String sql, int autoGenKeys) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, int)");
}
public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, int[])");
}
public final int executeUpdate(String sql, String[] columnNames) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "executeUpdate(String, String[])");
}
public final void addBatch(String sql) throws SQLException {
throw newSQLException(SQLState.NOT_FOR_PREPARED_STATEMENT, "addBatch(String)");
}
/**
* Additional close to close our activation.
* In the case that a XAConnection is involved in the creation of this
* PreparedStatement for e.g in the following case
*
*
* XAConnection xaconn = xadatasource.getXAConnection();//where xadatasource is an object of XADataSource
* Connection conn = xaconnection.getConnection();
* PreparedStatement ps = conn.preparedStatement("values 1");
*
*
* In the above case the PreparedStatement will actually be a
* BrokeredPreparedStatement object. Hence when we call
* bcc.onStatementClose and pass the PreparedStatement that caused it
* applicationStatement will be the appropriate choice since it will
* contain the appropriate instance of PreparedStatement in each case
*
* @throws SQLException upon failure
*
*/
void closeActions() throws SQLException {
if (bcc!=null) {
java.sql.PreparedStatement ps_app =
(java.sql.PreparedStatement)applicationStatement;
bcc.onStatementClose(ps_app);
}
//we release the resource for preparedStatement
preparedStatement = null;
try{
setupContextStack();
} catch (SQLException se) {
//we may have already committed the transaction in which case
//setupContextStack will fail, the close should just return
return;
}
try
{
activation.close();
activation = null;
InterruptStatus.restoreIntrFlagIfSeen();
} catch (Throwable t)
{
throw handleException(t);
} finally {
restoreContextStack();
}
}
/*
* PreparedStatement interface; we have inherited from
* EmbedStatement to get the Statement interface for
* EmbedPreparedStatement (needed by PreparedStatement)
* These are the JDBC interface comments, so we know
* what to do.
*/
/**
* A prepared SQL query is executed and its ResultSet is returned.
*
* @return a ResultSet that contains the data produced by the
* query; never null
* @exception SQLException thrown on failure.
*/
public final java.sql.ResultSet executeQuery() throws SQLException {
try {
executeStatement(activation, true, false);
} catch(SQLException sqle) {
checkStatementValidity(sqle);
}
if (SanityManager.DEBUG) {
if (results == null)
SanityManager.THROWASSERT("no results returned on executeQuery()");
}
return results;
}
/**
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition,
* SQL statements that return nothing such as SQL DDL statements
* can be executed.
*
* @return either the row count for INSERT, UPDATE or DELETE; or 0
* for SQL statements that return nothing
* @exception SQLException thrown on failure.
*/
public final int executeUpdate() throws SQLException {
return (int) executeLargeUpdate();
}
/**
* Execute a SQL INSERT, UPDATE or DELETE statement. In addition,
* SQL statements that return nothing such as SQL DDL statements
* can be executed. For use with
* statements which may touch more than Integer.MAX_VALUE rows.
*/
public final long executeLargeUpdate() throws SQLException {
try {
executeStatement(activation, false, true);
} catch(SQLException sqle) {
checkStatementValidity(sqle);
}
return updateCount;
}
/**
* Set a parameter to SQL NULL.
*
*
Note: You must specify the parameter's SQL type.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param sqlType SQL type code defined by java.sql.Types
* @exception SQLException thrown on failure.
*/
public void setNull(int parameterIndex, int sqlType) throws SQLException {
Util.checkForSupportedDataType(sqlType);
checkStatus();
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (!DataTypeDescriptor.isJDBCTypeEquivalent(jdbcTypeId, sqlType)) {
throw dataTypeConversion(parameterIndex, Util.typeName(sqlType));
}
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setToNull();
} catch (StandardException t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java boolean value. According to the JDBC API spec,
* the driver converts this to a SQL BIT value when it sends it to the
* database. But we don't have to do this, since the database engine
* supports a boolean type.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setBoolean(int parameterIndex, boolean x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (StandardException t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java byte value. The driver converts this
* to a SQL TINYINT value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setByte(int parameterIndex, byte x) throws SQLException {
checkStatus();
try {
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java short value. The driver converts this
* to a SQL SMALLINT value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setShort(int parameterIndex, short x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java int value. The driver converts this
* to a SQL INTEGER value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setInt(int parameterIndex, int x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java long value. The driver converts this
* to a SQL BIGINT value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setLong(int parameterIndex, long x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a java.lang.BigDecimal value.
* The driver converts this to a SQL NUMERIC value when
* it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public final void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setBigDecimal(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java float value. The driver converts this
* to a SQL FLOAT value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setFloat(int parameterIndex, float x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java double value. The driver converts this
* to a SQL DOUBLE value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setDouble(int parameterIndex, double x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java String value. The driver converts this
* to a SQL VARCHAR or LONGVARCHAR value (depending on the arguments
* size relative to the driver's limits on VARCHARs) when it sends
* it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setString(int parameterIndex, String x) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a Java array of bytes. The driver converts
* this to a SQL VARBINARY or LONGVARBINARY (depending on the
* argument's size relative to the driver's limits on VARBINARYs)
* when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setBytes(int parameterIndex, byte x[]) throws SQLException {
checkStatus();
try {
/* JDBC is one-based, DBMS is zero-based */
getParms().getParameterForSet(parameterIndex - 1).setValue(x);
} catch (Throwable t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Set a parameter to a java.sql.Date value. The driver converts this
* to a SQL DATE value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setDate(int parameterIndex, Date x) throws SQLException {
setDate( parameterIndex, x, (Calendar) null);
}
/**
* Set a parameter to a java.sql.Time value. The driver converts this
* to a SQL TIME value when it sends it to the database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setTime(int parameterIndex, Time x) throws SQLException {
setTime( parameterIndex, x, (Calendar) null);
}
/**
* Set a parameter to a java.sql.Timestamp value. The driver
* converts this to a SQL TIMESTAMP value when it sends it to the
* database.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the parameter value
* @exception SQLException thrown on failure.
*/
public void setTimestamp(int parameterIndex, Timestamp x)
throws SQLException {
setTimestamp( parameterIndex, x, (Calendar) null);
}
/**
* We do this inefficiently and read it all in here. The target type
* is assumed to be a String.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the ASCII parameter value
* @param length the number of bytes in the stream
* @exception SQLException thrown on failure.
*/
public final void setAsciiStream(int parameterIndex, InputStream x, long length)
throws SQLException {
checkAsciiStreamConditions(parameterIndex);
java.io.Reader r = null;
if (x != null)
{
// Use ISO-8859-1 and not US-ASCII as JDBC seems to define
// ASCII as 8 bits. US-ASCII is 7.
try {
r = new java.io.InputStreamReader(x, "ISO-8859-1");
} catch (java.io.UnsupportedEncodingException uee) {
throw new SQLException(uee.getMessage());
}
}
setCharacterStreamInternal(parameterIndex, r, false, length);
}
/**
* We do this inefficiently and read it all in here. The target type
* is assumed to be a String.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the ASCII parameter value
* @param length the number of bytes in the stream
* @exception SQLException thrown on failure.
*/
public final void setAsciiStream(int parameterIndex, InputStream x, int length)
throws SQLException {
setAsciiStream(parameterIndex,x,(long)length);
}
/**
Deprecated in JDBC 3.0
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the
* UNICODE parameter value
* @param length the number of bytes in the stream
* @exception SQLException thrown on failure.
* @deprecated
*/
public void setUnicodeStream(int parameterIndex, InputStream x, int length)
throws SQLException
{
throw Util.notImplemented("setUnicodeStream");
}
/**
* When a very large UNICODE value is input to a LONGVARCHAR
* parameter, it may be more practical to send it via a
* java.io.Reader. JDBC will read the data from the stream
* as needed, until it reaches end-of-file. The JDBC driver will
* do any necessary conversion from UNICODE to the database char format.
*
*
Note: This stream object can either be a standard
* Java stream object or your own subclass that implements the
* standard interface.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param reader the java reader which contains the UNICODE data
* @param length the number of characters in the stream
* @exception SQLException if a database-access error occurs.
*/
public final void setCharacterStream(int parameterIndex,
java.io.Reader reader,
long length) throws SQLException {
checkCharacterStreamConditions(parameterIndex);
setCharacterStreamInternal(parameterIndex, reader, false, length);
}
/**
* When a very large UNICODE value is input to a LONGVARCHAR
* parameter, it may be more practical to send it via a
* java.io.Reader. JDBC will read the data from the stream
* as needed, until it reaches end-of-file. The JDBC driver will
* do any necessary conversion from UNICODE to the database char format.
*
*
Note: This stream object can either be a standard
* Java stream object or your own subclass that implements the
* standard interface.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param reader the java reader which contains the UNICODE data
* @param length the number of characters in the stream
* @exception SQLException if a database-access error occurs.
*/
public final void setCharacterStream(int parameterIndex,
java.io.Reader reader,
int length) throws SQLException {
setCharacterStream(parameterIndex,reader,(long)length);
}
/**
* Check general preconditions for setCharacterStream methods.
*
* @param parameterIndex 1-based index of the parameter.
*/
private final void checkCharacterStreamConditions(int parameterIndex)
throws SQLException {
checkStatus();
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (!DataTypeDescriptor.isCharacterStreamAssignable(jdbcTypeId)) {
throw dataTypeConversion(parameterIndex, "java.io.Reader");
}
}
/**
* Check general preconditions for setAsciiStream methods.
*
* @param parameterIndex 1-based index of the parameter.
*/
private final void checkAsciiStreamConditions(int parameterIndex)
throws SQLException {
checkStatus();
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (!DataTypeDescriptor.isAsciiStreamAssignable(jdbcTypeId)) {
throw dataTypeConversion(parameterIndex,
"java.io.InputStream(ASCII)");
}
}
/**
* Set the given character stream for the specified parameter.
*
* If lengthLess is true, the following
* conditions are either not checked or verified at the execution time
* of the prepared statement:
*
- If the stream length is negative.
*
- If the stream's actual length equals the specified length.
* The lengthLess variable was added to differentiate between
* streams with invalid lengths and streams without known lengths.
*
* @param parameterIndex the 1-based index of the parameter to set.
* @param reader the data.
* @param lengthLess tells whether we know the length of the data or not.
* @param length the length of the data. Ignored if lengthLess
* is true.
*/
private void setCharacterStreamInternal(int parameterIndex,
Reader reader,
final boolean lengthLess,
long length)
throws SQLException
{
// Check for negative length if length is specified.
if (!lengthLess && length < 0)
throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (reader == null) {
setNull(parameterIndex, jdbcTypeId);
return;
}
/*
The value stored should not exceed the maximum value that can be
stored in an integer
This checking needs to be done because currently derby does not
support Clob sizes greater than 2G-1
*/
if (!lengthLess && length > Integer.MAX_VALUE)
throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
getParameterSQLType(parameterIndex));
try {
ReaderToUTF8Stream utfIn;
final StringDataValue dvd = (StringDataValue)
getParms().getParameter(parameterIndex -1);
dvd.setStreamHeaderFormat(usePreTenFiveHdrFormat());
// Need column width to figure out if truncation is needed
DataTypeDescriptor dtd[] = preparedStatement
.getParameterTypes();
int colWidth = dtd[parameterIndex - 1].getMaximumWidth();
// Holds either UNKNOWN_LOGICAL_LENGTH or the exact logical length.
int usableLength = DataValueDescriptor.UNKNOWN_LOGICAL_LENGTH;
if (!lengthLess) {
// We cast the length from long to int. This wouldn't be
// appropriate if the limit of 2G-1 is decided to be increased
// at a later stage.
usableLength = (int)length;
int truncationLength = 0;
// Currently long varchar does not allow for truncation of
// trailing blanks.
// For char and varchar types, current mechanism of
// materializing when using streams seems fine given their max
// limits.
// This change is fix for DERBY-352: Insert of clobs using
// streams should not materialize the entire stream into memory
// In case of clobs, the truncation of trailing blanks is
// factored in when reading from the stream without
// materializing the entire stream, and so the special casing
// for clob below.
if (jdbcTypeId == Types.CLOB)
{
// It is possible that the length of the stream passed in
// is greater than the column width, in which case the data
// from the stream needs to be truncated.
// usableLength is the length of the data from stream that
// can be inserted which is min(colWidth,length) provided
// length - colWidth has trailing blanks only
if (usableLength > colWidth) {
truncationLength = usableLength - colWidth;
usableLength = colWidth;
}
}
// Create a stream with truncation.
utfIn = new ReaderToUTF8Stream(reader, usableLength,
truncationLength, getParameterSQLType(parameterIndex),
dvd.getStreamHeaderGenerator());
} else {
// Create a stream without exactness checks,
// but with a maximum limit.
utfIn = new ReaderToUTF8Stream(reader, colWidth,
getParameterSQLType(parameterIndex),
dvd.getStreamHeaderGenerator());
}
// JDBC is one-based, DBMS is zero-based.
// Note that for lengthless stream, usableLength will be
// the maximum length for the column.
// This is okay, based on the observation that
// setValue does not use the value for anything at all.
getParms().getParameterForSet(parameterIndex - 1).
setValue(utfIn, usableLength);
} catch (StandardException t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Determines which header format to use for CLOBs when writing them to
* the store.
*
* @return {@code true} if the pre Derby 10.5 header format is to be used,
* {@code false} if the new header format can be used (10.5 or newer)
* @throws StandardException if obtaining the access mode fails
*/
private Boolean usePreTenFiveHdrFormat()
throws StandardException {
// Determine the version of the database we are accessing.
// This is required to write the correct stream header format for Clobs.
if (usePreTenFiveHdrFormat == null) {
usePreTenFiveHdrFormat = Boolean.valueOf(
!lcc.getDataDictionary().checkVersion(
DataDictionary.DD_VERSION_DERBY_10_5, null));
}
return usePreTenFiveHdrFormat;
}
/**
* Sets the designated parameter to the given input stream.
* When a very large binary value is input to a LONGVARBINARY
* parameter, it may be more practical to send it via a
* java.io.InputStream object. The data will be read from the
* stream as needed until end-of-file is reached.
*
* Note: This stream object can either be a standard Java stream
* object or your own subclass that implements the standard interface.
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the binary parameter value
* @throws SQLException if a database access error occurs or this method is
* called on a closed PreparedStatement
*/
public void setBinaryStream(int parameterIndex, InputStream x)
throws SQLException {
checkBinaryStreamConditions(parameterIndex);
setBinaryStreamInternal(parameterIndex, x, true, -1);
}
/**
* sets the parameter to the Binary stream
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the binary parameter value
* @param length the number of bytes in the stream
* @exception SQLException thrown on failure.
*/
public final void setBinaryStream(int parameterIndex, InputStream x, long length)
throws SQLException {
checkBinaryStreamConditions(parameterIndex);
setBinaryStreamInternal(parameterIndex, x, false, length);
}
/**
* sets the parameter to the binary stream
*
* @param parameterIndex the first parameter is 1, the second is 2, ...
* @param x the java input stream which contains the binary parameter value
* @param length the number of bytes in the stream
* @exception SQLException thrown on failure.
*/
public final void setBinaryStream(int parameterIndex, InputStream x, int length)
throws SQLException {
setBinaryStream(parameterIndex,x,(long)length);
}
/**
* Set the given stream for the specified parameter.
*
* If lengthLess is true, the following
* conditions are either not checked or verified at the execution time
* of the prepared statement:
* - If the stream length is negative.
*
- If the stream's actual length equals the specified length.
* The lengthLess variable was added to differentiate between
* streams with invalid lengths and streams without known lengths.
*
* @param parameterIndex the 1-based index of the parameter to set.
* @param x the data.
* @param lengthLess tells whether we know the length of the data or not.
* @param length the length of the data. Ignored if lengthLess
* is true.
*/
private void setBinaryStreamInternal(int parameterIndex, InputStream x,
final boolean lengthLess, long length)
throws SQLException
{
if ( !lengthLess && length < 0 )
throw newSQLException(SQLState.NEGATIVE_STREAM_LENGTH);
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (x == null) {
setNull(parameterIndex, jdbcTypeId);
return;
}
// max number of bytes that can be set to be inserted
// in Derby is 2Gb-1 (ie Integer.MAX_VALUE).
// (e.g into a blob column).
// For now, we cast the length from long to int as a result.
// If we ever decide to increase these limits for lets say blobs,
// in that case the cast to int would not be appropriate.
if ( !lengthLess && length > Integer.MAX_VALUE ) {
throw newSQLException(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE,
getParameterMetaData().getParameterTypeName(
parameterIndex));
}
try {
RawToBinaryFormatStream rawStream;
if (lengthLess) {
// Indicate that we don't know the logical length of the stream.
length = DataValueDescriptor.UNKNOWN_LOGICAL_LENGTH;
DataTypeDescriptor dtd[] =
preparedStatement.getParameterTypes();
rawStream = new RawToBinaryFormatStream(x,
dtd[parameterIndex -1].getMaximumWidth(),
dtd[parameterIndex -1].getTypeName());
} else {
rawStream = new RawToBinaryFormatStream(x, (int)length);
}
getParms().getParameterForSet(parameterIndex - 1).setValue(
rawStream, (int)length);
} catch (StandardException t) {
throw EmbedResultSet.noStateChangeException(t);
}
}
/**
* Check general preconditions for setBinaryStream methods.
*
* @param parameterIndex 1-based index of the parameter.
*/
private final void checkBinaryStreamConditions(int parameterIndex)
throws SQLException {
checkStatus();
int jdbcTypeId = getParameterJDBCType(parameterIndex);
if (!DataTypeDescriptor.isBinaryStreamAssignable(jdbcTypeId)) {
throw dataTypeConversion(parameterIndex, "java.io.InputStream");
}
}
/////////////////////////////////////////////////////////////////////////
//
// JDBC 2.0 - New public methods
//
/////////////////////////////////////////////////////////////////////////
/**
*
* JDBC 2.0
*
* Sets the designated parameter to SQL NULL.
* This version of the method setNull should
* be used for user-defined types and REF type parameters. Examples
* of user-defined types include: STRUCT, DISTINCT, JAVA_OBJECT, and
* named array types.
*
* @param paramIndex the first parameter is 1, the second is 2, ...
* @param sqlType a value from java.sql.Types
* @param typeName the fully-qualified name of an SQL user-defined type;
* ignored if the parameter is not a user-defined type or REF
* @exception SQLException if a database access error occurs or
* this method is called on a closed PreparedStatement
* @exception java.sql.SQLFeatureNotSupportedException if Derby does not
* support the type specified in the {@code sqlType} parameter
*/
public void setNull(int paramIndex,
int sqlType,
String typeName)
throws SQLException {
setNull(paramIndex,sqlType);
}
/**
* JDBC 2.0
*
* Add a set of parameters to the batch.
*
* @exception SQLException if a database-access error occurs.
*/
public void addBatch() throws SQLException {
checkStatus();
// need to synchronized to ensure that two threads
// don't both create a Vector at the same time. This
// would lead to one of the set of parameters being thrown
// away
synchronized (getConnectionSynchronization()) {
if (batchStatements == null)
batchStatements = new Vector
Ads help maintain this website.