StandardReadMappings.java
/*
* 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 com.facebook.presto.plugin.clickhouse;
import com.facebook.presto.common.type.CharType;
import com.facebook.presto.common.type.DecimalType;
import com.facebook.presto.common.type.Decimals;
import com.facebook.presto.common.type.VarcharType;
import com.facebook.presto.spi.PrestoException;
import com.google.common.base.CharMatcher;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Optional;
import static com.facebook.presto.common.type.BigintType.BIGINT;
import static com.facebook.presto.common.type.BooleanType.BOOLEAN;
import static com.facebook.presto.common.type.CharType.createCharType;
import static com.facebook.presto.common.type.DateType.DATE;
import static com.facebook.presto.common.type.DecimalType.createDecimalType;
import static com.facebook.presto.common.type.Decimals.encodeScaledValue;
import static com.facebook.presto.common.type.Decimals.encodeShortScaledValue;
import static com.facebook.presto.common.type.DoubleType.DOUBLE;
import static com.facebook.presto.common.type.IntegerType.INTEGER;
import static com.facebook.presto.common.type.RealType.REAL;
import static com.facebook.presto.common.type.SmallintType.SMALLINT;
import static com.facebook.presto.common.type.TimeType.TIME;
import static com.facebook.presto.common.type.TimestampType.TIMESTAMP;
import static com.facebook.presto.common.type.TinyintType.TINYINT;
import static com.facebook.presto.common.type.VarbinaryType.VARBINARY;
import static com.facebook.presto.common.type.VarcharType.createUnboundedVarcharType;
import static com.facebook.presto.common.type.VarcharType.createVarcharType;
import static com.facebook.presto.plugin.clickhouse.ClickHouseErrorCode.JDBC_ERROR;
import static com.facebook.presto.plugin.clickhouse.DateTimeUtil.getMillisOfDay;
import static com.facebook.presto.plugin.clickhouse.ReadMapping.longReadMapping;
import static com.facebook.presto.plugin.clickhouse.ReadMapping.sliceReadMapping;
import static com.facebook.presto.plugin.clickhouse.TimestampUtil.getMillisecondsFromTimestampString;
import static io.airlift.slice.Slices.utf8Slice;
import static io.airlift.slice.Slices.wrappedBuffer;
import static java.lang.Float.floatToRawIntBits;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;
public final class StandardReadMappings
{
private StandardReadMappings() {}
public static ReadMapping booleanReadMapping()
{
return ReadMapping.booleanReadMapping(BOOLEAN, ResultSet::getBoolean);
}
public static ReadMapping tinyintReadMapping()
{
return longReadMapping(TINYINT, ResultSet::getByte);
}
public static ReadMapping smallintReadMapping()
{
return longReadMapping(SMALLINT, ResultSet::getShort);
}
public static ReadMapping integerReadMapping()
{
return longReadMapping(INTEGER, ResultSet::getInt);
}
public static ReadMapping bigintReadMapping()
{
return longReadMapping(BIGINT, ResultSet::getLong);
}
public static ReadMapping realReadMapping()
{
return longReadMapping(REAL, (resultSet, columnIndex) -> floatToRawIntBits(resultSet.getFloat(columnIndex)));
}
public static ReadMapping doubleReadMapping()
{
return ReadMapping.doubleReadMapping(DOUBLE, ResultSet::getDouble);
}
public static ReadMapping decimalReadMapping(DecimalType decimalType)
{
// JDBC driver can return BigDecimal with lower scale than column's scale when there are trailing zeroes
int scale = decimalType.getScale();
if (decimalType.isShort()) {
return longReadMapping(decimalType, (resultSet, columnIndex) -> encodeShortScaledValue(resultSet.getBigDecimal(columnIndex), scale));
}
return sliceReadMapping(decimalType, (resultSet, columnIndex) -> encodeScaledValue(resultSet.getBigDecimal(columnIndex), scale));
}
public static ReadMapping charReadMapping(CharType charType)
{
requireNonNull(charType, "charType is null");
return sliceReadMapping(charType, (resultSet, columnIndex) -> utf8Slice(CharMatcher.is(' ').trimTrailingFrom(resultSet.getString(columnIndex))));
}
public static ReadMapping varcharReadMapping(VarcharType varcharType)
{
return sliceReadMapping(varcharType, (resultSet, columnIndex) -> utf8Slice(resultSet.getString(columnIndex)));
}
public static ReadMapping varbinaryReadMapping()
{
return sliceReadMapping(VARBINARY, (resultSet, columnIndex) -> wrappedBuffer(resultSet.getBytes(columnIndex)));
}
public static ReadMapping dateReadMapping()
{
return longReadMapping(DATE, (resultSet, columnIndex) -> {
Date date = resultSet.getDate(columnIndex);
return DateTimeUtil.convertDateToZonedDays(date);
});
}
public static ReadMapping timeReadMapping()
{
return longReadMapping(TIME, (resultSet, columnIndex) -> {
Time time = resultSet.getTime(columnIndex);
return getMillisOfDay(time);
});
}
public static ReadMapping timestampReadMapping()
{
return longReadMapping(TIMESTAMP, (resultSet, columnIndex) -> {
Timestamp timestamp = resultSet.getTimestamp(columnIndex);
// getTimestamp loses the milliseconds, but we can get them from the getString
return timestamp.getTime() + getMillisecondsFromTimestampString(resultSet.getString(columnIndex));
});
}
public static Optional<ReadMapping> jdbcTypeToPrestoType(ClickHouseTypeHandle type, boolean mapStringAsVarchar)
{
String jdbcTypeName = type.getJdbcTypeName()
.orElseThrow(() -> new PrestoException(JDBC_ERROR, "Type name is missing: " + type));
int columnSize = type.getColumnSize();
switch (jdbcTypeName.replaceAll("\\(.*\\)$", "")) {
case "IPv4":
case "IPv6":
case "Enum8":
case "Enum16":
return Optional.of(varcharReadMapping(createUnboundedVarcharType()));
case "FixedString": // FixedString(n)
case "String":
if (mapStringAsVarchar) {
return Optional.of(varcharReadMapping(createUnboundedVarcharType()));
}
return Optional.of(varbinaryReadMapping());
case "DateTime64": // DateTime64(n)
return Optional.of(timestampReadMapping());
case "block":
return Optional.of(doubleReadMapping());
}
switch (type.getJdbcType()) {
case Types.BIT:
case Types.BOOLEAN:
return Optional.of(booleanReadMapping());
case Types.TINYINT:
return Optional.of(tinyintReadMapping());
case Types.SMALLINT:
return Optional.of(smallintReadMapping());
case Types.INTEGER:
return Optional.of(integerReadMapping());
case Types.BIGINT:
return Optional.of(bigintReadMapping());
case Types.REAL:
return Optional.of(realReadMapping());
case Types.FLOAT:
case Types.DOUBLE:
return Optional.of(doubleReadMapping());
case Types.NUMERIC:
case Types.DECIMAL:
int decimalDigits = type.getDecimalDigits();
int precision = columnSize + max(-decimalDigits, 0);
if (precision > Decimals.MAX_PRECISION) {
return Optional.empty();
}
return Optional.of(decimalReadMapping(createDecimalType(precision, max(decimalDigits, 0))));
case Types.CHAR:
case Types.NCHAR:
int charLength = min(columnSize, CharType.MAX_LENGTH);
return Optional.of(charReadMapping(createCharType(charLength)));
case Types.VARCHAR:
case Types.NVARCHAR:
case Types.LONGVARCHAR:
case Types.LONGNVARCHAR:
if (columnSize > VarcharType.MAX_LENGTH) {
return Optional.of(varcharReadMapping(createUnboundedVarcharType()));
}
return Optional.of(varcharReadMapping(createVarcharType(columnSize)));
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
return Optional.of(varbinaryReadMapping());
case Types.DATE:
return Optional.of(dateReadMapping());
case Types.TIME:
return Optional.of(timeReadMapping());
case Types.TIMESTAMP:
return Optional.of(timestampReadMapping());
}
return Optional.empty();
}
}