22
33package org.jetbrains.kotlinx.dataframe.io.local
44
5+ import io.kotest.assertions.withClue
56import io.kotest.matchers.shouldBe
7+ import kotlinx.datetime.LocalDate
8+ import kotlinx.datetime.LocalTime
69import org.duckdb.DuckDBConnection
710import org.duckdb.DuckDBResultSet
811import org.duckdb.JsonNode
@@ -36,11 +39,12 @@ import java.nio.file.Files
3639import java.sql.Blob
3740import java.sql.DriverManager
3841import java.sql.Timestamp
39- import java.time.LocalDate
40- import java.time.LocalTime
41- import java.time.OffsetDateTime
4242import java.util.UUID
4343import kotlin.io.path.createTempDirectory
44+ import kotlin.time.Instant
45+ import kotlin.time.toKotlinInstant
46+ import kotlin.uuid.Uuid
47+ import java.time.OffsetDateTime as JavaOffsetDateTime
4448
4549private const val URL = " jdbc:duckdb:"
4650
@@ -58,10 +62,10 @@ class DuckDbTest {
5862 ) {
5963 companion object {
6064 val expected = listOf (
61- Person (1 , " John Doe" , 30 , 50000.0 , LocalDate .of (2020 , 1 , 15 )),
62- Person (2 , " Jane Smith" , 28 , 55000.0 , LocalDate .of (2021 , 3 , 20 )),
63- Person (3 , " Bob Johnson" , 35 , 65000.0 , LocalDate .of (2019 , 11 , 10 )),
64- Person (4 , " Alice Brown" , 32 , 60000.0 , LocalDate .of (2020 , 7 , 1 )),
65+ Person (1 , " John Doe" , 30 , 50000.0 , LocalDate (2020 , 1 , 15 )),
66+ Person (2 , " Jane Smith" , 28 , 55000.0 , LocalDate (2021 , 3 , 20 )),
67+ Person (3 , " Bob Johnson" , 35 , 65000.0 , LocalDate (2019 , 11 , 10 )),
68+ Person (4 , " Alice Brown" , 32 , 60000.0 , LocalDate (2020 , 7 , 1 )),
6569 ).toDataFrame()
6670 }
6771 }
@@ -91,7 +95,7 @@ class DuckDbTest {
9195 @ColumnName(" date_col" )
9296 val dateCol : LocalDate ,
9397 @ColumnName(" datetime_col" )
94- val datetimeCol : Timestamp ,
98+ val datetimeCol : Instant ,
9599 @ColumnName(" decimal_col" )
96100 val decimalCol : BigDecimal ,
97101 @ColumnName(" double_col" )
@@ -151,11 +155,11 @@ class DuckDbTest {
151155 @ColumnName(" time_col" )
152156 val timeCol : LocalTime ,
153157 @ColumnName(" timestamp_col" )
154- val timestampCol : Timestamp ,
158+ val timestampCol : Instant ,
155159 @ColumnName(" timestamptz_col" )
156- val timestamptzCol : OffsetDateTime ,
160+ val timestamptzCol : JavaOffsetDateTime ,
157161 @ColumnName(" timestampwtz_col" )
158- val timestampwtzCol : OffsetDateTime ,
162+ val timestampwtzCol : JavaOffsetDateTime ,
159163 @ColumnName(" tinyint_col" )
160164 val tinyintCol : Byte ,
161165 @ColumnName(" ubigint_col" )
@@ -179,7 +183,7 @@ class DuckDbTest {
179183 @ColumnName(" utinyint_col" )
180184 val utinyintCol : Short ,
181185 @ColumnName(" uuid_col" )
182- val uuidCol : UUID ,
186+ val uuidCol : Uuid ,
183187 @ColumnName(" varbinary_col" )
184188 val varbinaryCol : Blob ,
185189 @ColumnName(" varchar_col" )
@@ -199,7 +203,7 @@ class DuckDbTest {
199203 byteaCol = DuckDBResultSet .DuckDBBlobResult (ByteBuffer .wrap(" DEADBEEF" .toByteArray())),
200204 charCol = " test" ,
201205 dateCol = LocalDate .parse(" 2025-06-19" ),
202- datetimeCol = Timestamp .valueOf(" 2025-06-19 12:34:56" ),
206+ datetimeCol = Timestamp .valueOf(" 2025-06-19 12:34:56" ).toInstant().toKotlinInstant() ,
203207 decimalCol = BigDecimal (" 123.45" ),
204208 doubleCol = 3.14159 ,
205209 enumCol = " female" ,
@@ -229,9 +233,9 @@ class DuckDbTest {
229233 stringCol = " test string" ,
230234 textCol = " test text" ,
231235 timeCol = LocalTime .parse(" 12:34:56" ),
232- timestampCol = Timestamp .valueOf(" 2025-06-19 12:34:56" ),
233- timestamptzCol = OffsetDateTime .parse(" 2025-06-19T12:34:56+02:00" ),
234- timestampwtzCol = OffsetDateTime .parse(" 2025-06-19T12:34:56+02:00" ),
236+ timestampCol = Timestamp .valueOf(" 2025-06-19 12:34:56" ).toInstant().toKotlinInstant() ,
237+ timestamptzCol = JavaOffsetDateTime .parse(" 2025-06-19T12:34:56+02:00" ),
238+ timestampwtzCol = JavaOffsetDateTime .parse(" 2025-06-19T12:34:56+02:00" ),
235239 tinyintCol = 127 ,
236240 ubigintCol = BigInteger (" 18446744073709551615" ),
237241 uhugeintCol = BigInteger (" 340282366920938463463374607431768211455" ),
@@ -243,7 +247,7 @@ class DuckDbTest {
243247 uintCol = 4294967295L ,
244248 usmallintCol = 65535 ,
245249 utinyintCol = 255 ,
246- uuidCol = UUID .fromString (" a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" ),
250+ uuidCol = Uuid .parse (" a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11" ),
247251 varbinaryCol = DuckDBResultSet .DuckDBBlobResult (ByteBuffer .wrap(" DEADBEEF" .toByteArray())),
248252 varcharCol = " test string" ,
249253 ),
@@ -254,21 +258,21 @@ class DuckDbTest {
254258 @DataSchema
255259 data class NestedTypes (
256260 @ColumnName(" ijstruct_col" )
257- val ijstructCol : java.sql.Struct ,
261+ val ijstructCol : java.sql.Struct , // TODO
258262 @ColumnName(" intarray_col" )
259- val intarrayCol : java.sql. Array ,
263+ val intarrayCol : List < Int ?> ,
260264 @ColumnName(" intlist_col" )
261- val intlistCol : java.sql. Array ,
265+ val intlistCol : List < Int ?> ,
262266 @ColumnName(" intstringmap_col" )
263267 val intstringmapCol : Map <Int , String ?>,
264268 @ColumnName(" intstrinstinggmap_col" )
265269 val intstrinstinggmapCol : Map <Int , Map <String , String ?>? >,
266270 @ColumnName(" stringarray_col" )
267- val stringarrayCol : java.sql. Array ,
271+ val stringarrayCol : List < String ?> ,
268272 @ColumnName(" stringlist_col" )
269- val stringlistCol : java.sql. Array ,
273+ val stringlistCol : List < String ?> ,
270274 @ColumnName(" stringlistlist_col" )
271- val stringlistlistCol : java.sql. Array ,
275+ val stringlistlistCol : List < List < String ?> ? > ,
272276 @ColumnName(" union_col" )
273277 val unionCol : Any ,
274278 )
@@ -310,7 +314,19 @@ class DuckDbTest {
310314 subset = DataFrame .readSqlQuery(connection, """ SELECT test_table.name, test_table.age FROM test_table""" )
311315 }
312316
313- schema.compare(Person .expected.schema()).isSuperOrMatches() shouldBe true
317+ withClue({
318+ """
319+ |Read schema must be <: expected schema
320+ |
321+ |Read Schema:
322+ |${schema.toString().lines().joinToString(" \n |" )}
323+ |
324+ |expected Schema:
325+ |${Person .expected.schema().toString().lines().joinToString(" \n |" )}
326+ """ .trimMargin()
327+ }) {
328+ schema.compare(Person .expected.schema()).isSuperOrMatches() shouldBe true
329+ }
314330
315331 df.cast<Person >(verify = true ) shouldBe Person .expected
316332 df.assertInferredTypesMatchSchema()
@@ -545,10 +561,24 @@ class DuckDbTest {
545561 df = DataFrame .readSqlTable(connection, " table1" ).reorderColumnsByName()
546562 }
547563
548- schema.compare(GeneralPurposeTypes .expected.schema()).isSuperOrMatches() shouldBe true
564+ // schema.toString().lines().sorted().joinToString("\n") shouldBe
565+ // GeneralPurposeTypes.expected.schema().toString().lines().sorted().joinToString("\n")
566+ withClue({
567+ """
568+ |Read schema must be <: expected schema
569+ |
570+ |Read Schema:
571+ |${schema.toString().lines().joinToString(" \n |" )}
572+ |
573+ |expected Schema:
574+ |${GeneralPurposeTypes .expected.schema().toString().lines().joinToString(" \n |" )}
575+ """ .trimMargin()
576+ }) {
577+ schema.compare(GeneralPurposeTypes .expected.schema()).isSuperOrMatches() shouldBe true
578+ }
549579
550580 // on some systems OffsetDateTime's get converted to UTC sometimes, let's compare them as Instant instead
551- fun AnyFrame.fixOffsetDateTime () = convert { colsOf<OffsetDateTime >() }.with { it.toInstant() }
581+ fun AnyFrame.fixOffsetDateTime () = convert { colsOf<JavaOffsetDateTime >() }.with { it.toInstant() }
552582
553583 df.cast<GeneralPurposeTypes >(verify = true ).fixOffsetDateTime() shouldBe
554584 GeneralPurposeTypes .expected.fixOffsetDateTime()
@@ -606,19 +636,18 @@ class DuckDbTest {
606636 df as DataFrame <NestedTypes >
607637
608638 df.single().let {
609- it[{ " intarray_col" < java.sql.Array > () }].array shouldBe arrayOf(1 , 2 , null )
610- it[{ " stringarray_col" < java.sql.Array > () }].array shouldBe arrayOf(" a" , " ab" , " abc" )
611- it[{ " intlist_col" < java.sql.Array > () }].array shouldBe arrayOf(1 , 2 , 3 )
612- it[{ " stringlist_col" < java.sql.Array > () }].array shouldBe arrayOf(" a" , " ab" , " abc" )
613- (it[{ " stringlistlist_col" < java.sql.Array > () }].array as Array <* >)
614- .map { (it as java.sql.Array? )?.array } shouldBe listOf (arrayOf(" a" , " ab" ), arrayOf(" abc" ), null )
615- it[{ " intstringmap_col" <Map <Int , String ?>>() }] shouldBe mapOf (1 to " value1" , 200 to " value2" )
616- it[{ " intstrinstinggmap_col" <Map <Int , Map <String , String ?>>>() }] shouldBe mapOf (
639+ it[" intarray_col" ] shouldBe listOf (1 , 2 , null )
640+ it[" stringarray_col" ] shouldBe listOf (" a" , " ab" , " abc" )
641+ it[" intlist_col" ] shouldBe listOf (1 , 2 , 3 )
642+ it[" stringlist_col" ] shouldBe listOf (" a" , " ab" , " abc" )
643+ it[" stringlistlist_col" ] shouldBe listOf (listOf (" a" , " ab" ), listOf (" abc" ), null )
644+ it[" intstringmap_col" ] shouldBe mapOf (1 to " value1" , 200 to " value2" )
645+ it[" intstrinstinggmap_col" ] shouldBe mapOf (
617646 1 to mapOf (" value1" to " a" , " value2" to " b" ),
618647 200 to mapOf (" value1" to " c" , " value2" to " d" ),
619648 )
620649 it[{ " ijstruct_col" < java.sql.Struct > () }].attributes shouldBe arrayOf<Any >(42 , " answer" )
621- it[{ " union_col" < Any >() } ] shouldBe 2
650+ it[" union_col" ] shouldBe 2
622651 }
623652 }
624653
0 commit comments