@@ -7,8 +7,17 @@ import java.util.Locale
77
88private 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+
1017/* *
1118 * 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.
1221 *
1322 * @param [connection] the database connection.
1423 * @return the corresponding [DbType].
@@ -21,78 +30,81 @@ public fun extractDBTypeFromConnection(connection: Connection): DbType {
2130 ? : throw IllegalStateException (" URL information is missing in connection meta data!" )
2231 logger.info { " Processing DB type extraction for connection url: $url " }
2332
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- }
36- }
37- }
38-
39- // H2 doesn't support MariaDB and SQLite
40- when (mode.lowercase(Locale .getDefault())) {
41- H2 .MODE_MYSQL .lowercase(Locale .getDefault()) -> H2 (MySql )
33+ // First, determine the base database type from URL
34+ val baseDbType = extractDBTypeFromUrl(url)
4235
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 }
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+ }
5346
54- throw IllegalArgumentException (message)
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 " }
5561 }
5662 }
57- } else {
58- val dbType = extractDBTypeFromUrl(url)
59- logger.info { " Identified DB type as $dbType from url: $url " }
60- dbType
6163 }
64+
65+ return mode?.trim()?.takeIf { it.isNotEmpty() }
66+ }
67+
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 }
82+ }
6283}
6384
6485/* *
6586 * Extracts the database type from the given JDBC URL.
6687 *
6788 * @param [url] the JDBC URL.
6889 * @return the corresponding [DbType].
69- * @throws [RuntimeException] if the url is null.
90+ * @throws [SQLException] if the url is null.
91+ * @throws [IllegalArgumentException] if the URL specifies an unsupported database type.
7092 */
7193public fun extractDBTypeFromUrl (url : String? ): DbType {
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 . " +
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 . " +
91106 " Only H2, MariaDB, MySQL, MSSQL, SQLite, PostgreSQL, and DuckDB are supported!" ,
92- )
93- }
94- } else {
95- throw SQLException (" Database URL could not be null. The existing value is $url " )
107+ )
96108 }
97109}
98110
@@ -104,30 +116,8 @@ public fun extractDBTypeFromUrl(url: String?): DbType {
104116 * @throws [IllegalArgumentException] if the provided URL does not contain a valid mode.
105117 */
106118private fun createH2Instance (url : String ): DbType {
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- }
119+ val mode = H2_MODE_URL_PATTERN .find(url)?.groupValues?.getOrNull(1 )
120+ return parseH2ModeOrThrow(mode?.takeIf { it.isNotBlank() })
131121}
132122
133123/* *
0 commit comments