From 508de127d44af8b1c6a225eb6b7bb1f67e911c84 Mon Sep 17 00:00:00 2001 From: Sam Guymer Date: Sat, 27 Aug 2022 09:16:52 +1000 Subject: [PATCH] Update Postgres Java time instances The Postgres Java time instances have been updated to more closely match what the driver allows. To verify this, the check tests have been updated to also fetch the value. --- build.sbt | 2 +- .../doobie/postgres/JavaTimeInstances.scala | 8 +- .../scala/doobie/postgres/CheckSuite.scala | 111 +++++++++++------- project/build.sbt | 2 +- 4 files changed, 72 insertions(+), 51 deletions(-) diff --git a/build.sbt b/build.sbt index c4cca12e9..c03c250d5 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ lazy val h2Version = "1.4.200" lazy val hikariVersion = "4.0.3" // N.B. Hikari v4 introduces a breaking change via slf4j v2 lazy val kindProjectorVersion = "0.11.2" lazy val postGisVersion = "2.5.1" -lazy val postgresVersion = "42.3.5" +lazy val postgresVersion = "42.4.2" lazy val refinedVersion = "0.9.28" lazy val scalaCheckVersion = "1.15.4" lazy val scalatestVersion = "3.2.10" diff --git a/modules/postgres/src/main/scala/doobie/postgres/JavaTimeInstances.scala b/modules/postgres/src/main/scala/doobie/postgres/JavaTimeInstances.scala index ded5da084..059448760 100644 --- a/modules/postgres/src/main/scala/doobie/postgres/JavaTimeInstances.scala +++ b/modules/postgres/src/main/scala/doobie/postgres/JavaTimeInstances.scala @@ -27,7 +27,7 @@ trait JavaTimeInstances extends MetaConstructors { implicit val JavaTimeOffsetDateTimeMeta: Meta[java.time.OffsetDateTime] = Basic.one[java.time.OffsetDateTime]( JT.Timestamp, - List(JT.Char, JT.VarChar, JT.LongVarChar, JT.Date, JT.Time), + List(JT.Time), _.getObject(_, classOf[java.time.OffsetDateTime]), _.setObject(_, _), _.updateObject(_, _)) /** @@ -51,7 +51,7 @@ trait JavaTimeInstances extends MetaConstructors { implicit val JavaTimeLocalDateTimeMeta: Meta[java.time.LocalDateTime] = Basic.one[java.time.LocalDateTime]( JT.Timestamp, - List(JT.Char, JT.VarChar, JT.LongVarChar, JT.Date, JT.Time), + Nil, _.getObject(_, classOf[java.time.LocalDateTime]), _.setObject(_, _), _.updateObject(_, _)) /** @@ -60,7 +60,7 @@ trait JavaTimeInstances extends MetaConstructors { implicit val JavaTimeLocalDateMeta: Meta[java.time.LocalDate] = Basic.one[java.time.LocalDate]( JT.Date, - List(JT.Char, JT.VarChar, JT.LongVarChar, JT.Timestamp), + List(JT.Timestamp), _.getObject(_, classOf[java.time.LocalDate]), _.setObject(_, _), _.updateObject(_, _)) /** @@ -69,7 +69,7 @@ trait JavaTimeInstances extends MetaConstructors { implicit val JavaTimeLocalTimeMeta: Meta[java.time.LocalTime] = Basic.one[java.time.LocalTime]( JT.Time, - List(JT.Char, JT.VarChar, JT.LongVarChar, JT.Timestamp), + Nil, _.getObject(_, classOf[java.time.LocalTime]), _.setObject(_, _), _.updateObject(_, _)) } diff --git a/modules/postgres/src/test/scala/doobie/postgres/CheckSuite.scala b/modules/postgres/src/test/scala/doobie/postgres/CheckSuite.scala index e2425e128..a3b3ffc7a 100644 --- a/modules/postgres/src/test/scala/doobie/postgres/CheckSuite.scala +++ b/modules/postgres/src/test/scala/doobie/postgres/CheckSuite.scala @@ -5,12 +5,12 @@ package doobie.postgres import cats.effect.IO -import doobie._, doobie.implicits._ +import doobie._ +import doobie.implicits._ import doobie.postgres.enums._ import doobie.postgres.implicits._ -import doobie.util.analysis.{ColumnTypeWarning, ColumnTypeError} - -import java.time.{Instant, OffsetDateTime, LocalDate, LocalDateTime, LocalTime} +import doobie.util.analysis.{ColumnTypeError, ColumnTypeWarning, ParameterTypeError} +import java.time.{Instant, LocalDate, LocalDateTime, LocalTime, OffsetDateTime} class CheckSuite extends munit.FunSuite { @@ -35,18 +35,18 @@ class CheckSuite extends munit.FunSuite { successReadUnfortunately[OffsetDateTime](sql"SELECT '2019-02-13T22:03:21.000' :: TIMESTAMP") successWriteUnfortunately[OffsetDateTime](t, "TIMESTAMP") - warnRead[OffsetDateTime](sql"SELECT '2019-02-13T22:03:21.000' :: TEXT") - warnRead[OffsetDateTime](sql"SELECT '03:21' :: TIME") + failedRead[OffsetDateTime](sql"SELECT '2019-02-13T22:03:21.000' :: TEXT") + _warnRead[OffsetDateTime](sql"SELECT '03:21' :: TIME") // driver cannot read but TIME and TIMETZ are returned as the same JDBC type warnRead[OffsetDateTime](sql"SELECT '03:21' :: TIMETZ") - warnRead[OffsetDateTime](sql"SELECT '2019-02-13' :: DATE") + failedRead[OffsetDateTime](sql"SELECT '2019-02-13' :: DATE") - successWriteUnfortunately[OffsetDateTime](t, "TEXT") - successWriteUnfortunately[OffsetDateTime](t, "TIME") - successWriteUnfortunately[OffsetDateTime](t, "TIMETZ") - successWriteUnfortunately[OffsetDateTime](t, "DATE") + errorWrite[OffsetDateTime](t, "TEXT") + errorWrite[OffsetDateTime](t, "TIME") + errorWrite[OffsetDateTime](t, "TIMETZ") + errorWrite[OffsetDateTime](t, "DATE") failedRead[OffsetDateTime](sql"SELECT '123' :: BYTEA") - successWriteUnfortunately[OffsetDateTime](t, "BYTEA") + errorWrite[OffsetDateTime](t, "BYTEA") } test("Instant Read and Write typechecks") { @@ -57,18 +57,18 @@ class CheckSuite extends munit.FunSuite { successReadUnfortunately[Instant](sql"SELECT '2019-02-13T22:03:21.000' :: TIMESTAMP") successWriteUnfortunately[Instant](t, "TIMESTAMP") - warnRead[Instant](sql"SELECT '2019-02-13T22:03:21.000' :: TEXT") - warnRead[Instant](sql"SELECT '03:21' :: TIME") + failedRead[Instant](sql"SELECT '2019-02-13T22:03:21.000' :: TEXT") + _warnRead[Instant](sql"SELECT '03:21' :: TIME") // driver cannot read but TIME and TIMETZ are returned as the same JDBC type warnRead[Instant](sql"SELECT '03:21' :: TIMETZ") - warnRead[Instant](sql"SELECT '2019-02-13' :: DATE") + failedRead[Instant](sql"SELECT '2019-02-13' :: DATE") - successWriteUnfortunately[Instant](t, "TEXT") - successWriteUnfortunately[Instant](t, "TIME") - successWriteUnfortunately[Instant](t, "TIMETZ") - successWriteUnfortunately[Instant](t, "DATE") + errorWrite[Instant](t, "TEXT") + errorWrite[Instant](t, "TIME") + errorWrite[Instant](t, "TIMETZ") + errorWrite[Instant](t, "DATE") failedRead[Instant](sql"SELECT '123' :: BYTEA") - successWriteUnfortunately[Instant](t, "BYTEA") + errorWrite[Instant](t, "BYTEA") } test("LocalDateTime Read and Write typechecks") { @@ -79,18 +79,18 @@ class CheckSuite extends munit.FunSuite { successReadUnfortunately[LocalDateTime](sql"SELECT '2019-02-13T22:03:21.051' :: TIMESTAMPTZ") successWriteUnfortunately[LocalDateTime](t, "TIMESTAMPTZ") - warnRead[LocalDateTime](sql"SELECT '2019-02-13T22:03:21.051' :: TEXT") - warnRead[LocalDateTime](sql"SELECT '03:21' :: TIME") - warnRead[LocalDateTime](sql"SELECT '03:21' :: TIMETZ") - warnRead[LocalDateTime](sql"SELECT '2019-02-13' :: DATE") + failedRead[LocalDateTime](sql"SELECT '2019-02-13T22:03:21.051' :: TEXT") + failedRead[LocalDateTime](sql"SELECT '03:21' :: TIME") + failedRead[LocalDateTime](sql"SELECT '03:21' :: TIMETZ") + failedRead[LocalDateTime](sql"SELECT '2019-02-13' :: DATE") - successWriteUnfortunately[LocalDateTime](t, "TEXT") - successWriteUnfortunately[LocalDateTime](t, "TIME") - successWriteUnfortunately[LocalDateTime](t, "TIMETZ") - successWriteUnfortunately[LocalDateTime](t, "DATE") + errorWrite[LocalDateTime](t, "TEXT") + errorWrite[LocalDateTime](t, "TIME") + errorWrite[LocalDateTime](t, "TIMETZ") + errorWrite[LocalDateTime](t, "DATE") failedRead[LocalDateTime](sql"SELECT '123' :: BYTEA") - successWriteUnfortunately[LocalDateTime](t, "BYTEA") + errorWrite[LocalDateTime](t, "BYTEA") } test("LocalDate Read and Write typechecks") { @@ -99,18 +99,17 @@ class CheckSuite extends munit.FunSuite { successWrite[LocalDate](t, "DATE") warnRead[LocalDate](sql"SELECT '2015-02-23T01:23:13.000' :: TIMESTAMP") - warnRead[LocalDate](sql"SELECT '2015-02-23T01:23:13.000Z' :: TIMESTAMPTZ") - warnRead[LocalDate](sql"SELECT '2015-02-23' :: TEXT") + _warnRead[LocalDate](sql"SELECT '2015-02-23T01:23:13.000Z' :: TIMESTAMPTZ") // driver cannot read but TIMESTAMP and TIMESTAMPTZ are returned as the same JDBC type + failedRead[LocalDate](sql"SELECT '2015-02-23' :: TEXT") failedRead[LocalDate](sql"SELECT '03:21' :: TIME") failedRead[LocalDate](sql"SELECT '03:21' :: TIMETZ") - successWriteUnfortunately[LocalDate](t, "TEXT") - successWriteUnfortunately[LocalDate](t, "TIME") - successWriteUnfortunately[LocalDate](t, "TIMETZ") - successWriteUnfortunately[LocalDate](t, "DATE") + errorWrite[LocalDate](t, "TEXT") + errorWrite[LocalDate](t, "TIME") + errorWrite[LocalDate](t, "TIMETZ") failedRead[LocalDate](sql"SELECT '123' :: BYTEA") - successWriteUnfortunately[LocalDate](t, "BYTEA") + errorWrite[LocalDate](t, "BYTEA") } test("LocalTime Read and Write typechecks") { @@ -118,32 +117,41 @@ class CheckSuite extends munit.FunSuite { successRead[LocalTime](sql"SELECT '23:13' :: TIME") successWrite[LocalTime](t, "TIME") - warnRead[LocalTime](sql"SELECT '2015-02-23T01:23:13.000' :: TIMESTAMP") - warnRead[LocalTime](sql"SELECT '2015-02-23T01:23:13.000Z' :: TIMESTAMPTZ") - warnRead[LocalTime](sql"SELECT '2015-02-23' :: TEXT") + failedRead[LocalTime](sql"SELECT '2015-02-23T01:23:13.000' :: TIMESTAMP") + failedRead[LocalTime](sql"SELECT '2015-02-23T01:23:13.000Z' :: TIMESTAMPTZ") + failedRead[LocalTime](sql"SELECT '2015-02-23' :: TEXT") failedRead[LocalTime](sql"SELECT '2015-02-23' :: DATE") - successWriteUnfortunately[LocalTime](t, "TEXT") - successWriteUnfortunately[LocalTime](t, "TIME") + errorWrite[LocalTime](t, "TEXT") successWriteUnfortunately[LocalTime](t, "TIMETZ") - successWriteUnfortunately[LocalTime](t, "DATE") + errorWrite[LocalTime](t, "DATE") failedRead[LocalTime](sql"SELECT '123' :: BYTEA") - successWriteUnfortunately[LocalTime](t, "BYTEA") + errorWrite[LocalTime](t, "BYTEA") } private def successRead[A: Read](frag: Fragment): Unit = { val analysisResult = frag.query[A].analysis.transact(xa).unsafeRunSync() assertEquals(analysisResult.columnAlignmentErrors, Nil) + + val result = frag.query[A].unique.transact(xa).attempt.unsafeRunSync() + assert(result.isRight) } private def successWrite[A: Put](value: A, dbType: String): Unit = { val frag = sql"SELECT $value :: " ++ Fragment.const(dbType) val analysisResult = frag.update.analysis.transact(xa).unsafeRunSync() - assertEquals(analysisResult.columnAlignmentErrors, Nil) + assertEquals(analysisResult.parameterAlignmentErrors, Nil) } private def warnRead[A: Read](frag: Fragment): Unit = { + _warnRead[A](frag) + + val result = frag.query[A].unique.transact(xa).attempt.unsafeRunSync() + assert(result.isRight) + } + + private def _warnRead[A: Read](frag: Fragment): Unit = { val analysisResult = frag.query[A].analysis.transact(xa).unsafeRunSync() val errorClasses = analysisResult.columnAlignmentErrors.map(_.getClass) assertEquals(errorClasses, List(classOf[ColumnTypeWarning])) @@ -153,11 +161,24 @@ class CheckSuite extends munit.FunSuite { val analysisResult = frag.query[A].analysis.transact(xa).unsafeRunSync() val errorClasses = analysisResult.columnAlignmentErrors.map(_.getClass) assertEquals(errorClasses, List(classOf[ColumnTypeError])) + + val result = frag.query[A].unique.transact(xa).attempt.unsafeRunSync() + assert(result.isLeft) + } + + private def errorWrite[A: Put](value: A, dbType: String): Unit = { + val frag = sql"SELECT $value :: " ++ Fragment.const(dbType) + val analysisResult = frag.update.analysis.transact(xa).unsafeRunSync() + val errorClasses = analysisResult.parameterAlignmentErrors.map(_.getClass) + assertEquals(errorClasses, List(classOf[ParameterTypeError])) } private def successWriteUnfortunately[A: Put](value: A, dbType: String): Unit = successWrite(value, dbType) // Some DB types really shouldn't type check but driver is too lenient - private def successReadUnfortunately[A: Read](frag: Fragment): Unit = successRead(frag) + private def successReadUnfortunately[A: Read](frag: Fragment): Unit = { + val analysisResult = frag.query[A].analysis.transact(xa).unsafeRunSync() + assertEquals(analysisResult.columnAlignmentErrors, Nil) + } } diff --git a/project/build.sbt b/project/build.sbt index 7d3f9cb7c..ee9b39bac 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,3 +1,3 @@ // Required for the freegen definition for postgres in ../build.sbt -val postgresVersion = "42.3.5" +val postgresVersion = "42.4.2" libraryDependencies += "org.postgresql" % "postgresql" % postgresVersion