Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.trino.spi.type.CharType;
import io.trino.spi.type.DecimalType;
import io.trino.spi.type.Decimals;
import io.trino.spi.type.LongTimestamp;
import io.trino.spi.type.TimeType;
import io.trino.spi.type.TimestampType;
import io.trino.spi.type.Type;
Expand Down Expand Up @@ -62,7 +63,6 @@
import static io.trino.spi.type.RealType.REAL;
import static io.trino.spi.type.SmallintType.SMALLINT;
import static io.trino.spi.type.TimeType.TIME;
import static io.trino.spi.type.TimestampType.MAX_SHORT_PRECISION;
import static io.trino.spi.type.TimestampType.TIMESTAMP_MILLIS;
import static io.trino.spi.type.Timestamps.MICROSECONDS_PER_SECOND;
import static io.trino.spi.type.Timestamps.NANOSECONDS_PER_DAY;
Expand Down Expand Up @@ -91,6 +91,8 @@

public final class StandardColumnMappings
{
private static final int MAX_LOCAL_DATE_TIME_PRECISION = 9;

private StandardColumnMappings() {}

public static ColumnMapping booleanColumnMapping()
Expand Down Expand Up @@ -439,7 +441,7 @@ public static LongWriteFunction timeWriteFunction(int precision)
public static ColumnMapping timestampColumnMappingUsingSqlTimestamp(TimestampType timestampType)
{
// TODO support higher precision
checkArgument(timestampType.getPrecision() <= MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
checkArgument(timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return ColumnMapping.longMapping(
timestampType,
(resultSet, columnIndex) -> {
Expand All @@ -457,19 +459,34 @@ public static ColumnMapping timestampColumnMapping()

public static ColumnMapping timestampColumnMapping(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() <= MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return ColumnMapping.longMapping(
if (timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION) {
return ColumnMapping.longMapping(
timestampType,
timestampReadFunction(timestampType),
timestampWriteFunction(timestampType));
}
checkArgument(timestampType.getPrecision() <= MAX_LOCAL_DATE_TIME_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return ColumnMapping.objectMapping(
Comment thread
nineinchnick marked this conversation as resolved.
Outdated
Comment thread
nineinchnick marked this conversation as resolved.
Outdated
timestampType,
timestampReadFunction(timestampType),
timestampWriteFunction(timestampType));
longTimestampReadFunction(timestampType),
longTimestampWriteFunction(timestampType));
}

public static LongReadFunction timestampReadFunction(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() <= MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
checkArgument(timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return (resultSet, columnIndex) -> toPrestoTimestamp(timestampType, resultSet.getObject(columnIndex, LocalDateTime.class));
}

private static ObjectReadFunction longTimestampReadFunction(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() > TimestampType.MAX_SHORT_PRECISION && timestampType.getPrecision() < MAX_LOCAL_DATE_TIME_PRECISION,
"Precision is out of range: %s", timestampType.getPrecision());
return ObjectReadFunction.of(
LongTimestamp.class,
(resultSet, columnIndex) -> toLongTimestamp(timestampType, resultSet.getObject(columnIndex, LocalDateTime.class)));
}

/**
* @deprecated This method uses {@link java.sql.Timestamp} and the class cannot represent date-time value when JVM zone had
* forward offset change (a 'gap'). This includes regular DST changes (e.g. Europe/Warsaw) and one-time policy changes
Expand All @@ -479,22 +496,51 @@ public static LongReadFunction timestampReadFunction(TimestampType timestampType
@Deprecated
public static LongWriteFunction timestampWriteFunctionUsingSqlTimestamp(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() <= MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
checkArgument(timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return (statement, index, value) -> statement.setTimestamp(index, Timestamp.valueOf(fromPrestoTimestamp(value)));
}

public static LongWriteFunction timestampWriteFunction(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() <= MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
checkArgument(timestampType.getPrecision() <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return (statement, index, value) -> statement.setObject(index, fromPrestoTimestamp(value));
}

public static ObjectWriteFunction longTimestampWriteFunction(TimestampType timestampType)
{
checkArgument(timestampType.getPrecision() > TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", timestampType.getPrecision());
return ObjectWriteFunction.of(
LongTimestamp.class,
(statement, index, value) -> statement.setObject(index, fromLongTimestamp(value, timestampType.getPrecision())));
}

public static long toPrestoTimestamp(TimestampType timestampType, LocalDateTime localDateTime)
{
long precision = timestampType.getPrecision();
checkArgument(precision <= MAX_SHORT_PRECISION, "Precision is out of range: %s", precision);
checkArgument(precision <= TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", precision);
Instant instant = localDateTime.atZone(UTC).toInstant();
return instant.getEpochSecond() * MICROSECONDS_PER_SECOND + roundDiv(instant.getNano(), NANOSECONDS_PER_MICROSECOND);
long epochMicros = instant.getEpochSecond() * MICROSECONDS_PER_SECOND + roundDiv(instant.getNano(), NANOSECONDS_PER_MICROSECOND);
verify(
epochMicros == round(epochMicros, TimestampType.MAX_SHORT_PRECISION - timestampType.getPrecision()),
"Invalid value of epochMicros for precision %s: %s",
timestampType.getPrecision(),
epochMicros);
return epochMicros;
}

public static LongTimestamp toLongTimestamp(TimestampType timestampType, LocalDateTime localDateTime)
{
long precision = timestampType.getPrecision();
checkArgument(precision > TimestampType.MAX_SHORT_PRECISION, "Precision is out of range: %s", precision);
Instant instant = localDateTime.atZone(UTC).toInstant();
long epochMicros = instant.getEpochSecond() * MICROSECONDS_PER_SECOND + floorDiv(instant.getNano(), NANOSECONDS_PER_MICROSECOND);
int picosOfMicro = (instant.getNano() % NANOSECONDS_PER_MICROSECOND) * PICOSECONDS_PER_NANOSECOND;
verify(
picosOfMicro == round(picosOfMicro, TimestampType.MAX_PRECISION - timestampType.getPrecision()),
"Invalid value of picosOfMicro for precision %s: %s",
timestampType.getPrecision(),
picosOfMicro);
return new LongTimestamp(epochMicros, picosOfMicro);
}

public static LocalDateTime fromPrestoTimestamp(long epochMicros)
Expand All @@ -505,6 +551,17 @@ public static LocalDateTime fromPrestoTimestamp(long epochMicros)
return LocalDateTime.ofInstant(instant, UTC);
}

public static LocalDateTime fromLongTimestamp(LongTimestamp timestamp, int precision)
{
long epochSeconds = floorDiv(timestamp.getEpochMicros(), MICROSECONDS_PER_SECOND);
int microsOfSecond = floorMod(timestamp.getEpochMicros(), MICROSECONDS_PER_SECOND);
long picosOfMicro = round(timestamp.getPicosOfMicro(), TimestampType.MAX_PRECISION - precision);
int nanosOfSecond = (microsOfSecond * NANOSECONDS_PER_MICROSECOND) + toIntExact(picosOfMicro / PICOSECONDS_PER_NANOSECOND);

Instant instant = Instant.ofEpochSecond(epochSeconds, nanosOfSecond);
return LocalDateTime.ofInstant(instant, UTC);
}

public static LocalTime fromPrestoTime(long value)
{
// value can round up to NANOSECONDS_PER_DAY, so we need to do % to keep it in the desired range
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import io.trino.spi.type.CharType;
import io.trino.spi.type.DecimalType;
import io.trino.spi.type.Decimals;
import io.trino.spi.type.TimestampType;
import io.trino.spi.type.Type;
import io.trino.spi.type.VarbinaryType;
import io.trino.spi.type.VarcharType;
Expand Down Expand Up @@ -86,11 +87,13 @@
import static io.trino.plugin.jdbc.StandardColumnMappings.doubleWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.integerColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.integerWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.longTimestampWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.realColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.smallintColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.smallintWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.timeColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.timestampColumnMappingUsingSqlTimestamp;
import static io.trino.plugin.jdbc.StandardColumnMappings.timestampColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.timestampWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.tinyintColumnMapping;
import static io.trino.plugin.jdbc.StandardColumnMappings.tinyintWriteFunction;
import static io.trino.plugin.jdbc.StandardColumnMappings.varcharWriteFunction;
Expand All @@ -106,10 +109,12 @@
import static io.trino.spi.type.IntegerType.INTEGER;
import static io.trino.spi.type.SmallintType.SMALLINT;
import static io.trino.spi.type.TimeType.TIME;
import static io.trino.spi.type.TimestampType.TIMESTAMP;
import static io.trino.spi.type.TimestampType.MAX_SHORT_PRECISION;
import static io.trino.spi.type.TimestampType.createTimestampType;
import static io.trino.spi.type.TinyintType.TINYINT;
import static io.trino.spi.type.VarbinaryType.VARBINARY;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.String.format;
import static java.lang.String.join;
import static java.math.RoundingMode.UNNECESSARY;
Expand All @@ -131,6 +136,8 @@ public class SqlServerClient

private final AggregateFunctionRewriter aggregateFunctionRewriter;

private static final int MAX_SUPPORTED_TIMESTAMP_PRECISION = 7;

@Inject
public SqlServerClient(BaseJdbcConfig config, ConnectionFactory connectionFactory)
{
Expand Down Expand Up @@ -233,7 +240,7 @@ public Optional<ColumnMapping> toColumnMapping(ConnectorSession session, Connect
case Types.NUMERIC:
case Types.DECIMAL: {
int columnSize = typeHandle.getRequiredColumnSize();
int decimalDigits = typeHandle.getDecimalDigits().orElseThrow(() -> new IllegalStateException("decimal digits not present"));
int decimalDigits = typeHandle.getRequiredDecimalDigits();
// TODO does sql server support negative scale?
int precision = columnSize + max(-decimalDigits, 0); // Map decimal(p, -s) (negative scale) to decimal(p+s, 0).
if (precision > Decimals.MAX_PRECISION) {
Expand Down Expand Up @@ -262,7 +269,8 @@ public Optional<ColumnMapping> toColumnMapping(ConnectorSession session, Connect
return Optional.of(timeColumnMapping(TIME));

case Types.TIMESTAMP:
return Optional.of(timestampColumnMappingUsingSqlTimestamp(TIMESTAMP));
int precision = typeHandle.getRequiredDecimalDigits();
return Optional.of(timestampColumnMapping(createTimestampType(precision)));
}

// TODO (https://github.com/trinodb/trino/issues/4593) implement proper type mapping
Expand Down Expand Up @@ -325,6 +333,15 @@ public WriteMapping toWriteMapping(ConnectorSession session, Type type)
return WriteMapping.longMapping("date", dateWriteFunction());
}

if (type instanceof TimestampType) {
TimestampType timestampType = (TimestampType) type;
String dataType = format("datetime2(%d)", min(timestampType.getPrecision(), MAX_SUPPORTED_TIMESTAMP_PRECISION));
if (timestampType.getPrecision() <= MAX_SHORT_PRECISION) {
return WriteMapping.longMapping(dataType, timestampWriteFunction(timestampType));
}
return WriteMapping.objectMapping(dataType, longTimestampWriteFunction(timestampType));
}

// TODO implement proper type mapping
return legacyToWriteMapping(session, type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ protected Optional<DataMappingTestSetup> filterDataMappingSmokeTestData(DataMapp
{
String typeName = dataMappingTestSetup.getTrinoTypeName();
if (typeName.equals("time")
|| typeName.equals("timestamp")
|| typeName.equals("timestamp(3) with time zone")) {
return Optional.of(dataMappingTestSetup.asUnsupported());
}
Expand Down
Loading