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

org.tarantool.jdbc.SQLPreparedStatement Maven / Gradle / Ivy

package org.tarantool.jdbc;

import org.tarantool.util.SQLStates;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.Array;
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.SQLFeatureNotSupportedException;
import java.sql.SQLXML;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SQLPreparedStatement extends SQLStatement implements PreparedStatement {

    private static final String INVALID_CALL_MESSAGE = "The method cannot be called on a PreparedStatement.";
    private static final int STREAM_WRITE_CHUNK_SIZE = 4096;

    private final String sql;
    private final Map parameters;
    private final int autoGeneratedKeys;
    private List> batchParameters = new ArrayList<>();

    public SQLPreparedStatement(SQLConnection connection, String sql, int autoGeneratedKeys) throws SQLException {
        super(connection);
        this.sql = sql;
        this.parameters = new HashMap<>();
        this.autoGeneratedKeys = autoGeneratedKeys;
        setPoolable(true);
    }

    public SQLPreparedStatement(SQLConnection connection,
                                String sql,
                                int resultSetType,
                                int resultSetConcurrency,
                                int resultSetHoldability) throws SQLException {
        super(connection, resultSetType, resultSetConcurrency, resultSetHoldability);
        this.sql = sql;
        this.parameters = new HashMap<>();
        this.autoGeneratedKeys = NO_GENERATED_KEYS;
        setPoolable(true);
    }

    @Override
    public ResultSet executeQuery() throws SQLException {
        checkNotClosed();
        if (!executeInternal(autoGeneratedKeys, sql, toParametersList(parameters))) {
            throw new SQLException("No results were returned", SQLStates.NO_DATA.getSqlState());
        }
        return resultSet;
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        checkNotClosed();
        throw new SQLException(INVALID_CALL_MESSAGE);
    }

    @Override
    public int executeUpdate() throws SQLException {
        checkNotClosed();
        if (executeInternal(autoGeneratedKeys, sql, toParametersList(parameters))) {
            throw new SQLException(
                "Result was returned but nothing was expected",
                SQLStates.TOO_MANY_RESULTS.getSqlState()
            );
        }
        return updateCount;
    }

    @Override
    public int executeUpdate(String sql) throws SQLException {
        checkNotClosed();
        throw new SQLException(INVALID_CALL_MESSAGE);
    }

    @Override
    public void setNull(int parameterIndex, int sqlType) throws SQLException {
        setParameter(parameterIndex, null);
    }

    @Override
    public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException {
        setParameter(parameterIndex, null);
    }

    @Override
    public void setBoolean(int parameterIndex, boolean parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setByte(int parameterIndex, byte parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setBytes(int parameterIndex, byte[] parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setShort(int parameterIndex, short parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setInt(int parameterIndex, int parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setLong(int parameterIndex, long parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setFloat(int parameterIndex, float parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setDouble(int parameterIndex, double parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setBigDecimal(int parameterIndex, BigDecimal parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setString(int parameterIndex, String parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setDate(int parameterIndex, Date parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setDate(int parameterIndex, Date parameterValue, Calendar calendar) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setTime(int parameterIndex, Time parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setTime(int parameterIndex, Time parameterValue, Calendar calendar) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp parameterValue, Calendar calendar) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

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

    @Override
    public void setAsciiStream(int parameterIndex, InputStream parameterValue) throws SQLException {
        setCharStream(parameterIndex, parameterValue, Integer.MAX_VALUE, "ASCII");
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream parameterValue, long length) throws SQLException {
        ensureLengthLowerBound(length);
        setCharStream(parameterIndex, parameterValue, length, "ASCII");
    }

    @Override
    public void setUnicodeStream(int parameterIndex, InputStream parameterValue, int length) throws SQLException {
        ensureLengthLowerBound(length);
        setCharStream(parameterIndex, parameterValue, length, "UTF-8");
    }

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

    @Override
    public void setBinaryStream(int parameterIndex, InputStream parameterValue, long length) throws SQLException {
        ensureLengthLowerBound(length);
        setBinStream(parameterIndex, parameterValue, length);
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream parameterValue) throws SQLException {
        setBinStream(parameterIndex, parameterValue, Integer.MAX_VALUE);
    }

    @Override
    public void clearParameters() throws SQLException {
        parameters.clear();
    }

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

    @Override
    public void setObject(int parameterIndex, Object value) throws SQLException {
        setParameter(parameterIndex, value);
    }

    @Override
    public void setObject(int parameterIndex,
                          Object parameterValue,
                          int targetSqlType,
                          int scaleOrLength) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

    private void setParameter(int parameterIndex, Object value) throws SQLException {
        checkNotClosed();
        parameters.put(parameterIndex, value);
    }

    @Override
    public boolean execute() throws SQLException {
        checkNotClosed();
        return executeInternal(autoGeneratedKeys, sql, toParametersList(parameters));
    }

    @Override
    public boolean execute(String sql) throws SQLException {
        checkNotClosed();
        throw new SQLException(INVALID_CALL_MESSAGE);
    }

    @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 {
        ensureLengthLowerBound(length);
        setCharStream(parameterIndex, reader, length);
    }

    @Override
    public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
        setCharStream(parameterIndex, reader, Integer.MAX_VALUE);
    }

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

    @Override
    public void setBlob(int parameterIndex, Blob x) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setClob(int parameterIndex, Clob x) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setClob(int parameterIndex, Reader reader) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setArray(int parameterIndex, Array x) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public ResultSetMetaData getMetaData() throws SQLException {
        if (resultSet != null && !resultSet.isClosed()) {
            return resultSet.getMetaData();
        }
        // XXX: it's required a support of dry-run mode to obtain
        // a statement metadata without real query execution.
        // see https://github.com/tarantool/tarantool/issues/3292
        return null;
    }

    @Override
    public void setURL(int parameterIndex, URL parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue.toString());
    }

    @Override
    public ParameterMetaData getParameterMetaData() throws SQLException {
        return null;
    }

    @Override
    public void setRowId(int parameterIndex, RowId x) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void setNString(int parameterIndex, String parameterValue) throws SQLException {
        setParameter(parameterIndex, parameterValue);
    }

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

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

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

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

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

    @Override
    public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
        throw new SQLFeatureNotSupportedException();
    }

    @Override
    public void addBatch(String sql) throws SQLException {
        checkNotClosed();
        throw new SQLException(INVALID_CALL_MESSAGE);
    }

    @Override
    public void addBatch() throws SQLException {
        checkNotClosed();
        // shadow copy of the current parameters
        batchParameters.add(new HashMap<>(parameters));
    }

    @Override
    public int[] executeBatch() throws SQLException {
        checkNotClosed();
        try {
            List queries = new ArrayList<>();
            for (Map p : batchParameters) {
                SQLQueryHolder of = SQLQueryHolder.of(sql, toParametersList(p));
                queries.add(of);
            }
            return executeBatchInternal(queries);
        } finally {
            batchParameters.clear();
        }
    }

    @Override
    public void clearBatch() throws SQLException {
        checkNotClosed();
        batchParameters.clear();
    }

    private Object[] toParametersList(Map parameters) throws SQLException {
        Object[] objects = new Object[parameters.size()];
        for (int i = 1; i <= parameters.size(); i++) {
            if (parameters.containsKey(i)) {
                objects[i - 1] = parameters.get(i);
            } else {
                throw new SQLException("Parameter " + i + " is missing");
            }
        }
        return objects;
    }

    private void ensureLengthLowerBound(long length) throws SQLException {
        if (length < 0) {
            throw new SQLException("Stream size cannot be negative", SQLStates.INVALID_PARAMETER_VALUE.getSqlState());
        }
    }

    private void ensureLengthUpperBound(long length) throws SQLException {
        if (length > Integer.MAX_VALUE) {
            throw new SQLException("Stream size is too large", SQLStates.INVALID_PARAMETER_VALUE.getSqlState());
        }
    }

    private void setCharStream(int parameterIndex,
                               InputStream parameterValue,
                               long length,
                               String encoding) throws SQLException {
        ensureLengthUpperBound(length);
        try {
            byte[] bytes = convertToBytes(parameterValue, length);
            setParameter(parameterIndex, new String(bytes, 0, bytes.length, encoding));
        } catch (UnsupportedEncodingException e) {
            throw new SQLException("Unsupported encoding", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
        }
    }

    private void setCharStream(int parameterIndex, Reader reader, long length) throws SQLException {
        ensureLengthUpperBound(length);
        try {
            StringBuilder value = new StringBuilder(STREAM_WRITE_CHUNK_SIZE);
            char[] buffer = new char[STREAM_WRITE_CHUNK_SIZE];
            int totalRead = 0;
            int charsRead;
            while (totalRead < length &&
                (charsRead = reader.read(buffer, 0,
                    (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) {
                value.append(buffer, 0, charsRead);
                totalRead += charsRead;
            }
            setParameter(parameterIndex, value.toString());
        } catch (IOException e) {
            throw new SQLException("Cannot read from the reader", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
        }
    }

    private void setBinStream(int parameterIndex,
                              InputStream parameterValue,
                              long length) throws SQLException {
        ensureLengthUpperBound(length);
        setBytes(parameterIndex, convertToBytes(parameterValue, length));
    }

    private byte[] convertToBytes(InputStream parameterValue, long length) throws SQLException {
        try {
            int bytesRead;
            int totalRead = 0;
            byte[] buffer = new byte[STREAM_WRITE_CHUNK_SIZE];
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(STREAM_WRITE_CHUNK_SIZE);
            while (totalRead < length &&
                (bytesRead = parameterValue.read(buffer, 0,
                    (int) Math.min(length - totalRead, STREAM_WRITE_CHUNK_SIZE))) != -1) {
                outputStream.write(buffer, 0, bytesRead);
                totalRead += bytesRead;
            }
            return outputStream.toByteArray();
        } catch (IOException e) {
            throw new SQLException("Cannot read stream", SQLStates.INVALID_PARAMETER_VALUE.getSqlState(), e);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy