Skip to content

Commit 8091ae3

Browse files
authored
Fix option null values failing to put (#42)
This PR fixes the error described in #41 Additionally it fixes it for Enum values on Postgres (which I'm fairly sure had the same error) It appears keeping record of the JDBCType on TypeMappers is not necessary anymore, but nonetheless I kept the value there to touch as little as possible. I did update the DocString.
1 parent 0e4e39b commit 8091ae3

File tree

4 files changed

+171
-3
lines changed

4 files changed

+171
-3
lines changed

docs/reference.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10159,6 +10159,86 @@ OptCols.select.sortBy(_.myInt).desc.nullsFirst
1015910159
1016010160
1016110161
10162+
### Optional.sorting.roundTripOptionalValues
10163+
10164+
This example demonstrates a range of different data types being written
10165+
as options, both with Some(v) and None values
10166+
10167+
```scala
10168+
object MyEnum extends Enumeration {
10169+
val foo, bar, baz = Value
10170+
10171+
implicit def make: String => Value = withName
10172+
}
10173+
case class OptDataTypes[T[_]](
10174+
myTinyInt: T[Option[Byte]],
10175+
mySmallInt: T[Option[Short]],
10176+
myInt: T[Option[Int]],
10177+
myBigInt: T[Option[Long]],
10178+
myDouble: T[Option[Double]],
10179+
myBoolean: T[Option[Boolean]],
10180+
myLocalDate: T[Option[LocalDate]],
10181+
myLocalTime: T[Option[LocalTime]],
10182+
myLocalDateTime: T[Option[LocalDateTime]],
10183+
myUtilDate: T[Option[Date]],
10184+
myInstant: T[Option[Instant]],
10185+
myVarBinary: T[Option[geny.Bytes]],
10186+
myUUID: T[Option[java.util.UUID]],
10187+
myEnum: T[Option[MyEnum.Value]]
10188+
)
10189+
10190+
object OptDataTypes extends Table[OptDataTypes] {
10191+
override def tableName: String = "data_types"
10192+
}
10193+
10194+
val rowSome = OptDataTypes[Sc](
10195+
myTinyInt = Some(123.toByte),
10196+
mySmallInt = Some(12345.toShort),
10197+
myInt = Some(12345678),
10198+
myBigInt = Some(12345678901L),
10199+
myDouble = Some(3.14),
10200+
myBoolean = Some(true),
10201+
myLocalDate = Some(LocalDate.parse("2023-12-20")),
10202+
myLocalTime = Some(LocalTime.parse("10:15:30")),
10203+
myLocalDateTime = Some(LocalDateTime.parse("2011-12-03T10:15:30")),
10204+
myUtilDate = Some(
10205+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2011-12-03T10:15:30.000")
10206+
),
10207+
myInstant = Some(Instant.parse("2011-12-03T10:15:30Z")),
10208+
myVarBinary = Some(new geny.Bytes(Array[Byte](1, 2, 3, 4, 5, 6, 7, 8))),
10209+
myUUID = Some(new java.util.UUID(1234567890L, 9876543210L)),
10210+
myEnum = Some(MyEnum.bar)
10211+
)
10212+
10213+
val rowNone = OptDataTypes[Sc](
10214+
myTinyInt = None,
10215+
mySmallInt = None,
10216+
myInt = None,
10217+
myBigInt = None,
10218+
myDouble = None,
10219+
myBoolean = None,
10220+
myLocalDate = None,
10221+
myLocalTime = None,
10222+
myLocalDateTime = None,
10223+
myUtilDate = None,
10224+
myInstant = None,
10225+
myVarBinary = None,
10226+
myUUID = None,
10227+
myEnum = None
10228+
)
10229+
10230+
db.run(
10231+
OptDataTypes.insert.values(rowSome, rowNone)
10232+
) ==> 2
10233+
10234+
db.run(OptDataTypes.select) ==> Seq(rowSome, rowNone)
10235+
```
10236+
10237+
10238+
10239+
10240+
10241+
1016210242
## PostgresDialect
1016310243
Operations specific to working with Postgres Databases
1016410244
### PostgresDialect.distinctOn

scalasql/core/src/TypeMapper.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ import java.util.UUID
3232
trait TypeMapper[T] { outer =>
3333

3434
/**
35-
* The JDBC type of this type. Used for `setNull` which needs to know the
36-
* `java.sql.Types` integer ID of the type to set it properly
35+
* The JDBC type of this type.
3736
*/
3837
def jdbcType: JDBCType
3938

scalasql/src/dialects/Dialect.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ trait Dialect extends DialectTypeMappers {
263263

264264
def put(r: PreparedStatement, idx: Int, v: Option[T]): Unit = {
265265
v match {
266-
case None => r.setNull(idx, jdbcType.getVendorTypeNumber)
266+
case None => r.setNull(idx, JDBCType.NULL.getVendorTypeNumber)
267267
case Some(value) => inner.put(r, idx, value)
268268
}
269269
}

scalasql/test/src/datatypes/OptionalTests.scala

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ import utest._
66
import utils.ScalaSqlSuite
77
import sourcecode.Text
88

9+
import java.time.{
10+
Instant,
11+
LocalDate,
12+
LocalDateTime,
13+
LocalTime,
14+
OffsetDateTime,
15+
ZoneId,
16+
ZonedDateTime
17+
}
18+
import java.util.Date
19+
import java.text.SimpleDateFormat
20+
import java.util.UUID
21+
922
case class OptCols[T[_]](myInt: T[Option[Int]], myInt2: T[Option[Int]])
1023

1124
object OptCols extends Table[OptCols]
@@ -516,6 +529,82 @@ trait OptionalTests extends ScalaSqlSuite {
516529
OptCols[Sc](Some(1), Some(2))
517530
)
518531
)
532+
test("roundTripOptionalValues") - checker.recorded(
533+
"""
534+
This example demonstrates a range of different data types being written
535+
as options, both with Some(v) and None values
536+
""",
537+
Text {
538+
object MyEnum extends Enumeration {
539+
val foo, bar, baz = Value
540+
541+
implicit def make: String => Value = withName
542+
}
543+
case class OptDataTypes[T[_]](
544+
myTinyInt: T[Option[Byte]],
545+
mySmallInt: T[Option[Short]],
546+
myInt: T[Option[Int]],
547+
myBigInt: T[Option[Long]],
548+
myDouble: T[Option[Double]],
549+
myBoolean: T[Option[Boolean]],
550+
myLocalDate: T[Option[LocalDate]],
551+
myLocalTime: T[Option[LocalTime]],
552+
myLocalDateTime: T[Option[LocalDateTime]],
553+
myUtilDate: T[Option[Date]],
554+
myInstant: T[Option[Instant]],
555+
myVarBinary: T[Option[geny.Bytes]],
556+
myUUID: T[Option[java.util.UUID]],
557+
myEnum: T[Option[MyEnum.Value]]
558+
)
559+
560+
object OptDataTypes extends Table[OptDataTypes] {
561+
override def tableName: String = "data_types"
562+
}
563+
564+
val rowSome = OptDataTypes[Sc](
565+
myTinyInt = Some(123.toByte),
566+
mySmallInt = Some(12345.toShort),
567+
myInt = Some(12345678),
568+
myBigInt = Some(12345678901L),
569+
myDouble = Some(3.14),
570+
myBoolean = Some(true),
571+
myLocalDate = Some(LocalDate.parse("2023-12-20")),
572+
myLocalTime = Some(LocalTime.parse("10:15:30")),
573+
myLocalDateTime = Some(LocalDateTime.parse("2011-12-03T10:15:30")),
574+
myUtilDate = Some(
575+
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").parse("2011-12-03T10:15:30.000")
576+
),
577+
myInstant = Some(Instant.parse("2011-12-03T10:15:30Z")),
578+
myVarBinary = Some(new geny.Bytes(Array[Byte](1, 2, 3, 4, 5, 6, 7, 8))),
579+
myUUID = Some(new java.util.UUID(1234567890L, 9876543210L)),
580+
myEnum = Some(MyEnum.bar)
581+
)
582+
583+
val rowNone = OptDataTypes[Sc](
584+
myTinyInt = None,
585+
mySmallInt = None,
586+
myInt = None,
587+
myBigInt = None,
588+
myDouble = None,
589+
myBoolean = None,
590+
myLocalDate = None,
591+
myLocalTime = None,
592+
myLocalDateTime = None,
593+
myUtilDate = None,
594+
myInstant = None,
595+
myVarBinary = None,
596+
myUUID = None,
597+
myEnum = None
598+
)
599+
600+
db.run(
601+
OptDataTypes.insert.values(rowSome, rowNone)
602+
) ==> 2
603+
604+
db.run(OptDataTypes.select) ==> Seq(rowSome, rowNone)
605+
}
606+
)
607+
519608
}
520609
}
521610
}

0 commit comments

Comments
 (0)