1717import com .google .inject .Inject ;
1818import io .airlift .slice .Slices ;
1919import io .trino .plugin .base .mapping .IdentifierMapping ;
20- import io .trino .plugin .jdbc .BaseJdbcClient ;
21- import io .trino .plugin .jdbc .BaseJdbcConfig ;
22- import io .trino .plugin .jdbc .ColumnMapping ;
23- import io .trino .plugin .jdbc .ConnectionFactory ;
24- import io .trino .plugin .jdbc .JdbcColumnHandle ;
25- import io .trino .plugin .jdbc .JdbcExpression ;
26- import io .trino .plugin .jdbc .JdbcJoinCondition ;
27- import io .trino .plugin .jdbc .JdbcOutputTableHandle ;
28- import io .trino .plugin .jdbc .JdbcSortItem ;
29- import io .trino .plugin .jdbc .JdbcTableHandle ;
30- import io .trino .plugin .jdbc .JdbcTypeHandle ;
31- import io .trino .plugin .jdbc .LongReadFunction ;
32- import io .trino .plugin .jdbc .LongWriteFunction ;
33- import io .trino .plugin .jdbc .QueryBuilder ;
34- import io .trino .plugin .jdbc .SliceReadFunction ;
35- import io .trino .plugin .jdbc .SliceWriteFunction ;
36- import io .trino .plugin .jdbc .WriteFunction ;
37- import io .trino .plugin .jdbc .WriteMapping ;
20+ import io .trino .plugin .jdbc .*;
3821import io .trino .plugin .jdbc .logging .RemoteQueryModifier ;
3922import io .trino .spi .TrinoException ;
4023import io .trino .spi .connector .AggregateFunction ;
4326import io .trino .spi .connector .ColumnPosition ;
4427import io .trino .spi .connector .ConnectorSession ;
4528import io .trino .spi .connector .ConnectorTableMetadata ;
29+ import io .trino .spi .type .LongTimestamp ;
30+ import io .trino .spi .type .TimestampType ;
4631import io .trino .spi .type .Type ;
4732
48- import java .sql .Connection ;
49- import java .sql .Date ;
50- import java .sql .Types ;
33+ import java .sql .*;
5134import java .time .LocalDate ;
35+ import java .time .LocalDateTime ;
5236import java .util .HexFormat ;
5337import java .util .List ;
5438import java .util .Map ;
5741import java .util .Set ;
5842import java .util .function .BiFunction ;
5943
60- import static io .trino .plugin .jdbc .StandardColumnMappings .bigintColumnMapping ;
61- import static io .trino .plugin .jdbc .StandardColumnMappings .booleanColumnMapping ;
62- import static io .trino .plugin .jdbc .StandardColumnMappings .decimalColumnMapping ;
63- import static io .trino .plugin .jdbc .StandardColumnMappings .defaultCharColumnMapping ;
64- import static io .trino .plugin .jdbc .StandardColumnMappings .defaultVarcharColumnMapping ;
65- import static io .trino .plugin .jdbc .StandardColumnMappings .doubleColumnMapping ;
66- import static io .trino .plugin .jdbc .StandardColumnMappings .integerColumnMapping ;
67- import static io .trino .plugin .jdbc .StandardColumnMappings .smallintColumnMapping ;
44+ import static com .google .common .base .Preconditions .checkArgument ;
45+ import static io .trino .plugin .jdbc .PredicatePushdownController .FULL_PUSHDOWN ;
46+ import static io .trino .plugin .jdbc .StandardColumnMappings .*;
6847import static io .trino .plugin .jdbc .TypeHandlingJdbcSessionProperties .getUnsupportedTypeHandling ;
6948import static io .trino .plugin .jdbc .UnsupportedTypeHandling .CONVERT_TO_VARCHAR ;
7049import static io .trino .spi .StandardErrorCode .NOT_SUPPORTED ;
7150import static io .trino .spi .connector .ConnectorMetadata .MODIFYING_ROWS_MESSAGE ;
7251import static io .trino .spi .type .DateType .DATE ;
7352import static io .trino .spi .type .DecimalType .createDecimalType ;
53+ import static io .trino .spi .type .TimestampType .createTimestampType ;
7454import static io .trino .spi .type .VarbinaryType .VARBINARY ;
7555import static java .lang .String .format ;
7656import static java .util .Locale .ENGLISH ;
@@ -84,6 +64,8 @@ public class ExasolClient
8464 .add ("SYS" )
8565 .build ();
8666
67+ private static final int MAX_EXASOL_TIMESTAMP_PRECISION = 9 ;
68+
8769 @ Inject
8870 public ExasolClient (
8971 BaseJdbcConfig config ,
@@ -239,8 +221,12 @@ public Optional<ColumnMapping> toColumnMapping(ConnectorSession session, Connect
239221 // String data is sorted by its binary representation.
240222 // https://docs.exasol.com/db/latest/sql/select.htm#UsageNotes
241223 return Optional .of (defaultVarcharColumnMapping (typeHandle .requiredColumnSize (), true ));
224+ // DATE and TIMESTAMP types are described here in more details:
225+ // https://docs.exasol.com/db/latest/sql_references/data_types/datatypedetails.htm
242226 case Types .DATE :
243227 return Optional .of (dateColumnMapping ());
228+ case Types .TIMESTAMP :
229+ return Optional .of (timestampColumnMapping (typeHandle ));
244230 }
245231
246232 if (getUnsupportedTypeHandling (session ) == CONVERT_TO_VARCHAR ) {
@@ -307,6 +293,128 @@ private static SliceWriteFunction hashTypeWriteFunction()
307293 });
308294 }
309295
296+ private static ColumnMapping timestampColumnMapping (JdbcTypeHandle typeHandle )
297+ {
298+ int timestampPrecision = typeHandle .requiredDecimalDigits ();
299+ TimestampType timestampType = createTimestampType (timestampPrecision );
300+ if (timestampType .isShort ()) {
301+ return ColumnMapping .longMapping (
302+ timestampType ,
303+ longTimestampReadFunction (timestampType ),
304+ longTimestampWriteFunction (timestampType ),
305+ FULL_PUSHDOWN );
306+ }
307+ return ColumnMapping .objectMapping (
308+ timestampType ,
309+ objectTimestampReadFunction (timestampType ),
310+ objectTimestampWriteFunction (timestampType ),
311+ FULL_PUSHDOWN );
312+ }
313+
314+ private static LongReadFunction longTimestampReadFunction (TimestampType timestampType )
315+ {
316+ return (resultSet , columnIndex ) -> {
317+ Timestamp timestamp = resultSet .getTimestamp (columnIndex );
318+ return toTrinoTimestamp (timestampType , timestamp .toLocalDateTime ());
319+ };
320+ }
321+
322+ private static LongWriteFunction longTimestampWriteFunction (TimestampType timestampType )
323+ {
324+ return new LongWriteFunction ()
325+ {
326+ @ Override
327+ public String getBindExpression ()
328+ {
329+ return getTimestampBindExpression (timestampType .getPrecision ());
330+ }
331+
332+ @ Override
333+ public void set (PreparedStatement statement , int index , long epochMicros )
334+ throws SQLException
335+ {
336+ LocalDateTime localDateTime = fromTrinoTimestamp (epochMicros );
337+ Timestamp timestampValue = Timestamp .valueOf (localDateTime );
338+ statement .setTimestamp (index , timestampValue );
339+ }
340+
341+ @ Override
342+ public void setNull (PreparedStatement statement , int index )
343+ throws SQLException
344+ {
345+ statement .setNull (index , Types .VARCHAR );
346+ }
347+ };
348+ }
349+
350+ private static ObjectReadFunction objectTimestampReadFunction (TimestampType timestampType )
351+ {
352+ verifyObjectTimestampPrecision (timestampType );
353+ return ObjectReadFunction .of (
354+ LongTimestamp .class ,
355+ (resultSet , columnIndex ) -> {
356+ Timestamp timestamp = resultSet .getTimestamp (columnIndex );
357+ return toLongTrinoTimestamp (timestampType , timestamp .toLocalDateTime ());
358+ });
359+ }
360+
361+ private static ObjectWriteFunction objectTimestampWriteFunction (TimestampType timestampType )
362+ {
363+ int precision = timestampType .getPrecision ();
364+ verifyObjectTimestampPrecision (timestampType );
365+
366+ return new ObjectWriteFunction () {
367+ @ Override
368+ public Class <?> getJavaType ()
369+ {
370+ return LongTimestamp .class ;
371+ }
372+
373+ @ Override
374+ public void set (PreparedStatement statement , int index , Object value )
375+ throws SQLException
376+ {
377+ LocalDateTime localDateTime = fromLongTrinoTimestamp ((LongTimestamp ) value , precision );
378+ Timestamp timestamp = Timestamp .valueOf (localDateTime );
379+ statement .setTimestamp (index , timestamp );
380+ }
381+
382+ @ Override
383+ public String getBindExpression ()
384+ {
385+ return getTimestampBindExpression (timestampType .getPrecision ());
386+ }
387+
388+ @ Override
389+ public void setNull (PreparedStatement statement , int index )
390+ throws SQLException
391+ {
392+ statement .setNull (index , Types .VARCHAR );
393+ }
394+ };
395+ }
396+
397+ private static void verifyObjectTimestampPrecision (TimestampType timestampType )
398+ {
399+ int precision = timestampType .getPrecision ();
400+ checkArgument (precision > TimestampType .MAX_SHORT_PRECISION && precision <= MAX_EXASOL_TIMESTAMP_PRECISION ,
401+ "Precision is out of range: %s" , precision );
402+ }
403+
404+ /**
405+ * Returns a {@code TO_TIMESTAMP} bind expression using the appropriate format model
406+ * based on the given fractional seconds precision.
407+ * See for more details: <a href="https://docs.exasol.com/db/latest/sql_references/formatmodels.htm">Date/time format models</a>
408+ */
409+ private static String getTimestampBindExpression (int precision )
410+ {
411+ checkArgument (precision >= 0 , "Precision is negative: %s" , precision );
412+ if (precision == 0 ) {
413+ return "TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')" ;
414+ }
415+ return format ("TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS.FF%d')" , precision );
416+ }
417+
310418 @ Override
311419 public WriteMapping toWriteMapping (ConnectorSession session , Type type )
312420 {
0 commit comments