From d4645b07ca21b3fd017dd661712ca0e254869634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Kr=C3=BCger?= Date: Wed, 27 May 2020 19:24:47 +0200 Subject: [PATCH 01/20] added jt400 driver and basic DB2 Dialect --- .../org/jetbrains/exposed/sql/Database.kt | 3 ++ .../org/jetbrains/exposed/sql/vendors/DB2.kt | 37 +++++++++++++++++++ .../jdbc/JdbcDatabaseMetadataImpl.kt | 1 + 3 files changed, 41 insertions(+) create mode 100644 exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt index d5b4e0cab6..39462eed8f 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt @@ -70,6 +70,7 @@ class Database private constructor(private val resolvedVendor: String? = null, v registerDialect(OracleDialect.dialectName) { OracleDialect() } registerDialect(SQLServerDialect.dialectName) { SQLServerDialect() } registerDialect(MariaDBDialect.dialectName) { MariaDBDialect() } + registerDialect(DB2Dialect.dialectName) { DB2Dialect() } } fun registerDialect(prefix:String, dialect: () -> DatabaseDialect) { @@ -131,6 +132,7 @@ class Database private constructor(private val resolvedVendor: String? = null, v url.startsWith("jdbc:oracle") -> "oracle.jdbc.OracleDriver" url.startsWith("jdbc:sqlite") -> "org.sqlite.JDBC" url.startsWith("jdbc:sqlserver") -> "com.microsoft.sqlserver.jdbc.SQLServerDriver" + url.startsWith("jdbc:as400") -> "com.ibm.as400.access.AS400JDBCDriver" else -> error("Database driver not found for $url") } @@ -143,6 +145,7 @@ class Database private constructor(private val resolvedVendor: String? = null, v url.startsWith("jdbc:oracle") -> OracleDialect.dialectName url.startsWith("jdbc:sqlite") -> SQLiteDialect.dialectName url.startsWith("jdbc:sqlserver") -> SQLServerDialect.dialectName + url.startsWith("jdbc:as400") -> DB2Dialect.dialectName else -> error("Can't resolve dialect for connection: $url") } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt new file mode 100644 index 0000000000..2f18ebbc33 --- /dev/null +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -0,0 +1,37 @@ +package org.jetbrains.exposed.sql.vendors + +import org.jetbrains.exposed.sql.* + +internal object DB2DataTypeProvider : DataTypeProvider() { + override fun binaryType(): String { + exposedLogger.error("The length of the Binary column is missing.") + error("The length of the Binary column is missing.") + } + + override fun dateTimeType(): String = "TIMESTAMP" + + override fun ulongType(): String = "BIGINT" + + override fun textType(): String = "VARCHAR(32704)" +} + +internal open class DB2FunctionProvider : FunctionProvider() { + internal object INSTANCE : DB2FunctionProvider() + + override fun random(seed: Int?) = "RAND(${seed?.toString().orEmpty()})" +} + +/** + * DB2 dialect implementation. + */ +class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionProvider.INSTANCE) { + override val name: String = dialectName + override val functionProvider: FunctionProvider = MariaDBFunctionProvider + override val supportsOnlyIdentifiersInGeneratedKeys: Boolean = true + + + companion object { + /** DB2 dialect name */ + const val dialectName: String = "db2" + } +} \ No newline at end of file diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index d30b3c54a3..6121452f1c 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -28,6 +28,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) "pgjdbc-ng" -> PostgreSQLNGDialect.dialectName "PostgreSQL JDBC Driver" -> PostgreSQLDialect.dialectName "Oracle JDBC driver" -> OracleDialect.dialectName + "AS/400 Toolbox for Java JDBC Driver" -> DB2Dialect.dialectName else -> { if (driverName.startsWith("Microsoft JDBC Driver ")) SQLServerDialect.dialectName From 3eee1371a3612d51cd24c299a35157746e4a9404 Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 7 Mar 2021 18:16:08 +0100 Subject: [PATCH 02/20] Update exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt Co-authored-by: Andrey.Tarashevskiy --- .../src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index 2f18ebbc33..738916cc9a 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -26,7 +26,7 @@ internal open class DB2FunctionProvider : FunctionProvider() { */ class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionProvider.INSTANCE) { override val name: String = dialectName - override val functionProvider: FunctionProvider = MariaDBFunctionProvider + override val functionProvider: FunctionProvider = DB2FunctionProvider override val supportsOnlyIdentifiersInGeneratedKeys: Boolean = true @@ -34,4 +34,4 @@ class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionPr /** DB2 dialect name */ const val dialectName: String = "db2" } -} \ No newline at end of file +} From 284197635f144e1ccae0c949bba69e60c3759b29 Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 2 May 2021 20:27:55 +0200 Subject: [PATCH 03/20] Changed DB2FunctionProvider to internal object --- .../main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index 738916cc9a..dbccbfabeb 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -15,8 +15,7 @@ internal object DB2DataTypeProvider : DataTypeProvider() { override fun textType(): String = "VARCHAR(32704)" } -internal open class DB2FunctionProvider : FunctionProvider() { - internal object INSTANCE : DB2FunctionProvider() +internal object DB2FunctionProvider : FunctionProvider() { override fun random(seed: Int?) = "RAND(${seed?.toString().orEmpty()})" } @@ -24,9 +23,8 @@ internal open class DB2FunctionProvider : FunctionProvider() { /** * DB2 dialect implementation. */ -class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionProvider.INSTANCE) { +class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionProvider) { override val name: String = dialectName - override val functionProvider: FunctionProvider = DB2FunctionProvider override val supportsOnlyIdentifiersInGeneratedKeys: Boolean = true From b5ef8b90cb56fedfbb21145721a80e52650ecd73 Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 2 May 2021 21:29:45 +0200 Subject: [PATCH 04/20] Added support for db2 LUW --- .../src/main/kotlin/org/jetbrains/exposed/sql/Database.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt index a68b7902cb..017146f144 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Database.kt @@ -70,7 +70,8 @@ class Database private constructor(private val resolvedVendor: String? = null, v "jdbc:oracle" to "oracle.jdbc.OracleDriver", "jdbc:sqlite" to "org.sqlite.JDBC", "jdbc:sqlserver" to "com.microsoft.sqlserver.jdbc.SQLServerDriver", - "jdbc:as400" to "com.ibm.as400.access.AS400JDBCDriver" + "jdbc:as400" to "com.ibm.as400.access.AS400JDBCDriver", + "jdbc:db2" to "com.ibm.db2.jcc.DB2Driver" ) private val dialectMapping = mutableMapOf( "jdbc:h2" to H2Dialect.dialectName, @@ -81,7 +82,8 @@ class Database private constructor(private val resolvedVendor: String? = null, v "jdbc:oracle" to OracleDialect.dialectName, "jdbc:sqlite" to SQLiteDialect.dialectName, "jdbc:sqlserver" to SQLServerDialect.dialectName, - "jdbc:as400" to DB2Dialect.dialectName + "jdbc:as400" to DB2Dialect.dialectName, + "jdbc:db2" to DB2Dialect.dialectName ) init { From 3ad3164544e9c2b5cf4b6c53bf01c1b5fb005071 Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 2 May 2021 21:32:10 +0200 Subject: [PATCH 05/20] Added non working db2 test database a jdbc connection error needs somebody's attention --- buildScripts/docker/docker-compose-db2.yml | 19 +++++++++++++++++++ .../exposed/gradle/DockerTestContainers.kt | 2 ++ .../org/jetbrains/exposed/gradle/Versions.kt | 1 + .../exposed/sql/tests/DatabaseTestsBase.kt | 10 +++++++++- 4 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 buildScripts/docker/docker-compose-db2.yml diff --git a/buildScripts/docker/docker-compose-db2.yml b/buildScripts/docker/docker-compose-db2.yml new file mode 100644 index 0000000000..4c2f655484 --- /dev/null +++ b/buildScripts/docker/docker-compose-db2.yml @@ -0,0 +1,19 @@ +version: '3.3' + +services: + db2: + container_name: DB2 + image: ibmcom/db2:latest + ports: + - "50000:50000" + environment: + LICENSE: accept + DB2INSTANCE: "inst" + DB2INST1_PASSWORD: "yourStrong(!)Password" + DBNAME: "testdb" + volumes: + - db:/database + privileged: true +volumes: + db: + external: false diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt index 799fd90dd7..282e2ca64e 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt @@ -25,6 +25,7 @@ fun Project.setupDialectTest(dialect: String) { systemProperty("exposed.test.mariadb.port", containerInfo.ports[3306] ?: -1) systemProperty("exposed.test.mysql.port", containerInfo.ports[3306] ?: -1) systemProperty("exposed.test.mysql8.port", containerInfo.ports[3306] ?: -1) + systemProperty("exposed.test.db2.port", 50000) } } } @@ -50,6 +51,7 @@ fun setupTestDriverDependencies(dialect: String, testImplementationSetup: (group "mysql8" -> testImplementationSetup("mysql", "mysql-connector-java", Versions.mysql80) "oracle" -> testImplementationSetup("com.oracle.database.jdbc", "ojdbc8", Versions.oracle12) "sqlserver" -> testImplementationSetup("com.microsoft.sqlserver", "mssql-jdbc", Versions.sqlserver) + "db2" -> testImplementationSetup("com.ibm.db2.jcc", "db2jcc", Versions.db2) // Test against db2 luw; testing for db2 as400 is hard else -> { testImplementationSetup("com.h2database", "h2", Versions.h2) testImplementationSetup("mysql", "mysql-connector-java", Versions.mysql51) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt index 9060cd053e..77a05e97e7 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt @@ -14,6 +14,7 @@ object Versions { const val postgreNG = "0.8.6" const val sqlLite3 = "3.32.3.2" const val sqlserver = "8.4.1.jre8" + const val db2 = "db2jcc4" /** Spring **/ const val springFramework = "5.3.3" diff --git a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt index 2bdf5e4d98..26d0363e5b 100644 --- a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt +++ b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt @@ -89,7 +89,15 @@ enum class TestDB( ":${System.getProperty("exposed.test.mariadb.port", "3306")}/testdb" }, "org.mariadb.jdbc.Driver" - ); + ), + + DB2( + { + "jdbc:db2:${System.getProperty("exposed.test.db2.host", "192.168.99.100")}" + + ":${System.getProperty("exposed.test.db2.port", "50000")}" + }, + "com.ibm.db2.jcc.DB2Driver", "inst", "yourStrong(!)Password" + ),; fun connect() = Database.connect(connection(), user = user, password = pass, driver = driver) From fe707896f38e486855ec42c41ff6ea5b50a928c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Maximilian=20Kr=C3=BCger?= Date: Thu, 13 May 2021 12:33:30 +0200 Subject: [PATCH 06/20] Applied db2-tests patch from @Tapac --- .../exposed/gradle/DockerTestContainers.kt | 2 +- .../org/jetbrains/exposed/gradle/Versions.kt | 2 +- .../jdbc/JdbcDatabaseMetadataImpl.kt | 1 + exposed-tests/build.gradle.kts | 1 + .../exposed/sql/tests/DatabaseTestsBase.kt | 33 ++++++++++++++++--- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt index 282e2ca64e..d29d4c0285 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DockerTestContainers.kt @@ -51,7 +51,7 @@ fun setupTestDriverDependencies(dialect: String, testImplementationSetup: (group "mysql8" -> testImplementationSetup("mysql", "mysql-connector-java", Versions.mysql80) "oracle" -> testImplementationSetup("com.oracle.database.jdbc", "ojdbc8", Versions.oracle12) "sqlserver" -> testImplementationSetup("com.microsoft.sqlserver", "mssql-jdbc", Versions.sqlserver) - "db2" -> testImplementationSetup("com.ibm.db2.jcc", "db2jcc", Versions.db2) // Test against db2 luw; testing for db2 as400 is hard + "db2" -> testImplementationSetup("com.ibm.db2.jcc", "db2jcc4", Versions.db2) // Test against db2 luw; testing for db2 as400 is hard else -> { testImplementationSetup("com.h2database", "h2", Versions.h2) testImplementationSetup("mysql", "mysql-connector-java", Versions.mysql51) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt index 77a05e97e7..01e49f02c0 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt @@ -14,7 +14,7 @@ object Versions { const val postgreNG = "0.8.6" const val sqlLite3 = "3.32.3.2" const val sqlserver = "8.4.1.jre8" - const val db2 = "db2jcc4" + const val db2 = "11.1.1.1" /** Spring **/ const val springFramework = "5.3.3" diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index dc12dd3e26..f12901efaf 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -26,6 +26,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) "PostgreSQL JDBC - NG" -> PostgreSQLNGDialect.dialectName "PostgreSQL JDBC Driver" -> PostgreSQLDialect.dialectName "Oracle JDBC driver" -> OracleDialect.dialectName + "IBM Data Server Driver for JDBC and SQLJ", "AS/400 Toolbox for Java JDBC Driver" -> DB2Dialect.dialectName else -> { if (driverName.startsWith("Microsoft JDBC Driver ")) diff --git a/exposed-tests/build.gradle.kts b/exposed-tests/build.gradle.kts index 1cbbad7126..7c2d46f355 100644 --- a/exposed-tests/build.gradle.kts +++ b/exposed-tests/build.gradle.kts @@ -11,6 +11,7 @@ plugins { repositories { mavenCentral() + maven("https://repository.novatec-gmbh.de/content/repositories/novatec/") } val dialect: String by project diff --git a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt index 26d0363e5b..d955d0bdbe 100644 --- a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt +++ b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt @@ -92,11 +92,36 @@ enum class TestDB( ), DB2( - { - "jdbc:db2:${System.getProperty("exposed.test.db2.host", "192.168.99.100")}" + - ":${System.getProperty("exposed.test.db2.port", "50000")}" + connection = { + "jdbc:db2://${System.getProperty("exposed.test.db2.host", "192.168.99.100")}" + + ":${System.getProperty("exposed.test.db2.port", "50000")}/testdb" }, - "com.ibm.db2.jcc.DB2Driver", "inst", "yourStrong(!)Password" + driver = "com.ibm.db2.jcc.DB2Driver", + user = "inst", + pass = "yourStrong(!)Password", + beforeConnection = { + val tmp = Database.connect( + DB2.connection(), user = TestDB.DB2.user, password = TestDB.DB2.pass, driver = TestDB.DB2.driver + ) + var dbInitialized = false + repeat(10) { + if (!dbInitialized) { + transaction(Connection.TRANSACTION_READ_COMMITTED, 1, tmp) { + try { + exec("SELECT 1 FROM SYSIBM.SYSDUMMY1;") + dbInitialized = true + } catch (e: Exception) { + if (it < 9) + exposedLogger.info("Awaiting on DB2 creates database (${it+1}/10)") + else + exposedLogger.error("DB2 wasn't initialized in 100 sec", e) + } + } + Thread.sleep(10000) + } + } + require(dbInitialized) { "DB2 wasn't initialized in 100 sec" } + } ),; fun connect() = Database.connect(connection(), user = user, password = pass, driver = driver) From 67053565d0475ad1ee424147ee023ecfc1d6f38b Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 30 May 2021 11:10:38 +0200 Subject: [PATCH 07/20] hide output from docker ibm/db2 image --- buildScripts/docker/docker-compose-db2.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/buildScripts/docker/docker-compose-db2.yml b/buildScripts/docker/docker-compose-db2.yml index 4c2f655484..ebf0636609 100644 --- a/buildScripts/docker/docker-compose-db2.yml +++ b/buildScripts/docker/docker-compose-db2.yml @@ -6,6 +6,9 @@ services: image: ibmcom/db2:latest ports: - "50000:50000" + # Hide console output of db2 image + logging: + driver: "none" environment: LICENSE: accept DB2INSTANCE: "inst" From 657b1424e1ea4b840fafab08d3cfb34cc31c0523 Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Sun, 30 May 2021 11:31:14 +0200 Subject: [PATCH 08/20] do not specify NULL on column definition for db2 in db2 columns are by default nullable there is no extra keyword for that --- .../src/main/kotlin/org/jetbrains/exposed/sql/Column.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt index ff776ad5d4..8b1e58d00b 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt @@ -4,6 +4,7 @@ import org.jetbrains.exposed.exceptions.throwUnsupportedException import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.H2Dialect import org.jetbrains.exposed.sql.vendors.SQLiteDialect +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.currentDialect import org.jetbrains.exposed.sql.vendors.inProperCase import java.util.* @@ -99,6 +100,7 @@ class Column( } if (colType.nullable || (defaultValue != null && defaultValueFun == null && !currentDialect.isAllowedAsColumnDefault(defaultValue))) { + if (currentDialect !is DB2Dialect) // in db2 columns are null by default - there is no NULL keyword append(" NULL") } else if (!isPKColumn || (currentDialect is SQLiteDialect && !colType.isAutoInc)) { append(" NOT NULL") From 9d05134c803b7975d0b716bedcbea9ad9e3c4d5e Mon Sep 17 00:00:00 2001 From: Paule <44635962+V3lop5@users.noreply.github.com> Date: Tue, 15 Mar 2022 00:50:02 +0100 Subject: [PATCH 09/20] Added db2 test Updated to latest db2 driver com.imb.db2:jcc:11.5.7.0 --- .../kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt | 4 ++++ .../src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt index c25822e138..ada1c70a88 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt @@ -67,6 +67,10 @@ class DBTestingPlugin : Plugin { testRuntimeOnly("org.mariadb.jdbc", "mariadb-java-client", Versions.mariaDB) } + val db2 = register("db2Test", Parameters("DB2", 50000)) { + testRuntimeOnly("com.ibm.db2", "jcc", Versions.db2) + } + named("test") { delegatedTo( h2, diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt index fb941aa958..62453224da 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/Versions.kt @@ -18,7 +18,7 @@ object Versions { const val postgreNG = "0.8.9" const val sqlLite3 = "3.36.0.3" const val sqlserver = "8.4.1.jre8" - const val db2 = "11.1.1.1" + const val db2 = "11.5.7.0" /** Spring **/ const val springFramework = "5.3.13" From 024162a7a776a0cd9765ce0872432607580849b3 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 11:08:18 +0800 Subject: [PATCH 10/20] db2 resultset compat --- .../org/jetbrains/exposed/sql/vendors/DB2.kt | 13 + .../sql/vendors/db2compat/DB2ResultSet.kt | 796 ++++++++++++++++++ .../jdbc/JdbcPreparedStatementImpl.kt | 14 +- 3 files changed, 822 insertions(+), 1 deletion(-) create mode 100644 exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/db2compat/DB2ResultSet.kt diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index dbccbfabeb..5ab4b00abf 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -1,6 +1,10 @@ package org.jetbrains.exposed.sql.vendors import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.vendors.db2compat.DB2ResultSet +import java.lang.reflect.Method +import java.sql.PreparedStatement +import java.sql.ResultSet internal object DB2DataTypeProvider : DataTypeProvider() { override fun binaryType(): String { @@ -33,3 +37,12 @@ class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionPr const val dialectName: String = "db2" } } + +private val DB2Class by lazy { Class.forName("com.ibm.db2.jcc.DB2PreparedStatement") as Class } +val GET_DB_GENERATED_KEYS: Method by lazy { DB2Class.getDeclaredMethod("getDBGeneratedKeys"); } + +val PreparedStatement.db2ResultSetCompat: ResultSet + get() { + @Suppress("UNCHECKED_CAST") + return DB2ResultSet((GET_DB_GENERATED_KEYS.invoke(this) as Array)) + } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/db2compat/DB2ResultSet.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/db2compat/DB2ResultSet.kt new file mode 100644 index 0000000000..32caa90d2c --- /dev/null +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/db2compat/DB2ResultSet.kt @@ -0,0 +1,796 @@ +package org.jetbrains.exposed.sql.vendors.db2compat + +import java.io.InputStream +import java.io.Reader +import java.math.BigDecimal +import java.net.URL +import java.sql.* +import java.sql.Date +import java.util.* + +/** + * @author : youhuajie + * @since : 2022/3/20 + * @description: + **/ + +class DB2ResultSet(val resultSet: Array): ResultSet { + + var index = 0 + var current = resultSet[0] + + override fun unwrap(iface: Class?): T { + return current.unwrap(iface) + } + + override fun isWrapperFor(iface: Class<*>?): Boolean { + return current.isWrapperFor(iface) + } + + override fun close() { + resultSet.forEach { it.close() } + } + + override fun next(): Boolean { + if (current.next()) { + return true + } else { + index++ + return if (index == resultSet.size) { + false + } else { + current = resultSet[index] + return current.next() + } + } + } + + override fun wasNull(): Boolean { + return current.wasNull() + } + + override fun getString(columnIndex: Int): String { + return current.getString(columnIndex) + } + + override fun getString(columnLabel: String?): String { + return current.getString(columnLabel) + } + + override fun getBoolean(columnIndex: Int): Boolean { + return current.getBoolean(columnIndex) + } + + override fun getBoolean(columnLabel: String?): Boolean { + return current.getBoolean(columnLabel) + } + + override fun getByte(columnIndex: Int): Byte { + return current.getByte(columnIndex) + } + + override fun getByte(columnLabel: String?): Byte { + return current.getByte(columnLabel) + } + + override fun getShort(columnIndex: Int): Short { + return current.getShort(columnIndex) + } + + override fun getShort(columnLabel: String?): Short { + return current.getShort(columnLabel) + } + + override fun getInt(columnIndex: Int): Int { + return current.getInt(columnIndex) + } + + override fun getInt(columnLabel: String?): Int { + return current.getInt(columnLabel) + } + + override fun getLong(columnIndex: Int): Long { + return current.getLong(columnIndex) + } + + override fun getLong(columnLabel: String?): Long { + return current.getLong(columnLabel) + } + + override fun getFloat(columnIndex: Int): Float { + return current.getFloat(columnIndex) + } + + override fun getFloat(columnLabel: String?): Float { + return current.getFloat(columnLabel) + } + + override fun getDouble(columnIndex: Int): Double { + return current.getDouble(columnIndex) + } + + override fun getDouble(columnLabel: String?): Double { + return current.getDouble(columnLabel) + } + + override fun getBigDecimal(columnIndex: Int, scale: Int): BigDecimal { + return current.getBigDecimal(columnIndex) + } + + override fun getBigDecimal(columnLabel: String?, scale: Int): BigDecimal { + return current.getBigDecimal(columnLabel, scale) + } + + override fun getBigDecimal(columnIndex: Int): BigDecimal { + return current.getBigDecimal(columnIndex) + } + + override fun getBigDecimal(columnLabel: String?): BigDecimal { + return current.getBigDecimal(columnLabel) + } + + override fun getBytes(columnIndex: Int): ByteArray { + return current.getBytes(columnIndex) + } + + override fun getBytes(columnLabel: String?): ByteArray { + return current.getBytes(columnLabel) + } + + override fun getDate(columnIndex: Int): Date { + return current.getDate(columnIndex) + } + + override fun getDate(columnLabel: String?): Date { + return current.getDate(columnLabel) + } + + override fun getDate(columnIndex: Int, cal: Calendar?): Date { + return current.getDate(columnIndex, cal) + } + + override fun getDate(columnLabel: String?, cal: Calendar?): Date { + return current.getDate(columnLabel) + } + + override fun getTime(columnIndex: Int): Time { + return current.getTime(columnIndex) + } + + override fun getTime(columnLabel: String?): Time { + return current.getTime(columnLabel) + } + + override fun getTime(columnIndex: Int, cal: Calendar?): Time { + return current.getTime(columnIndex, cal) + } + + override fun getTime(columnLabel: String?, cal: Calendar?): Time { + return current.getTime(columnLabel) + } + + override fun getTimestamp(columnIndex: Int): Timestamp { + return current.getTimestamp(columnIndex) + } + + override fun getTimestamp(columnLabel: String?): Timestamp { + return current.getTimestamp(columnLabel) + } + + override fun getTimestamp(columnIndex: Int, cal: Calendar?): Timestamp { + return current.getTimestamp(columnIndex) + } + + override fun getTimestamp(columnLabel: String?, cal: Calendar?): Timestamp { + return current.getTimestamp(columnLabel) + } + + override fun getAsciiStream(columnIndex: Int): InputStream { + return current.getAsciiStream(columnIndex) + } + + override fun getAsciiStream(columnLabel: String?): InputStream { + return current.getAsciiStream(columnLabel) + } + + override fun getUnicodeStream(columnIndex: Int): InputStream { + return current.getUnicodeStream(columnIndex) + } + + override fun getUnicodeStream(columnLabel: String?): InputStream { + return current.getUnicodeStream(columnLabel) + } + + override fun getBinaryStream(columnIndex: Int): InputStream { + return current.getBinaryStream(columnIndex) + } + + override fun getBinaryStream(columnLabel: String?): InputStream { + return current.getBinaryStream(columnLabel) + } + + override fun getWarnings(): SQLWarning { + return current.warnings + } + + override fun clearWarnings() { + current.clearWarnings() + } + + override fun getCursorName(): String { + return current.cursorName + } + + override fun getMetaData(): ResultSetMetaData { + return current.metaData + } + + override fun getObject(columnIndex: Int): Any { + return current.getObject(columnIndex) + } + + override fun getObject(columnLabel: String?): Any { + return current.getObject(columnLabel) + } + + override fun getObject(columnIndex: Int, map: MutableMap>?): Any { + return current.getObject(columnIndex, map) + } + + override fun getObject(columnLabel: String?, map: MutableMap>?): Any { + return current.getObject(columnLabel, map) + } + + override fun getObject(columnIndex: Int, type: Class?): T { + return current.getObject(columnIndex, type) + } + + override fun getObject(columnLabel: String?, type: Class?): T? { + return current.getObject(columnLabel, type) + } + + override fun findColumn(columnLabel: String?): Int { + return current.findColumn(columnLabel) + } + + override fun getCharacterStream(columnIndex: Int): Reader { + return current.getCharacterStream(columnIndex) + } + + override fun getCharacterStream(columnLabel: String?): Reader { + return current.getCharacterStream(columnLabel) + } + + override fun isBeforeFirst(): Boolean { + return current.isBeforeFirst + } + + override fun isAfterLast(): Boolean { + return current.isAfterLast + } + + override fun isFirst(): Boolean { + return current.isFirst + } + + override fun isLast(): Boolean { + return current.isLast + } + + override fun beforeFirst() { + current.beforeFirst() + } + + override fun afterLast() { + current.afterLast() + } + + override fun first(): Boolean { + return current.first() + } + + override fun last(): Boolean { + return current.last() + } + + override fun getRow(): Int { + return current.row + } + + override fun absolute(row: Int): Boolean { + return current.absolute(row) + } + + override fun relative(rows: Int): Boolean { + return current.relative(rows) + } + + override fun previous(): Boolean { + return current.previous() + } + + override fun setFetchDirection(direction: Int) { + current.fetchDirection = direction + } + + override fun getFetchDirection(): Int { + return current.fetchDirection + } + + override fun setFetchSize(rows: Int) { + return current.setFetchSize(rows) + } + + override fun getFetchSize(): Int { + return current.fetchSize + } + + override fun getType(): Int { + return current.type + } + + override fun getConcurrency(): Int { + return current.concurrency + } + + override fun rowUpdated(): Boolean { + return current.rowUpdated() + } + + override fun rowInserted(): Boolean { + return current.rowInserted() + } + + override fun rowDeleted(): Boolean { + return current.rowDeleted() + } + + override fun updateNull(columnIndex: Int) { + current.updateNull(columnIndex) + } + + override fun updateNull(columnLabel: String?) { + current.updateNull(columnLabel) + } + + override fun updateBoolean(columnIndex: Int, x: Boolean) { + current.updateBoolean(columnIndex, x) + } + + override fun updateBoolean(columnLabel: String?, x: Boolean) { + current.updateBoolean(columnLabel, x) + } + + override fun updateByte(columnIndex: Int, x: Byte) { + current.updateByte(columnIndex, x) + } + + override fun updateByte(columnLabel: String?, x: Byte) { + current.updateByte(columnLabel, x) + } + + override fun updateShort(columnIndex: Int, x: Short) { + current.updateShort(columnIndex, x) + } + + override fun updateShort(columnLabel: String?, x: Short) { + current.updateShort(columnLabel, x) + } + + override fun updateInt(columnIndex: Int, x: Int) { + current.updateInt(columnIndex, x) + } + + override fun updateInt(columnLabel: String?, x: Int) { + current.updateInt(columnLabel, x) + } + + override fun updateLong(columnIndex: Int, x: Long) { + current.updateLong(columnIndex, x) + } + + override fun updateLong(columnLabel: String?, x: Long) { + current.updateLong(columnLabel, x) + } + + override fun updateFloat(columnIndex: Int, x: Float) { + current.updateFloat(columnIndex, x) + } + + override fun updateFloat(columnLabel: String?, x: Float) { + current.updateFloat(columnLabel, x) + } + + override fun updateDouble(columnIndex: Int, x: Double) { + current.updateDouble(columnIndex, x) + } + + override fun updateDouble(columnLabel: String?, x: Double) { + current.updateDouble(columnLabel, x) + } + + override fun updateBigDecimal(columnIndex: Int, x: BigDecimal?) { + current.updateBigDecimal(columnIndex, x) + } + + override fun updateBigDecimal(columnLabel: String?, x: BigDecimal?) { + current.updateBigDecimal(columnLabel, x) + } + + override fun updateString(columnIndex: Int, x: String?) { + current.updateString(columnIndex, x) + } + + override fun updateString(columnLabel: String?, x: String?) { + current.updateString(columnLabel, x) + } + + override fun updateBytes(columnIndex: Int, x: ByteArray?) { + current.updateBytes(columnIndex, x) + } + + override fun updateBytes(columnLabel: String?, x: ByteArray?) { + current.updateBytes(columnLabel, x) + } + + override fun updateDate(columnIndex: Int, x: Date?) { + current.updateDate(columnIndex, x) + } + + override fun updateDate(columnLabel: String?, x: Date?) { + current.updateDate(columnLabel, x) + } + + override fun updateTime(columnIndex: Int, x: Time?) { + current.updateTime(columnIndex, x) + } + + override fun updateTime(columnLabel: String?, x: Time?) { + current.updateTime(columnLabel, x) + } + + override fun updateTimestamp(columnIndex: Int, x: Timestamp?) { + current.updateTimestamp(columnIndex, x) + } + + override fun updateTimestamp(columnLabel: String?, x: Timestamp?) { + current.updateTimestamp(columnLabel, x) + } + + override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Int) { + current.updateAsciiStream(columnIndex, x, length) + } + + override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Int) { + current.updateAsciiStream(columnLabel, x) + } + + override fun updateAsciiStream(columnIndex: Int, x: InputStream?, length: Long) { + current.updateAsciiStream(columnIndex, x, length) + } + + override fun updateAsciiStream(columnLabel: String?, x: InputStream?, length: Long) { + current.updateAsciiStream(columnLabel, x) + } + + override fun updateAsciiStream(columnIndex: Int, x: InputStream?) { + current.updateAsciiStream(columnIndex, x) + } + + override fun updateAsciiStream(columnLabel: String?, x: InputStream?) { + current.updateAsciiStream(columnLabel, x) + } + + override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Int) { + current.updateBinaryStream(columnIndex, x) + } + + override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Int) { + current.updateBinaryStream(columnLabel, x, length) + } + + override fun updateBinaryStream(columnIndex: Int, x: InputStream?, length: Long) { + current.updateBinaryStream(columnIndex, x, length) + } + + override fun updateBinaryStream(columnLabel: String?, x: InputStream?, length: Long) { + current.updateBinaryStream(columnLabel, x, length) + } + + override fun updateBinaryStream(columnIndex: Int, x: InputStream?) { + current.updateBinaryStream(columnIndex, x) + } + + override fun updateBinaryStream(columnLabel: String?, x: InputStream?) { + current.updateBinaryStream(columnLabel, x) + } + + override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Int) { + current.updateCharacterStream(columnIndex, x, length) + } + + override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Int) { + current.updateCharacterStream(columnLabel, reader, length) + } + + override fun updateCharacterStream(columnIndex: Int, x: Reader?, length: Long) { + current.updateCharacterStream(columnIndex, x, length) + } + + override fun updateCharacterStream(columnLabel: String?, reader: Reader?, length: Long) { + current.updateCharacterStream(columnLabel, reader, length) + } + + override fun updateCharacterStream(columnIndex: Int, x: Reader?) { + current.updateCharacterStream(columnIndex, x) + } + + override fun updateCharacterStream(columnLabel: String?, reader: Reader?) { + current.updateCharacterStream(columnLabel, reader) + } + + override fun updateObject(columnIndex: Int, x: Any?, scaleOrLength: Int) { + current.updateObject(columnIndex, x, scaleOrLength) + } + + override fun updateObject(columnIndex: Int, x: Any?) { + current.updateObject(columnIndex, x) + } + + override fun updateObject(columnLabel: String?, x: Any?, scaleOrLength: Int) { + current.updateObject(columnLabel, x, scaleOrLength) + } + + override fun updateObject(columnLabel: String?, x: Any?) { + current.updateObject(columnLabel, x) + } + + override fun insertRow() { + current.insertRow() + } + + override fun updateRow() { + current.updateRow() + } + + override fun deleteRow() { + current.deleteRow() + } + + override fun refreshRow() { + current.refreshRow() + } + + override fun cancelRowUpdates() { + current.cancelRowUpdates() + } + + override fun moveToInsertRow() { + current.moveToInsertRow() + } + + override fun moveToCurrentRow() { + current.moveToCurrentRow() + } + + override fun getStatement(): Statement { + return current.statement + } + + override fun getRef(columnIndex: Int): Ref { + return current.getRef(columnIndex) + } + + override fun getRef(columnLabel: String?): Ref { + return current.getRef(columnLabel) + } + + override fun getBlob(columnIndex: Int): Blob { + return current.getBlob(columnIndex) + } + + override fun getBlob(columnLabel: String?): Blob { + return current.getBlob(columnLabel) + } + + override fun getClob(columnIndex: Int): Clob { + return current.getClob(columnIndex) + } + + override fun getClob(columnLabel: String?): Clob { + return current.getClob(columnLabel) + } + + override fun getArray(columnIndex: Int): java.sql.Array { + return current.getArray(columnIndex) + } + + override fun getArray(columnLabel: String?): java.sql.Array { + return current.getArray(columnLabel) + } + + override fun getURL(columnIndex: Int): URL { + return current.getURL(columnIndex) + } + + override fun getURL(columnLabel: String?): URL { + return current.getURL(columnLabel) + } + + override fun updateRef(columnIndex: Int, x: Ref?) { + current.updateRef(columnIndex, x) + } + + override fun updateRef(columnLabel: String?, x: Ref?) { + current.updateRef(columnLabel, x) + } + + override fun updateBlob(columnIndex: Int, x: Blob?) { + current.updateBlob(columnIndex, x) + } + + override fun updateBlob(columnLabel: String?, x: Blob?) { + current.updateBlob(columnLabel, x) + } + + override fun updateBlob(columnIndex: Int, inputStream: InputStream?, length: Long) { + current.updateBlob(columnIndex, inputStream, length) + } + + override fun updateBlob(columnLabel: String?, inputStream: InputStream?, length: Long) { + current.updateBlob(columnLabel, inputStream, length) + } + + override fun updateBlob(columnIndex: Int, inputStream: InputStream?) { + current.updateBlob(columnIndex, inputStream) + } + + override fun updateBlob(columnLabel: String?, inputStream: InputStream?) { + current.updateBlob(columnLabel, inputStream) + } + + override fun updateClob(columnIndex: Int, x: Clob?) { + current.updateClob(columnIndex, x) + } + + override fun updateClob(columnLabel: String?, x: Clob?) { + current.updateClob(columnLabel, x) + } + + override fun updateClob(columnIndex: Int, reader: Reader?, length: Long) { + current.updateClob(columnIndex, reader, length) + } + + override fun updateClob(columnLabel: String?, reader: Reader?, length: Long) { + current.updateClob(columnLabel, reader, length) + } + + override fun updateClob(columnIndex: Int, reader: Reader?) { + current.updateClob(columnIndex, reader) + } + + override fun updateClob(columnLabel: String?, reader: Reader?) { + current.updateClob(columnLabel, reader) + } + + override fun updateArray(columnIndex: Int, x: java.sql.Array?) { + current.updateArray(columnIndex, x) + } + + override fun updateArray(columnLabel: String?, x: java.sql.Array?) { + current.updateArray(columnLabel, x) + } + + override fun getRowId(columnIndex: Int): RowId { + return current.getRowId(columnIndex) + } + + override fun getRowId(columnLabel: String?): RowId { + return current.getRowId(columnLabel) + } + + override fun updateRowId(columnIndex: Int, x: RowId?) { + current.updateRowId(columnIndex, x) + } + + override fun updateRowId(columnLabel: String?, x: RowId?) { + current.updateRowId(columnLabel, x) + } + + override fun getHoldability(): Int { + return resultSet.sumOf { it.holdability } + } + + override fun isClosed(): Boolean { + return resultSet.all { isClosed } + } + + override fun updateNString(columnIndex: Int, nString: String?) { + current.updateNString(columnIndex, nString) + } + + override fun updateNString(columnLabel: String?, nString: String?) { + current.updateNString(columnLabel, nString) + } + + override fun updateNClob(columnIndex: Int, nClob: NClob?) { + current.updateNClob(columnIndex, nClob) + } + + override fun updateNClob(columnLabel: String?, nClob: NClob?) { + current.updateNClob(columnLabel, nClob) + } + + override fun updateNClob(columnIndex: Int, reader: Reader?, length: Long) { + current.updateNClob(columnIndex, reader, length) + } + + override fun updateNClob(columnLabel: String?, reader: Reader?, length: Long) { + current.updateNClob(columnLabel, reader, length) + } + + override fun updateNClob(columnIndex: Int, reader: Reader?) { + current.updateNClob(columnIndex, reader!!) + } + + override fun updateNClob(columnLabel: String?, reader: Reader?) { + current.updateNClob(columnLabel, reader) + } + + override fun getNClob(columnIndex: Int): NClob { + return current.getNClob(columnIndex) + } + + override fun getNClob(columnLabel: String?): NClob { + return current.getNClob(columnLabel) + } + + override fun getSQLXML(columnIndex: Int): SQLXML { + return current.getSQLXML(columnIndex) + } + + override fun getSQLXML(columnLabel: String?): SQLXML { + return current.getSQLXML(columnLabel) + } + + override fun updateSQLXML(columnIndex: Int, xmlObject: SQLXML?) { + current.updateSQLXML(columnIndex, xmlObject) + } + + override fun updateSQLXML(columnLabel: String?, xmlObject: SQLXML?) { + current.updateSQLXML(columnLabel, xmlObject) + } + + override fun getNString(columnIndex: Int): String { + return current.getNString(columnIndex) + } + + override fun getNString(columnLabel: String?): String { + return current.getNString(columnLabel) + } + + override fun getNCharacterStream(columnIndex: Int): Reader { + return current.getNCharacterStream(columnIndex) + } + + override fun getNCharacterStream(columnLabel: String?): Reader { + return current.getNCharacterStream(columnLabel) + } + + override fun updateNCharacterStream(columnIndex: Int, x: Reader?, length: Long) { + current.updateNCharacterStream(columnIndex, x, length) + } + + override fun updateNCharacterStream(columnLabel: String?, reader: Reader?, length: Long) { + current.updateNCharacterStream(columnLabel, reader, length) + } + + override fun updateNCharacterStream(columnIndex: Int, x: Reader?) { + current.updateNCharacterStream(columnIndex, x) + } + + override fun updateNCharacterStream(columnLabel: String?, reader: Reader?) { + return current.updateNCharacterStream(columnLabel, reader) + } + +} diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcPreparedStatementImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcPreparedStatementImpl.kt index b0a9108279..44458e9806 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcPreparedStatementImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcPreparedStatementImpl.kt @@ -4,14 +4,26 @@ import org.jetbrains.exposed.sql.BinaryColumnType import org.jetbrains.exposed.sql.BlobColumnType import org.jetbrains.exposed.sql.IColumnType import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi +import org.jetbrains.exposed.sql.vendors.DB2Dialect +import org.jetbrains.exposed.sql.vendors.currentDialect +import org.jetbrains.exposed.sql.vendors.db2ResultSetCompat import java.io.InputStream import java.sql.PreparedStatement import java.sql.ResultSet import java.sql.Types class JdbcPreparedStatementImpl(val statement: PreparedStatement, val wasGeneratedKeysRequested: Boolean) : PreparedStatementApi { + override val resultSet: ResultSet? - get() = if (wasGeneratedKeysRequested) statement.generatedKeys else statement.resultSet + get() = if (wasGeneratedKeysRequested) { + // https://stackoverflow.com/questions/41725492/how-to-get-auto-generated-keys-of-batch-insert-statement + // https://github.com/ebean-orm/ebean/blob/15f1bd310bee6edddbf11dd51feb2f43c4f85528/ebean-core/src/main/java/io/ebeaninternal/server/persist/DB2GetKeys.java + if (currentDialect is DB2Dialect) { + statement.db2ResultSetCompat + } else { + statement.generatedKeys + } + } else statement.resultSet override var fetchSize: Int? get() = statement.fetchSize From 056c36ff63a07b5d2ddd25babafa5addda9e8cbd Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 12:36:06 +0800 Subject: [PATCH 11/20] local db2 config --- .../kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt | 5 +++-- .../org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt index ada1c70a88..ec00a0682d 100644 --- a/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt +++ b/buildSrc/src/main/kotlin/org/jetbrains/exposed/gradle/DBTestingPlugin.kt @@ -67,7 +67,7 @@ class DBTestingPlugin : Plugin { testRuntimeOnly("org.mariadb.jdbc", "mariadb-java-client", Versions.mariaDB) } - val db2 = register("db2Test", Parameters("DB2", 50000)) { + val db2 = register("db2Test", "db2") { testRuntimeOnly("com.ibm.db2", "jcc", Versions.db2) } @@ -77,7 +77,8 @@ class DBTestingPlugin : Plugin { sqlite, mysql51, postgres, - postgresNG + postgresNG, + db2 ) } } diff --git a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt index 29d845deeb..661e9219e6 100644 --- a/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt +++ b/exposed-tests/src/main/kotlin/org/jetbrains/exposed/sql/tests/DatabaseTestsBase.kt @@ -103,7 +103,7 @@ enum class TestDB( DB2( connection = { - "jdbc:db2://${System.getProperty("exposed.test.db2.host", "192.168.99.100")}" + + "jdbc:db2://${System.getProperty("exposed.test.db2.host", "localhost")}" + ":${System.getProperty("exposed.test.db2.port", "50000")}/testdb" }, driver = "com.ibm.db2.jcc.DB2Driver", From 3db771afb727dec9f90e190eee68ed665ffad079 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 12:43:16 +0800 Subject: [PATCH 12/20] fix limit --- .../src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index 5ab4b00abf..d3bfd91c22 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -22,6 +22,10 @@ internal object DB2DataTypeProvider : DataTypeProvider() { internal object DB2FunctionProvider : FunctionProvider() { override fun random(seed: Int?) = "RAND(${seed?.toString().orEmpty()})" + + override fun queryLimit(size: Int, offset: Long, alreadyOrdered: Boolean): String { + return (if (offset > 0) " OFFSET $offset ROWS" else "") + " FETCH FIRST $size ROWS ONLY" + } } /** From 31b63d0298310bc77bb1424be38cd2ed110c1c53 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 12:46:57 +0800 Subject: [PATCH 13/20] db2 column type and functionProvider --- .../org/jetbrains/exposed/sql/vendors/DB2.kt | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index d3bfd91c22..5ba95dcd26 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -1,6 +1,8 @@ package org.jetbrains.exposed.sql.vendors +import org.jetbrains.exposed.exceptions.throwUnsupportedException import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.db2compat.DB2ResultSet import java.lang.reflect.Method import java.sql.PreparedStatement @@ -12,20 +14,109 @@ internal object DB2DataTypeProvider : DataTypeProvider() { error("The length of the Binary column is missing.") } + override fun byteType(): String = "SMALLINT" + + override fun ubyteType(): String = "SMALLINT" + override fun dateTimeType(): String = "TIMESTAMP" override fun ulongType(): String = "BIGINT" - override fun textType(): String = "VARCHAR(32704)" + override fun textType(): String = "CLOB" + + override fun uuidType(): String = "CHAR(16) FOR BIT DATA" + + override fun integerAutoincType(): String = "INT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1)" + + override fun longAutoincType(): String = "BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1)" } internal object DB2FunctionProvider : FunctionProvider() { + override val DEFAULT_VALUE_EXPRESSION: String = "VALUES DEFAULT" + override fun random(seed: Int?) = "RAND(${seed?.toString().orEmpty()})" + override fun delete(ignore: Boolean, table: Table, where: String?, limit: Int?, transaction: Transaction): String { + if (!DB2Dialect.ENABLE_UPDATE_DELETE_LIMIT && limit != null) { + transaction.throwUnsupportedException("SQLite doesn't support LIMIT in UPDATE clause.") + } + return super.delete(ignore, table, where, limit, transaction) + } + + override fun update( + targets: Join, + columnsAndValues: List, Any?>>, + limit: Int?, + where: Op?, + transaction: Transaction + ): String = with(QueryBuilder(true)) { + columnsAndValues.map { it.first.table }.distinct().singleOrNull() + ?: transaction.throwUnsupportedException("DB2 supports a join updates with a single table columns to update.") + if (targets.joinParts.any { it.joinType != JoinType.INNER }) { + exposedLogger.warn("All tables in UPDATE statement will be joined with inner join") + } + +"UPDATE (" + val columnsToSelect = columnsAndValues.flatMap { + listOfNotNull(it.first, it.second as? Expression<*>) + }.mapIndexed { index, expression -> expression to expression.alias("c$index") }.toMap() + + val subQuery = targets.slice(columnsToSelect.values.toList()).selectAll() + where?.let { + subQuery.adjustWhere { it } + } + subQuery.prepareSQL(this) + +") x" + + columnsAndValues.appendTo(this, prefix = " SET ") { (col, value) -> + val alias = columnsToSelect.getValue(col) + +alias.alias + +"=" + (value as? Expression<*>)?.let { + +columnsToSelect.getValue(it).alias + } ?: registerArgument(col, value) + } + + limit?.let { + "WHERE ROWNUM <= $it" + } + + toString() + } + + override fun concat( + separator: String, + queryBuilder: QueryBuilder, + vararg expr: Expression<*> + ): Unit = queryBuilder { + if (separator == "") { + expr.appendTo(separator = " || ") { +it } + } else { + expr.appendTo(separator = " || '$separator' || ") { +it } + } + } + + override fun groupConcat( + expr: GroupConcat, + queryBuilder: QueryBuilder + ): Unit = queryBuilder { + if (expr.orderBy.size != 1) { + TransactionManager.current().throwUnsupportedException("DB2 supports only single column in ORDER BY clause in LISTAGG") + } + append("LISTAGG(") + append(expr.expr) + expr.separator?.let { + append(", '$it'") + } + append(") WITHIN GROUP (ORDER BY ") + val (col, order) = expr.orderBy.single() + append(col, " ", order.name, ")") + } + override fun queryLimit(size: Int, offset: Long, alreadyOrdered: Boolean): String { return (if (offset > 0) " OFFSET $offset ROWS" else "") + " FETCH FIRST $size ROWS ONLY" } + } /** @@ -39,6 +130,9 @@ class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionPr companion object { /** DB2 dialect name */ const val dialectName: String = "db2" + + // todo it not supported in earlier verion + val ENABLE_UPDATE_DELETE_LIMIT = true } } From 747349ef52506655008006629dc90e1cde6365b5 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 13:06:22 +0800 Subject: [PATCH 14/20] make dual sysdummy1 in db2 --- .../src/main/kotlin/org/jetbrains/exposed/sql/Query.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt index 10fcb40ce6..82eaea8bd8 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Query.kt @@ -2,6 +2,7 @@ package org.jetbrains.exposed.sql import org.jetbrains.exposed.sql.statements.Statement import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.currentDialect import java.sql.ResultSet import java.util.* @@ -119,6 +120,10 @@ open class Query(override var set: FieldSet, where: Op?) : AbstractQuer set.source.describe(transaction, this) } + if (set.source == Table.Dual && currentDialect is DB2Dialect) { + append(" FROM SYSIBM.SYSDUMMY1 ") + } + where?.let { append(" WHERE ") +it From e083b8e43da550eff9bee976e5f63474a27bbd41 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 13:45:51 +0800 Subject: [PATCH 15/20] fix insert tests,fix table exists in db2,fix seq max number in db2 --- .../org/jetbrains/exposed/sql/Column.kt | 20 +++++++++---------- .../org/jetbrains/exposed/sql/Constraints.kt | 15 ++++++++++---- .../kotlin/org/jetbrains/exposed/sql/Table.kt | 7 ++----- .../exposed/sql/statements/InsertStatement.kt | 4 ++-- .../jetbrains/exposed/sql/vendors/Default.kt | 2 +- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt index 11631d62b0..16f201f6b1 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt @@ -46,12 +46,13 @@ class Column( private val isLastColumnInPK: Boolean get() = table.primaryKey?.columns?.last() == this - internal val isPrimaryConstraintWillBeDefined: Boolean get() = when { - currentDialect is SQLiteDialect && columnType.isAutoInc -> false - table.isCustomPKNameDefined() -> isLastColumnInPK - isOneColumnPK() -> false - else -> isLastColumnInPK - } + internal val isPrimaryConstraintWillBeDefined: Boolean + get() = when { + currentDialect is SQLiteDialect && columnType.isAutoInc -> false + table.isCustomPKNameDefined() -> isLastColumnInPK + isOneColumnPK() -> false + else -> isLastColumnInPK + } override fun createStatement(): List { val alterTablePrefix = "ALTER TABLE ${TransactionManager.current().identity(table)} ADD" @@ -115,11 +116,10 @@ class Column( if (columnType.nullable || (defaultValue != null && defaultValueFun == null && !currentDialect.isAllowedAsColumnDefault(defaultValue))) { if (currentDialect !is DB2Dialect) // in db2 columns are null by default - there is no NULL keyword - append(" NULL") - } else if (!isPKColumn || (currentDialect is SQLiteDialect && !isSQLiteAutoIncColumn)) { + append(" NULL") + } else if (!isPKColumn || (currentDialect is DB2Dialect && !columnType.isAutoInc) || (currentDialect is SQLiteDialect && !isSQLiteAutoIncColumn)) { append(" NOT NULL") } - if (!modify && isOneColumnPK() && !isPrimaryConstraintWillBeDefined && !isSQLiteAutoIncColumn) { append(" PRIMARY KEY") } @@ -133,7 +133,7 @@ class Column( table = this.table, name = this.name, columnType = columnType - ).also{ + ).also { it.foreignKey = this.foreignKey it.defaultValueFun = this.defaultValueFun it.dbDefaultValue = this.dbDefaultValue diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Constraints.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Constraints.kt index 950401f84e..eb4504efeb 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Constraints.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Constraints.kt @@ -1,6 +1,7 @@ package org.jetbrains.exposed.sql import org.jetbrains.exposed.sql.transactions.TransactionManager +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.MysqlDialect import org.jetbrains.exposed.sql.vendors.OracleDialect import org.jetbrains.exposed.sql.vendors.currentDialect @@ -118,10 +119,16 @@ data class ForeignKeyConstraint( append(" ON DELETE $deleteRule") } if (updateRule != ReferenceOption.NO_ACTION) { - if (currentDialect is OracleDialect) { - exposedLogger.warn("Oracle doesn't support FOREIGN KEY with ON UPDATE clause. Please check your $fromTableName table.") - } else { - append(" ON UPDATE $updateRule") + when { + currentDialect is OracleDialect -> { + exposedLogger.warn("Oracle doesn't support FOREIGN KEY with ON UPDATE clause. Please check your $fromTableName table.") + } + currentDialect is DB2Dialect && updateRule == ReferenceOption.CASCADE -> { + exposedLogger.warn("DB2 doesn't support FOREIGN KEY with ON UPDATE clause. Please check your $fromTableName table.") + } + else -> { + append(" ON UPDATE $updateRule") + } } } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt index e61270242c..71cb67eaf2 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Table.kt @@ -6,10 +6,7 @@ import org.jetbrains.exposed.dao.id.IdTable import org.jetbrains.exposed.exceptions.DuplicateColumnException import org.jetbrains.exposed.sql.statements.api.ExposedBlob import org.jetbrains.exposed.sql.transactions.TransactionManager -import org.jetbrains.exposed.sql.vendors.OracleDialect -import org.jetbrains.exposed.sql.vendors.PostgreSQLDialect -import org.jetbrains.exposed.sql.vendors.SQLiteDialect -import org.jetbrains.exposed.sql.vendors.currentDialect +import org.jetbrains.exposed.sql.vendors.* import org.jetbrains.exposed.sql.vendors.currentDialectIfAvailable import org.jetbrains.exposed.sql.vendors.inProperCase import java.math.BigDecimal @@ -1068,7 +1065,7 @@ open class Table(name: String = "") : ColumnSet(), DdlAware { it, startWith = 1, minValue = 1, - maxValue = Long.MAX_VALUE + maxValue = if (currentDialect is DB2Dialect) Int.MAX_VALUE.toLong() else Long.MAX_VALUE ).createStatement() }.orEmpty() diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt index 3478a1fbb9..3f0ac2051d 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt @@ -147,12 +147,12 @@ open class InsertStatement(val table: Table, val isIgnore: Boolean = override fun prepared(transaction: Transaction, sql: String): PreparedStatementApi = when { // https://github.com/pgjdbc/pgjdbc/issues/1168 // Column names always escaped/quoted in RETURNING clause - autoIncColumns.isNotEmpty() && currentDialect is PostgreSQLDialect -> + autoIncColumns.isNotEmpty() && currentDialect is PostgreSQLDialect -> transaction.connection.prepareStatement(sql, true) autoIncColumns.isNotEmpty() -> // http://viralpatel.net/blogs/oracle-java-jdbc-get-primary-key-insert-sql/ - transaction.connection.prepareStatement(sql, autoIncColumns.map { it.name.inProperCase() }.toTypedArray()) + transaction.connection.prepareStatement(sql, autoIncColumns.map { transaction.identity(it) }.toTypedArray()) else -> transaction.connection.prepareStatement(sql, false) } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt index 4c86d3baa7..c02c4c80e2 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt @@ -685,7 +685,7 @@ abstract class VendorDialect( override fun tableExists(table: Table): Boolean { val tableScheme = table.tableName.substringBefore('.', "").takeIf { it.isNotEmpty() } - val scheme = tableScheme?.inProperCase() ?: TransactionManager.current().connection.metadata { currentScheme } + val scheme = tableScheme?.inProperCase() ?: TransactionManager.current().connection.metadata { currentScheme }.trim() val allTables = getAllTableNamesCache().getValue(scheme) return allTables.any { when { From 2cf2243cee130588d70c6761b93c76bed8242c22 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 15:19:15 +0800 Subject: [PATCH 16/20] fix tableExist --- .../org/jetbrains/exposed/sql/Column.kt | 2 +- .../org/jetbrains/exposed/sql/vendors/DB2.kt | 60 +++++++++++++++++++ .../jdbc/JdbcDatabaseMetadataImpl.kt | 1 + 3 files changed, 62 insertions(+), 1 deletion(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt index 16f201f6b1..3b7e0f0565 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Column.kt @@ -117,7 +117,7 @@ class Column( if (columnType.nullable || (defaultValue != null && defaultValueFun == null && !currentDialect.isAllowedAsColumnDefault(defaultValue))) { if (currentDialect !is DB2Dialect) // in db2 columns are null by default - there is no NULL keyword append(" NULL") - } else if (!isPKColumn || (currentDialect is DB2Dialect && !columnType.isAutoInc) || (currentDialect is SQLiteDialect && !isSQLiteAutoIncColumn)) { + } else if (!isPKColumn || (currentDialect is DB2Dialect && !columnType.isAutoInc)|| (currentDialect is SQLiteDialect && !isSQLiteAutoIncColumn)) { append(" NOT NULL") } if (!modify && isOneColumnPK() && !isPrimaryConstraintWillBeDefined && !isSQLiteAutoIncColumn) { diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt index 5ba95dcd26..c8e3c39279 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/DB2.kt @@ -1,5 +1,6 @@ package org.jetbrains.exposed.sql.vendors +import org.jetbrains.exposed.exceptions.UnsupportedByDialectException import org.jetbrains.exposed.exceptions.throwUnsupportedException import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -125,8 +126,67 @@ internal object DB2FunctionProvider : FunctionProvider() { class DB2Dialect : VendorDialect(dialectName, DB2DataTypeProvider, DB2FunctionProvider) { override val name: String = dialectName override val supportsOnlyIdentifiersInGeneratedKeys: Boolean = true + override val supportsIfNotExists: Boolean = false + override fun createDatabase(name: String): String { + throw UnsupportedByDialectException("Create database can only run in clp(command line processor), thus it can not run here", currentDialect) + } + + override fun dropDatabase(name: String): String { + throw UnsupportedByDialectException("Drop database can only run in clp(command line processor), thus it can not run here", currentDialect) + } + + override fun createIndex(index: Index): String { + return super.createIndex(index) + } + + override fun createSchema(schema: Schema): String = buildString { + append("CREATE SCHEMA ") + append(schema.identifier) + + if (schema.authorization != null) { + append(" ") + append("AUTHORIZATION ") + append(schema.authorization) + } + } + + override fun modifyColumn(column: Column<*>, columnDiff: ColumnDiff): List { + val sqls = mutableListOf() + val identify = TransactionManager.current().identity(column) + val ddlSql = "ALTER TABLE ${TransactionManager.current().identity(column.table)} ALTER COLUMN " + + column.descriptionDdl(true).replace("NOT NULL", "") + .replace("MODIFY", "ALTER") + .replace(identify, "$identify SET DATA TYPE ") + sqls += ddlSql + + + + if (columnDiff.nullability) { + val nullableSql = if (column.columnType.nullable) { + "ALTER TABLE ${TransactionManager.current().identity(column.table)} ALTER COLUMN $identify DROP NOT NULL" + } else { + "ALTER TABLE ${TransactionManager.current().identity(column.table)} ALTER COLUMN $identify SET NOT NULL" + } + sqls += nullableSql + } + sqls += "CALL SYSPROC.ADMIN_CMD('REORG TABLE ${TransactionManager.current().identity(column.table)}')" + + return sqls + } + + + override fun dropSchema(schema: Schema, cascade: Boolean): String { + if (cascade) { + throw UnsupportedByDialectException( + "${currentDialect.name} There is no cascading drop function in DB2; you will have to drop each individual object that uses that schema first", + currentDialect + ) + } + return "DROP SCHEMA ${schema.identifier} RESTRICT" + } + companion object { /** DB2 dialect name */ const val dialectName: String = "db2" diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index a494c3c970..14ff2481e6 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -46,6 +46,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) private val databaseName get() = when (databaseDialectName) { MysqlDialect.dialectName, MariaDBDialect.dialectName -> currentScheme + DB2Dialect.dialectName -> null else -> database } From 1d0871644779e90ffd5d9b787f743389fa792441 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 15:38:27 +0800 Subject: [PATCH 17/20] fix tableNames for db2 --- .../exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt index 14ff2481e6..a678644ef3 100644 --- a/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt +++ b/exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/sql/statements/jdbc/JdbcDatabaseMetadataImpl.kt @@ -104,6 +104,7 @@ class JdbcDatabaseMetadataImpl(database: String, val metadata: DatabaseMetaData) val (catalogName, schemeName) = when { useCatalogInsteadOfScheme -> scheme to "%" currentDialect is OracleDialect -> databaseName to databaseName + currentDialect is DB2Dialect -> null to scheme else -> databaseName to scheme.ifEmpty { "%" } } val resultSet = getTables(catalogName, schemeName, "%", arrayOf("TABLE")) From c9eb5725ba55ae3fbfd44958ed30cb9bbc15a267 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 15:59:51 +0800 Subject: [PATCH 18/20] ignore some tests which db2 not support --- .../jetbrains/exposed/sql/tests/shared/DDLTests.kt | 12 ++++++++---- .../sql/tests/shared/ddl/CreateDatabaseTest.kt | 3 ++- .../exposed/sql/tests/shared/ddl/CreateIndexTests.kt | 2 +- .../exposed/sql/tests/shared/dml/DeleteTests.kt | 4 ++++ .../exposed/sql/tests/shared/dml/OrderByTests.kt | 3 ++- .../exposed/sql/tests/shared/dml/ReplaceTests.kt | 2 +- .../exposed/sql/tests/shared/dml/SelectTests.kt | 2 +- .../exposed/sql/tests/shared/dml/UpdateTests.kt | 2 +- .../sql/tests/shared/functions/FunctionsTestBase.kt | 7 +++++++ .../sql/tests/shared/functions/MathFunctionTests.kt | 2 +- .../shared/functions/TrigonometricalFunctionTests.kt | 2 +- 11 files changed, 29 insertions(+), 12 deletions(-) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt index f073d44f4c..b431869535 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/DDLTests.kt @@ -14,8 +14,10 @@ import org.jetbrains.exposed.sql.tests.TestDB import org.jetbrains.exposed.sql.tests.currentDialectTest import org.jetbrains.exposed.sql.tests.inProperCase import org.jetbrains.exposed.sql.tests.shared.dml.DMLTestsData +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.SQLServerDialect import org.jetbrains.exposed.sql.vendors.SQLiteDialect +import org.jetbrains.exposed.sql.vendors.currentDialect import org.junit.Test import org.postgresql.util.PGobject import java.util.* @@ -71,7 +73,7 @@ class DDLTests : DatabaseTestsBase() { } @Test fun unnamedTableWithQuotesSQL() { - withTables(excludeSettings = listOf(TestDB.SQLITE), tables = arrayOf(UnnamedTable)) { + withTables(excludeSettings = listOf(TestDB.SQLITE, TestDB.DB2), tables = arrayOf(UnnamedTable)) { val q = db.identifierManager.quoteString val tableName = if (currentDialectTest.needsQuotesWhenSymbolsInNames) { "$q${"UnnamedTable$1".inProperCase()}$q" } else { "UnnamedTable$1".inProperCase() } assertEquals( @@ -115,7 +117,7 @@ class DDLTests : DatabaseTestsBase() { override val primaryKey = PrimaryKey(name) } - withTables(excludeSettings = listOf(TestDB.MYSQL, TestDB.ORACLE, TestDB.MARIADB, TestDB.SQLITE), tables = arrayOf(TestTable)) { + withTables(excludeSettings = listOf(TestDB.MYSQL, TestDB.ORACLE, TestDB.MARIADB, TestDB.SQLITE, TestDB.DB2), tables = arrayOf(TestTable)) { assertEquals( "CREATE TABLE " + addIfNotExistsIfSupported() + "${"different_column_types".inProperCase()} " + "(${"id".inProperCase()} ${currentDialectTest.dataTypeProvider.integerAutoincType()} NOT NULL, " + @@ -135,7 +137,7 @@ class DDLTests : DatabaseTestsBase() { override val primaryKey = PrimaryKey(id, name) } - withTables(excludeSettings = listOf(TestDB.MYSQL, TestDB.SQLITE), tables = arrayOf(TestTable)) { + withTables(excludeSettings = listOf(TestDB.MYSQL, TestDB.SQLITE, TestDB.DB2), tables = arrayOf(TestTable)) { val q = db.identifierManager.quoteString val tableDescription = "CREATE TABLE " + addIfNotExistsIfSupported() + "with_different_column_types".inProperCase() val idDescription = "${"id".inProperCase()} ${currentDialectTest.dataTypeProvider.integerType()}" @@ -767,7 +769,9 @@ class DDLTests : DatabaseTestsBase() { assertEquals(1L, TableFromSchemeOne.selectAll().count()) assertEquals(1L, TableFromSchemeTwo.selectAll().count()) - if (currentDialectTest is SQLServerDialect) { + if (currentDialectTest is SQLServerDialect + || /* db2 not support cascade*/ currentDialectTest is DB2Dialect + ) { SchemaUtils.drop(TableFromSchemeTwo, TableFromSchemeOne) } } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateDatabaseTest.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateDatabaseTest.kt index f47c8a0446..4804424e8b 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateDatabaseTest.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateDatabaseTest.kt @@ -10,7 +10,8 @@ class CreateDatabaseTest : DatabaseTestsBase() { @Test fun `create database test`() { // PostgreSQL will be tested in the next test function - withDb(excludeSettings = listOf(TestDB.POSTGRESQL, TestDB.POSTGRESQLNG)) { + // DB2:create database in db2 is a clp command, thus it cannot run in jdbc + withDb(excludeSettings = listOf(TestDB.POSTGRESQL, TestDB.POSTGRESQLNG, TestDB.DB2)) { val dbName = "jetbrains" SchemaUtils.createDatabase(dbName) SchemaUtils.dropDatabase(dbName) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateIndexTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateIndexTests.kt index d8a58bdaed..a99fb1d038 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateIndexTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/ddl/CreateIndexTests.kt @@ -39,7 +39,7 @@ class CreateIndexTests : DatabaseTestsBase() { val byNameHash = index("test_table_by_name", isUnique = false, name, indexType = "HASH") } - withTables(excludeSettings = listOf(TestDB.H2_MYSQL, TestDB.SQLSERVER, TestDB.ORACLE), tables = arrayOf(TestTable)) { + withTables(excludeSettings = listOf(TestDB.H2_MYSQL, TestDB.SQLSERVER, TestDB.ORACLE, TestDB.DB2), tables = arrayOf(TestTable)) { SchemaUtils.createMissingTablesAndColumns(TestTable) assertTrue(TestTable.exists()) } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/DeleteTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/DeleteTests.kt index b909aff5c7..c946faf61f 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/DeleteTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/DeleteTests.kt @@ -9,6 +9,7 @@ import org.jetbrains.exposed.sql.tests.TestDB import org.jetbrains.exposed.sql.tests.currentDialectTest import org.jetbrains.exposed.sql.tests.shared.assertEquals import org.jetbrains.exposed.sql.tests.shared.expectException +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.H2Dialect import org.jetbrains.exposed.sql.vendors.SQLiteDialect import org.junit.Test @@ -19,6 +20,9 @@ class DeleteTests : DatabaseTestsBase() { if (!SQLiteDialect.ENABLE_UPDATE_DELETE_LIMIT) { exclude.add(TestDB.SQLITE) } + if (!DB2Dialect.ENABLE_UPDATE_DELETE_LIMIT) { + exclude.add(TestDB.DB2) + } exclude } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/OrderByTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/OrderByTests.kt index c64f9f1ba0..9226ef7127 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/OrderByTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/OrderByTests.kt @@ -5,6 +5,7 @@ import org.jetbrains.exposed.sql.tests.DatabaseTestsBase import org.jetbrains.exposed.sql.tests.TestDB import org.jetbrains.exposed.sql.tests.currentDialectTest import org.jetbrains.exposed.sql.tests.shared.assertEquals +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.OracleDialect import org.jetbrains.exposed.sql.vendors.PostgreSQLDialect import org.junit.Test @@ -24,7 +25,7 @@ class OrderByTests : DatabaseTestsBase() { } private fun isNullFirst() = when (currentDialectTest) { - is OracleDialect, is PostgreSQLDialect -> true + is OracleDialect, is PostgreSQLDialect, is DB2Dialect -> true else -> false } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/ReplaceTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/ReplaceTests.kt index 9b7f526e7b..f8ea147a94 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/ReplaceTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/ReplaceTests.kt @@ -12,7 +12,7 @@ import java.util.* class ReplaceTests : DatabaseTestsBase() { - private val notSupportsReplace = listOf(TestDB.ORACLE, TestDB.SQLSERVER) + private val notSupportsReplace = listOf(TestDB.ORACLE, TestDB.SQLSERVER, TestDB.DB2) // GitHub issue #98: Parameter index out of range when using Table.replace @Test diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/SelectTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/SelectTests.kt index 9816d917ee..3834315b3a 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/SelectTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/SelectTests.kt @@ -101,7 +101,7 @@ class SelectTests : DatabaseTestsBase() { @Test fun testInList03() { - withCitiesAndUsers(listOf(TestDB.SQLITE, TestDB.SQLSERVER)) { _, users, _ -> + withCitiesAndUsers(listOf(TestDB.SQLITE, TestDB.SQLSERVER, TestDB.DB2)) { _, users, _ -> val r = users.select { users.id to users.name inList listOf("andrey" to "Andrey", "alex" to "Alex") }.orderBy(users.name).toList() diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UpdateTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UpdateTests.kt index 9121d3fd57..032dcdaf1d 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UpdateTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UpdateTests.kt @@ -71,7 +71,7 @@ class UpdateTests : DatabaseTestsBase() { @Test fun testUpdateWithJoin() { - val dialects = listOf(TestDB.SQLITE) + val dialects = listOf(TestDB.SQLITE, TestDB.DB2) withCitiesAndUsers(dialects) { cities, users, userData -> val join = users.innerJoin(userData) join.update { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTestBase.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTestBase.kt index 3b248a0d4f..c757ffc39d 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTestBase.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTestBase.kt @@ -20,6 +20,13 @@ abstract class FunctionsTestBase : DatabaseTestsBase() { } } + protected fun withTableExcludeList(excludeDB: List = emptyList(), body: Transaction.(TestDB) -> Unit) { + withTables(excludeSettings = excludeDB, FakeTestTable) { + FakeTestTable.insert { } + body(it) + } + } + protected fun Transaction.assertExpressionEqual(expected: T, expression: Function) { val result = FakeTestTable.slice(expression).selectAll().first()[expression] if (expected is BigDecimal && result is BigDecimal) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/MathFunctionTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/MathFunctionTests.kt index 96964bce9c..84a62eed38 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/MathFunctionTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/MathFunctionTests.kt @@ -114,7 +114,7 @@ class MathFunctionTests : FunctionsTestBase() { assertExpressionEqual(null, SqrtFunction(intLiteral(-100))) } } - TestDB.SQLITE, TestDB.POSTGRESQL, TestDB.POSTGRESQLNG, TestDB.ORACLE -> { + TestDB.SQLITE, TestDB.POSTGRESQL, TestDB.POSTGRESQLNG, TestDB.ORACLE, TestDB.DB2 -> { // SQLite, PSQL, Oracle fail to execute sqrt with negative value expectException { assertExpressionEqual(null, SqrtFunction(intLiteral(-100))) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/TrigonometricalFunctionTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/TrigonometricalFunctionTests.kt index 0d85c409b2..9a846db32b 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/TrigonometricalFunctionTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/TrigonometricalFunctionTests.kt @@ -77,7 +77,7 @@ class TrigonometricalFunctionTests : FunctionsTestBase() { @Test fun testPiFunction() { - withTable(excludeDB = TestDB.ORACLE) { testDb -> + withTableExcludeList(excludeDB = listOf(TestDB.ORACLE, TestDB.DB2)) { testDb -> when (testDb) { TestDB.MYSQL, TestDB.MARIADB -> assertExpressionEqual(BigDecimal("3.141593"), PiFunction) else -> assertExpressionEqual(BigDecimal("3.141592653589793"), PiFunction) From 134b89e6264946d080b86c2babdeded7e17e0b65 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 16:36:27 +0800 Subject: [PATCH 19/20] union did't gurantee the order,so I sort the result to compare in test case. --- .../exposed/sql/tests/shared/dml/UnionTests.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UnionTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UnionTests.kt index 29daebd797..e03cf68404 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UnionTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/UnionTests.kt @@ -7,6 +7,7 @@ import org.jetbrains.exposed.sql.tests.shared.assertEqualCollections import org.jetbrains.exposed.sql.tests.shared.assertEqualLists import org.jetbrains.exposed.sql.tests.shared.assertEquals import org.jetbrains.exposed.sql.tests.shared.expectException +import org.jetbrains.exposed.sql.vendors.DB2Dialect import org.jetbrains.exposed.sql.vendors.H2Dialect import org.jetbrains.exposed.sql.vendors.MariaDBDialect import org.jetbrains.exposed.sql.vendors.PostgreSQLDialect @@ -85,7 +86,7 @@ class UnionTests : DatabaseTestsBase() { val sergeyQuery = users.select { users.id eq "sergey" } val expectedUsers = usersQuery.map { it[users.id] } + "sergey" val intersectAppliedFirst = when (currentDialect) { - is PostgreSQLDialect, is SQLServerDialect, is MariaDBDialect -> true + is PostgreSQLDialect, is SQLServerDialect, is MariaDBDialect, is DB2Dialect -> true is H2Dialect -> (currentDialect as H2Dialect).isSecondVersion else -> false } @@ -251,7 +252,9 @@ class UnionTests : DatabaseTestsBase() { val andreyQuery2 = users.slice(users.id, exp1b, exp2b).select { users.id eq "andrey" } val unionAlias = andreyQuery1.unionAll(andreyQuery2) unionAlias.map { Triple(it[users.id], it[exp1a], it[exp2a]) }.apply { - assertEqualLists(this, listOf(Triple("andrey", 10, "aaa"), Triple("andrey", 100, "bbb"))) + assertEqualLists( + this.sortedBy { it.toString() }, + listOf(Triple("andrey", 10, "aaa"), Triple("andrey", 100, "bbb"))) } } } @@ -267,7 +270,9 @@ class UnionTests : DatabaseTestsBase() { val andreyQuery2 = users.slice(users.id, exp1b, exp2b).select { users.id eq "andrey" } val unionAlias = andreyQuery1.unionAll(andreyQuery2).alias("unionAlias") unionAlias.selectAll().map { Triple(it[unionAlias[users.id]], it[unionAlias[exp1a]], it[unionAlias[exp2a]]) }.apply { - assertEqualLists(this, listOf(Triple("andrey", 10, "aaa"), Triple("andrey", 100, "bbb"))) + assertEqualLists( + this.sortedBy { it.toString() }, + listOf(Triple("andrey", 10, "aaa"), Triple("andrey", 100, "bbb"))) } } } From d6a2ac6bdc8d12fa31fee368b68d12ef0e732a40 Mon Sep 17 00:00:00 2001 From: sola1tmy Date: Sun, 20 Mar 2022 16:37:36 +0800 Subject: [PATCH 20/20] ignore 2 tests igore emoji test in db2 ignore a groupconcat test which try order by 2 colums --- .../org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt | 2 +- .../org/jetbrains/exposed/sql/tests/shared/dml/InsertTests.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt index 9378c92260..dcc64b0297 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt @@ -156,7 +156,7 @@ class GroupByTests : DatabaseTestsBase() { @Test fun testGroupConcat() { - withCitiesAndUsers(listOf(TestDB.SQLITE)) { cities, users, _ -> + withCitiesAndUsers(listOf(TestDB.SQLITE, TestDB.DB2)) { cities, users, _ -> fun GroupConcat.checkExcept(vararg dialects: KClass, assert: (Map) -> Unit) { try { val result = cities.leftJoin(users) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/InsertTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/InsertTests.kt index 0691a27127..5b25be8d02 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/InsertTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/InsertTests.kt @@ -368,7 +368,7 @@ class InsertTests : DatabaseTestsBase() { } val emojis = "\uD83D\uDC68\uD83C\uDFFF\u200D\uD83D\uDC69\uD83C\uDFFF\u200D\uD83D\uDC67\uD83C\uDFFF\u200D\uD83D\uDC66\uD83C\uDFFF" - withTables(listOf(TestDB.H2, TestDB.H2_MYSQL, TestDB.SQLSERVER, TestDB.ORACLE), table) { + withTables(listOf(TestDB.H2, TestDB.H2_MYSQL, TestDB.SQLSERVER, TestDB.ORACLE, TestDB.DB2), table) { val isOldMySQL = currentDialectTest is MysqlDialect && db.isVersionCovers(BigDecimal("5.5")) if (isOldMySQL) { exec("ALTER TABLE ${table.nameInDatabaseCase()} DEFAULT CHARSET utf8mb4, MODIFY emoji VARCHAR(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;")