3030import  io .trino .plugin .jdbc .JdbcTypeHandle ;
3131import  io .trino .plugin .jdbc .LongReadFunction ;
3232import  io .trino .plugin .jdbc .LongWriteFunction ;
33+ import  io .trino .plugin .jdbc .ObjectReadFunction ;
34+ import  io .trino .plugin .jdbc .ObjectWriteFunction ;
3335import  io .trino .plugin .jdbc .QueryBuilder ;
3436import  io .trino .plugin .jdbc .SliceReadFunction ;
3537import  io .trino .plugin .jdbc .SliceWriteFunction ;
4345import  io .trino .spi .connector .ColumnPosition ;
4446import  io .trino .spi .connector .ConnectorSession ;
4547import  io .trino .spi .connector .ConnectorTableMetadata ;
48+ import  io .trino .spi .type .LongTimestamp ;
49+ import  io .trino .spi .type .TimestampType ;
4650import  io .trino .spi .type .Type ;
4751
4852import  java .sql .Connection ;
4953import  java .sql .Date ;
54+ import  java .sql .PreparedStatement ;
55+ import  java .sql .SQLException ;
56+ import  java .sql .Timestamp ;
5057import  java .sql .Types ;
5158import  java .time .LocalDate ;
59+ import  java .time .LocalDateTime ;
5260import  java .util .HexFormat ;
5361import  java .util .List ;
5462import  java .util .Map ;
5765import  java .util .Set ;
5866import  java .util .function .BiFunction ;
5967
68+ import  static  com .google .common .base .Preconditions .checkArgument ;
69+ import  static  io .trino .plugin .jdbc .PredicatePushdownController .FULL_PUSHDOWN ;
6070import  static  io .trino .plugin .jdbc .StandardColumnMappings .bigintColumnMapping ;
6171import  static  io .trino .plugin .jdbc .StandardColumnMappings .booleanColumnMapping ;
6272import  static  io .trino .plugin .jdbc .StandardColumnMappings .decimalColumnMapping ;
6373import  static  io .trino .plugin .jdbc .StandardColumnMappings .defaultCharColumnMapping ;
6474import  static  io .trino .plugin .jdbc .StandardColumnMappings .defaultVarcharColumnMapping ;
6575import  static  io .trino .plugin .jdbc .StandardColumnMappings .doubleColumnMapping ;
76+ import  static  io .trino .plugin .jdbc .StandardColumnMappings .fromLongTrinoTimestamp ;
77+ import  static  io .trino .plugin .jdbc .StandardColumnMappings .fromTrinoTimestamp ;
6678import  static  io .trino .plugin .jdbc .StandardColumnMappings .integerColumnMapping ;
6779import  static  io .trino .plugin .jdbc .StandardColumnMappings .smallintColumnMapping ;
80+ import  static  io .trino .plugin .jdbc .StandardColumnMappings .toLongTrinoTimestamp ;
81+ import  static  io .trino .plugin .jdbc .StandardColumnMappings .toTrinoTimestamp ;
6882import  static  io .trino .plugin .jdbc .TypeHandlingJdbcSessionProperties .getUnsupportedTypeHandling ;
6983import  static  io .trino .plugin .jdbc .UnsupportedTypeHandling .CONVERT_TO_VARCHAR ;
7084import  static  io .trino .spi .StandardErrorCode .NOT_SUPPORTED ;
7185import  static  io .trino .spi .connector .ConnectorMetadata .MODIFYING_ROWS_MESSAGE ;
7286import  static  io .trino .spi .type .DateType .DATE ;
7387import  static  io .trino .spi .type .DecimalType .createDecimalType ;
88+ import  static  io .trino .spi .type .TimestampType .createTimestampType ;
7489import  static  io .trino .spi .type .VarbinaryType .VARBINARY ;
7590import  static  java .lang .String .format ;
7691import  static  java .util .Locale .ENGLISH ;
@@ -84,6 +99,8 @@ public class ExasolClient
8499            .add ("SYS" )
85100            .build ();
86101
102+     private  static  final  int  MAX_EXASOL_TIMESTAMP_PRECISION  = 9 ;
103+ 
87104    @ Inject 
88105    public  ExasolClient (
89106            BaseJdbcConfig  config ,
@@ -239,8 +256,12 @@ public Optional<ColumnMapping> toColumnMapping(ConnectorSession session, Connect
239256                // String data is sorted by its binary representation. 
240257                // https://docs.exasol.com/db/latest/sql/select.htm#UsageNotes 
241258                return  Optional .of (defaultVarcharColumnMapping (typeHandle .requiredColumnSize (), true ));
259+                 // DATE and TIMESTAMP types are described here in more details: 
260+                 // https://docs.exasol.com/db/latest/sql_references/data_types/datatypedetails.htm 
242261            case  Types .DATE :
243262                return  Optional .of (dateColumnMapping ());
263+             case  Types .TIMESTAMP :
264+                 return  Optional .of (timestampColumnMapping (typeHandle ));
244265        }
245266
246267        if  (getUnsupportedTypeHandling (session ) == CONVERT_TO_VARCHAR ) {
@@ -307,6 +328,128 @@ private static SliceWriteFunction hashTypeWriteFunction()
307328        });
308329    }
309330
331+     private  static  ColumnMapping  timestampColumnMapping (JdbcTypeHandle  typeHandle )
332+     {
333+         int  timestampPrecision  = typeHandle .requiredDecimalDigits ();
334+         TimestampType  timestampType  = createTimestampType (timestampPrecision );
335+         if  (timestampType .isShort ()) {
336+             return  ColumnMapping .longMapping (
337+                     timestampType ,
338+                     longTimestampReadFunction (timestampType ),
339+                     longTimestampWriteFunction (timestampType ),
340+                     FULL_PUSHDOWN );
341+         }
342+         return  ColumnMapping .objectMapping (
343+                 timestampType ,
344+                 objectTimestampReadFunction (timestampType ),
345+                 objectTimestampWriteFunction (timestampType ),
346+                 FULL_PUSHDOWN );
347+     }
348+ 
349+     private  static  LongReadFunction  longTimestampReadFunction (TimestampType  timestampType )
350+     {
351+         return  (resultSet , columnIndex ) -> {
352+             Timestamp  timestamp  = resultSet .getTimestamp (columnIndex );
353+             return  toTrinoTimestamp (timestampType , timestamp .toLocalDateTime ());
354+         };
355+     }
356+ 
357+     private  static  LongWriteFunction  longTimestampWriteFunction (TimestampType  timestampType )
358+     {
359+         return  new  LongWriteFunction ()
360+         {
361+             @ Override 
362+             public  String  getBindExpression ()
363+             {
364+                 return  getTimestampBindExpression (timestampType .getPrecision ());
365+             }
366+ 
367+             @ Override 
368+             public  void  set (PreparedStatement  statement , int  index , long  epochMicros )
369+                     throws  SQLException 
370+             {
371+                 LocalDateTime  localDateTime  = fromTrinoTimestamp (epochMicros );
372+                 Timestamp  timestampValue  = Timestamp .valueOf (localDateTime );
373+                 statement .setTimestamp (index , timestampValue );
374+             }
375+ 
376+             @ Override 
377+             public  void  setNull (PreparedStatement  statement , int  index )
378+                     throws  SQLException 
379+             {
380+                 statement .setNull (index , Types .VARCHAR );
381+             }
382+         };
383+     }
384+ 
385+     private  static  ObjectReadFunction  objectTimestampReadFunction (TimestampType  timestampType )
386+     {
387+         verifyObjectTimestampPrecision (timestampType );
388+         return  ObjectReadFunction .of (
389+                 LongTimestamp .class ,
390+                 (resultSet , columnIndex ) -> {
391+                     Timestamp  timestamp  = resultSet .getTimestamp (columnIndex );
392+                     return  toLongTrinoTimestamp (timestampType , timestamp .toLocalDateTime ());
393+                 });
394+     }
395+ 
396+     private  static  ObjectWriteFunction  objectTimestampWriteFunction (TimestampType  timestampType )
397+     {
398+         int  precision  = timestampType .getPrecision ();
399+         verifyObjectTimestampPrecision (timestampType );
400+ 
401+         return  new  ObjectWriteFunction () {
402+             @ Override 
403+             public  Class <?> getJavaType ()
404+             {
405+                 return  LongTimestamp .class ;
406+             }
407+ 
408+             @ Override 
409+             public  void  set (PreparedStatement  statement , int  index , Object  value )
410+                     throws  SQLException 
411+             {
412+                 LocalDateTime  localDateTime  = fromLongTrinoTimestamp ((LongTimestamp ) value , precision );
413+                 Timestamp  timestamp  = Timestamp .valueOf (localDateTime );
414+                 statement .setTimestamp (index , timestamp );
415+             }
416+ 
417+             @ Override 
418+             public  String  getBindExpression ()
419+             {
420+                 return  getTimestampBindExpression (timestampType .getPrecision ());
421+             }
422+ 
423+             @ Override 
424+             public  void  setNull (PreparedStatement  statement , int  index )
425+                     throws  SQLException 
426+             {
427+                 statement .setNull (index , Types .VARCHAR );
428+             }
429+         };
430+     }
431+ 
432+     private  static  void  verifyObjectTimestampPrecision (TimestampType  timestampType )
433+     {
434+         int  precision  = timestampType .getPrecision ();
435+         checkArgument (precision  > TimestampType .MAX_SHORT_PRECISION  && precision  <= MAX_EXASOL_TIMESTAMP_PRECISION ,
436+                 "Precision is out of range: %s" , precision );
437+     }
438+ 
439+     /** 
440+      * Returns a {@code TO_TIMESTAMP} bind expression using the appropriate format model 
441+      * based on the given fractional seconds precision. 
442+      * See for more details: <a href="https://docs.exasol.com/db/latest/sql_references/formatmodels.htm">Date/time format models</a> 
443+      */ 
444+     private  static  String  getTimestampBindExpression (int  precision )
445+     {
446+         checkArgument (precision  >= 0 , "Precision is negative: %s" , precision );
447+         if  (precision  == 0 ) {
448+             return  "TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS')" ;
449+         }
450+         return  format ("TO_TIMESTAMP(?, 'YYYY-MM-DD HH24:MI:SS.FF%d')" , precision );
451+     }
452+ 
310453    @ Override 
311454    public  WriteMapping  toWriteMapping (ConnectorSession  session , Type  type )
312455    {
0 commit comments