@@ -380,6 +380,10 @@ public abstract class DbType(public val dbTypeInJdbcUrl: String) {
380380 tableColumnMetadata.jdbcType == Types .NUMERIC &&
381381 tableColumnMetadata.javaClassName == " java.lang.Double" -> Double ::class
382382
383+ // Force BIGINT to always be Long, regardless of javaClassName
384+ // Some JDBC drivers (e.g., MariaDB) may report Integer for small BIGINT values
385+ // TODO: tableColumnMetadata.jdbcType == Types.BIGINT -> Long::class
386+
383387 else -> jdbcTypeToKTypeMapping[tableColumnMetadata.jdbcType] ? : String ::class
384388 }
385389
@@ -402,14 +406,22 @@ public abstract class DbType(public val dbTypeInJdbcUrl: String) {
402406 /* *
403407 * Retrieves column metadata from a JDBC ResultSet.
404408 *
405- * By default, this method reads column metadata from [ResultSetMetaData],
406- * which is fast and supported by most JDBC drivers.
407- * If the driver does not provide sufficient information (e.g., `isNullable` unknown),
408- * it falls back to using [DatabaseMetaData.getColumns] for affected columns.
409+ * This method reads column metadata from [ResultSetMetaData] with graceful fallbacks
410+ * for JDBC drivers that throw [java.sql.SQLFeatureNotSupportedException] for certain methods
411+ * (e.g., Apache Hive).
412+ *
413+ * Fallback behavior for unsupported methods:
414+ * - `getColumnName()` → `getColumnLabel()` → `"column_N"`
415+ * - `getTableName()` → extract from column name if contains '.' → `null`
416+ * - `isNullable()` → [DatabaseMetaData.getColumns] → `true` (assume nullable)
417+ * - `getColumnTypeName()` → `"OTHER"`
418+ * - `getColumnType()` → [java.sql.Types.OTHER]
419+ * - `getColumnDisplaySize()` → `0`
420+ * - `getColumnClassName()` → `"java.lang.Object"`
409421 *
410422 * Override this method in subclasses to provide database-specific behavior
411423 * (for example, to disable fallback for databases like Teradata or Oracle
412- * where ` DatabaseMetaData.getColumns` is known to be slow).
424+ * where [ DatabaseMetaData.getColumns] is known to be slow).
413425 *
414426 * @param resultSet The [ResultSet] containing query results.
415427 * @return A list of [TableColumnMetadata] objects.
@@ -418,16 +430,44 @@ public abstract class DbType(public val dbTypeInJdbcUrl: String) {
418430 val rsMetaData = resultSet.metaData
419431 val connection = resultSet.statement.connection
420432 val dbMetaData = connection.metaData
421- val catalog = connection.catalog.takeUnless { it.isNullOrBlank() }
422- val schema = connection.schema.takeUnless { it.isNullOrBlank() }
433+
434+ // Some JDBC drivers (e.g., Hive) throw SQLFeatureNotSupportedException
435+ val catalog = try {
436+ connection.catalog.takeUnless { it.isNullOrBlank() }
437+ } catch (_: Exception ) {
438+ null
439+ }
440+
441+ val schema = try {
442+ connection.schema.takeUnless { it.isNullOrBlank() }
443+ } catch (_: Exception ) {
444+ null
445+ }
423446
424447 val columnCount = rsMetaData.columnCount
425448 val columns = mutableListOf<TableColumnMetadata >()
426449 val nameCounter = mutableMapOf<String , Int >()
427450
428451 for (index in 1 .. columnCount) {
429- val columnName = rsMetaData.getColumnName(index)
430- val tableName = rsMetaData.getTableName(index)
452+ // Try to getColumnName, fallback to getColumnLabel, then generate name
453+ val columnName = try {
454+ rsMetaData.getColumnName(index)
455+ } catch (_: Exception ) {
456+ try {
457+ rsMetaData.getColumnLabel(index)
458+ } catch (_: Exception ) {
459+ " column$index "
460+ }
461+ }
462+
463+ // Some JDBC drivers (e.g., Apache Hive) throw SQLFeatureNotSupportedException
464+ val tableName = try {
465+ rsMetaData.getTableName(index).takeUnless { it.isBlank() }
466+ } catch (_: Exception ) {
467+ // Fallback: try to extract table name from column name if it contains '.'
468+ val dotIndex = columnName.lastIndexOf(' .' )
469+ if (dotIndex > 0 ) columnName.take(dotIndex) else null
470+ }
431471
432472 // Try to detect nullability from ResultSetMetaData
433473 val isNullable = try {
@@ -436,25 +476,48 @@ public abstract class DbType(public val dbTypeInJdbcUrl: String) {
436476
437477 ResultSetMetaData .columnNullable -> true
438478
439- ResultSetMetaData .columnNullableUnknown -> {
440- // Unknown nullability: assume it nullable, may trigger fallback
441- true
442- }
479+ // Unknown nullability: assume it nullable, may trigger fallback
480+ ResultSetMetaData .columnNullableUnknown -> true
443481
444482 else -> true
445483 }
446484 } catch (_: Exception ) {
447485 // Some drivers may throw for unsupported features
448- // In that case, fallback to DatabaseMetaData
449- dbMetaData.getColumns(catalog, schema, tableName, columnName).use { cols ->
450- if (cols.next()) ! cols.getString(" IS_NULLABLE" ).equals(" NO" , ignoreCase = true ) else true
486+ // Try fallback to DatabaseMetaData, with additional safety
487+ try {
488+ dbMetaData.getColumns(catalog, schema, tableName, columnName).use { cols ->
489+ if (cols.next()) ! cols.getString(" IS_NULLABLE" ).equals(" NO" , ignoreCase = true ) else true
490+ }
491+ } catch (_: Exception ) {
492+ // Fallback failed, assume nullable as the safest default
493+ true
451494 }
452495 }
453496
454- val columnType = rsMetaData.getColumnTypeName(index)
455- val jdbcType = rsMetaData.getColumnType(index)
456- val displaySize = rsMetaData.getColumnDisplaySize(index)
457- val javaClassName = rsMetaData.getColumnClassName(index)
497+ // adding fallbacks to avoid SQLException
498+ val columnType = try {
499+ rsMetaData.getColumnTypeName(index)
500+ } catch (_: Exception ) {
501+ " OTHER"
502+ }
503+
504+ val jdbcType = try {
505+ rsMetaData.getColumnType(index)
506+ } catch (_: Exception ) {
507+ Types .OTHER
508+ }
509+
510+ val displaySize = try {
511+ rsMetaData.getColumnDisplaySize(index)
512+ } catch (_: Exception ) {
513+ 0
514+ }
515+
516+ val javaClassName = try {
517+ rsMetaData.getColumnClassName(index)
518+ } catch (_: Exception ) {
519+ " java.lang.Object"
520+ }
458521
459522 val uniqueName = manageColumnNameDuplication(nameCounter, columnName)
460523
0 commit comments