55import com .clickhouse .client .api .metadata .TableSchema ;
66import com .clickhouse .client .api .query .QueryResponse ;
77import com .clickhouse .data .ClickHouseColumn ;
8+ import com .clickhouse .data .ClickHouseDataType ;
89import com .clickhouse .jdbc .internal .ExceptionUtils ;
910import com .clickhouse .jdbc .internal .FeatureManager ;
1011import com .clickhouse .jdbc .internal .JdbcUtils ;
1112import com .clickhouse .jdbc .metadata .ResultSetMetaDataImpl ;
13+ import com .google .common .collect .ImmutableMap ;
1214import org .slf4j .Logger ;
1315import org .slf4j .LoggerFactory ;
1416
3436import java .sql .Statement ;
3537import java .sql .Time ;
3638import java .sql .Timestamp ;
37- import java .time .Instant ;
3839import java .time .LocalDate ;
3940import java .time .LocalDateTime ;
40- import java .time .LocalDate ;
41- import java .time .LocalDateTime ;
42- import java .time .LocalTime ;
43- import java .time .ZoneOffset ;
4441import java .time .ZonedDateTime ;
4542import java .util .Calendar ;
4643import java .util .Collections ;
@@ -72,19 +69,30 @@ public class ResultSetImpl implements ResultSet, JdbcV2Wrapper {
7269 private final int maxRows ;
7370
7471 private Consumer <Exception > onDataTransferException ;
72+ private final Map <String , ColumnTypeBinding > columnTypeBindings ;
7573
7674 public ResultSetImpl (StatementImpl parentStatement , QueryResponse response , ClickHouseBinaryFormatReader reader ,
7775 Consumer <Exception > onDataTransferException ) throws SQLException {
76+ this (parentStatement , response , reader , onDataTransferException , JdbcUtils .DATA_TYPE_CLASS_MAP );
77+ }
78+
79+ public ResultSetImpl (StatementImpl parentStatement , QueryResponse response , ClickHouseBinaryFormatReader reader ,
80+ Consumer <Exception > onDataTransferException ,
81+ Map <ClickHouseDataType , Class <?>> defaultTypeMap ) throws SQLException {
7882 this .parentStatement = parentStatement ;
7983 this .response = response ;
8084 this .reader = reader ;
8185 this .featureManager = new FeatureManager (parentStatement .getConnection ().getJdbcConfig ());
8286 TableSchema tableMetadata = reader .getSchema ();
8387
88+ final Map <ClickHouseDataType , Class <?>> resolvedDefaultTypeMap =
89+ defaultTypeMap != null ? defaultTypeMap : JdbcUtils .DATA_TYPE_CLASS_MAP ;
90+ this .columnTypeBindings = buildColumnTypeBindings (tableMetadata , resolvedDefaultTypeMap );
91+
8492 // Result set contains columns from one database (there is a special table engine 'Merge' to do cross DB queries)
8593 this .metaData = new ResultSetMetaDataImpl (tableMetadata
8694 .getColumns (), response .getSettings ().getDatabase (), "" , tableMetadata .getTableName (),
87- JdbcUtils . DATA_TYPE_CLASS_MAP );
95+ resolvedDefaultTypeMap );
8896 this .closed = false ;
8997 this .wasNull = false ;
9098 this .defaultCalendar = parentStatement .getConnection ().defaultCalendar ;
@@ -96,6 +104,41 @@ public ResultSetImpl(StatementImpl parentStatement, QueryResponse response, Clic
96104 this .onDataTransferException = onDataTransferException ;
97105 }
98106
107+ private static Map <String , ColumnTypeBinding > buildColumnTypeBindings (TableSchema schema ,
108+ Map <ClickHouseDataType , Class <?>> typeMap ) {
109+ ImmutableMap .Builder <String , ColumnTypeBinding > bindings = ImmutableMap .builder ();
110+
111+ for (ClickHouseColumn column : schema .getColumns ()) {
112+ ClickHouseDataType dataType = column .getDataType ();
113+ bindings .put (column .getColumnName (), new ColumnTypeBinding (typeMap .get (dataType ),
114+ JdbcUtils .convertToSqlType (dataType )));
115+ }
116+ return bindings .buildKeepingLast ();
117+ }
118+
119+ /**
120+ * Immutable pair of pre-resolved values for a single column: the Java class to materialize when
121+ * no typeMap is supplied, and the JDBC {@link SQLType} that corresponds to the column's ClickHouse
122+ * data type (used as a secondary key when looking up a user-provided typeMap).
123+ */
124+ private static final class ColumnTypeBinding {
125+ private final Class <?> aClass ;
126+ private final SQLType jdbcType ;
127+
128+ ColumnTypeBinding (Class <?> aClass , SQLType jdbcType ) {
129+ this .aClass = aClass ;
130+ this .jdbcType = jdbcType ;
131+ }
132+
133+ public Class <?> getAClass () {
134+ return aClass ;
135+ }
136+
137+ public SQLType getJdbcType () {
138+ return jdbcType ;
139+ }
140+ }
141+
99142 private void checkClosed () throws SQLException {
100143 if (closed ) {
101144 throw new SQLException ("ResultSet is closed." , ExceptionUtils .SQL_STATE_CONNECTION_EXCEPTION );
@@ -1497,22 +1540,7 @@ public <T> T getObjectImpl(String columnLabel, Class<?> type, Map<String, Class<
14971540 wasNull = false ;
14981541
14991542 if (type == null ) {
1500- switch (column .getDataType ()) {
1501- case Point :
1502- case Ring :
1503- case LineString :
1504- case Polygon :
1505- case MultiPolygon :
1506- case MultiLineString :
1507- case Geometry :
1508- break ; // read as is
1509- default :
1510- if (typeMap == null || typeMap .isEmpty ()) {
1511- type = JdbcUtils .convertToJavaClass (column .getDataType ());
1512- } else {
1513- type = typeMap .get (JdbcUtils .convertToSqlType (column .getDataType ()).getName ());
1514- }
1515- }
1543+ type = resolveTargetType (columnLabel , column , typeMap );
15161544 } else {
15171545 /// shortcut
15181546 if (type == Timestamp .class ) {
@@ -1539,6 +1567,32 @@ public <T> T getObjectImpl(String columnLabel, Class<?> type, Map<String, Class<
15391567 }
15401568 }
15411569
1570+ private Class <?> resolveTargetType (String columnLabel , ClickHouseColumn column , Map <String , Class <?>> typeMap ) {
1571+ switch (column .getDataType ()) {
1572+ case Point :
1573+ case Ring :
1574+ case LineString :
1575+ case Polygon :
1576+ case MultiPolygon :
1577+ case MultiLineString :
1578+ case Geometry :
1579+ return null ; // read as is
1580+ default :
1581+ break ;
1582+ }
1583+
1584+ ColumnTypeBinding binding = columnTypeBindings .get (columnLabel );
1585+ if (typeMap == null || typeMap .isEmpty ()) {
1586+ return binding .getAClass ();
1587+ }
1588+
1589+ Class <?> resolved = typeMap .get (column .getDataType ().name ());
1590+ if (resolved == null ) {
1591+ resolved = typeMap .get (binding .getJdbcType ().getName ());
1592+ }
1593+ return resolved ;
1594+ }
1595+
15421596 @ Override
15431597 public void updateObject (int columnIndex , Object x , SQLType targetSqlType , int scaleOrLength ) throws SQLException {
15441598 updateObject (columnIndexToName (columnIndex ), x , targetSqlType , scaleOrLength );
0 commit comments