/* Copyright (c) 2001-2008, 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.Reader; import java.io.Serializable; import java.math.BigDecimal; import java.sql.Date; //#ifdef JAVA6 /* import java.sql.NClob; import java.sql.RowId; import java.sql.SQLXML; */ //#endif JAVA6 import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLXML; import java.sql.Time; import java.sql.Timestamp; import java.util.Calendar; //#ifdef JAVA2 import java.sql.Array; import java.sql.Blob; import java.sql.Clob; import java.sql.Ref; //#endif JAVA2 //#ifdef JAVA4 import java.sql.ParameterMetaData; //#endif JAVA4 import org.hsqldb.Column; import org.hsqldb.HsqlDateTime; import org.hsqldb.HsqlException; import org.hsqldb.Result; import org.hsqldb.ResultConstants; import org.hsqldb.Trace; import org.hsqldb.Types; import org.hsqldb.lib.ArrayUtil; import org.hsqldb.lib.HsqlByteArrayOutputStream; import org.hsqldb.lib.Iterator; import org.hsqldb.lib.StringConverter; import org.hsqldb.types.Binary; import org.hsqldb.types.JavaObject; // fredt@users 20020320 - patch 1.7.0 - JDBC 2 support and error trapping // JDBC 2 methods can now be called from jdk 1.1.x - see javadoc comments // boucherb@users 20020509 - added "throws SQLException" to all methods where // it was missing here but specified in the java.sql.PreparedStatement and // java.sqlCallableStatement interfaces, updated generic documentation to // JDK 1.4, and added JDBC3 methods and docs // boucherb@users and fredt@users 20020409/20020505 extensive review and update // of docs and behaviour to comply with previous and latest java.sql specification // fredt@users 20030620 - patch 1.7.2 - rewritten to support real prepared statements // boucherb@users 20030801 - patch 1.7.2 - support for batch execution // boucherb@users 20030801 - patch 1.7.2 - support for getMetaData and getParameterMetadata // boucherb@users 20030801 - patch 1.7.2 - updated some setXXX methods // boucherb@users 200403/4xx - doc 1.7.2 - javadoc updates toward 1.7.2 final // boucherb@users 200403/4xx - patch 1.7.2 - eliminate eager buffer allocation from setXXXStream/Blob/Clob // fredt@users 20060215 - patch 1.8.0 - check for unset parameters /** * <!-- start generic documentation --> * * An object that represents a precompiled SQL statement. <p> * * An SQL statement is precompiled and stored in a * <code>PreparedStatement</code> object. This object can then be used to * efficiently execute this statement multiple times. * * <P><B>Note:</B> The setter methods (<code>setShort</code>, * <code>setString</code>, and so on) for setting IN parameter values * must specify types that are compatible with the defined SQL type of * the input parameter. For instance, if the IN parameter has SQL type * <code>INTEGER</code>, then the method <code>setInt</code> should be * used. <p> * * If arbitrary parameter type conversions are required, the method * <code>setObject</code> should be used with a target SQL type. * <P> * In the following example of setting a parameter, <code>con</code> * represents an active connection: * <PRE> * PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES * SET SALARY = ? WHERE ID = ?"); * pstmt.setBigDecimal(1, 153833.00) * pstmt.setInt(2, 110592) * </PRE> <p> * <!-- end generic documentation --> * <!-- start Release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Starting with HSQLDB 1.7.2, jdbcPreparedStatement objects are backed by * a true compiled parameteric representation. Hence, there are now significant * performance gains to be had by using a jdbcPreparedStatement object in * preference to a jdbcStatement object, if a short-running SQL statement is * to be executed more than a small number of times. <p> * * When it can be otherwise avoided, it is to be considered poor * practice to fully prepare (construct), parameterize, execute, fetch and * close a jdbcPreparedStatement object for each execution cycle. Indeed, under * HSQLDB 1.8.0, this practice is likely to be noticably <em>less</em> * performant for short-running statements than the equivalent process using * jdbcStatement objects, albeit far more convenient, less error prone and * certainly much less resource-intensive, especially when large binary and * character values are involved, due to the optimized parameterization * facility. <p> * * Instead, when developing an application that is not totally oriented toward * the execution of ad hoc SQL, it is recommended to expend some effort toward * identifing the SQL statements that are good candidates for regular reuse and * adapting the structure of the application accordingly. Often, this is done * by recording the text of candidate SQL statements in an application resource * object (which has the nice side-benefit of isolating and hiding differences * in SQL dialects across different drivers) and caching for possible reuse the * PreparedStatement objects derived from the recorded text. <p> * * <b>Multi thread use:</b> <p> * * A PreparedStatement object is stateful and should not normally be shared * by multiple threads. If it has to be shared, the calls to set the * parameters, calls to add batch statements, the execute call and any * post-execute calls should be made within a block synchronized on the * PreparedStatement Object.<p> * * <b>JRE 1.1.x Notes:</b> <p> * * In general, JDBC 2 support requires Java 1.2 and above, and JDBC3 requires * Java 1.4 and above. In HSQLDB, support for methods introduced in different * versions of JDBC depends on the JDK version used for compiling and building * HSQLDB.<p> * * Since 1.7.0, it is possible to build the product so that * all JDBC 2 methods can be called while executing under the version 1.1.x * <em>Java Runtime Environment</em><sup><font size="-2">TM</font></sup>. * However, in addition to requiring explicit casts to the org.hsqldb.jdbcXXX * interface implementations, some of these method calls require * <code>int</code> values that are defined only in the JDBC 2 or greater * version of * <a href="http://java.sun.com/j2se/1.4/docs/api/java/sql/ResultSet.html"> * <code>ResultSet</code></a> interface. For this reason, when the * product is compiled under JDK 1.1.x, these values are defined in * {@link jdbcResultSet jdbcResultSet}.<p> * * In a JRE 1.1.x environment, calling JDBC 2 methods that take or return the * JDBC2-only <code>ResultSet</code> values can be achieved by referring * to them in parameter specifications and return value comparisons, * respectively, as follows: <p> * * <pre class="JavaCodeExample"> * jdbcResultSet.FETCH_FORWARD * jdbcResultSet.TYPE_FORWARD_ONLY * jdbcResultSet.TYPE_SCROLL_INSENSITIVE * jdbcResultSet.CONCUR_READ_ONLY * // etc. * </pre> * * However, please note that code written in such a manner will not be * compatible for use with other JDBC 2 drivers, since they expect and use * <code>ResultSet</code>, rather than <code>jdbcResultSet</code>. Also * note, this feature is offered solely as a convenience to developers * who must work under JDK 1.1.x due to operating constraints, yet wish to * use some of the more advanced features available under the JDBC 2 * specification.<p> * * (fredt@users)<br> * (boucherb@users) * </div> * <!-- end Release-specific documentation --> * * @author boucherb@users * @author fredt@users * @version 1.8.0 * @see jdbcConnection#prepareStatement * @see jdbcResultSet */ public class jdbcPreparedStatement extends jdbcStatement implements PreparedStatement { /** The parameter values for the next non-batch execution. */ protected Object[] parameterValues; /** Flags for bound variables. */ protected boolean[] parameterSet; /** Flags for bound stream variables. */ protected boolean[] parameterStream; /** The SQL types of the parameters. */ protected int[] parameterTypes; /** The (IN, IN OUT, or OUT) modes of parameters */ protected int[] parameterModes; /** Lengths for streams. */ protected int[] streamLengths; /** Has a stream or CLOB / BLOB parameter value. */ protected boolean hasStreams; /** * Description of result set metadata. <p> */ protected Result rsmdDescriptor; /** Description of parameter metadata. */ protected Result pmdDescriptor; /** This object's one and one ResultSetMetaData object. */ protected jdbcResultSetMetaData rsmd; // NOTE: pmd is declared as Object to avoid yet another #ifdef. /** This object's one and only ParameterMetaData object. */ protected Object pmd; /** The SQL character sequence that this object represents. */ protected String sql; /** * The id with which this object's corresponding * {@link org.hsqldb.CompiledStatement CompiledStatement} * object is registered in the engine's * {@link org.hsqldb.CompiledStatementManager CompiledStatementManager} * object. */ protected int statementID; /** * Whether this statement generates only a single row update count in * response to execution. */ protected boolean isRowCount; // fredt@users 20020215 - patch 517028 by peterhudson@users - method defined // fredt@users 20020215 - patch 517028 by peterhudson@users - method defined // // changes by fredt // SimpleDateFormat objects moved out of methods to improve performance // this is safe because only one thread at a time should access a // PreparedStatement object until it has finished executing the statement // fredt@users 20020215 - patch 517028 by peterhudson@users - method defined // minor changes by fredt /** * <!-- start generic documentation --> * Sets escape processing on or off. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.0, the implementation follows the standard * behaviour by overriding the same method in jdbcStatement * class. <p> * * In other words, calling this method has no effect. * </div> * <!-- end release-specific documentation --> * * @param enable <code>true</code> to enable escape processing; * <code>false</code> to disable it * @exception SQLException if a database access error occurs */ public void setEscapeProcessing(boolean enable) throws SQLException { checkClosed(); } /** * <!-- start generic documentation --> * Executes the SQL statement in this <code>PreparedStatement</code> * object, which may be any kind of SQL statement. * Some prepared statements return multiple results; the * <code>execute</code> method handles these complex statements as well * as the simpler form of statements handled by the methods * <code>executeQuery</code>and <code>executeUpdate</code>. <p> * * The <code>execute</code> method returns a <code>boolean</code> to * indicate the form of the first result. You must call either the method * <code>getResultSet</code> or <code>getUpdateCount</code> * to retrieve the result; you must call <code>getMoreResults</code> to * move to any subsequent result(s). <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Including 1.8.0, prepared statements do not generate * multiple fetchable results. <p> * * In future versions, it will be possible that statements * generate multiple fetchable results under certain conditions. * </div> * * @return <code>true</code> if the first result is a <code>ResultSet</code> * object; <code>false</code> if the first result is an update * count or there is no result * @exception SQLException if a database access error occurs or an argument * is supplied to this method * @see jdbcStatement#execute * @see jdbcStatement#getResultSet * @see jdbcStatement#getUpdateCount * @see jdbcStatement#getMoreResults */ public boolean execute() throws SQLException { checkClosed(); connection.clearWarningsNoCheck(); resultIn = null; try { resultOut.setMaxRows(maxRows); resultOut.setParameterData(parameterValues); resultIn = connection.sessionProxy.execute(resultOut); } catch (HsqlException e) { throw Util.sqlException(e); } if (resultIn.isError()) { Util.throwError(resultIn); } return resultIn.isData(); } /** * <!-- start generic documentation --> * Executes the SQL query in this <code>PreparedStatement</code> object * and returns the <code>ResultSet</code> object generated by the query.<p> * <!-- end generic documentation --> * * @return a <code>ResultSet</code> object that contains the data produced * by the query; never <code>null</code> * @exception SQLException if a database access error occurs or the SQL * statement does not return a <code>ResultSet</code> object */ public ResultSet executeQuery() throws SQLException { checkClosed(); connection.clearWarningsNoCheck(); checkIsRowCount(false); checkParametersSet(); resultIn = null; try { resultOut.setMaxRows(maxRows); resultOut.setParameterData(parameterValues); resultIn = connection.sessionProxy.execute(resultOut); } catch (HsqlException e) { throw Util.sqlException(e); } if (resultIn.isError()) { Util.throwError(resultIn); } else if (!resultIn.isData()) { String msg = "Expected but did not recieve a result set"; throw Util.sqlException(Trace.UNEXPECTED_EXCEPTION, msg); } return new jdbcResultSet(this, resultIn, connection.connProperties, connection.isNetConn); } /** * <!-- start generic documentation --> * Executes the SQL statement in this <code>PreparedStatement</code> * object, which must be an SQL <code>INSERT</code>, * <code>UPDATE</code> or <code>DELETE</code> statement; or an SQL * statement that returns nothing, such as a DDL statement.<p> * <!-- end generic documentation --> * * @return either (1) the row count for <code>INSERT</code>, * <code>UPDATE</code>, or <code>DELETE</code> * statements or (2) 0 for SQL statements that * return nothing * @exception SQLException if a database access error occurs or the SQL * statement returns a <code>ResultSet</code> object */ public int executeUpdate() throws SQLException { checkClosed(); connection.clearWarningsNoCheck(); checkIsRowCount(true); checkParametersSet(); resultIn = null; try { resultOut.setParameterData(parameterValues); resultIn = connection.sessionProxy.execute(resultOut); } catch (HsqlException e) { throw Util.sqlException(e); } if (resultIn.isError()) { Util.throwError(resultIn); } else if (resultIn.mode != ResultConstants.UPDATECOUNT) { String msg = "Expected but did not recieve a row update count"; throw Util.sqlException(Trace.UNEXPECTED_EXCEPTION, msg); } return resultIn.getUpdateCount(); } /** * <!-- start generic documentation --> * Submits a batch of commands to the database for execution and * if all commands execute successfully, returns an array of update counts. * The <code>int</code> elements of the array that is returned are ordered * to correspond to the commands in the batch, which are ordered * according to the order in which they were added to the batch. * The elements in the array returned by the method <code>executeBatch</code> * may be one of the following: * <OL> * <LI>A number greater than or equal to zero -- indicates that the * command was processed successfully and is an update count giving the * number of rows in the database that were affected by the command's * execution * <LI>A value of <code>SUCCESS_NO_INFO</code> -- indicates that the command was * processed successfully but that the number of rows affected is * unknown * <P> * If one of the commands in a batch update fails to execute properly, * this method throws a <code>BatchUpdateException</code>, and a JDBC * driver may or may not continue to process the remaining commands in * the batch. However, the driver's behavior must be consistent with a * particular DBMS, either always continuing to process commands or never * continuing to process commands. If the driver continues processing * after a failure, the array returned by the method * <code>BatchUpdateException.getUpdateCounts</code> * will contain as many elements as there are commands in the batch, and * at least one of the elements will be the following: * <P> * <LI>A value of <code>EXECUTE_FAILED</code> -- indicates that the command failed * to execute successfully and occurs only if a driver continues to * process commands after a command fails * </OL> * <P> * A driver is not required to implement this method. * The possible implementations and return values have been modified in * the Java 2 SDK, Standard Edition, version 1.3 to * accommodate the option of continuing to proccess commands in a batch * update after a <code>BatchUpdateException</code> obejct has been thrown. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Starting with HSQLDB 1.7.2, this feature is supported. <p> * * HSQLDB stops execution of commands in a batch when one of the commands * results in an exception. The size of the returned array equals the * number of commands that were executed successfully.<p> * * When the product is built under the JAVA1 target, an exception * is never thrown and it is the responsibility of the client software to * check the size of the returned update count array to determine if any * batch items failed. To build and run under the JAVA2 target, JDK/JRE * 1.3 or higher must be used. * </div> * <!-- end release-specific documentation --> * * @return an array of update counts containing one element for each * command in the batch. The elements of the array are ordered according * to the order in which commands were added to the batch. * @exception SQLException if a database access error occurs or the * driver does not support batch statements. Throws * {@link java.sql.BatchUpdateException} * (a subclass of <code>java.sql.SQLException</code>) if one of the commands * sent to the database fails to execute properly or attempts to return a * result set. * @since JDK 1.3 (JDK 1.1.x developers: read the new overview * for jdbcStatement) */ public int[] executeBatch() throws SQLException { if (batchResultOut == null) { batchResultOut = new Result(ResultConstants.BATCHEXECUTE, parameterTypes, statementID); } return super.executeBatch(); } /** * <!-- start generic documentation --> * Sets the designated parameter to SQL <code>NULL</code>. <p> * * <B>Note:</B> You must specify the parameter's SQL type.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB ignores the sqlType argument. * </div> * <!-- end release-specific documentation --> * * @param paramIndex the first parameter is 1, the second is 2, ... * @param sqlType the SQL type code defined in <code>java.sql.Types</code> * @exception SQLException if a database access error occurs */ public void setNull(int paramIndex, int sqlType) throws SQLException { setParameter(paramIndex, null); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>boolean</code> * value. The driver converts this to an SQL <code>BIT</code> value * when it sends it to the database.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, HSQLDB uses the BOOLEAN type instead of BIT, as * per SQL 200n (SQL 3). * </div> * <!-- end release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setBoolean(int parameterIndex, boolean x) throws SQLException { Boolean b = x ? Boolean.TRUE : Boolean.FALSE; setParameter(parameterIndex, b); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>byte</code> value. * The driver converts this to an SQL <code>TINYINT</code> value when * it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setByte(int parameterIndex, byte x) throws SQLException { setIntParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>short</code> * value. The driver converts this to an SQL <code>SMALLINT</code> * value when it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setShort(int parameterIndex, short x) throws SQLException { setIntParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>int</code> value. * The driver converts this to an SQL <code>INTEGER</code> value when * it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setInt(int parameterIndex, int x) throws SQLException { setIntParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>long</code> value. * The driver converts this to an SQL <code>BIGINT</code> value when * it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setLong(int parameterIndex, long x) throws SQLException { setLongParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>float</code> value. * The driver converts this to an SQL <code>FLOAT</code> value when * it sends it to the database.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.1, HSQLDB handles Java positive/negative Infinity * and NaN <code>float</code> values consistent with the Java Language * Specification; these <em>special</em> values are now correctly stored * to and retrieved from the database. * </div> * <!-- start release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setFloat(int parameterIndex, float x) throws SQLException { setDouble(parameterIndex, (double) x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>double</code> value. * The driver converts this to an SQL <code>DOUBLE</code> value when it * sends it to the database.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.1, HSQLDB handles Java positive/negative Infinity * and NaN <code>double</code> values consistent with the Java Language * Specification; these <em>special</em> values are now correctly stored * to and retrieved from the database. * </div> * <!-- start release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setDouble(int parameterIndex, double x) throws SQLException { Double d = new Double(x); setParameter(parameterIndex, d); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given * <code>java.math.BigDecimal</code> value. * The driver converts this to an SQL <code>NUMERIC</code> value when * it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java <code>String</code> value. * The driver converts this * to an SQL <code>VARCHAR</code> or <code>LONGVARCHAR</code> value * (depending on the argument's * size relative to the driver's limits on <code>VARCHAR</code> values) * when it sends it to the database.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Including 1.7.2, HSQLDB stores all XXXCHAR values as java.lang.String * objects; there is no appreciable difference between * CHAR, VARCHAR and LONGVARCHAR. * </div> * <!-- start release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setString(int parameterIndex, String x) throws SQLException { setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given Java array of bytes. * The driver converts this to an SQL <code>VARBINARY</code> or * <code>LONGVARBINARY</code> (depending on the argument's size relative * to the driver's limits on <code>VARBINARY</code> values) when it * sends it to the database.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Including 1.7.2, HSQLDB stores all XXXBINARY values the same way; there * is no appreciable difference between BINARY, VARBINARY and * LONGVARBINARY. * </div> * <!-- start release-specific documentation --> * * @param paramIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setBytes(int paramIndex, byte[] x) throws SQLException { setParameter(paramIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given * <code>java.sql.Date</code> value. The driver converts this * to an SQL <code>DATE</code> value when it sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setDate(int parameterIndex, Date x) throws SQLException { setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>java.sql.Time</code> * value. The driver converts this to an SQL <code>TIME</code> value when it * sends it to the database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setTime(int parameterIndex, Time x) throws SQLException { setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given * <code>java.sql.Timestamp</code> value. The driver converts this to * an SQL <code>TIMESTAMP</code> value when it sends it to the * database.<p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @exception SQLException if a database access error occurs */ public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given input stream, which will have * the specified number of bytes. * When a very large ASCII value is input to a <code>LONGVARCHAR</code> * parameter, it may be more practical to send it via a * <code>java.io.InputStream</code>. Data will be read from the stream * as needed until end-of-file is reached. The JDBC driver will * do any necessary conversion from ASCII to the database char format. <p> * * <b>Note:</b> This stream object can either be a standard * Java stream object or your own subclass that implements the * standard interface.<p> * <!-- end generic documentation --> * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * This method uses the default platform character encoding to convert bytes * from the stream into the characters of a String. In the future this is * likely to change to always treat the stream as ASCII.<p> * * Before HSQLDB 1.7.0, <code>setAsciiStream</code> and * <code>setUnicodeStream</code> were identical. * </div> * <!-- end release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the Java input stream that contains the ASCII parameter value * @param length the number of bytes in the stream * @exception SQLException if a database access error occurs */ public void setAsciiStream(int parameterIndex, java.io.InputStream x, int length) throws SQLException { checkSetParameterIndex(parameterIndex, true); String s; if (x == null) { s = "input stream is null"; throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, s); } try { s = StringConverter.inputStreamToString(x, length); setParameter(parameterIndex, s); } catch (IOException e) { throw Util.sqlException(Trace.INVALID_CHARACTER_ENCODING); } } /** * <!-- start generic documentation --> * Sets the designated parameter to the given input stream, which * will have the specified number of bytes. A Unicode character has * two bytes, with the first byte being the high byte, and the second * being the low byte. * * When a very large Unicode value is input to a <code>LONGVARCHAR</code> * parameter, it may be more practical to send it via a * <code>java.io.InputStream</code> object. The data will be read from the * stream as needed until end-of-file is reached. The JDBC driver will * do any necessary conversion from Unicode to the database char format. * * <P><B>Note:</B> This stream object can either be a standard * Java stream object or your own subclass that implements the * standard interface.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.0, this method complies with behavior as defined by the * JDBC3 specification. * </div> * <!-- end release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x a <code>java.io.InputStream</code> object that contains the * Unicode parameter value as two-byte Unicode characters * @param length the number of bytes in the stream * @exception SQLException if a database access error occurs * @deprecated Sun does not include a reason, but presumably * this is because setCharacterStream is now prefered */ //#ifdef DEPRECATEDJDBC public void setUnicodeStream(int parameterIndex, java.io.InputStream x, int length) throws SQLException { checkSetParameterIndex(parameterIndex, true); String msg = null; if (x == null) { msg = "input stream is null"; } else if (length % 2 != 0) { msg = "odd length argument"; } if (msg != null) { throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, msg); } int chlen = length / 2; int chread = 0; StringBuffer sb = new StringBuffer(); int hi; int lo; try { for (; chread < chlen; chread++) { hi = x.read(); if (hi == -1) { break; } lo = x.read(); if (lo == -1) { break; } sb.append((char) (hi << 8 | lo)); } } catch (IOException e) { throw Util.sqlException(Trace.TRANSFER_CORRUPTED); } setParameter(parameterIndex, sb.toString()); } //#endif /** * <!-- start generic documentation --> * Sets the designated parameter to the given input stream, which will have * the specified number of bytes. * When a very large binary value is input to a <code>LONGVARBINARY</code> * parameter, it may be more practical to send it via a * <code>java.io.InputStream</code> object. The data will be read from the * stream as needed until end-of-file is reached. * * <P><B>Note:</B> This stream object can either be a standard * Java stream object or your own subclass that implements the * standard interface.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, this method works according to the standard. * </div> * <!-- end release-specific documentation --> * * @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 if a database access error occurs */ public void setBinaryStream(int parameterIndex, java.io.InputStream x, int length) throws SQLException { checkSetParameterIndex(parameterIndex, true); if (x == null) { throw Util.sqlException(Trace.error(Trace.INVALID_JDBC_ARGUMENT, Trace.JDBC_NULL_STREAM)); } HsqlByteArrayOutputStream out = null; try { out = new HsqlByteArrayOutputStream(); int size = 2048; byte[] buff = new byte[size]; for (int left = length; left > 0; ) { int read = x.read(buff, 0, left > size ? size : left); if (read == -1) { break; } out.write(buff, 0, read); left -= read; } setParameter(parameterIndex, out.toByteArray()); } catch (IOException e) { throw Util.sqlException(Trace.INPUTSTREAM_ERROR, e.toString()); } finally { if (out != null) { try { out.close(); } catch (IOException e) {} } } } /** * <!-- start generic documentation --> * Clears the current parameter values immediately. <p> * * In general, parameter values remain in force for repeated use of a * statement. Setting a parameter value automatically clears its * previous value. However, in some cases it is useful to immediately * release the resources used by the current parameter values; this can * be done by calling the method <code>clearParameters</code>.<p> * <!-- end generic documentation --> * * @exception SQLException if a database access error occurs */ public void clearParameters() throws SQLException { checkClosed(); ArrayUtil.fillArray(parameterValues, null); ArrayUtil.clearArray(ArrayUtil.CLASS_CODE_BOOLEAN, parameterSet, 0, parameterSet.length); if (parameterStream != null) { ArrayUtil.clearArray(ArrayUtil.CLASS_CODE_BOOLEAN, parameterStream, 0, parameterStream.length); } } //---------------------------------------------------------------------- // Advanced features: /** * <!-- start generic documentation --> * Sets the value of the designated parameter with the given object. <p> * * The second argument must be an object type; for integral values, the * <code>java.lang</code> equivalent objects should be used. <p> * * The given Java object will be converted to the given targetSqlType * before being sent to the database. * * If the object has a custom mapping (is of a class implementing the * interface <code>SQLData</code>), * the JDBC driver should call the method <code>SQLData.writeSQL</code> to * write it to the SQL data stream. * If, on the other hand, the object is of a class implementing * <code>Ref</code>, <code>Blob</code>, <code>Clob</code>, * <code>Struct</code>, or <code>Array</code>, the driver should pass it * to the database as a value of the corresponding SQL type. <p> * * Note that this method may be used to pass database-specific * abstract data types.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Inculding 1.7.1,this method was identical to * {@link #setObject(int, Object, int) setObject(int, Object, int)}. * That is, this method simply called setObject(int, Object, int), * ignoring the scale specification. <p> * * Since 1.7.2, this method supports the conversions listed in the * conversion table B-5 of the JDBC 3 specification. The scale argument * is not used. * </div> * <!-- start release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the object containing the input parameter value * @param targetSqlType the SQL type (as defined in java.sql.Types) to be * sent to the database. The scale argument may further qualify this type. * @param scale for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, * this is the number of digits after the decimal point. For all * other types, this value will be ignored. <p> * * Up to and including HSQLDB 1.7.0, this parameter is ignored. * @exception SQLException if a database access error occurs * @see java.sql.Types * @see #setObject(int,Object,int) */ public void setObject(int parameterIndex, Object x, int targetSqlType, int scale) throws SQLException { /** @todo fredt - implement SQLData support */ setObject(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the value of the designated parameter with the given object. * This method is like the method <code>setObject</code> * above, except that it assumes a scale of zero. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, this method supports conversions listed in the * conversion table B-5 of the JDBC 3 specification. * </div> * <!-- end release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the object containing the input parameter value * @param targetSqlType the SQL type (as defined in java.sql.Types) to be * sent to the database * @exception SQLException if a database access error occurs * @see #setObject(int,Object) */ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { setObject(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the value of the designated parameter using the given object. <p> * * The second parameter must be of type <code>Object</code>; therefore, * the <code>java.lang</code> equivalent objects should be used for * built-in types. <p> * * The JDBC specification specifies a standard mapping from * Java <code>Object</code> types to SQL types. The given argument * will be converted to the corresponding SQL type before being * sent to the database. <p> * * Note that this method may be used to pass datatabase- * specific abstract data types, by using a driver-specific Java * type. If the object is of a class implementing the interface * <code>SQLData</code>, the JDBC driver should call the method * <code>SQLData.writeSQL</code> to write it to the SQL data stream. * If, on the other hand, the object is of a class implementing * <code>Ref</code>, <code>Blob</code>, <code>Clob</code>, * <code>Struct</code>, or <code>Array</code>, the driver should pass * it to the database as a value of the corresponding SQL type. <p> * * This method throws an exception if there is an ambiguity, for * example, if the object is of a class implementing more than one * of the interfaces named above.<p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3><p> * * Since 1.7.2, this method supports conversions listed in the conversion * table B-5 of the JDBC 3 specification.<p> * * </div> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the object containing the input parameter value * @exception SQLException if a database access error occurs or the type * of the given object is ambiguous */ public void setObject(int parameterIndex, Object x) throws SQLException { setParameter(parameterIndex, x); } //--------------------------JDBC 2.0----------------------------- /** * <!-- start generic documentation --> * Adds a set of parameters to this <code>PreparedStatement</code> * object's batch of commands. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, this feature is supported. * </div> * <!-- end release-specific documentation --> * * @exception SQLException if a database access error occurs * @see jdbcStatement#addBatch * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // boucherb@users 20030801 - method implemented public void addBatch() throws SQLException { checkClosed(); int len = parameterValues.length; Object[] bpValues = new Object[len]; checkParametersSet(); System.arraycopy(parameterValues, 0, bpValues, 0, len); if (batchResultOut == null) { batchResultOut = new Result(ResultConstants.BATCHEXECUTE, parameterTypes, statementID); } batchResultOut.add(bpValues); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>Reader</code> * object, which is the given number of characters long. * When a very large UNICODE value is input to a <code>LONGVARCHAR</code> * parameter, it may be more practical to send it via a * <code>java.io.Reader</code> object. The data will be read from the * stream as needed until end-of-file is reached. The JDBC driver will * do any necessary conversion from UNICODE to the database char format. * * <P><B>Note:</B> This stream object can either be a standard * Java stream object or your own subclass that implements the * standard interface. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB stores CHARACTER and related SQL types as Unicode so * this method does not perform any conversion. * </div> * <!-- end release-specific documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param reader the <code>java.io.Reader</code> object that contains the * Unicode data * @param length the number of characters in the stream * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // fredt@users 20020429 - patch 1.7.0 - method defined // fredt@users 20020627 - patch 574234 by ohioedge@users // boucherb@users 20030801 - patch 1.7.2 - updated public void setCharacterStream(int parameterIndex, java.io.Reader reader, int length) throws SQLException { checkSetParameterIndex(parameterIndex, true); if (reader == null) { String msg = "reader is null"; throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, msg); } final StringBuffer sb = new StringBuffer(); final int size = 2048; final char[] buff = new char[size]; try { for (int left = length; left > 0; ) { final int read = reader.read(buff, 0, left > size ? size : left); if (read == -1) { break; } sb.append(buff, 0, read); left -= read; } } catch (IOException e) { throw Util.sqlException(Trace.TRANSFER_CORRUPTED, e.toString()); } setParameter(parameterIndex, sb.toString()); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given * <code>REF(<structured-type>)</code> value. * The driver converts this to an SQL <code>REF</code> value when it * sends it to the database. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB 1.7.2 does not support the SQL REF type. Calling this method * throws an exception. * * </div> * <!-- end release-specific documentation --> * @param i the first parameter is 1, the second is 2, ... * @param x an SQL <code>REF</code> value * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ //#ifdef JAVA2 public void setRef(int i, Ref x) throws SQLException { throw Util.notSupported(); } //#endif JAVA2 /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>Blob</code> object. * The driver converts this to an SQL <code>BLOB</code> value when it * sends it to the database. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Previous to 1.7.2, this feature was not supported. <p> * * Since 1.7.2, setBlob is supported. With 1.7.2, setting Blob objects is * limited to those of length less than or equal to Integer.MAX_VALUE. * In 1.7.2, setBlob(i,x) is roughly equivalent (null and length handling * not shown) to: * * <pre class="JavaCodeExample"> * <b>setBinaryStream</b>(i, x.<b>getBinaryStream</b>(), (<span class="JavaKeyWord">int</span>) x.<b>length</b>()); * </pre></div> * <!-- end release-specific documentation --> * * @param i the first parameter is 1, the second is 2, ... * @param x a <code>Blob</code> object that maps an SQL <code>BLOB</code> * value * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // boucherb@users 20030801 - method implemented //#ifdef JAVA2 public void setBlob(int i, Blob x) throws SQLException { if (x instanceof jdbcBlob) { setParameter(i, ((jdbcBlob) x).data); return; } else if (x == null) { setParameter(i, null); return; } checkSetParameterIndex(i, false); final long length = x.length(); if (length > Integer.MAX_VALUE) { String msg = "Maximum Blob input octet length exceeded: " + length; throw Util.sqlException(Trace.INPUTSTREAM_ERROR, msg); } HsqlByteArrayOutputStream out = null; try { out = new HsqlByteArrayOutputStream(); java.io.InputStream in = x.getBinaryStream(); int buffSize = 2048; byte[] buff = new byte[buffSize]; for (int left = (int) length; left > 0; ) { int read = in.read(buff, 0, left > buffSize ? buffSize : left); if (read == -1) { break; } out.write(buff, 0, read); left -= read; } setParameter(i, out.toByteArray()); } catch (IOException e) { throw Util.sqlException(Trace.INPUTSTREAM_ERROR, e.toString()); } finally { if (out != null) { try { out.close(); } catch (IOException e) {} } } } //#endif JAVA2 /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>Clob</code> object. * The driver converts this to an SQL <code>CLOB</code> value when it * sends it to the database. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Previous to 1.7.2, this feature was not supported. <p> * * Since 1.7.2, setClob is supported. With 1.7.2, setting Blob objects is * limited to those of length less than or equal to Integer.MAX_VALUE. * In 1.7.2, setClob(i,x) is rougly equivalent (null and length handling * not shown) to: <p> * * <pre class="JavaCodeExample"> * <b>setCharacterStream</b>(i, x.<b>getCharacterStream</b>(), (<span class="JavaKeyWord">int</span>) x.<b>length</b>()); * </pre></div> * <!-- end release-specific documentation --> * @param i the first parameter is 1, the second is 2, ... * @param x a <code>Clob</code> object that maps an SQL <code>CLOB</code> * value * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // boucherb@users 20030801 - method implemented //#ifdef JAVA2 public void setClob(int i, Clob x) throws SQLException { if (x instanceof jdbcClob) { setParameter(i, ((jdbcClob) x).data); return; } else if (x == null) { setParameter(i, null); return; } checkSetParameterIndex(i, false); final long length = x.length(); if (length > Integer.MAX_VALUE) { String msg = "Max Clob input character length exceeded: " + length; throw Util.sqlException(Trace.INPUTSTREAM_ERROR, msg); } java.io.Reader reader = x.getCharacterStream(); final StringBuffer sb = new StringBuffer(); final int size = 2048; final char[] buff = new char[size]; try { for (int left = (int) length; left > 0; ) { final int read = reader.read(buff, 0, left > size ? size : left); if (read == -1) { break; } sb.append(buff, 0, read); left -= read; } } catch (IOException e) { throw Util.sqlException(Trace.TRANSFER_CORRUPTED, e.toString()); } setParameter(i, sb.toString()); } //#endif JAVA2 /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>Array</code> object. * The driver converts this to an SQL <code>ARRAY</code> value when it * sends it to the database. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB 1.7.2 does not support the SQL ARRAY type. Calling this method * throws an exception. * * </div> * <!-- end release-specific documentation --> * @param i the first parameter is 1, the second is 2, ... * @param x an <code>Array</code> object that maps an SQL <code>ARRAY</code> * value * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ //#ifdef JAVA2 public void setArray(int i, Array x) throws SQLException { throw Util.notSupported(); } //#endif JAVA2 /** * <!-- start generic documentation --> * Retrieves a <code>ResultSetMetaData</code> object that contains * information about the columns of the <code>ResultSet</code> object * that will be returned when this <code>PreparedStatement</code> object * is executed. * <P> * Because a <code>PreparedStatement</code> object is precompiled, it is * possible to know about the <code>ResultSet</code> object that it will * return without having to execute it. Consequently, it is possible * to invoke the method <code>getMetaData</code> on a * <code>PreparedStatement</code> object rather than waiting to execute * it and then invoking the <code>ResultSet.getMetaData</code> method * on the <code>ResultSet</code> object that is returned. * <P> * <B>NOTE:</B> Using this method may be expensive for some drivers due * to the lack of underlying DBMS support. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, this feature is supported. If the statement * generates an update count, then null is returned. * * </div> * <!-- end release-specific documentation --> * @return the description of a <code>ResultSet</code> object's columns or * <code>null</code> if the driver cannot return a * <code>ResultSetMetaData</code> object * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // boucherb@users 20030801 - method implemented public ResultSetMetaData getMetaData() throws SQLException { checkClosed(); if (isRowCount) { return null; } if (rsmd == null) { rsmd = new jdbcResultSetMetaData(rsmdDescriptor, connection.connProperties); } return rsmd; } /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>java.sql.Date</code> * value, using the given <code>Calendar</code> object. The driver uses * the <code>Calendar</code> object to construct an SQL <code>DATE</code> * value,which the driver then sends to the database. With a * a <code>Calendar</code> object, the driver can calculate the date * taking into account a custom timezone. If no * <code>Calendar</code> object is specified, the driver uses the default * timezone, which is that of the virtual machine running the * application. <p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @param cal the <code>Calendar</code> object the driver will use * to construct the date * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // fredt@users 20020414 - patch 517028 by peterhudson@users - method defined // changes by fredt - moved conversion to HsqlDateTime public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { String s; try { s = HsqlDateTime.getDateString(x, cal); } catch (Exception e) { throw Util.sqlException(Trace.INVALID_ESCAPE, e.toString()); } setParameter(parameterIndex, s); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>java.sql.Time</code> * value, using the given <code>Calendar</code> object. The driver uses * the <code>Calendar</code> object to construct an SQL <code>TIME</code> * value, which the driver then sends to the database. With a * a <code>Calendar</code> object, the driver can calculate the time * taking into account a custom timezone. If no * <code>Calendar</code> object is specified, the driver uses the default * timezone, which is that of the virtual machine running the * application. <p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @param cal the <code>Calendar</code> object the driver will use * to construct the time * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // fredt@users 20020414 - patch 517028 by peterhudson@users - method defined // changes by fredt - moved conversion to HsqlDateTime public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { String s; try { s = HsqlDateTime.getTimeString(x, cal); } catch (Exception e) { throw Util.sqlException(Trace.INVALID_ESCAPE, e.toString()); } setParameter(parameterIndex, s); } /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>java.sql.Timestamp</code> * value, using the given <code>Calendar</code> object. The driver uses * the <code>Calendar</code> object to construct an SQL <code>TIMESTAMP</code> * value, which the driver then sends to the database. With a * <code>Calendar</code> object, the driver can calculate the timestamp * taking into account a custom timezone. If no * <code>Calendar</code> object is specified, the driver uses the default * timezone, which is that of the virtual machine running the application. <p> * <!-- end generic documentation --> * * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the parameter value * @param cal the <code>Calendar</code> object the driver will use * to construct the timestamp * @exception SQLException if a database access error occurs * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ // fredt@users 20020414 - patch 517028 by peterhudson@users - method defined // changes by fredt - moved conversion to HsqlDateTime public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { checkSetParameterIndex(parameterIndex, false); if (cal != null && x != null) { int ns = x.getNanos(); x = new Timestamp(HsqlDateTime.getTimeInMillis(x, cal, null)); x.setNanos(ns); } setParameter(parameterIndex, x); } /** * <!-- start generic documentation --> * Sets the designated parameter to SQL <code>NULL</code>. * This version of the method <code>setNull</code> 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. * * <P><B>Note:</B> To be portable, applications must give the * SQL type code and the fully-qualified SQL type name when specifying * a NULL user-defined or REF parameter. In the case of a user-defined * type the name is the type name of the parameter itself. For a REF * parameter, the name is the type name of the referenced type. If * a JDBC driver does not need the type code or type name information, * it may ignore it. * * Although it is intended for user-defined and Ref parameters, * this method may be used to set a null parameter of any JDBC type. * If the parameter does not have a user-defined or REF type, the given * typeName is ignored. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB ignores the sqlType and typeName arguments. * </div> * <!-- end release-specific documentation --> * * @param paramIndex the first parameter is 1, the second is 2, ... * @param sqlType a value from <code>java.sql.Types</code> * @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 * @since JDK 1.2 (JDK 1.1.x developers: read the new overview for * jdbcPreparedStatement) */ public void setNull(int paramIndex, int sqlType, String typeName) throws SQLException { setParameter(paramIndex, null); } //------------------------- JDBC 3.0 ----------------------------------- /** * <!-- start generic documentation --> * Sets the designated parameter to the given <code>java.net.URL</code> * value. The driver converts this to an SQL <code>DATALINK</code> value * when it sends it to the database. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * HSQLDB 1.7.2 does not support the DATALINK SQL type for which this * method is intended. Calling this method throws an exception. * * </div> * <!-- end release-specific documentation --> * @param parameterIndex the first parameter is 1, the second is 2, ... * @param x the <code>java.net.URL</code> object to be set * @exception SQLException if a database access error occurs * @since JDK 1.4, HSQL 1.7.0 */ //#ifdef JAVA4 public void setURL(int parameterIndex, java.net.URL x) throws SQLException { throw Util.notSupported(); } //#endif JAVA4 /** * <!-- start generic documentation --> * Retrieves the number, types and properties of this * <code>PreparedStatement</code> object's parameters. <p> * <!-- end generic documentation --> * * <!-- start release-specific documentation --> * <div class="ReleaseSpecificDocumentation"> * <h3>HSQLDB-Specific Information:</h3> <p> * * Since 1.7.2, this feature is supported. * </div> * <!-- end release-specific documentation --> * * @return a <code>ParameterMetaData</code> object that contains information * about the number, types and properties of this * <code>PreparedStatement</code> object's parameters * @exception SQLException if a database access error occurs * @see java.sql.ParameterMetaData * @since JDK 1.4, HSQL 1.7.0 */ // boucherb@users 20030801 - method implemented //#ifdef JAVA4 public ParameterMetaData getParameterMetaData() throws SQLException { checkClosed(); if (pmd == null) { pmd = new jdbcParameterMetaData(pmdDescriptor); } // NOTE: pmd is declared as Object to avoid yet another #ifdef. return (ParameterMetaData) pmd; } //#endif JAVA4 //-------------------- Internal Implementation ----------------------------- /** * Constructs a statement that produces results of the requested * <code>type</code>. <p> * * A prepared statement must be a single SQL statement. <p> * * @param c the Connection used execute this statement * @param sql the SQL statement this object represents * @param type the type of result this statement will produce * @throws HsqlException if the statement is not accepted by the database * @throws SQLException if preprocessing by driver fails */ jdbcPreparedStatement(jdbcConnection c, String sql, int type) throws HsqlException, SQLException { super(c, type); sql = c.nativeSQL(sql); resultOut.setResultType(ResultConstants.SQLPREPARE); resultOut.setMainString(sql); Result in = connection.sessionProxy.execute(resultOut); if (in.isError()) { Util.throwError(in); } // else it's a MULTI result encapsulating three sub results: // 1.) a PREPARE_ACK // // Indicates the statement id to be communicated in SQLEXECUTE // requests to allow the engine to find the corresponding // CompiledStatement object, parameterize and execute it. // // 2.) a description of the statement's result set metadata // // This is communicated in the same way as for result sets. That is, // the metadata arrays of Result, such as colTypes, are used in the // "conventional" fashion. With some work, it may be possible // to speed up internal execution of prepared statements by // dispensing with generating most rsmd values while generating // the result, safe in the knowlege that the client already // has a copy of the rsmd. In general, only the colTypes array // must be available at the engine, and only for network // communications so that the row output and row input // interfaces can do their work. One caveat is that the // columnDisplaySize values are not accurate, as we do // not consistently enforce column size yet and instead // approximate the value when a result with actual data is // retrieved // // 3.) a description of the statement's parameter metadata // // This is communicated in a similar fashion to 2.), but has // a slighly different layout to allow the parameter modes // to be transmitted. The values of this object are used // to set up the parameter management of this class. The // object is also used to construct the jdbcParameterMetaData // object later, if requested. That is, it holds information // additional to that used by this class, so it should not be // altered or disposed of // // (boucherb@users) Iterator i; i = in.iterator(); try { Object[] row; // PREPARE_ACK row = (Object[]) i.next(); statementID = ((Result) row[0]).getStatementID(); // DATA - isParameterDescription == false row = (Object[]) i.next(); rsmdDescriptor = (Result) row[0]; isRowCount = rsmdDescriptor.mode == ResultConstants.UPDATECOUNT; // DATA - isParameterDescription == true row = (Object[]) i.next(); pmdDescriptor = (Result) row[0]; parameterTypes = pmdDescriptor.metaData.getParameterTypes(); parameterValues = new Object[parameterTypes.length]; parameterSet = new boolean[parameterTypes.length]; parameterModes = pmdDescriptor.metaData.paramMode; } catch (Exception e) { throw Trace.error(Trace.GENERAL_ERROR, e.toString()); } resultOut = new Result(ResultConstants.SQLEXECUTE, parameterTypes, statementID); // for toString() this.sql = sql; } /** * Checks if execution does or does not generate a single row * update count, throwing if the argument, yes, does not match. <p> * * @param yes if true, check that execution generates a single * row update count, else check that execution generates * something other than a single row update count. * @throws SQLException if the argument, yes, does not match */ protected void checkIsRowCount(boolean yes) throws SQLException { if (yes != isRowCount) { int msg = yes ? Trace.JDBC_STATEMENT_NOT_ROW_COUNT : Trace.JDBC_STATEMENT_NOT_RESULTSET; throw Util.sqlException(msg); } } /** * Checks if the specified parameter index value is valid in terms of * setting an IN or IN OUT parameter value. <p> * * @param i The parameter index to check * @throws SQLException if the specified parameter index is invalid */ protected void checkSetParameterIndex(int i, boolean isStream) throws SQLException { int mode; String msg; checkClosed(); if (i < 1 || i > parameterValues.length) { msg = "parameter index out of range: " + i; throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, msg); } if (isStream) { if (parameterStream == null) { parameterStream = new boolean[parameterTypes.length]; } parameterStream[i - 1] = true; parameterSet[i - 1] = false; } else { parameterSet[i - 1] = true; } /* mode = parameterModes[i - 1]; switch (mode) { default : msg = "Not IN or IN OUT mode: " + mode + " for parameter: " + i; throw Util.sqlException(Trace.INVALID_JDBC_ARGUMENT, msg); case Expression.PARAM_IN : case Expression.PARAM_IN_OUT : break; } */ } /** * Called just before execution or adding to batch, this ensures all the * parameters have been set.<p> * * If a parameter has been set using a stream method, it should be set * again for the next reuse. When set using other methods, the parameter * setting is retained for the next use. */ private void checkParametersSet() throws SQLException { ; } /* // do not check for compatibility with 1.8.0.x private void checkParametersSet() throws SQLException { if (parameterStream == null) { for (int i = 0; i < parameterSet.length; i++) { if (!parameterSet[i]) { throw Util.sqlException(Trace.JDBC_PARAMETER_NOT_SET); } } } else { for (int i = 0; i < parameterSet.length; i++) { if (!parameterSet[i] &&!parameterStream[i]) { throw Util.sqlException(Trace.JDBC_PARAMETER_NOT_SET); } } ArrayUtil.clearArray(ArrayUtil.CLASS_CODE_BOOLEAN, parameterStream, 0, parameterStream.length); } } */ /** * The internal parameter value setter always converts the parameter to * the Java type required for data transmission. Target BINARY and OTHER * types are converted directly. All other target types are converted * by Column.convertObject(). This also normalizes DATETIME values. * * @param i parameter index * @param o object * @throws SQLException if either argument is not acceptable. */ private void setParameter(int i, Object o) throws SQLException { checkSetParameterIndex(i, false); i--; if (o == null) { parameterValues[i] = null; return; } int outType = parameterTypes[i]; try { switch (outType) { case Types.OTHER : o = new JavaObject((Serializable) o); break; case Types.BINARY : case Types.VARBINARY : case Types.LONGVARBINARY : if (!(o instanceof byte[])) { throw Util.sqlException( Trace.error(Trace.INVALID_CONVERSION)); } o = new Binary((byte[]) o, !connection.isNetConn); break; case Types.DATE : if (o instanceof java.util.Date) { long t = HsqlDateTime.getNormalisedDate( ((java.util.Date) o).getTime()); o = new Date(t); } else { o = Column.convertObject(o, outType); } break; case Types.TIME : if (o instanceof java.util.Date) { long m = HsqlDateTime.getNormalisedTime( ((java.util.Date) o).getTime()); o = new Time(m); } else { o = Column.convertObject(o, outType); } break; case Types.TIMESTAMP : if (o instanceof Timestamp) { long m = ((Timestamp) o).getTime(); int n = ((Timestamp) o).getNanos(); o = new Timestamp(m); ((Timestamp) o).setNanos(n); } else { o = Column.convertObject(o, outType); } break; default : o = Column.convertObject(o, outType); break; } } catch (HsqlException e) { Util.throwError(e); } parameterValues[i] = o; } /** * Used with int and narrower integral primitives * @param i parameter index * @param value object to set * @throws SQLException if either argument is not acceptable */ private void setIntParameter(int i, int value) throws SQLException { checkSetParameterIndex(i, false); int outType = parameterTypes[i - 1]; switch (outType) { case Types.TINYINT : case Types.SMALLINT : case Types.INTEGER : Object o = new Integer(value); parameterValues[i - 1] = o; break; default : setLongParameter(i, value); } } /** * Used with long and narrower integral primitives. Conversion to BINARY * or OTHER types will throw here and not passed to setParameter(). * * @param i parameter index * @param value object to set * @throws SQLException if either argument is not acceptable */ private void setLongParameter(int i, long value) throws SQLException { checkSetParameterIndex(i, false); int outType = parameterTypes[i - 1]; switch (outType) { case Types.BIGINT : Object o = new Long(value); parameterValues[i - 1] = o; break; case Types.BINARY : case Types.OTHER : throw Util.sqlException(Trace.error(Trace.INVALID_CONVERSION)); default : setParameter(i, new Long(value)); } } /** * This method should always throw if called for a PreparedStatement or * CallableStatment. * * @param sql ignored * @throws SQLException always */ public void addBatch(String sql) throws SQLException { throw Util.notSupported(); } /** * This method should always throw if called for a PreparedStatement or * CallableStatment. * * @param sql ignored * @throws SQLException always * @return nothing */ public ResultSet executeQuery(String sql) throws SQLException { throw Util.notSupported(); } /** * This method should always throw if called for a PreparedStatement or * CallableStatment. * * @param sql ignored * @throws SQLException always * @return nothing */ public boolean execute(String sql) throws SQLException { throw Util.notSupported(); } /** * This method should always throw if called for a PreparedStatement or * CallableStatment. * * @param sql ignored * @throws SQLException always * @return nothing */ public int executeUpdate(String sql) throws SQLException { throw Util.notSupported(); } /** * Does the specialized work required to free this object's resources and * that of it's parent class. <p> * * @throws SQLException if a database access error occurs */ public synchronized void close() throws SQLException { if (isClosed()) { return; } HsqlException he = null; try { // fredt - if this is called by Connection.close() then there's no // need to free the prepared statements on the server - it is done // by Connection.close() if (!connection.isClosed) { connection.sessionProxy.execute( Result.newFreeStmtRequest(statementID)); } } catch (HsqlException e) { he = e; } parameterValues = null; parameterSet = null; parameterStream = null; parameterTypes = null; parameterModes = null; rsmdDescriptor = null; pmdDescriptor = null; rsmd = null; pmd = null; super.close(); if (he != null) { throw Util.sqlException(he); } } /** * Retrieves a String representation of this object. <p> * * The representation is of the form: <p> * * class-name@hash[sql=[char-sequence], parameters=[p1, ...pi, ...pn]] <p> * * p1, ...pi, ...pn are the String representations of the currently set * parameter values that will be used with the non-batch execution * methods. <p> * * @return a String representation of this object */ public String toString() { StringBuffer sb = new StringBuffer(); String sql; Object[] pv; sb.append(super.toString()); sql = this.sql; pv = parameterValues; if (sql == null || pv == null) { sb.append("[closed]"); return sb.toString(); } sb.append("[sql=[").append(sql).append("]"); if (pv.length > 0) { sb.append(", parameters=["); for (int i = 0; i < pv.length; i++) { sb.append('['); sb.append(pv[i]); sb.append("], "); } sb.setLength(sb.length() - 2); sb.append(']'); } sb.append(']'); return sb.toString(); } @Override public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { // TODO Auto-generated method stub } @Override public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { // TODO Auto-generated method stub } @Override public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { // TODO Auto-generated method stub } @Override public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { // TODO Auto-generated method stub } @Override public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setClob(int parameterIndex, Reader reader) throws SQLException { // TODO Auto-generated method stub } @Override public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { // TODO Auto-generated method stub } @Override public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setNClob(int parameterIndex, NClob value) throws SQLException { // TODO Auto-generated method stub } @Override public void setNClob(int parameterIndex, Reader reader) throws SQLException { // TODO Auto-generated method stub } @Override public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { // TODO Auto-generated method stub } @Override public void setNString(int parameterIndex, String value) throws SQLException { // TODO Auto-generated method stub } @Override public void setRowId(int parameterIndex, RowId x) throws SQLException { // TODO Auto-generated method stub } @Override public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { // TODO Auto-generated method stub } @Override public boolean isPoolable() throws SQLException { // TODO Auto-generated method stub return false; } @Override public void setPoolable(boolean poolable) throws SQLException { // TODO Auto-generated method stub } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { // TODO Auto-generated method stub return false; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { // TODO Auto-generated method stub return null; } //#ifdef JAVA6 /* public void setPoolable(boolean poolable) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public boolean isPoolable() throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public <T> T unwrap(Class<T> iface) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public boolean isWrapperFor(Class<?> iface) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setRowId(int parameterIndex, RowId x) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNString(int parameterIndex, String value) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNClob(int parameterIndex, NClob value) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setClob(int parameterIndex, Reader reader) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } public void setNClob(int parameterIndex, Reader reader) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } */ //#endif JAVA6 }