diff --git a/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MariaDb.kt b/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MariaDb.kt index 9060004e62..852dca437d 100644 --- a/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MariaDb.kt +++ b/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MariaDb.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlinx.dataframe.io.db +import java.math.BigInteger import java.sql.ResultSet import kotlin.reflect.KType import kotlin.reflect.full.withNullability @@ -34,6 +35,9 @@ public object MariaDb : DbType("mariadb") { if (tableColumnMetadata.sqlTypeName == "SMALLINT" && tableColumnMetadata.javaClassName == "java.lang.Short") { return typeOf().withNullability(tableColumnMetadata.isNullable) } + if (tableColumnMetadata.sqlTypeName == "BIGINT UNSIGNED") { + return typeOf().withNullability(tableColumnMetadata.isNullable) + } return super.getExpectedJdbcType(tableColumnMetadata) } diff --git a/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MySql.kt b/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MySql.kt index 7c4f74486c..5dd92c7f32 100644 --- a/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MySql.kt +++ b/dataframe-jdbc/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/db/MySql.kt @@ -1,5 +1,6 @@ package org.jetbrains.kotlinx.dataframe.io.db +import java.math.BigInteger import java.sql.ResultSet import java.util.Locale import kotlin.reflect.KType @@ -20,6 +21,9 @@ public object MySql : DbType("mysql") { if (tableColumnMetadata.sqlTypeName == "INT UNSIGNED") { return typeOf().withNullability(tableColumnMetadata.isNullable) } + if (tableColumnMetadata.sqlTypeName == "BIGINT UNSIGNED") { + return typeOf().withNullability(tableColumnMetadata.isNullable) + } return super.getExpectedJdbcType(tableColumnMetadata) } diff --git a/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/db/jdbcTypesTest.kt b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/db/jdbcTypesTest.kt new file mode 100644 index 0000000000..a2ee381032 --- /dev/null +++ b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/db/jdbcTypesTest.kt @@ -0,0 +1,75 @@ +@file:Suppress("ClassName") + +package org.jetbrains.kotlinx.dataframe.io.db + +import io.kotest.matchers.shouldBe +import org.junit.Test +import org.junit.experimental.runners.Enclosed +import org.junit.runner.RunWith +import java.math.BigInteger +import kotlin.reflect.KType +import kotlin.reflect.typeOf + +// TODO: complete and enhance (#1736) +@RunWith(Enclosed::class) +class JdbcTypesTest { + + abstract class ColumnType( + val sqlTypeName: String, + val jdbcType: Int, + val javaClassName: String, + val expectedKotlinType: KType, + ) { + fun mockkColMetaData() = + TableColumnMetadata( + "name", + sqlTypeName, + jdbcType, + 10, + javaClassName, + false, + ) + } + + class MariaDBTypes { + + object BIGINT_UNSIGNED : ColumnType( + "BIGINT UNSIGNED", + 20, + "java.math.BigInteger", + typeOf(), + ) + + val types: List = listOf( + BIGINT_UNSIGNED, + ) + + @Test + fun `all MariaDB SQL types should match expected type`() { + types.forEach { type -> + MariaDb.getExpectedJdbcType(type.mockkColMetaData()) shouldBe type.expectedKotlinType + } + } + } + + class MySqlDBTypes { + + object BIGINT_UNSIGNED : ColumnType( + "BIGINT UNSIGNED", + 20, + "java.math.BigInteger", + typeOf(), + ) + + val types: List = listOf( + BIGINT_UNSIGNED, + ) + + @Test + fun `all MariaDB SQL types should match expected type`() { + types.forEach { type -> + MySql.getExpectedJdbcType(type.mockkColMetaData()) shouldBe type.expectedKotlinType + } + } + } +} diff --git a/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mariadbTest.kt b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mariadbTest.kt index b8a0d08124..551099084c 100644 --- a/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mariadbTest.kt +++ b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mariadbTest.kt @@ -18,6 +18,7 @@ import org.junit.BeforeClass import org.junit.Ignore import org.junit.Test import java.math.BigDecimal +import java.math.BigInteger import java.sql.Blob import java.sql.Connection import java.sql.DriverManager @@ -64,6 +65,7 @@ interface Table1MariaDb { val longtextCol: String val enumCol: String val setCol: Char + val bigintUnsignedCol: BigInteger val jsonCol: String } @@ -100,6 +102,7 @@ interface Table2MariaDb { val longtextCol: String? val enumCol: String? val setCol: Char? + val bigintUnsignedCol: BigInteger? val jsonCol: String? } @@ -176,6 +179,7 @@ class MariadbTest { longtextCol LONGTEXT NOT NULL, enumCol ENUM('Value1', 'Value2', 'Value3') NOT NULL, setCol SET('Option1', 'Option2', 'Option3') NOT NULL, + bigintUnsignedCol BIGINT UNSIGNED NOT NULL, jsonCol JSON NOT NULL CHECK (JSON_VALID(jsonCol)) ) @@ -215,7 +219,8 @@ class MariadbTest { mediumtextCol MEDIUMTEXT, longtextCol LONGTEXT, enumCol ENUM('Value1', 'Value2', 'Value3'), - setCol SET('Option1', 'Option2', 'Option3') + setCol SET('Option1', 'Option2', 'Option3'), + bigintUnsignedCol BIGINT UNSIGNED ) """ connection.createStatement().execute(createTableQuery2.trimIndent()) @@ -227,8 +232,8 @@ class MariadbTest { bitCol, tinyintCol, smallintCol, mediumintCol, mediumintUnsignedCol, integerCol, intCol, integerUnsignedCol, bigintCol, floatCol, doubleCol, decimalCol, dateCol, datetimeCol, timestampCol, timeCol, yearCol, varcharCol, charCol, binaryCol, varbinaryCol, tinyblobCol, blobCol, - mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, jsonCol - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, bigintUnsignedCol, jsonCol + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """.trimIndent() @Language("SQL") @@ -238,8 +243,8 @@ class MariadbTest { bitCol, tinyintCol, smallintCol, mediumintCol, mediumintUnsignedCol, integerCol, intCol, integerUnsignedCol, bigintCol, floatCol, doubleCol, decimalCol, dateCol, datetimeCol, timestampCol, timeCol, yearCol, varcharCol, charCol, binaryCol, varbinaryCol, tinyblobCol, blobCol, - mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, bigintUnsignedCol + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """.trimIndent() connection.prepareStatement(insertData1).use { st -> @@ -275,7 +280,8 @@ class MariadbTest { st.setString(28, "longtextValue$i") st.setString(29, "Value$i") st.setString(30, "Option$i") - st.setString(31, JSON_STRING) + st.setObject(31, BigInteger.valueOf((i * 1000).toLong())) + st.setString(32, JSON_STRING) st.executeUpdate() } @@ -314,6 +320,7 @@ class MariadbTest { st.setString(28, "longtextValue$i") st.setString(29, "Value$i") st.setString(30, "Option$i") + st.setObject(31, BigInteger.valueOf((i * 2000).toLong())) st.executeUpdate() } } @@ -396,7 +403,8 @@ class MariadbTest { table1Df.filter { "integerCol"() > 100 }.rowsCount() shouldBe 2 table1Df[0][11] shouldBe 10.0 table1Df[0][26] shouldBe "textValue1" - table1Df[0][31] shouldBe JSON_STRING // TODO: https://github.com/Kotlin/dataframe/issues/462 + table1Df[0][31] shouldBe BigInteger.valueOf(1000L) + table1Df[0][32] shouldBe JSON_STRING // TODO: https://github.com/Kotlin/dataframe/issues/462 val table2Df = dataframes[1].cast() @@ -418,7 +426,7 @@ class MariadbTest { result[0][1] shouldBe 1 val result1 = df1.select("smallintCol") - .add("smallintCol2") { "smallintCol"() } + .add("smallintCol2") { "smallintCol"() } result1[0][1] shouldBe 10 @@ -442,6 +450,11 @@ class MariadbTest { result5[0][1] shouldBe 100 + val result5a = df1.select("bigintUnsignedCol") + .add("bigintUnsignedCol2") { "bigintUnsignedCol"() } + + result5a[0][1] shouldBe BigInteger.valueOf(1000) + val result6 = df1.select("floatCol") .add("floatCol2") { "floatCol"() } @@ -465,6 +478,7 @@ class MariadbTest { schema.columns["mediumintUnsignedCol"]!!.type shouldBe typeOf() schema.columns["integerUnsignedCol"]!!.type shouldBe typeOf() schema.columns["bigintCol"]!!.type shouldBe typeOf() + schema.columns["bigintUnsignedCol"]!!.type shouldBe typeOf() schema.columns["floatCol"]!!.type shouldBe typeOf() schema.columns["doubleCol"]!!.type shouldBe typeOf() schema.columns["decimalCol"]!!.type shouldBe typeOf() diff --git a/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mysqlTest.kt b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mysqlTest.kt index 114807edb2..3edb798ea4 100644 --- a/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mysqlTest.kt +++ b/dataframe-jdbc/src/test/kotlin/org/jetbrains/kotlinx/dataframe/io/local/mysqlTest.kt @@ -19,6 +19,7 @@ import org.junit.BeforeClass import org.junit.Ignore import org.junit.Test import java.math.BigDecimal +import java.math.BigInteger import java.sql.Connection import java.sql.DriverManager import java.sql.SQLException @@ -65,6 +66,7 @@ interface Table1MySql { val longtextCol: String val enumCol: String val setCol: Char + val bigintUnsignedCol: BigInteger } @DataSchema @@ -100,6 +102,7 @@ interface Table2MySql { val longtextCol: String? val enumCol: String? val setCol: Char? + val bigintUnsignedCol: BigInteger? val jsonCol: String? } @@ -171,6 +174,7 @@ class MySqlTest { longtextCol LONGTEXT NOT NULL, enumCol ENUM('Value1', 'Value2', 'Value3') NOT NULL, setCol SET('Option1', 'Option2', 'Option3') NOT NULL, + bigintUnsignedCol BIGINT UNSIGNED NOT NULL, location GEOMETRY, data JSON CHECK (JSON_VALID(data)) @@ -213,6 +217,7 @@ class MySqlTest { longtextCol LONGTEXT, enumCol ENUM('Value1', 'Value2', 'Value3'), setCol SET('Option1', 'Option2', 'Option3'), + bigintUnsignedCol BIGINT UNSIGNED, location GEOMETRY, data JSON CHECK (JSON_VALID(data)) @@ -228,8 +233,8 @@ class MySqlTest { bitCol, tinyintCol, smallintCol, mediumintCol, mediumintUnsignedCol, integerCol, intCol, integerUnsignedCol, bigintCol, floatCol, doubleCol, decimalCol, dateCol, datetimeCol, timestampCol, timeCol, yearCol, varcharCol, charCol, binaryCol, varbinaryCol, tinyblobCol, blobCol, - mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, location, data - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText('POINT(1 1)'), ?) + mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, bigintUnsignedCol, location, data + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText('POINT(1 1)'), ?) """.trimIndent() @Language("SQL") @@ -239,8 +244,8 @@ class MySqlTest { bitCol, tinyintCol, smallintCol, mediumintCol, mediumintUnsignedCol, integerCol, intCol, integerUnsignedCol, bigintCol, floatCol, doubleCol, decimalCol, dateCol, datetimeCol, timestampCol, timeCol, yearCol, varcharCol, charCol, binaryCol, varbinaryCol, tinyblobCol, blobCol, - mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, location, data - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText('POINT(1 1)'), ?) + mediumblobCol, longblobCol, textCol, mediumtextCol, longtextCol, enumCol, setCol, bigintUnsignedCol, location, data + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ST_GeomFromText('POINT(1 1)'), ?) """.trimIndent() connection.prepareStatement(insertData1).use { st -> @@ -276,7 +281,8 @@ class MySqlTest { st.setString(28, "longtextValue$i") st.setString(29, "Value$i") st.setString(30, "Option$i") - st.setString(31, "{\"key\": \"value\"}") + st.setObject(31, BigInteger.valueOf((i * 1000).toLong())) + st.setString(32, "{\"key\": \"value\"}") st.executeUpdate() } } @@ -314,7 +320,8 @@ class MySqlTest { st.setString(28, "longtextValue$i") st.setString(29, "Value$i") st.setString(30, "Option$i") - st.setString(31, "{\"key\": \"value\"}") + st.setObject(31, BigInteger.valueOf((i * 2000).toLong())) + st.setString(32, "{\"key\": \"value\"}") st.executeUpdate() } } @@ -441,6 +448,11 @@ class MySqlTest { result5[0][1] shouldBe 100 + val result5a = df1.select("bigintUnsignedCol") + .add("bigintUnsignedCol2") { "bigintUnsignedCol"() } + + result5a[0][1] shouldBe BigInteger.valueOf(1000) + val result6 = df1.select("floatCol") .add("floatCol2") { "floatCol"() } @@ -464,6 +476,7 @@ class MySqlTest { schema.columns["mediumintUnsignedCol"]!!.type shouldBe typeOf() schema.columns["integerUnsignedCol"]!!.type shouldBe typeOf() schema.columns["bigintCol"]!!.type shouldBe typeOf() + schema.columns["bigintUnsignedCol"]!!.type shouldBe typeOf() schema.columns["floatCol"]!!.type shouldBe typeOf() schema.columns["doubleCol"]!!.type shouldBe typeOf() schema.columns["decimalCol"]!!.type shouldBe typeOf()