Skip to content

Commit 0037070

Browse files
committed
Revert "Add enhanced support for H2 compatibility modes via Mode enumeration and refine database handling"
This reverts commit dcaab7c.
1 parent dcaab7c commit 0037070

File tree

4 files changed

+133
-294
lines changed
  • dataframe-jdbc/src
    • main/kotlin/org/jetbrains/kotlinx/dataframe/io/db
    • test/kotlin/org/jetbrains/kotlinx/dataframe/io/h2
  • docs/StardustDocs/topics/dataSources/sql

4 files changed

+133
-294
lines changed
Lines changed: 31 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package org.jetbrains.kotlinx.dataframe.io.db
22

33
import org.jetbrains.kotlinx.dataframe.schema.ColumnSchema
4-
import org.jetbrains.kotlinx.dataframe.io.db.MariaDb as MariaDbType
5-
import org.jetbrains.kotlinx.dataframe.io.db.MsSql as MsSqlType
6-
import org.jetbrains.kotlinx.dataframe.io.db.MySql as MySqlType
7-
import org.jetbrains.kotlinx.dataframe.io.db.PostgreSql as PostgreSqlType
84
import java.sql.ResultSet
95
import java.util.Locale
106
import kotlin.reflect.KType
@@ -17,89 +13,12 @@ import kotlin.reflect.KType
1713
*
1814
* NOTE: All date and timestamp-related types are converted to String to avoid java.sql.* types.
1915
*/
16+
public open class H2(public val dialect: DbType = MySql) : DbType("h2") {
17+
init {
18+
require(dialect::class != H2::class) { "H2 database could not be specified with H2 dialect!" }
19+
}
2020

21-
public open class H2(public val mode: Mode = Mode.Regular) : DbType("h2") {
22-
@Deprecated("Use H2(mode = Mode.XXX) instead", ReplaceWith("H2(H2.Mode.MySql)"))
23-
public constructor(dialect: DbType) : this(
24-
Mode.fromDbType(dialect)
25-
?: throw IllegalArgumentException("H2 database could not be specified with H2 dialect!"),
26-
)
27-
28-
private val delegate: DbType? = mode.toDbType()
29-
30-
/**
31-
* Represents the compatibility modes supported by an H2 database.
32-
*
33-
* @property value The string value used in H2 JDBC URL and settings.
34-
*/
35-
public enum class Mode(public val value: String) {
36-
/** Native H2 mode (no compatibility), our synthetic marker. */
37-
Regular("H2-Regular"),
38-
MySql("MySQL"),
39-
PostgreSql("PostgreSQL"),
40-
MsSqlServer("MSSQLServer"),
41-
MariaDb("MariaDB");
42-
43-
/**
44-
* Converts this Mode to the corresponding DbType delegate.
45-
*
46-
* @return The DbType for this mode, or null for Regular mode.
47-
*/
48-
public fun toDbType(): DbType? = when (this) {
49-
Regular -> null
50-
MySql -> MySqlType
51-
PostgreSql -> PostgreSqlType
52-
MsSqlServer -> MsSqlType
53-
MariaDb -> MariaDbType
54-
}
55-
56-
public companion object {
57-
/**
58-
* Creates a Mode from the given DbType.
59-
*
60-
* @param dialect The DbType to convert.
61-
* @return The corresponding Mode, or null if the dialect is H2.
62-
*/
63-
public fun fromDbType(dialect: DbType): Mode? = when (dialect) {
64-
is H2 -> null
65-
MySqlType -> MySql
66-
PostgreSqlType -> PostgreSql
67-
MsSqlType -> MsSqlServer
68-
MariaDbType -> MariaDb
69-
else -> Regular
70-
}
71-
72-
/**
73-
* Finds a Mode by its string value (case-insensitive).
74-
* Handles both URL values (MySQL, PostgreSQL, etc.) and
75-
* INFORMATION_SCHEMA values (Regular).
76-
*
77-
* @param value The string value to search for.
78-
* @return The matching Mode, or null if not found.
79-
*/
80-
public fun fromValue(value: String): Mode? {
81-
// "Regular" from INFORMATION_SCHEMA or "H2-Regular" from URL
82-
if (value.equals("regular", ignoreCase = true) ||
83-
value.equals("h2-regular", ignoreCase = true)) {
84-
return Regular
85-
}
86-
return entries.find { it.value.equals(value, ignoreCase = true) }
87-
}
8821

89-
/**
90-
* Parses a string that may be an H2 MODE value into a Mode.
91-
* Accepts case-insensitive `regular` and `h2-regular` as Regular.
92-
*
93-
* @param mode The mode string to parse, or null for Regular mode.
94-
* @return The corresponding Mode for null/empty input or supported modes.
95-
* @throws IllegalArgumentException if the mode is not null and not supported.
96-
*/
97-
public fun fromString(mode: String?): Mode? {
98-
if (mode == null) return null
99-
return fromValue(mode)
100-
}
101-
}
102-
}
10322
/**
10423
* It contains constants related to different database modes.
10524
*
@@ -111,6 +30,28 @@ public open class H2(public val mode: Mode = Mode.Regular) : DbType("h2") {
11130
* @see [createH2Instance]
11231
*/
11332
public companion object {
33+
/**
34+
* Represents the compatibility modes supported by an H2 database.
35+
*
36+
* @property value The string value used in H2 JDBC URL and settings.
37+
*/
38+
public enum class Mode(public val value: String) {
39+
MySql("MySQL"),
40+
PostgreSql("PostgreSQL"),
41+
MsSqlServer("MSSQLServer"),
42+
MariaDb("MariaDB");
43+
44+
public companion object {
45+
/**
46+
* Finds a Mode by its string value (case-insensitive).
47+
*
48+
* @param value The string value to search for.
49+
* @return The matching Mode, or null if not found.
50+
*/
51+
public fun fromValue(value: String): Mode? =
52+
entries.find { it.value.equals(value, ignoreCase = true) }
53+
}
54+
}
11455

11556
@Deprecated("Use Mode.MySql.value instead", ReplaceWith("Mode.MySql.value"))
11657
public const val MODE_MYSQL: String = "MySQL"
@@ -126,7 +67,7 @@ public open class H2(public val mode: Mode = Mode.Regular) : DbType("h2") {
12667
get() = "org.h2.Driver"
12768

12869
override fun convertSqlTypeToColumnSchemaValue(tableColumnMetadata: TableColumnMetadata): ColumnSchema? =
129-
delegate?.convertSqlTypeToColumnSchemaValue(tableColumnMetadata)
70+
dialect.convertSqlTypeToColumnSchemaValue(tableColumnMetadata)
13071

13172
override fun isSystemTable(tableMetadata: TableMetadata): Boolean {
13273
val locale = Locale.getDefault()
@@ -137,24 +78,14 @@ public open class H2(public val mode: Mode = Mode.Regular) : DbType("h2") {
13778
// could be extended for other symptoms of the system tables for H2
13879
val isH2SystemTable = schemaName.containsWithLowercase("information_schema")
13980

140-
return if (delegate == null) {
141-
isH2SystemTable
142-
} else {
143-
isH2SystemTable || delegate.isSystemTable(tableMetadata)
144-
}
81+
return isH2SystemTable || dialect.isSystemTable(tableMetadata)
14582
}
14683

147-
override fun buildTableMetadata(tables: ResultSet): TableMetadata =
148-
delegate?.buildTableMetadata(tables)
149-
?: TableMetadata(
150-
tables.getString("table_name"),
151-
tables.getString("table_schem"),
152-
tables.getString("table_cat"),
153-
)
84+
override fun buildTableMetadata(tables: ResultSet): TableMetadata = dialect.buildTableMetadata(tables)
15485

15586
override fun convertSqlTypeToKType(tableColumnMetadata: TableColumnMetadata): KType? =
156-
delegate?.convertSqlTypeToKType(tableColumnMetadata)
87+
dialect.convertSqlTypeToKType(tableColumnMetadata)
15788

15889
public override fun buildSqlQueryWithLimit(sqlQuery: String, limit: Int): String =
159-
delegate?.buildSqlQueryWithLimit(sqlQuery, limit) ?: super.buildSqlQueryWithLimit(sqlQuery, limit)
90+
dialect.buildSqlQueryWithLimit(sqlQuery, limit)
16091
}

dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/util.kt

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,8 @@ import java.util.Locale
77

88
private val logger = KotlinLogging.logger {}
99

10-
private const val UNSUPPORTED_H2_MODE_MESSAGE =
11-
"Unsupported H2 MODE: %s. Supported: MySQL, PostgreSQL, MSSQLServer, MariaDB, REGULAR/H2-Regular (or omit MODE)."
12-
13-
private const val H2_MODE_QUERY = "SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME = 'MODE'"
14-
15-
private val H2_MODE_URL_PATTERN = "MODE=([^;:&]+)".toRegex(RegexOption.IGNORE_CASE)
16-
1710
/**
1811
* Extracts the database type from the given connection.
19-
* For H2, fetches the actual MODE from the active connection settings.
20-
* For other databases, extracts type from URL.
2112
*
2213
* @param [connection] the database connection.
2314
* @return the corresponding [DbType].
@@ -30,81 +21,78 @@ public fun extractDBTypeFromConnection(connection: Connection): DbType {
3021
?: throw IllegalStateException("URL information is missing in connection meta data!")
3122
logger.info { "Processing DB type extraction for connection url: $url" }
3223

33-
// First, determine the base database type from URL
34-
val baseDbType = extractDBTypeFromUrl(url)
35-
36-
// For H2, refine the mode by querying the active connection settings
37-
// This handles cases where MODE is not specified in URL, but H2 returns "Regular" from settings
38-
return if (baseDbType is H2) {
39-
val mode = fetchH2ModeFromConnection(connection)
40-
parseH2ModeOrThrow(mode)
41-
} else {
42-
logger.info { "Identified DB type as $baseDbType from url: $url" }
43-
baseDbType
44-
}
45-
}
46-
47-
/**
48-
* Fetches H2 database mode from an active connection.
49-
* Works only for H2 version 2.
50-
*
51-
* @param [connection] the database connection.
52-
* @return the mode string or null if not set.
53-
*/
54-
private fun fetchH2ModeFromConnection(connection: Connection): String? {
55-
var mode: String? = null
56-
connection.prepareStatement(H2_MODE_QUERY).use { st ->
57-
st.executeQuery().use { rs ->
58-
if (rs.next()) {
59-
mode = rs.getString("SETTING_VALUE")
60-
logger.debug { "Fetched H2 DB mode: $mode" }
24+
return if (url.contains(H2().dbTypeInJdbcUrl)) {
25+
// works only for H2 version 2
26+
val modeQuery = "SELECT SETTING_VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE SETTING_NAME = 'MODE'"
27+
var mode = ""
28+
connection.prepareStatement(modeQuery).use { st ->
29+
st.executeQuery().use { rs ->
30+
if (rs.next()) {
31+
mode = rs.getString("SETTING_VALUE")
32+
logger.debug { "Fetched H2 DB mode: $mode" }
33+
} else {
34+
throw IllegalStateException("The information about H2 mode is not found in the H2 meta-data!")
35+
}
6136
}
6237
}
63-
}
6438

65-
return mode?.trim()?.takeIf { it.isNotEmpty() }
66-
}
39+
// H2 doesn't support MariaDB and SQLite
40+
when (mode.lowercase(Locale.getDefault())) {
41+
H2.MODE_MYSQL.lowercase(Locale.getDefault()) -> H2(MySql)
6742

68-
/**
69-
* Parses H2 mode string and returns the corresponding H2 DbType instance.
70-
*
71-
* @param [mode] the mode string (maybe null or empty for Regular mode).
72-
* @return H2 instance with the appropriate mode.
73-
* @throws [IllegalArgumentException] if the mode is not supported.
74-
*/
75-
private fun parseH2ModeOrThrow(mode: String?): H2 {
76-
if (mode.isNullOrEmpty()) {
77-
return H2(H2.Mode.Regular)
78-
}
79-
return H2.Mode.fromValue(mode)?.let { H2(it) }
80-
?: throw IllegalArgumentException(UNSUPPORTED_H2_MODE_MESSAGE.format(mode)).also {
81-
logger.error { it.message }
43+
H2.MODE_MSSQLSERVER.lowercase(Locale.getDefault()) -> H2(MsSql)
44+
45+
H2.MODE_POSTGRESQL.lowercase(Locale.getDefault()) -> H2(PostgreSql)
46+
47+
H2.MODE_MARIADB.lowercase(Locale.getDefault()) -> H2(MariaDb)
48+
49+
else -> {
50+
val message = "Unsupported database type in the url: $url. " +
51+
"Only MySQL, MariaDB, MSSQL and PostgreSQL are supported!"
52+
logger.error { message }
53+
54+
throw IllegalArgumentException(message)
55+
}
8256
}
57+
} else {
58+
val dbType = extractDBTypeFromUrl(url)
59+
logger.info { "Identified DB type as $dbType from url: $url" }
60+
dbType
61+
}
8362
}
8463

8564
/**
8665
* Extracts the database type from the given JDBC URL.
8766
*
8867
* @param [url] the JDBC URL.
8968
* @return the corresponding [DbType].
90-
* @throws [SQLException] if the url is null.
91-
* @throws [IllegalArgumentException] if the URL specifies an unsupported database type.
69+
* @throws [RuntimeException] if the url is null.
9270
*/
9371
public fun extractDBTypeFromUrl(url: String?): DbType {
94-
url ?: throw SQLException("Database URL could not be null.")
95-
96-
return when {
97-
H2().dbTypeInJdbcUrl in url -> createH2Instance(url)
98-
MariaDb.dbTypeInJdbcUrl in url -> MariaDb
99-
MySql.dbTypeInJdbcUrl in url -> MySql
100-
Sqlite.dbTypeInJdbcUrl in url -> Sqlite
101-
PostgreSql.dbTypeInJdbcUrl in url -> PostgreSql
102-
MsSql.dbTypeInJdbcUrl in url -> MsSql
103-
DuckDb.dbTypeInJdbcUrl in url -> DuckDb
104-
else -> throw IllegalArgumentException(
105-
"Unsupported database type in the url: $url. " +
72+
if (url != null) {
73+
val helperH2Instance = H2()
74+
return when {
75+
helperH2Instance.dbTypeInJdbcUrl in url -> createH2Instance(url)
76+
77+
MariaDb.dbTypeInJdbcUrl in url -> MariaDb
78+
79+
MySql.dbTypeInJdbcUrl in url -> MySql
80+
81+
Sqlite.dbTypeInJdbcUrl in url -> Sqlite
82+
83+
PostgreSql.dbTypeInJdbcUrl in url -> PostgreSql
84+
85+
MsSql.dbTypeInJdbcUrl in url -> MsSql
86+
87+
DuckDb.dbTypeInJdbcUrl in url -> DuckDb
88+
89+
else -> throw IllegalArgumentException(
90+
"Unsupported database type in the url: $url. " +
10691
"Only H2, MariaDB, MySQL, MSSQL, SQLite, PostgreSQL, and DuckDB are supported!",
107-
)
92+
)
93+
}
94+
} else {
95+
throw SQLException("Database URL could not be null. The existing value is $url")
10896
}
10997
}
11098

@@ -116,8 +104,30 @@ public fun extractDBTypeFromUrl(url: String?): DbType {
116104
* @throws [IllegalArgumentException] if the provided URL does not contain a valid mode.
117105
*/
118106
private fun createH2Instance(url: String): DbType {
119-
val mode = H2_MODE_URL_PATTERN.find(url)?.groupValues?.getOrNull(1)
120-
return parseH2ModeOrThrow(mode?.takeIf { it.isNotBlank() })
107+
val modePattern = "MODE=(.*?);".toRegex()
108+
val matchResult = modePattern.find(url)
109+
110+
val mode: String = if (matchResult != null && matchResult.groupValues.size == 2) {
111+
matchResult.groupValues[1]
112+
} else {
113+
throw IllegalArgumentException("The provided URL `$url` does not contain a valid mode.")
114+
}
115+
116+
// H2 doesn't support MariaDB and SQLite
117+
return when (mode.lowercase(Locale.getDefault())) {
118+
H2.MODE_MYSQL.lowercase(Locale.getDefault()) -> H2(MySql)
119+
120+
H2.MODE_MSSQLSERVER.lowercase(Locale.getDefault()) -> H2(MsSql)
121+
122+
H2.MODE_POSTGRESQL.lowercase(Locale.getDefault()) -> H2(PostgreSql)
123+
124+
H2.MODE_MARIADB.lowercase(Locale.getDefault()) -> H2(MariaDb)
125+
126+
else -> throw IllegalArgumentException(
127+
"Unsupported database mode: $mode. " +
128+
"Only MySQL, MariaDB, MSSQL, PostgreSQL modes are supported!",
129+
)
130+
}
121131
}
122132

123133
/**

0 commit comments

Comments
 (0)