All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.impossibl.postgres.jdbc.PGPreparedStatement Maven / Gradle / Ivy

Go to download

A new JDBC driver for PostgreSQL aimed at supporting the advanced features of JDBC and Postgres.

There is a newer version: 0.7.0-89abc52
Show newest version
/**
 * Copyright (c) 2013, impossibl.com
 * 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 impossibl.com 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 THE COPYRIGHT OWNER 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 com.impossibl.postgres.jdbc;

import com.impossibl.postgres.datetime.instants.Instants;
import com.impossibl.postgres.protocol.BindExecCommand;
import com.impossibl.postgres.protocol.DataRow;
import com.impossibl.postgres.protocol.PrepareCommand;
import com.impossibl.postgres.protocol.QueryCommand;
import com.impossibl.postgres.protocol.ResultField;
import com.impossibl.postgres.protocol.ServerObjectType;
import com.impossibl.postgres.types.Type;
import com.impossibl.postgres.utils.guava.ByteStreams;
import com.impossibl.postgres.utils.guava.CharStreams;

import static com.impossibl.postgres.jdbc.ErrorUtils.chainWarnings;
import static com.impossibl.postgres.jdbc.Exceptions.NOT_ALLOWED_ON_PREP_STMT;
import static com.impossibl.postgres.jdbc.Exceptions.NOT_IMPLEMENTED;
import static com.impossibl.postgres.jdbc.Exceptions.NOT_SUPPORTED;
import static com.impossibl.postgres.jdbc.Exceptions.PARAMETER_INDEX_OUT_OF_BOUNDS;
import static com.impossibl.postgres.jdbc.SQLTypeMetaData.getSQLType;
import static com.impossibl.postgres.jdbc.SQLTypeUtils.coerce;
import static com.impossibl.postgres.jdbc.SQLTypeUtils.mapSetType;
import static com.impossibl.postgres.jdbc.Unwrapping.unwrapBlob;
import static com.impossibl.postgres.jdbc.Unwrapping.unwrapClob;
import static com.impossibl.postgres.jdbc.Unwrapping.unwrapObject;
import static com.impossibl.postgres.jdbc.Unwrapping.unwrapRowId;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
import java.sql.BatchUpdateException;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.Date;
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.SQLWarning;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.asList;

class PGPreparedStatement extends PGStatement implements PreparedStatement {


  String sqlText;
  List parameterTypes;
  List parameterValues;
  int parameterCount;
  List parameterSet;
  List> batchParameterTypes;
  List> batchParameterValues;
  boolean wantsGeneratedKeys;
  boolean parsed;


  PGPreparedStatement(PGConnectionImpl connection, int type, int concurrency, int holdability, String name, String sqlText, int parameterCount, String cursorName) {
    super(connection, type, concurrency, holdability, name, null);
    this.sqlText = sqlText;
    this.parameterTypes = new ArrayList<>(asList(new Type[parameterCount]));
    this.parameterValues = new ArrayList<>(asList(new Object[parameterCount]));
    this.parameterCount = parameterCount;
    this.parameterSet = new ArrayList<>(asList(new Boolean[parameterCount]));
    this.cursorName = cursorName;
  }

  public boolean getWantsGeneratedKeys() {
    return wantsGeneratedKeys;
  }

  public void setWantsGeneratedKeys(boolean wantsGeneratedKeys) {
    this.wantsGeneratedKeys = wantsGeneratedKeys;
  }

  void set(int parameterIdx, Object val, int targetSQLType) throws SQLException {
    checkClosed();

    if (parameterIdx < 1 || parameterIdx > parameterValues.size()) {
      throw PARAMETER_INDEX_OUT_OF_BOUNDS;
    }

    parameterIdx -= 1;

    Type paramType = parameterTypes.get(parameterIdx);

    if (targetSQLType == Types.ARRAY || targetSQLType == Types.STRUCT || targetSQLType == Types.OTHER) {
      targetSQLType = Types.NULL;
    }

    if (paramType == null || targetSQLType != getSQLType(paramType)) {

      paramType = SQLTypeMetaData.getType(targetSQLType, connection.getRegistry());

      parameterTypes.set(parameterIdx, paramType);

      parsed = false;
    }

    parameterValues.set(parameterIdx, val);

    if (parameterCount > 0) {
      parameterSet.set(parameterIdx, Boolean.TRUE);
    }
  }

  @Override
  void internalClose() throws SQLException {

    super.internalClose();

    parameterTypes = null;
    parameterValues = null;
    parameterSet = null;
  }

  void verifyParameterSet() throws SQLException {
    if (parameterCount > 0) {
      int count = 0;
      for (Boolean b : parameterSet) {
        if (b != null && Boolean.TRUE.equals(b))
          count++;
      }
      if (count != parameterCount)
        throw new SQLException("Incorrect parameter count, was " + count + ", expected: " + parameterCount);
    }
  }

  void parseIfNeeded() throws SQLException {

    if (cursorName != null && command != null) {
      super.executeSimple("CLOSE " + cursorName);
    }

    if (!parsed) {

      if (name != null && !name.startsWith(CACHED_STATEMENT_PREFIX)) {
        connection.execute(connection.getProtocol().createClose(ServerObjectType.Statement, name), false);
      }

      CachedStatement cachedStatement;

      try {

        final CachedStatementKey key = new CachedStatementKey(sqlText, Collections. emptyList());

        cachedStatement = connection.getCachedStatement(key, new Callable() {

          @Override
          public CachedStatement call() throws Exception {

            String name = connection.isCacheEnabled() ? CACHED_STATEMENT_PREFIX + Integer.toString(key.hashCode()) :
              NO_CACHE_STATEMENT_PREFIX + Integer.toString(key.hashCode());

            PrepareCommand prep = connection.getProtocol().createPrepare(name, sqlText, Collections. emptyList());

            warningChain = connection.execute(prep, true);

            return new CachedStatement(name, prep.getDescribedParameterTypes(), prep.getDescribedResultFields());
          }

        });

      }
      catch (ExecutionException e) {
        throw (SQLException) e.getCause();
      }
      catch (SQLException e) {
        throw e;
      }
      catch (Exception e) {
        throw new SQLException(e);
      }

      name = cachedStatement.name;
      parameterTypes = copyNonNullTypes(parameterTypes, cachedStatement.parameterTypes);
      resultFields = cachedStatement.resultFields;
      parsed = true;

    }

  }

  boolean allowBatchSelects() {
    return false;
  }

  private void coerceParameters() throws SQLException {

    for (int c = 0, sz = parameterTypes.size(); c < sz; ++c) {

      Type parameterType = parameterTypes.get(c);
      Object parameterValue = parameterValues.get(c);

      if (parameterType != null && parameterValue != null) {

        Class targetType = mapSetType(parameterType);

        try {
          parameterValue = coerce(parameterValue, parameterType, targetType, Collections.>emptyMap(), TimeZone.getDefault(), connection);
        }
        catch (SQLException coercionException) {
          throw new SQLException("Error converting parameter " + c, coercionException);
        }
      }

      parameterValues.set(c, parameterValue);
    }

  }

  @Override
  public boolean execute() throws SQLException {
    checkClosed();

    parseIfNeeded();
    closeResultSets();
    verifyParameterSet();
    coerceParameters();

    boolean res = super.executeStatement(name, parameterTypes, parameterValues);

    if (cursorName != null) {
      res = super.executeSimple("FETCH ABSOLUTE 0 FROM " + cursorName);
    }

    if (wantsGeneratedKeys) {
      generatedKeysResultSet = getResultSet();
    }

    return res;
  }

  @Override
  public PGResultSet executeQuery() throws SQLException {

    execute();

    return getResultSet();
  }

  @Override
  public int executeUpdate() throws SQLException {

    execute();

    return getUpdateCount();
  }

  @Override
  public void addBatch() throws SQLException {
    checkClosed();

    if (batchParameterTypes == null) {
      batchParameterTypes = new ArrayList<>();
    }

    if (batchParameterValues == null) {
      batchParameterValues = new ArrayList<>();
    }

    coerceParameters();

    batchParameterTypes.add(new ArrayList<>(parameterTypes));
    batchParameterValues.add(new ArrayList<>(parameterValues));
  }

  @Override
  public void clearBatch() throws SQLException {
    checkClosed();

    batchParameterValues = null;
  }

  @Override
  public int[] executeBatch() throws SQLException {
    checkClosed();
    closeResultSets();

    try {

      warningChain = null;

      if (batchParameterValues == null || batchParameterValues.isEmpty()) {
        return new int[0];
      }

      int[] counts = new int[batchParameterValues.size()];
      Arrays.fill(counts, SUCCESS_NO_INFO);

      List generatedKeys = new ArrayList<>();

      BindExecCommand command = connection.getProtocol().createBindExec(null, null, parameterTypes, Collections.emptyList(), resultFields);

      List lastParameterTypes = null;
      List lastResultFields = null;

      int c = 0;
      int sz = batchParameterValues.size();

      try {
        while (c < sz) {

          List parameterTypes = mergeTypes(batchParameterTypes.get(c), lastParameterTypes);

          if (lastParameterTypes == null || !lastParameterTypes.equals(parameterTypes)) {

            PrepareCommand prep = connection.getProtocol().createPrepare(null, sqlText, parameterTypes);

            SQLWarning warnings = connection.execute(prep, true);
            warningChain = chainWarnings(warningChain, warnings);

            parameterTypes = prep.getDescribedParameterTypes();
            lastParameterTypes = parameterTypes;
            lastResultFields = prep.getDescribedResultFields();
          }

          List parameterValues = batchParameterValues.get(c);

          command.setParameterTypes(parameterTypes);
          command.setParameterValues(parameterValues);

          SQLWarning warnings = connection.execute(command, true);

          warningChain = chainWarnings(warningChain, warnings);

          List resultBatches = command.getResultBatches();
          if (resultBatches.size() != 1) {
            throw new BatchUpdateException(counts);
          }

          QueryCommand.ResultBatch resultBatch = resultBatches.get(0);
          if (!allowBatchSelects() && resultBatch.command.equals("SELECT")) {
            throw new SQLException("SELECT in executeBatch");
          }
          if (resultBatch.rowsAffected == null) {
            counts[c] = 0;
          }
          else {
            counts[c] = (int) (long) resultBatch.rowsAffected;
          }

          if (wantsGeneratedKeys) {
            generatedKeys.add(resultBatch.results.get(0));
          }

          c++;
        }
      }
      catch (SQLException se) {

        int[] updateCounts = new int[c + 1];
        System.arraycopy(counts, 0, updateCounts, 0, updateCounts.length - 1);

        updateCounts[c] = Statement.EXECUTE_FAILED;

        throw new BatchUpdateException(updateCounts, se);
      }

      generatedKeysResultSet = createResultSet(lastResultFields, generatedKeys);

      return counts;

    }
    finally {
      batchParameterTypes = null;
      batchParameterValues = null;
    }

  }

  List mergeTypes(List list, List defaultTypes) {

    if (defaultTypes == null)
      return list;

    for (int c = 0, sz = list.size(); c < sz; ++c) {

      Type type = list.get(c);
      if (type == null)
        type = defaultTypes.get(c);

      list.set(c, type);
    }

    return list;
  }

  List copyNonNullTypes(List list, List sourceTypes) {

    if (sourceTypes == null)
      return list;

    for (int c = 0, sz = list.size(); c < sz; ++c) {

      Type type = sourceTypes.get(c);
      if (type != null) {
        list.set(c, type);
      }
    }

    return list;
  }

  @Override
  public void clearParameters() throws SQLException {
    checkClosed();

    for (int c = 0; c < parameterValues.size(); ++c)
      parameterValues.set(c, null);

    for (int c = 0; c < parameterSet.size(); ++c)
      parameterSet.set(c, Boolean.FALSE);
  }

  @Override
  public ParameterMetaData getParameterMetaData() throws SQLException {
    checkClosed();

    parseIfNeeded();

    return new PGParameterMetaData(parameterTypes, connection.getTypeMap());
  }

  @Override
  public ResultSetMetaData getMetaData() throws SQLException {
    checkClosed();

    parseIfNeeded();

    return new PGResultSetMetaData(connection, resultFields, connection.getTypeMap());
  }

  @Override
  public void setNull(int parameterIndex, int sqlType) throws SQLException {
    set(parameterIndex, null, Types.NULL);
  }

  @Override
  public void setBoolean(int parameterIndex, boolean x) throws SQLException {
    set(parameterIndex, x, Types.BOOLEAN);
  }

  @Override
  public void setByte(int parameterIndex, byte x) throws SQLException {
    set(parameterIndex, x, Types.TINYINT);
  }

  @Override
  public void setShort(int parameterIndex, short x) throws SQLException {
    set(parameterIndex, x, Types.SMALLINT);
  }

  @Override
  public void setInt(int parameterIndex, int x) throws SQLException {
    set(parameterIndex, x, Types.INTEGER);
  }

  @Override
  public void setLong(int parameterIndex, long x) throws SQLException {
    set(parameterIndex, x, Types.BIGINT);
  }

  @Override
  public void setFloat(int parameterIndex, float x) throws SQLException {
    set(parameterIndex, x, Types.FLOAT);
  }

  @Override
  public void setDouble(int parameterIndex, double x) throws SQLException {
    set(parameterIndex, x, Types.DOUBLE);
  }

  @Override
  public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
    set(parameterIndex, x, Types.DECIMAL);
  }

  @Override
  public void setString(int parameterIndex, String x) throws SQLException {
    set(parameterIndex, x, Types.VARCHAR);
  }

  @Override
  public void setBytes(int parameterIndex, byte[] x) throws SQLException {
    set(parameterIndex, x, Types.BINARY);
  }

  @Override
  public void setDate(int parameterIndex, Date x) throws SQLException {
    setDate(parameterIndex, x, Calendar.getInstance());
  }

  @Override
  public void setTime(int parameterIndex, Time x) throws SQLException {
    setTime(parameterIndex, x, Calendar.getInstance());
  }

  @Override
  public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
    setTimestamp(parameterIndex, x, Calendar.getInstance());
  }

  @Override
  public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException {

    TimeZone zone = cal.getTimeZone();

    set(parameterIndex, x != null ? Instants.fromDate(x, zone) : null, Types.DATE);
  }

  @Override
  public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException {


    TimeZone zone = cal.getTimeZone();

    set(parameterIndex, x != null ? Instants.fromTime(x, zone) : null, Types.TIME);
  }

  @Override
  public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException {
    checkClosed();

    TimeZone zone = cal.getTimeZone();

    set(parameterIndex, x != null ? Instants.fromTimestamp(x, zone) : null, Types.TIMESTAMP);
  }

  @Override
  public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {

    set(parameterIndex, x, Types.BINARY);
  }

  @Override
  public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {

    if (length < 0) {
      throw new SQLException("Invalid length");
    }

    if (x != null) {

      x = ByteStreams.limit(x, length);
    }
    else if (length != 0) {
      throw new SQLException("Invalid length");
    }

    set(parameterIndex, x, Types.BINARY);
  }

  @Override
  public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {

    if (length < 0) {
      throw new SQLException("Invalid length");
    }

    if (x != null) {

      x = ByteStreams.limit(x, length);
    }
    else if (length != 0) {
      throw new SQLException("Invalid length");
    }

    set(parameterIndex, x, Types.BINARY);
  }

  @Override
  @Deprecated
  public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {

    InputStreamReader reader = new InputStreamReader(x, UTF_8);

    setCharacterStream(parameterIndex, reader, length);
  }

  @Override
  public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
    setAsciiStream(parameterIndex, x, (long) -1);
  }

  @Override
  public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
    setAsciiStream(parameterIndex, x, (long) length);
  }

  @Override
  public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {

    InputStreamReader reader = new InputStreamReader(x, US_ASCII);

    setCharacterStream(parameterIndex, reader, length);
  }

  @Override
  public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
    setCharacterStream(parameterIndex, reader, (long) -1);
  }

  @Override
  public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException {
    setCharacterStream(parameterIndex, reader, (long) length);
  }

  @Override
  public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {

    StringWriter writer = new StringWriter();
    try {
      CharStreams.copy(reader, writer);
    }
    catch (IOException e) {
      throw new SQLException(e);
    }

    set(parameterIndex, writer.toString(), Types.VARCHAR);
  }

  @Override
  public void setObject(int parameterIndex, Object x) throws SQLException {
    if (x == null) {
      setNull(parameterIndex, Types.NULL);
    }
    else if (x instanceof Boolean) {
      setBoolean(parameterIndex, ((Boolean)x).booleanValue());
    }
    else if (x instanceof Byte) {
      setByte(parameterIndex, ((Byte)x).byteValue());
    }
    else if (x instanceof Short) {
      setShort(parameterIndex, ((Short)x).shortValue());
    }
    else if (x instanceof Integer) {
      setInt(parameterIndex, ((Integer)x).intValue());
    }
    else if (x instanceof Long) {
      setLong(parameterIndex, ((Long)x).longValue());
    }
    else if (x instanceof Float) {
      setFloat(parameterIndex, ((Float)x).floatValue());
    }
    else if (x instanceof Double) {
      setDouble(parameterIndex, ((Double)x).doubleValue());
    }
    else if (x instanceof BigDecimal) {
      setBigDecimal(parameterIndex, (BigDecimal)x);
    }
    else if (x instanceof String) {
      setString(parameterIndex, (String)x);
    }
    else if (x instanceof byte[]) {
      setBytes(parameterIndex, (byte[])x);
    }
    else if (x instanceof Date) {
      setDate(parameterIndex, (Date)x);
    }
    else if (x instanceof Time) {
      setTime(parameterIndex, (Time)x);
    }
    else if (x instanceof Timestamp) {
      setTimestamp(parameterIndex, (Timestamp)x);
    }
    else if (x instanceof InputStream) {
      setBinaryStream(parameterIndex, (InputStream)x);
    }
    else if (x instanceof Blob) {
      setBlob(parameterIndex, (Blob)x);
    }
    else if (x instanceof Clob) {
      setClob(parameterIndex, (Clob)x);
    }
    else if (x instanceof Array) {
      setArray(parameterIndex, (Array)x);
    }
    else if (x instanceof URL) {
      setURL(parameterIndex, (URL)x);
    }
    else if (x instanceof SQLXML) {
      setSQLXML(parameterIndex, (SQLXML)x);
    }
    else if (x instanceof RowId) {
      setRowId(parameterIndex, (RowId)x);
    }
    else if (x instanceof Ref) {
      setRef(parameterIndex, (Ref)x);
    }
    else if (x instanceof NClob) {
      setNClob(parameterIndex, (NClob)x);
    }
    else {
      set(parameterIndex, x, Types.OTHER);
    }
  }

  @Override
  public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
    checkClosed();

    setObject(parameterIndex, x, targetSqlType, 0);
  }

  @Override
  public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException {
    checkClosed();

    set(parameterIndex, unwrapObject(connection, x), targetSqlType);
  }

  @Override
  public void setBlob(int parameterIndex, Blob x) throws SQLException {
    checkClosed();

    set(parameterIndex, unwrapBlob(connection, x), Types.BLOB);
  }

  @Override
  public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
    setBlob(parameterIndex, ByteStreams.limit(inputStream, length));
  }

  @Override
  public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
    checkClosed();

    Blob blob = connection.createBlob();

    try {
      ByteStreams.copy(inputStream, blob.setBinaryStream(1));
    }
    catch (IOException e) {
      throw new SQLException(e);
    }

    set(parameterIndex, blob, Types.BLOB);
  }

  @Override
  public void setClob(int parameterIndex, Clob x) throws SQLException {
    checkClosed();

    set(parameterIndex, unwrapClob(connection, x), Types.CLOB);
  }

  @Override
  public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
    setClob(parameterIndex, CharStreams.limit(reader, length));
  }

  @Override
  public void setClob(int parameterIndex, Reader reader) throws SQLException {
    checkClosed();

    Clob clob = connection.createClob();

    try {
      CharStreams.copy(reader, clob.setCharacterStream(1));
    }
    catch (IOException e) {
      throw new SQLException(e);
    }

    set(parameterIndex, clob, Types.CLOB);
  }

  @Override
  public void setArray(int parameterIndex, Array x) throws SQLException {
    set(parameterIndex, x, Types.ARRAY);
  }

  @Override
  public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
    set(parameterIndex, null, Types.NULL);
  }

  @Override
  public void setURL(int parameterIndex, URL x) throws SQLException {
    set(parameterIndex, x, Types.VARCHAR);
  }

  @Override
  public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
    checkClosed();

    if (!(xmlObject instanceof PGSQLXML)) {
      throw new SQLException("SQLXML object not created by driver");
    }

    PGSQLXML sqlXml = (PGSQLXML) xmlObject;

    set(parameterIndex, sqlXml.getData(), Types.SQLXML);
  }

  @Override
  public void setRowId(int parameterIndex, RowId x) throws SQLException {
    checkClosed();

    set(parameterIndex, unwrapRowId(connection, x), Types.ROWID);
  }

  @Override
  public void setRef(int parameterIndex, Ref x) throws SQLException {
    checkClosed();
    throw NOT_IMPLEMENTED;
  }

  @Override
  public void setNString(int parameterIndex, String value) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public void setNClob(int parameterIndex, NClob value) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public void setNClob(int parameterIndex, Reader reader) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
    checkClosed();
    throw NOT_SUPPORTED;
  }

  @Override
  public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public int executeUpdate(String sql, String[] columnNames) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public boolean execute(String sql, int[] columnIndexes) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public boolean execute(String sql, String[] columnNames) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public boolean execute(String sql) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public ResultSet executeQuery(String sql) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public int executeUpdate(String sql) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

  @Override
  public void addBatch(String sql) throws SQLException {
    throw NOT_ALLOWED_ON_PREP_STMT;
  }

}