content: package com.alibaba.wasp.jdbc;
  
  import com.alibaba.wasp.DataType;
  import com.alibaba.wasp.SQLErrorCode;
  import com.alibaba.wasp.jdbc.command.CommandInterface;
  import com.alibaba.wasp.jdbc.expression.ParameterInterface;
  import com.alibaba.wasp.jdbc.result.JdbcResultSet;
  import com.alibaba.wasp.jdbc.result.JdbcResultSetMetaData;
  import com.alibaba.wasp.jdbc.result.ResultInterface;
  import com.alibaba.wasp.jdbc.value.Value;
  import com.alibaba.wasp.jdbc.value.ValueBoolean;
  import com.alibaba.wasp.jdbc.value.ValueByte;
  import com.alibaba.wasp.jdbc.value.ValueBytes;
  import com.alibaba.wasp.jdbc.value.ValueDate;
  import com.alibaba.wasp.jdbc.value.ValueDecimal;
  import com.alibaba.wasp.jdbc.value.ValueDouble;
  import com.alibaba.wasp.jdbc.value.ValueFloat;
  import com.alibaba.wasp.jdbc.value.ValueInt;
  import com.alibaba.wasp.jdbc.value.ValueLong;
  import com.alibaba.wasp.jdbc.value.ValueNull;
  import com.alibaba.wasp.jdbc.value.ValueShort;
  import com.alibaba.wasp.jdbc.value.ValueString;
  import com.alibaba.wasp.jdbc.value.ValueTime;
  import com.alibaba.wasp.jdbc.value.ValueTimestamp;
  import com.alibaba.wasp.session.ExecuteSession;
  import com.alibaba.wasp.session.SessionFactory;
  import com.alibaba.wasp.util.DateTimeUtils;
  import com.alibaba.wasp.util.New;
  import org.apache.commons.logging.Log;
  import org.apache.commons.logging.LogFactory;
  
  import java.io.InputStream;
  import java.io.Reader;
  import java.math.BigDecimal;
  import java.net.URL;
  import java.sql.Array;
  import java.sql.Blob;
  import java.sql.Clob;
  import java.sql.NClob;
  import java.sql.ParameterMetaData;
  import java.sql.PreparedStatement;
  import java.sql.Ref;
  import java.sql.ResultSet;
  import java.sql.ResultSetMetaData;
  import java.sql.RowId;
  import java.sql.SQLException;
  import java.sql.SQLXML;
  import java.sql.Statement;
  import java.util.ArrayList;
  import java.util.Calendar;
  
  /**
   * Represents a prepared statement.
   */
  public class JdbcPreparedStatement extends JdbcStatement implements
      PreparedStatement {
  
    private Log log = LogFactory.getLog(JdbcPreparedStatement.class);
  
    protected CommandInterface command;
    private final String sqlStatement;
    private ArrayList<Value[]> batchParameters;
    protected ExecuteSession session;
  
    JdbcPreparedStatement(JdbcConnection conn, String sql, int resultSetType,
        int resultSetConcurrency, boolean closeWithResultSet) {
      super(conn, resultSetType, resultSetConcurrency, closeWithResultSet);
      this.sqlStatement = sql;
      this.session = SessionFactory.createExecuteSession();
      command = conn.prepareCommand(sql, fetchSize, session);
    }
  
    /**
     * Executes a query (select statement) and returns the result set. If another
     * result set exists for this statement, this will be closed (even if this
     * statement fails).
     * 
     * @return the result set
     * @throws java.sql.SQLException
     *           if this object is closed or invalid
     */
    @Override
    public ResultSet executeQuery() throws SQLException {
      try {
        synchronized (session) {
          checkClosed();
          closeOldResultSet();
          ResultInterface result;
          try {
            setExecutingStatement(command);
            result = command.executeQuery(maxRows);
          } finally {
            setExecutingStatement(null);
          }
          resultSet = new JdbcResultSet(conn, this, result, closedByResultSet);
        }
        return resultSet;
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Executes a statement (insert, update, delete, create, drop) and returns the
     * update count. If another result set exists for this statement, this will be
     * closed (even if this statement fails).
     * 
     * If auto commit is on, this statement will be committed. If the statement is
     * a DDL statement (create, drop, alter) and does not throw an exception, the
     * current transaction (if any) is committed after executing the statement.
     * 
     * @return the update count (number of row affected by an insert, update or
     *         delete, or 0 if no rows or the statement was a create, drop, commit
     *         or rollback)
     * @throws java.sql.SQLException
     *           if this object is closed or invalid
     */
    @Override
    public int executeUpdate() throws SQLException {
      try {
        checkClosedForWrite();
        try {
          return executeUpdateInternal();
        } finally {
          afterWriting();
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    private int executeUpdateInternal() throws SQLException {
      closeOldResultSet();
      synchronized (session) {
        try {
          setExecutingStatement(command);
          updateCount = command.executeUpdate();
        } finally {
          setExecutingStatement(null);
        }
      }
      return updateCount;
    }
  
    /**
     * Executes an arbitrary statement. If another result set exists for this
     * statement, this will be closed (even if this statement fails). If auto
     * commit is on, and the statement is not a select, this statement will be
     * committed.
     * 
     * @return true if a result set is available, false if not
     * @throws java.sql.SQLException
     *           if this object is closed or invalid
     */
    @Override
    public boolean execute() throws SQLException {
      try {
        checkClosedForWrite();
        try {
          boolean returnsResultSet;
          synchronized (conn.getSession()) {
            closeOldResultSet();
            try {
              setExecutingStatement(command);
              if (command.isQuery()) {
                returnsResultSet = true;
                ResultInterface result = command.executeQuery(maxRows);
                resultSet = new JdbcResultSet(conn, this, result,
                    closedByResultSet);
              } else {
                returnsResultSet = false;
                updateCount = command.executeUpdate();
              }
            } finally {
              setExecutingStatement(null);
            }
          }
          return returnsResultSet;
        } finally {
          afterWriting();
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Clears all parameters.
     * 
     * @throws java.sql.SQLException
     *           if this object is closed or invalid
     */
    @Override
    public void clearParameters() throws SQLException {
      try {
        checkClosed();
        ArrayList<? extends ParameterInterface> parameters = command
            .getParameters();
        for (int i = 0, size = parameters.size(); i < size; i++) {
          ParameterInterface param = parameters.get(i);
          // can only delete old temp files if they are not in the batch
          param.setValue(null, batchParameters == null);
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public void addBatch(String sql) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public int executeUpdate(String sql) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public boolean execute(String sql) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    // =============================================================
  
    /**
     * Sets a parameter to null.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param sqlType
     *          the data type (Types.x)
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNull(int parameterIndex, int sqlType) throws SQLException {
      try {
        setParameter(parameterIndex, ValueNull.INSTANCE);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setInt(int parameterIndex, int x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueInt.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setString(int parameterIndex, String x) throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueString.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBigDecimal(int parameterIndex, BigDecimal x)
        throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueDecimal.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setDate(int parameterIndex, java.sql.Date x) throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueDate.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setTime(int parameterIndex, java.sql.Time x) throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueTime.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
        throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueTimestamp.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter. Objects of unknown classes are serialized
     * (on the client side).
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setObject(int parameterIndex, Object x) throws SQLException {
      try {
        if (x == null) {
          // throw Errors.getInvalidValueException("null", "x");
          setParameter(parameterIndex, ValueNull.INSTANCE);
        } else {
          setParameter(parameterIndex,
              DataType.convertToValue(session, x, Value.UNKNOWN));
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter. The object is converted, if required, to the
     * specified data type before sending to the database. Objects of unknown
     * classes are serialized (on the client side).
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value, null is allowed
     * @param targetSqlType
     *          the type as defined in java.sql.Types
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType)
        throws SQLException {
      try {
        int type = DataType.convertSQLTypeToValueType(targetSqlType);
        if (x == null) {
          setParameter(parameterIndex, ValueNull.INSTANCE);
        } else {
          Value v = DataType.convertToValue(conn.getSession(), x, type);
          setParameter(parameterIndex, v.convertTo(type));
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter. The object is converted, if required, to the
     * specified data type before sending to the database. Objects of unknown
     * classes are serialized (on the client side).
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value, null is allowed
     * @param targetSqlType
     *          the type as defined in java.sql.Types
     * @param scale
     *          is ignored
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType,
        int scale) throws SQLException {
      try {
        setObject(parameterIndex, x, targetSqlType);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBoolean(int parameterIndex, boolean x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueBoolean.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setByte(int parameterIndex, byte x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueByte.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setShort(int parameterIndex, short x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueShort.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setLong(int parameterIndex, long x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueLong.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setFloat(int parameterIndex, float x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueFloat.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setDouble(int parameterIndex, double x) throws SQLException {
      try {
        setParameter(parameterIndex, ValueDouble.get(x));
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * [Not supported] Sets the value of a column as a reference.
     */
    @Override
    public void setRef(int parameterIndex, Ref x) throws SQLException {
      throw JdbcException.getUnsupportedException("ref");
    }
  
    /**
     * Sets the date using a specified time zone. The value will be converted to
     * the local time zone.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param calendar
     *          the calendar
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setDate(int parameterIndex, java.sql.Date x, Calendar calendar)
        throws SQLException {
      try {
        if (x == null) {
          setParameter(parameterIndex, ValueNull.INSTANCE);
        } else {
          setParameter(parameterIndex, DateTimeUtils.convertDate(x, calendar));
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the time using a specified time zone. The value will be converted to
     * the local time zone.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param calendar
     *          the calendar
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setTime(int parameterIndex, java.sql.Time x, Calendar calendar)
        throws SQLException {
      try {
        if (x == null) {
          setParameter(parameterIndex, ValueNull.INSTANCE);
        } else {
          setParameter(parameterIndex, DateTimeUtils.convertTime(x, calendar));
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the timestamp using a specified time zone. The value will be converted
     * to the local time zone.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param calendar
     *          the calendar
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
        Calendar calendar) throws SQLException {
      try {
        if (x == null) {
          setParameter(parameterIndex, ValueNull.INSTANCE);
        } else {
          setParameter(parameterIndex,
              DateTimeUtils.convertTimestamp(x, calendar));
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * [Not supported] This feature is deprecated and not supported.
     * 
     * @deprecated since JDBC 2.0, use setCharacterStream
     */
    @Override
    public void setUnicodeStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("unicodeStream");
    }
  
    /**
     * Sets a parameter to null.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param sqlType
     *          the data type (Types.x)
     * @param typeName
     *          this parameter is ignored
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNull(int parameterIndex, int sqlType, String typeName)
        throws SQLException {
      try {
        setNull(parameterIndex, sqlType);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter as a Blob.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBlob(int parameterIndex, Blob x) throws SQLException {
      throw JdbcException.getUnsupportedException("setBlob");
    }
  
    /**
     * Sets the value of a parameter as a Blob. This method does not close the
     * stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBlob(int parameterIndex, InputStream x) throws SQLException {
      throw JdbcException.getUnsupportedException("setBlob");
    }
  
    /**
     * Sets the value of a parameter as a Clob.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setClob(int parameterIndex, Clob x) throws SQLException {
      throw JdbcException.getUnsupportedException("setClob");
    }
  
    /**
     * Sets the value of a parameter as a Clob. This method does not close the
     * reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setClob(int parameterIndex, Reader x) throws SQLException {
      throw JdbcException.getUnsupportedException("setClob");
    }
  
    /**
     * [Not supported] Sets the value of a parameter as a Array.
     */
    @Override
    public void setArray(int parameterIndex, Array x) throws SQLException {
      throw JdbcException.getUnsupportedException("setArray");
    }
  
    /**
     * Sets the value of a parameter as a byte array.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBytes(int parameterIndex, byte[] x) throws SQLException {
      try {
        Value v = x == null ? (Value) ValueNull.INSTANCE : ValueBytes.get(x);
        setParameter(parameterIndex, v);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Sets the value of a parameter as an input stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of bytes
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBinaryStream(int parameterIndex, InputStream x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setBinaryStream");
    }
  
    /**
     * Sets the value of a parameter as an input stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of bytes
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBinaryStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
      setBinaryStream(parameterIndex, x, (long) length);
    }
  
    /**
     * Sets the value of a parameter as an input stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBinaryStream(int parameterIndex, InputStream x)
        throws SQLException {
      setBinaryStream(parameterIndex, x, -1);
    }
  
    /**
     * Sets the value of a parameter as an ASCII stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of bytes
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, int length)
        throws SQLException {
      setAsciiStream(parameterIndex, x, (long) length);
    }
  
    /**
     * Sets the value of a parameter as an ASCII stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of bytes
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setAsciiStream");
    }
  
    /**
     * Sets the value of a parameter as an ASCII stream. This method does not
     * close the stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setAsciiStream(int parameterIndex, InputStream x)
        throws SQLException {
      setAsciiStream(parameterIndex, x, -1);
    }
  
    /**
     * Sets the value of a parameter as a character stream. This method does not
     * close the reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of characters
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setCharacterStream(int parameterIndex, Reader x, int length)
        throws SQLException {
      setCharacterStream(parameterIndex, x, (long) length);
    }
  
    /**
     * Sets the value of a parameter as a character stream. This method does not
     * close the reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setCharacterStream(int parameterIndex, Reader x)
        throws SQLException {
      setCharacterStream(parameterIndex, x, -1);
    }
  
    /**
     * Sets the value of a parameter as a character stream. This method does not
     * close the reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of characters
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setCharacterStream(int parameterIndex, Reader x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setCharacterStream");
    }
  
    /**
     * [Not supported]
     */
    @Override
    public void setURL(int parameterIndex, URL x) throws SQLException {
      throw JdbcException.getUnsupportedException("url");
    }
  
    /**
     * Gets the result set metadata of the query returned when the statement is
     * executed. If this is not a query, this method returns null.
     * 
     * @return the meta data or null if this is not a query
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public ResultSetMetaData getMetaData() throws SQLException {
      try {
        checkClosed();
        ResultInterface result = command.getMetaData();
        if (result == null) {
          return null;
        }
        String catalog = conn.getCatalog();
        JdbcResultSetMetaData meta = new JdbcResultSetMetaData(null, result,
            catalog);
        return meta;
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Clears the batch.
     */
    @Override
    public void clearBatch() throws SQLException {
      try {
        checkClosed();
        batchParameters = null;
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Closes this statement. All result sets that where created by this statement
     * become invalid after calling this method.
     */
    @Override
    public void close() throws SQLException {
      try {
        super.close();
        batchParameters = null;
        if (command != null) {
          command.close();
          command = null;
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Executes the batch. If one of the batched statements fails, this database
     * will continue.
     * 
     * @return the array of update counts
     */
    @Override
    public int[] executeBatch() throws SQLException {
      try {
        if (batchParameters == null) {
          batchParameters = New.arrayList();
        }
        int size = batchParameters.size();
        int[] result = new int[size];
        boolean error = false;
        SQLException next = null;
        checkClosedForWrite();
        try {
          for (int i = 0; i < size; i++) {
            Value[] set = batchParameters.get(i);
            ArrayList<? extends ParameterInterface> parameters = command
                .getParameters();
            for (int j = 0; j < set.length; j++) {
              Value value = set[j];
              ParameterInterface param = parameters.get(j);
              param.setValue(value, false);
            }
            try {
              result[i] = executeUpdateInternal();
            } catch (Exception re) {
              SQLException e = Logger.logAndConvert(log, re);
              if (next == null) {
                next = e;
              } else {
                e.setNextException(next);
                next = e;
              }
              result[i] = Statement.EXECUTE_FAILED;
              error = true;
            }
          }
          batchParameters = null;
          if (error) {
            JdbcBatchUpdateException e = new JdbcBatchUpdateException(next,
                result);
            throw e;
          }
          return result;
        } finally {
          afterWriting();
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Adds the current settings to the batch.
     */
    @Override
    public void addBatch() throws SQLException {
      try {
        checkClosedForWrite();
        try {
          ArrayList<? extends ParameterInterface> parameters = command
              .getParameters();
          int size = parameters.size();
          Value[] set = new Value[size];
          for (int i = 0; i < size; i++) {
            ParameterInterface param = parameters.get(i);
            Value value = param.getParamValue();
            set[i] = value;
          }
          if (batchParameters == null) {
            batchParameters = New.arrayList();
          }
          batchParameters.add(set);
        } finally {
          afterWriting();
        }
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param autoGeneratedKeys
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public int executeUpdate(String sql, int autoGeneratedKeys)
        throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param columnIndexes
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param columnNames
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public int executeUpdate(String sql, String[] columnNames)
        throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param autoGeneratedKeys
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param columnIndexes
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public boolean execute(String sql, int[] columnIndexes) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Calling this method is not legal on a PreparedStatement.
     * 
     * @param sql
     *          ignored
     * @param columnNames
     *          ignored
     * @throws java.sql.SQLException
     *           Unsupported Feature
     */
    @Override
    public boolean execute(String sql, String[] columnNames) throws SQLException {
      try {
        throw JdbcException
            .get(SQLErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
      } catch (Exception e) {
        throw Logger.logAndConvert(log, e);
      }
    }
  
    /**
     * Get the parameter meta data of this prepared statement.
     * 
     * @return the meta data
     */
    @Override
    public ParameterMetaData getParameterMetaData() throws SQLException {
      throw JdbcException.getUnsupportedException("getParameterMetaData");
    }
  
    // =============================================================
  
    private void setParameter(int parameterIndex, Value value) {
      checkClosed();
      parameterIndex--;
      ArrayList<? extends ParameterInterface> parameters = command
          .getParameters();
      if (parameterIndex < 0 || parameterIndex >= parameters.size()) {
        throw JdbcException.getInvalidValueException("parameterIndex",
            parameterIndex + 1);
      }
      ParameterInterface param = parameters.get(parameterIndex);
      // can only delete old temp files if they are not in the batch
      param.setValue(value, batchParameters == null);
    }
  
    /**
     * [Not supported] Sets the value of a parameter as a row id.
     */
    @Override
    public void setRowId(int parameterIndex, RowId x) throws SQLException {
      throw JdbcException.getUnsupportedException("rowId");
    }
  
    // */@Override
  
    /**
     * Sets the value of a parameter.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNString(int parameterIndex, String x) throws SQLException {
      throw JdbcException.getUnsupportedException("setNString");
    }
  
    /**
     * Sets the value of a parameter as a character stream. This method does not
     * close the reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of characters
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNCharacterStream(int parameterIndex, Reader x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setNCharacterStream");
    }
  
    /**
     * Sets the value of a parameter as a character stream. This method does not
     * close the reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNCharacterStream(int parameterIndex, Reader x)
        throws SQLException {
      setNCharacterStream(parameterIndex, x, -1);
    }
  
    /**
     * Sets the value of a parameter as a Clob.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNClob(int parameterIndex, NClob x) throws SQLException {
      throw JdbcException.getUnsupportedException("setNClob");
    }
  
    // */@Override
  
    /**
     * Sets the value of a parameter as a Clob. This method does not close the
     * reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNClob(int parameterIndex, Reader x) throws SQLException {
      throw JdbcException.getUnsupportedException("setNClob");
    }
  
    /**
     * Sets the value of a parameter as a Clob. This method does not close the
     * reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of characters
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setClob(int parameterIndex, Reader x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setClob");
    }
  
    /**
     * Sets the value of a parameter as a Blob. This method does not close the
     * stream. The stream may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of bytes
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setBlob(int parameterIndex, InputStream x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setBlob");
    }
  
    /**
     * Sets the value of a parameter as a Clob. This method does not close the
     * reader. The reader may be closed after executing the statement.
     * 
     * @param parameterIndex
     *          the parameter index (1, 2, ...)
     * @param x
     *          the value
     * @param length
     *          the maximum number of characters
     * @throws java.sql.SQLException
     *           if this object is closed
     */
    @Override
    public void setNClob(int parameterIndex, Reader x, long length)
        throws SQLException {
      throw JdbcException.getUnsupportedException("setNClob");
    }
  
    /**
     * [Not supported] Sets the value of a parameter as a SQLXML object.
     */
    @Override
    public void setSQLXML(int parameterIndex, SQLXML x) throws SQLException {
      throw JdbcException.getUnsupportedException("SQLXML");
    }
  
    /**
     * INTERNAL
     */
    @Override
    public String toString() {
      return "JdbcPreparedStatement : " + command;
    }
  
    protected boolean checkClosed(boolean write) {
      if (super.checkClosed(write)) {
        // if the session was re-connected, re-prepare the statement
        ArrayList<? extends ParameterInterface> oldParams = command
            .getParameters();
        command = conn.prepareCommand(sqlStatement, fetchSize, session);
        ArrayList<? extends ParameterInterface> newParams = command
            .getParameters();
        for (int i = 0, size = oldParams.size(); i < size; i++) {
          ParameterInterface old = oldParams.get(i);
          Value value = old.getParamValue();
          if (value != null) {
            ParameterInterface n = newParams.get(i);
            n.setValue(value, false);
          }
        }
        return true;
      }
      return false;
    }
  
  }
  