@@ -404,6 +404,119 @@ def test_delta_timestamps(make_mocked_engine_adapter: t.Callable):
404404 }
405405
406406
407+ def test_timestamp_mapping ():
408+ """Test that timestamp_mapping config property is properly defined and accessible."""
409+ config = TrinoConnectionConfig (
410+ user = "user" ,
411+ host = "host" ,
412+ catalog = "catalog" ,
413+ )
414+
415+ adapter = config .create_engine_adapter ()
416+ assert adapter .timestamp_mapping is None
417+
418+ config = TrinoConnectionConfig (
419+ user = "user" ,
420+ host = "host" ,
421+ catalog = "catalog" ,
422+ timestamp_mapping = {
423+ "TIMESTAMP" : "TIMESTAMP(6)" ,
424+ "TIMESTAMP(3)" : "TIMESTAMP WITH TIME ZONE" ,
425+ },
426+ )
427+ adapter = config .create_engine_adapter ()
428+ assert adapter .timestamp_mapping is not None
429+ assert adapter .timestamp_mapping [exp .DataType .build ("TIMESTAMP" )] == exp .DataType .build (
430+ "TIMESTAMP(6)"
431+ )
432+
433+
434+ def test_delta_timestamps_with_custom_mapping (make_mocked_engine_adapter : t .Callable ):
435+ """Test that _apply_timestamp_mapping + _to_delta_ts respects custom timestamp_mapping."""
436+ # Create config with custom timestamp mapping
437+ # Mapped columns are skipped by _to_delta_ts
438+ config = TrinoConnectionConfig (
439+ user = "user" ,
440+ host = "host" ,
441+ catalog = "catalog" ,
442+ timestamp_mapping = {
443+ "TIMESTAMP" : "TIMESTAMP(3)" ,
444+ "TIMESTAMP(1)" : "TIMESTAMP(3)" ,
445+ "TIMESTAMP WITH TIME ZONE" : "TIMESTAMP(6) WITH TIME ZONE" ,
446+ "TIMESTAMP(1) WITH TIME ZONE" : "TIMESTAMP(6) WITH TIME ZONE" ,
447+ },
448+ )
449+
450+ adapter = make_mocked_engine_adapter (
451+ TrinoEngineAdapter , timestamp_mapping = config .timestamp_mapping
452+ )
453+
454+ ts3 = exp .DataType .build ("timestamp(3)" )
455+ ts6_tz = exp .DataType .build ("timestamp(6) with time zone" )
456+
457+ columns_to_types = {
458+ "ts" : exp .DataType .build ("TIMESTAMP" ),
459+ "ts_1" : exp .DataType .build ("TIMESTAMP(1)" ),
460+ "ts_tz" : exp .DataType .build ("TIMESTAMP WITH TIME ZONE" ),
461+ "ts_tz_1" : exp .DataType .build ("TIMESTAMP(1) WITH TIME ZONE" ),
462+ }
463+
464+ # Apply mapping first, then convert to delta types (skipping mapped columns)
465+ mapped_columns_to_types , mapped_column_names = adapter ._apply_timestamp_mapping (
466+ columns_to_types
467+ )
468+ delta_columns_to_types = adapter ._to_delta_ts (mapped_columns_to_types , mapped_column_names )
469+
470+ # All types were mapped, so _to_delta_ts skips them - they keep their mapped types
471+ assert delta_columns_to_types == {
472+ "ts" : ts3 ,
473+ "ts_1" : ts3 ,
474+ "ts_tz" : ts6_tz ,
475+ "ts_tz_1" : ts6_tz ,
476+ }
477+
478+
479+ def test_delta_timestamps_with_partial_mapping (make_mocked_engine_adapter : t .Callable ):
480+ """Test that _apply_timestamp_mapping + _to_delta_ts uses custom mapping for specified types."""
481+ config = TrinoConnectionConfig (
482+ user = "user" ,
483+ host = "host" ,
484+ catalog = "catalog" ,
485+ timestamp_mapping = {
486+ "TIMESTAMP" : "TIMESTAMP(3)" ,
487+ },
488+ )
489+
490+ adapter = make_mocked_engine_adapter (
491+ TrinoEngineAdapter , timestamp_mapping = config .timestamp_mapping
492+ )
493+
494+ ts3 = exp .DataType .build ("TIMESTAMP(3)" )
495+ ts6 = exp .DataType .build ("timestamp(6)" )
496+ ts3_tz = exp .DataType .build ("timestamp(3) with time zone" )
497+
498+ columns_to_types = {
499+ "ts" : exp .DataType .build ("TIMESTAMP" ),
500+ "ts_1" : exp .DataType .build ("TIMESTAMP(1)" ),
501+ "ts_tz" : exp .DataType .build ("TIMESTAMP WITH TIME ZONE" ),
502+ }
503+
504+ # Apply mapping first, then convert to delta types (skipping mapped columns)
505+ mapped_columns_to_types , mapped_column_names = adapter ._apply_timestamp_mapping (
506+ columns_to_types
507+ )
508+ delta_columns_to_types = adapter ._to_delta_ts (mapped_columns_to_types , mapped_column_names )
509+
510+ # TIMESTAMP is in mapping → TIMESTAMP(3), skipped by _to_delta_ts
511+ # TIMESTAMP(1) is NOT in mapping, uses default TIMESTAMP → ts6
512+ # TIMESTAMP WITH TIME ZONE is NOT in mapping, uses default TIMESTAMPTZ → ts3_tz
513+ assert delta_columns_to_types == {
514+ "ts" : ts3 , # Mapped to TIMESTAMP(3), skipped by _to_delta_ts
515+ "ts_1" : ts6 , # Not in mapping, uses default
516+ "ts_tz" : ts3_tz , # Not in mapping, uses default
517+ }
518+
519+
407520def test_table_format (trino_mocked_engine_adapter : TrinoEngineAdapter , mocker : MockerFixture ):
408521 adapter = trino_mocked_engine_adapter
409522 mocker .patch (
@@ -755,3 +868,77 @@ def test_insert_overwrite_time_partition_iceberg(
755868 'DELETE FROM "my_catalog"."schema"."test_table" WHERE "b" BETWEEN \' 2022-01-01\' AND \' 2022-01-02\' ' ,
756869 'INSERT INTO "my_catalog"."schema"."test_table" ("a", "b") SELECT "a", "b" FROM (SELECT "a", "b" FROM "tbl") AS "_subquery" WHERE "b" BETWEEN \' 2022-01-01\' AND \' 2022-01-02\' ' ,
757870 ]
871+
872+
873+ def test_delta_timestamps_with_non_timestamp_columns (make_mocked_engine_adapter : t .Callable ):
874+ """Test that _apply_timestamp_mapping + _to_delta_ts handles non-timestamp columns."""
875+ config = TrinoConnectionConfig (
876+ user = "user" ,
877+ host = "host" ,
878+ catalog = "catalog" ,
879+ timestamp_mapping = {
880+ "TIMESTAMP" : "TIMESTAMP(3)" ,
881+ },
882+ )
883+
884+ adapter = make_mocked_engine_adapter (
885+ TrinoEngineAdapter , timestamp_mapping = config .timestamp_mapping
886+ )
887+
888+ ts3 = exp .DataType .build ("TIMESTAMP(3)" )
889+ ts6 = exp .DataType .build ("timestamp(6)" )
890+
891+ columns_to_types = {
892+ "ts" : exp .DataType .build ("TIMESTAMP" ),
893+ "ts_1" : exp .DataType .build ("TIMESTAMP(1)" ),
894+ "int_col" : exp .DataType .build ("INT" ),
895+ "varchar_col" : exp .DataType .build ("VARCHAR(100)" ),
896+ "decimal_col" : exp .DataType .build ("DECIMAL(10,2)" ),
897+ }
898+
899+ # Apply mapping first, then convert to delta types (skipping mapped columns)
900+ mapped_columns_to_types , mapped_column_names = adapter ._apply_timestamp_mapping (
901+ columns_to_types
902+ )
903+ delta_columns_to_types = adapter ._to_delta_ts (mapped_columns_to_types , mapped_column_names )
904+
905+ # TIMESTAMP is in mapping → TIMESTAMP(3), skipped by _to_delta_ts
906+ # TIMESTAMP(1) is NOT in mapping (exact match), uses default TIMESTAMP → ts6
907+ # Non-timestamp columns should pass through unchanged
908+ assert delta_columns_to_types == {
909+ "ts" : ts3 , # Mapped to TIMESTAMP(3), skipped by _to_delta_ts
910+ "ts_1" : ts6 , # Not in mapping, uses default
911+ "int_col" : exp .DataType .build ("INT" ),
912+ "varchar_col" : exp .DataType .build ("VARCHAR(100)" ),
913+ "decimal_col" : exp .DataType .build ("DECIMAL(10,2)" ),
914+ }
915+
916+
917+ def test_delta_timestamps_with_empty_mapping (make_mocked_engine_adapter : t .Callable ):
918+ """Test that _to_delta_ts handles empty custom mapping dictionary."""
919+ config = TrinoConnectionConfig (
920+ user = "user" ,
921+ host = "host" ,
922+ catalog = "catalog" ,
923+ timestamp_mapping = {},
924+ )
925+
926+ adapter = make_mocked_engine_adapter (
927+ TrinoEngineAdapter , timestamp_mapping = config .timestamp_mapping
928+ )
929+
930+ ts6 = exp .DataType .build ("timestamp(6)" )
931+ ts3_tz = exp .DataType .build ("timestamp(3) with time zone" )
932+
933+ columns_to_types = {
934+ "ts" : exp .DataType .build ("TIMESTAMP" ),
935+ "ts_tz" : exp .DataType .build ("TIMESTAMP WITH TIME ZONE" ),
936+ }
937+
938+ delta_columns_to_types = adapter ._to_delta_ts (columns_to_types )
939+
940+ # With empty custom mapping, should fall back to defaults
941+ assert delta_columns_to_types == {
942+ "ts" : ts6 ,
943+ "ts_tz" : ts3_tz ,
944+ }
0 commit comments