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

io.prestosql.jdbc.PrestoPreparedStatement Maven / Gradle / Ivy

The newest version!
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.prestosql.jdbc;

import io.prestosql.jdbc.$internal.guava.annotations.VisibleForTesting;
import io.prestosql.jdbc.$internal.guava.base.Joiner;
import io.prestosql.jdbc.$internal.guava.base.Splitter;
import io.prestosql.jdbc.$internal.guava.collect.ImmutableList;
import io.prestosql.jdbc.$internal.guava.primitives.Ints;
import io.prestosql.jdbc.$internal.client.ClientTypeSignature;
import io.prestosql.jdbc.$internal.client.ClientTypeSignatureParameter;
import io.prestosql.jdbc.$internal.joda.time.DateTimeZone;

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.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.SQLType;
import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static io.prestosql.jdbc.$internal.guava.base.Preconditions.checkArgument;
import static io.prestosql.jdbc.$internal.guava.io.BaseEncoding.base16;
import static io.prestosql.jdbc.$internal.client.ClientTypeSignature.VARCHAR_UNBOUNDED_LENGTH;
import static io.prestosql.jdbc.AbstractPrestoResultSet.DATE_FORMATTER;
import static io.prestosql.jdbc.AbstractPrestoResultSet.TIMESTAMP_FORMATTER;
import static io.prestosql.jdbc.AbstractPrestoResultSet.TIME_FORMATTER;
import static io.prestosql.jdbc.ColumnInfo.setTypeInfo;
import static io.prestosql.jdbc.ObjectCasts.castToBigDecimal;
import static io.prestosql.jdbc.ObjectCasts.castToBinary;
import static io.prestosql.jdbc.ObjectCasts.castToBoolean;
import static io.prestosql.jdbc.ObjectCasts.castToByte;
import static io.prestosql.jdbc.ObjectCasts.castToDouble;
import static io.prestosql.jdbc.ObjectCasts.castToFloat;
import static io.prestosql.jdbc.ObjectCasts.castToInt;
import static io.prestosql.jdbc.ObjectCasts.castToLong;
import static io.prestosql.jdbc.ObjectCasts.castToShort;
import static io.prestosql.jdbc.ObjectCasts.invalidConversion;
import static java.lang.Long.parseLong;
import static java.lang.String.format;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE;
import static java.time.format.DateTimeFormatter.ISO_LOCAL_TIME;
import static java.util.Objects.requireNonNull;

public class PrestoPreparedStatement
        extends PrestoStatement
        implements PreparedStatement
{
    private static final DateTimeFormatter LOCAL_DATE_TIME_FORMATTER =
            new DateTimeFormatterBuilder()
                    .append(ISO_LOCAL_DATE)
                    .appendLiteral(' ')
                    .append(ISO_LOCAL_TIME)
                    .toFormatter();

    private static final DateTimeFormatter OFFSET_TIME_FORMATTER =
            new DateTimeFormatterBuilder()
                    .append(ISO_LOCAL_TIME)
                    .appendOffset("+HH:mm", "+00:00")
                    .toFormatter();

    private static final Pattern TOP_LEVEL_TYPE_PATTERN = Pattern.compile("(.+?)\\((.+)\\)");
    private static final Pattern TIMESTAMP_WITH_TIME_ZONE_PRECISION_PATTERN = Pattern.compile("timestamp\\((\\d+)\\) with time zone");
    private static final Pattern TIME_WITH_TIME_ZONE_PRECISION_PATTERN = Pattern.compile("time\\((\\d+)\\) with time zone");

    private final Map parameters = new HashMap<>();
    private final List> batchValues = new ArrayList<>();
    private final String statementName;
    private final String originalSql;
    private boolean isBatch;

    PrestoPreparedStatement(PrestoConnection connection, String statementName, String sql)
            throws SQLException
    {
        super(connection);
        this.statementName = requireNonNull(statementName, "statementName is null");
        this.originalSql = requireNonNull(sql, "sql is null");
        super.execute(format("PREPARE %s FROM %s", statementName, sql));
    }

    @Override
    public void close()
            throws SQLException
    {
        super.execute(format("DEALLOCATE PREPARE %s", statementName));
        super.close();
    }

    @Override
    public ResultSet executeQuery()
            throws SQLException
    {
        requireNonBatchStatement();
        if (!super.execute(getExecuteSql(statementName, toValues(parameters)))) {
            throw new SQLException("Prepared SQL statement is not a query: " + originalSql);
        }
        return getResultSet();
    }

    @Override
    public int executeUpdate()
            throws SQLException
    {
        requireNonBatchStatement();
        return Ints.saturatedCast(executeLargeUpdate());
    }

    @Override
    public long executeLargeUpdate()
            throws SQLException
    {
        requireNonBatchStatement();
        if (super.execute(getExecuteSql(statementName, toValues(parameters)))) {
            throw new SQLException("Prepared SQL is not an update statement: " + originalSql);
        }
        return getLargeUpdateCount();
    }

    @Override
    public boolean execute()
            throws SQLException
    {
        requireNonBatchStatement();
        return super.execute(getExecuteSql(statementName, toValues(parameters)));
    }

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

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

    @Override
    public void setByte(int parameterIndex, byte x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("TINYINT", Byte.toString(x)));
    }

    @Override
    public void setShort(int parameterIndex, short x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("SMALLINT", Short.toString(x)));
    }

    @Override
    public void setInt(int parameterIndex, int x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("INTEGER", Integer.toString(x)));
    }

    @Override
    public void setLong(int parameterIndex, long x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("BIGINT", Long.toString(x)));
    }

    @Override
    public void setFloat(int parameterIndex, float x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("REAL", Float.toString(x)));
    }

    @Override
    public void setDouble(int parameterIndex, double x)
            throws SQLException
    {
        checkOpen();
        setParameter(parameterIndex, formatLiteral("DOUBLE", Double.toString(x)));
    }

    @Override
    public void setBigDecimal(int parameterIndex, BigDecimal x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.DECIMAL);
        }
        else {
            setParameter(parameterIndex, formatLiteral("DECIMAL", x.toString()));
        }
    }

    @Override
    public void setString(int parameterIndex, String x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.VARCHAR);
        }
        else {
            setParameter(parameterIndex, formatStringLiteral(x));
        }
    }

    @Override
    public void setBytes(int parameterIndex, byte[] x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.VARBINARY);
        }
        else {
            setParameter(parameterIndex, formatBinaryLiteral(x));
        }
    }

    @Override
    public void setDate(int parameterIndex, Date x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.DATE);
        }
        else {
            setAsDate(parameterIndex, x);
        }
    }

    private void setAsDate(int parameterIndex, Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");

        String literal = toDateLiteral(value);
        setParameter(parameterIndex, formatLiteral("DATE", literal));
    }

    private String toDateLiteral(Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");
        if (value instanceof java.util.Date) {
            return DATE_FORMATTER.print(((java.util.Date) value).getTime());
        }
        if (value instanceof LocalDate) {
            return ISO_LOCAL_DATE.format(((LocalDate) value));
        }
        if (value instanceof LocalDateTime) {
            return ISO_LOCAL_DATE.format(((LocalDateTime) value));
        }
        if (value instanceof String) {
            // TODO validate proper format
            return (String) value;
        }
        throw invalidConversion(value, "date");
    }

    @Override
    public void setTime(int parameterIndex, Time x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.TIME);
        }
        else {
            setAsTime(parameterIndex, x);
        }
    }

    private void setAsTime(int parameterIndex, Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");

        String literal = toTimeLiteral(value);
        setParameter(parameterIndex, formatLiteral("TIME", literal));
    }

    private String toTimeLiteral(Object value)
            throws SQLException
    {
        if (value instanceof java.util.Date) {
            return TIME_FORMATTER.print(((java.util.Date) value).getTime());
        }
        if (value instanceof LocalTime) {
            return ISO_LOCAL_TIME.format((LocalTime) value);
        }
        if (value instanceof LocalDateTime) {
            return ISO_LOCAL_TIME.format((LocalDateTime) value);
        }
        if (value instanceof String) {
            // TODO validate proper format
            return (String) value;
        }
        throw invalidConversion(value, "time");
    }

    private void setAsTimeWithTimeZone(int parameterIndex, Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");

        String literal = toTimeWithTimeZoneLiteral(value);
        setParameter(parameterIndex, formatLiteral("TIME", literal));
    }

    private String toTimeWithTimeZoneLiteral(Object value)
            throws SQLException
    {
        if (value instanceof OffsetTime) {
            return OFFSET_TIME_FORMATTER.format((OffsetTime) value);
        }
        if (value instanceof String) {
            // TODO validate proper format
            return (String) value;
        }
        throw invalidConversion(value, "time with time zone");
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.TIMESTAMP);
        }
        else {
            setAsTimestamp(parameterIndex, x);
        }
    }

    private void setAsTimestamp(int parameterIndex, Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");

        String literal = toTimestampLiteral(value);
        setParameter(parameterIndex, formatLiteral("TIMESTAMP", literal));
    }

    private String toTimestampLiteral(Object value)
            throws SQLException
    {
        if (value instanceof java.util.Date) {
            return TIMESTAMP_FORMATTER.print(((java.util.Date) value).getTime());
        }
        if (value instanceof LocalDateTime) {
            return LOCAL_DATE_TIME_FORMATTER.format(((LocalDateTime) value));
        }
        if (value instanceof String) {
            // TODO validate proper format
            return (String) value;
        }
        throw invalidConversion(value, "timestamp");
    }

    private void setAsTimestampWithTimeZone(int parameterIndex, Object value)
            throws SQLException
    {
        requireNonNull(value, "value is null");

        String literal = toTimestampWithTimeZoneLiteral(value);
        setParameter(parameterIndex, formatLiteral("TIMESTAMP", literal));
    }

    private String toTimestampWithTimeZoneLiteral(Object value)
            throws SQLException
    {
        // TODO (https://github.com/prestosql/presto/issues/6299) support ZonedDateTime
        if (value instanceof String) {
            // TODO validate proper format
            return (String) value;
        }
        throw invalidConversion(value, "timestamp with time zone");
    }

    @Override
    public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
            throws SQLException
    {
        checkOpen();
        if (x == null || cal == null) {
            setTimestamp(parameterIndex, x);
        }
        else {
            String formattedDateTime = TIMESTAMP_FORMATTER.withZone(DateTimeZone.forTimeZone(cal.getTimeZone())).print(x.getTime());
            setParameter(parameterIndex, formatLiteral("TIMESTAMP", formattedDateTime));
        }
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, int length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setAsciiStream");
    }

    @Override
    public void setUnicodeStream(int parameterIndex, InputStream x, int length)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setUnicodeStream");
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream x, int length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setBinaryStream");
    }

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

    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, targetSqlType);
            return;
        }
        switch (targetSqlType) {
            case Types.BOOLEAN:
            case Types.BIT:
                setBoolean(parameterIndex, castToBoolean(x, targetSqlType));
                return;
            case Types.TINYINT:
                setByte(parameterIndex, castToByte(x, targetSqlType));
                return;
            case Types.SMALLINT:
                setShort(parameterIndex, castToShort(x, targetSqlType));
                return;
            case Types.INTEGER:
                setInt(parameterIndex, castToInt(x, targetSqlType));
                return;
            case Types.BIGINT:
                setLong(parameterIndex, castToLong(x, targetSqlType));
                return;
            case Types.FLOAT:
            case Types.REAL:
                setFloat(parameterIndex, castToFloat(x, targetSqlType));
                return;
            case Types.DOUBLE:
                setDouble(parameterIndex, castToDouble(x, targetSqlType));
                return;
            case Types.DECIMAL:
            case Types.NUMERIC:
                setBigDecimal(parameterIndex, castToBigDecimal(x, targetSqlType));
                return;
            case Types.CHAR:
            case Types.NCHAR:
            case Types.VARCHAR:
            case Types.NVARCHAR:
            case Types.LONGVARCHAR:
            case Types.LONGNVARCHAR:
                setString(parameterIndex, x.toString());
                return;
            case Types.BINARY:
            case Types.VARBINARY:
            case Types.LONGVARBINARY:
                setBytes(parameterIndex, castToBinary(x, targetSqlType));
                return;
            case Types.DATE:
                setAsDate(parameterIndex, x);
                return;
            case Types.TIME:
                setAsTime(parameterIndex, x);
                return;
            case Types.TIME_WITH_TIMEZONE:
                setAsTimeWithTimeZone(parameterIndex, x);
                return;
            case Types.TIMESTAMP:
                setAsTimestamp(parameterIndex, x);
                return;
            case Types.TIMESTAMP_WITH_TIMEZONE:
                setAsTimestampWithTimeZone(parameterIndex, x);
                return;
        }
        throw new SQLException("Unsupported target SQL type: " + targetSqlType);
    }

    @Override
    public void setObject(int parameterIndex, Object x, SQLType targetSqlType)
            throws SQLException
    {
        setObject(parameterIndex, x, targetSqlType.getVendorTypeNumber());
    }

    @Override
    public void setObject(int parameterIndex, Object x)
            throws SQLException
    {
        checkOpen();
        if (x == null) {
            setNull(parameterIndex, Types.NULL);
        }
        else if (x instanceof Boolean) {
            setBoolean(parameterIndex, (Boolean) x);
        }
        else if (x instanceof Byte) {
            setByte(parameterIndex, (Byte) x);
        }
        else if (x instanceof Short) {
            setShort(parameterIndex, (Short) x);
        }
        else if (x instanceof Integer) {
            setInt(parameterIndex, (Integer) x);
        }
        else if (x instanceof Long) {
            setLong(parameterIndex, (Long) x);
        }
        else if (x instanceof Float) {
            setFloat(parameterIndex, (Float) x);
        }
        else if (x instanceof Double) {
            setDouble(parameterIndex, (Double) x);
        }
        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 LocalDate) {
            setAsDate(parameterIndex, x);
        }
        else if (x instanceof Time) {
            setTime(parameterIndex, (Time) x);
        }
        // TODO (https://github.com/prestosql/presto/issues/6299) LocalTime -> setAsTime
        else if (x instanceof OffsetTime) {
            setAsTimeWithTimeZone(parameterIndex, x);
        }
        else if (x instanceof Timestamp) {
            setTimestamp(parameterIndex, (Timestamp) x);
        }
        else {
            throw new SQLException("Unsupported object type: " + x.getClass().getName());
        }
    }

    @Override
    public void addBatch()
            throws SQLException
    {
        checkOpen();
        batchValues.add(toValues(parameters));
        isBatch = true;
    }

    @Override
    public void clearBatch()
            throws SQLException
    {
        checkOpen();
        batchValues.clear();
        isBatch = false;
    }

    @Override
    public int[] executeBatch()
            throws SQLException
    {
        try {
            int[] batchUpdateCounts = new int[batchValues.size()];
            for (int i = 0; i < batchValues.size(); i++) {
                super.execute(getExecuteSql(statementName, batchValues.get(i)));
                batchUpdateCounts[i] = getUpdateCount();
            }
            return batchUpdateCounts;
        }
        finally {
            clearBatch();
        }
    }

    @Override
    public void setCharacterStream(int parameterIndex, Reader reader, int length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setCharacterStream");
    }

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

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

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

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

    @Override
    public ResultSetMetaData getMetaData()
            throws SQLException
    {
        try (Statement statement = connection().createStatement(); ResultSet resultSet = statement.executeQuery("DESCRIBE OUTPUT " + statementName)) {
            return new PrestoResultSetMetaData(getDescribeOutputColumnInfoList(resultSet));
        }
    }

    @Override
    public void setDate(int parameterIndex, Date x, Calendar cal)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setDate");
    }

    @Override
    public void setTime(int parameterIndex, Time x, Calendar cal)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setTime");
    }

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

    @Override
    public void setURL(int parameterIndex, URL x)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setURL");
    }

    @Override
    public ParameterMetaData getParameterMetaData()
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "getParameterMetaData");
    }

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

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

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

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

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

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

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

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

    @Override
    public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setObject");
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x, long length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setAsciiStream");
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream x, long length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setBinaryStream");
    }

    @Override
    public void setCharacterStream(int parameterIndex, Reader reader, long length)
            throws SQLException
    {
        throw new NotImplementedException("PreparedStatement", "setCharacterStream");
    }

    @Override
    public void setAsciiStream(int parameterIndex, InputStream x)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setAsciiStream");
    }

    @Override
    public void setBinaryStream(int parameterIndex, InputStream x)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setBinaryStream");
    }

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

    @Override
    public void setNCharacterStream(int parameterIndex, Reader value)
            throws SQLException
    {
        throw new SQLFeatureNotSupportedException("setNCharacterStream");
    }

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

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

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

    @Override
    public ResultSet executeQuery(String sql)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public int executeUpdate(String sql)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public int executeUpdate(String sql, int autoGeneratedKeys)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public int executeUpdate(String sql, int[] columnIndexes)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public int executeUpdate(String sql, String[] columnNames)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public long executeLargeUpdate(String sql)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public long executeLargeUpdate(String sql, int autoGeneratedKeys)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public long executeLargeUpdate(String sql, int[] columnIndexes)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public long executeLargeUpdate(String sql, String[] columnNames)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public boolean execute(String sql)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public boolean execute(String sql, int autoGeneratedKeys)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public boolean execute(String sql, int[] columnIndexes)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public boolean execute(String sql, String[] columnNames)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    @Override
    public void addBatch(String sql)
            throws SQLException
    {
        throw new SQLException("This method cannot be called on PreparedStatement");
    }

    private void setParameter(int parameterIndex, String value)
            throws SQLException
    {
        if (parameterIndex < 1) {
            throw new SQLException("Parameter index out of bounds: " + parameterIndex);
        }
        parameters.put(parameterIndex - 1, value);
    }

    private static List toValues(Map parameters)
            throws SQLException
    {
        ImmutableList.Builder values = ImmutableList.builder();
        for (int index = 0; index < parameters.size(); index++) {
            if (!parameters.containsKey(index)) {
                throw new SQLException("No value specified for parameter " + (index + 1));
            }
            values.add(parameters.get(index));
        }
        return values.build();
    }

    private void requireNonBatchStatement()
            throws SQLException
    {
        if (isBatch) {
            throw new SQLException("Batch prepared statement must be executed using executeBatch method");
        }
    }

    private static String getExecuteSql(String statementName, List values)
    {
        StringBuilder sql = new StringBuilder();
        sql.append("EXECUTE ").append(statementName);
        if (!values.isEmpty()) {
            sql.append(" USING ");
            Joiner.on(", ").appendTo(sql, values);
        }
        return sql.toString();
    }

    private static String formatLiteral(String type, String x)
    {
        return type + " " + formatStringLiteral(x);
    }

    private static String formatBooleanLiteral(boolean x)
    {
        return Boolean.toString(x);
    }

    private static String formatStringLiteral(String x)
    {
        return "'" + x.replace("'", "''") + "'";
    }

    private static String formatBinaryLiteral(byte[] x)
    {
        return "X'" + base16().encode(x) + "'";
    }

    private static String typedNull(int targetSqlType)
            throws SQLException
    {
        switch (targetSqlType) {
            case Types.BOOLEAN:
            case Types.BIT:
                return typedNull("BOOLEAN");
            case Types.TINYINT:
                return typedNull("TINYINT");
            case Types.SMALLINT:
                return typedNull("SMALLINT");
            case Types.INTEGER:
                return typedNull("INTEGER");
            case Types.BIGINT:
                return typedNull("BIGINT");
            case Types.FLOAT:
            case Types.REAL:
                return typedNull("REAL");
            case Types.DOUBLE:
                return typedNull("DOUBLE");
            case Types.DECIMAL:
            case Types.NUMERIC:
                return typedNull("DECIMAL");
            case Types.CHAR:
            case Types.NCHAR:
                return typedNull("CHAR");
            case Types.VARCHAR:
            case Types.NVARCHAR:
            case Types.LONGVARCHAR:
            case Types.LONGNVARCHAR:
            case Types.CLOB:
            case Types.NCLOB:
                return typedNull("VARCHAR");
            case Types.BINARY:
            case Types.VARBINARY:
            case Types.LONGVARBINARY:
            case Types.BLOB:
                return typedNull("VARBINARY");
            case Types.DATE:
                return typedNull("DATE");
            case Types.TIME:
                return typedNull("TIME");
            case Types.TIMESTAMP:
                return typedNull("TIMESTAMP");
            // TODO Types.TIME_WITH_TIMEZONE
            // TODO Types.TIMESTAMP_WITH_TIMEZONE
            case Types.NULL:
                return "NULL";
        }
        throw new SQLException("Unsupported target SQL type: " + targetSqlType);
    }

    private static String typedNull(String prestoType)
    {
        return format("CAST(NULL AS %s)", prestoType);
    }

    private static List getDescribeOutputColumnInfoList(ResultSet resultSet)
            throws SQLException
    {
        ImmutableList.Builder list = ImmutableList.builder();
        while (resultSet.next()) {
            String columnName = resultSet.getString("Column Name");
            String catalog = resultSet.getString("Catalog");
            String schema = resultSet.getString("Schema");
            String table = resultSet.getString("Table");
            ClientTypeSignature clientTypeSignature = getClientTypeSignatureFromTypeString(resultSet.getString("Type"));
            ColumnInfo.Builder builder = new ColumnInfo.Builder()
                    .setColumnName(columnName)
                    .setColumnLabel(columnName)
                    .setCatalogName(catalog)
                    .setSchemaName(schema)
                    .setTableName(table)
                    .setColumnTypeSignature(clientTypeSignature)
                    .setNullable(ColumnInfo.Nullable.UNKNOWN);
            setTypeInfo(builder, clientTypeSignature);
            list.add(builder.build());
        }
        return list.build();
    }

    @VisibleForTesting
    static ClientTypeSignature getClientTypeSignatureFromTypeString(String type)
    {
        String topLevelType;
        List arguments = new ArrayList<>();
        Matcher topLevelMatcher = TOP_LEVEL_TYPE_PATTERN.matcher(type);
        if (topLevelMatcher.matches()) {
            topLevelType = topLevelMatcher.group(1);
            String typeParameters = topLevelMatcher.group(2);
            if (topLevelType.equals("decimal")) {
                List precisionAndScale = Splitter.on(',').splitToList(typeParameters);
                checkArgument(precisionAndScale.size() == 2, "Invalid decimal parameters: %s", typeParameters);
                arguments.add(ClientTypeSignatureParameter.ofLong(parseLong(precisionAndScale.get(0))));
                arguments.add(ClientTypeSignatureParameter.ofLong(parseLong(precisionAndScale.get(1))));
            }
            else if (topLevelType.equals("char") || topLevelType.equals("varchar")) {
                long precision = parseLong(typeParameters);
                arguments.add(ClientTypeSignatureParameter.ofLong(precision));
            }
            // TODO support array, map, row etc top level types' parameters using recursive parser, current behavior is their parameter list will be empty
        }
        else {
            Matcher timestampMatcher = TIMESTAMP_WITH_TIME_ZONE_PRECISION_PATTERN.matcher(type);
            Matcher timeMatcher = TIME_WITH_TIME_ZONE_PRECISION_PATTERN.matcher(type);
            if (timestampMatcher.matches()) {
                topLevelType = "timestamp with time zone";
                arguments.add(ClientTypeSignatureParameter.ofLong(parseLong(timestampMatcher.group(1))));
            }
            else if (timeMatcher.matches()) {
                topLevelType = "time with time zone";
                arguments.add(ClientTypeSignatureParameter.ofLong(parseLong(timeMatcher.group(1))));
            }
            else {
                topLevelType = type;
                if (topLevelType.equals("varchar")) {
                    arguments.add(ClientTypeSignatureParameter.ofLong(VARCHAR_UNBOUNDED_LENGTH));
                }
            }
        }
        return new ClientTypeSignature(topLevelType, arguments);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy