Skip to content

Commit

Permalink
Fix exception on null json / jsonb value in postgres
Browse files Browse the repository at this point in the history
  • Loading branch information
vladimirkl committed May 7, 2024
1 parent 092001b commit 8c9afd3
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ trait PostgresJsonExtensions extends Encoders with Decoders {
implicit def jsonbAstDecoder: Decoder[JsonbValue[Json]] = astDecoder(JsonbValue(_))

def astEncoder[Wrapper](valueToString: Wrapper => String, jsonType: String): Encoder[Wrapper] =
encoder(Types.VARCHAR, (index, jsonValue, row) => {
encoder(Types.OTHER, (index, jsonValue, row) => {
val obj = new org.postgresql.util.PGobject()
obj.setType(jsonType)
val jsonString = valueToString(jsonValue)
Expand All @@ -50,7 +50,7 @@ trait PostgresJsonExtensions extends Encoders with Decoders {
jsonType: String,
jsonEncoder: JsonEncoder[JsValue]
): Encoder[Wrapper] =
encoder(Types.VARCHAR, (index, jsonValue, row) => {
encoder(Types.OTHER, (index, jsonValue, row) => {
val obj = new org.postgresql.util.PGobject()
obj.setType(jsonType)
val jsonString = jsonEncoder.encodeJson(unwrap(jsonValue), None).toString
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.getquill.postgres

import io.getquill._
import org.scalatest.BeforeAndAfterEach
import zio.Chunk
import zio.json.ast.Json
import zio.json.{ DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder } //
import zio.json.{DeriveJsonDecoder, DeriveJsonEncoder, JsonDecoder, JsonEncoder}

class PostgresJsonSpec extends ZioSpec {
class PostgresJsonSpec extends ZioSpec with BeforeAndAfterEach {
val context = testContext
import testContext._

Expand All @@ -15,22 +16,28 @@ class PostgresJsonSpec extends ZioSpec {
case class JsonEntity(name: String, value: JsonValue[PersonJson])
case class JsonbEntity(name: String, value: JsonbValue[PersonJsonb])

val jsonJoe = JsonValue(PersonJson("Joe", 123))
val jsonValue = JsonEntity("JoeEntity", jsonJoe)
val jsonbJoe = JsonbValue(PersonJsonb("Joe", 123))
case class JsonOptEntity(name: String, value: Option[JsonValue[PersonJson]])
case class JsonbOptEntity(name: String, value: Option[JsonbValue[PersonJsonb]])

val jsonJoe = JsonValue(PersonJson("Joe", 123))
val jsonValue = JsonEntity("JoeEntity", jsonJoe)
val jsonbJoe = JsonbValue(PersonJsonb("Joe", 123))
val jsonbValue = JsonbEntity("JoeEntity", jsonbJoe)

case class JsonAstEntity(name: String, value: JsonValue[Json])
case class JsonbAstEntity(name: String, value: JsonbValue[Json])

case class JsonAstOptEntity(name: String, value: Option[JsonValue[Json]])
case class JsonbAstOptEntity(name: String, value: Option[JsonbValue[Json]])

implicit val personJsonEncoder: JsonEncoder[PersonJson] = DeriveJsonEncoder.gen[PersonJson]
implicit val personJsonDecoder: JsonDecoder[PersonJson] = DeriveJsonDecoder.gen[PersonJson]

implicit val personJsonbEncoder: JsonEncoder[PersonJsonb] = DeriveJsonEncoder.gen[PersonJsonb]
implicit val personJsonbDecoder: JsonDecoder[PersonJsonb] = DeriveJsonDecoder.gen[PersonJsonb]

override def beforeAll() = {
super.beforeAll()
override def beforeEach() = {
super.beforeEach()
testContext.run(quote(query[JsonbEntity].delete)).runSyncUnsafe()
testContext.run(quote(query[JsonEntity].delete)).runSyncUnsafe()
()
Expand All @@ -50,23 +57,99 @@ class PostgresJsonSpec extends ZioSpec {
}
}

"encodes and decodes optional json entity" - {
inline def jsonOptQuery = quote(querySchema[JsonOptEntity]("JsonEntity"))
inline def jsonbOptQuery = quote(querySchema[JsonbOptEntity]("JsonEntity"))

"some json" in {
val value = JsonOptEntity("JoeEntity", Some(jsonJoe))

testContext.run(jsonOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"some jsonb" in {
val value = JsonbOptEntity("JoeEntity", Some(jsonbJoe))

testContext.run(jsonbOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonbOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"none json" in {
val value = JsonOptEntity("JoeEntity", None)

testContext.run(jsonOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"none jsonb" in {
val value = JsonbOptEntity("JoeEntity", None)

testContext.run(jsonbOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonbOptQuery).runSyncUnsafe().head
inserted mustEqual value
}
}

"encodes and decodes json ast" - {
val jsonJoe = Json.Obj(Chunk("age" -> Json.Num(123), "name" -> Json.Str("Joe")))
inline def jsonAstQuery = quote { querySchema[JsonAstEntity]("JsonEntity") }
inline def jsonbAstQuery = quote { querySchema[JsonbAstEntity]("JsonbEntity") }
val jsonJoe = Json.Obj(Chunk("age" -> Json.Num(123), "name" -> Json.Str("Joe")))
inline def jsonAstQuery = quote(querySchema[JsonAstEntity]("JsonEntity"))
inline def jsonbAstQuery = quote(querySchema[JsonbAstEntity]("JsonbEntity"))

val jsonAstValue = JsonAstEntity("JoeEntity", JsonValue(jsonJoe))
val jsonAstValue = JsonAstEntity("JoeEntity", JsonValue(jsonJoe))
val jsonbAstValue = JsonbAstEntity("JoeEntity", JsonbValue(jsonJoe))

"json" in {
testContext.run(jsonAstQuery.insertValue(lift(jsonAstValue))).runSyncUnsafe()
val inserted = testContext.run(jsonAstQuery).runSyncUnsafe().head
inserted mustEqual jsonAstValue
}

"jsonb" in {
testContext.run(jsonbAstQuery.insertValue(lift(jsonbAstValue))).runSyncUnsafe()
val inserted = testContext.run(jsonbAstQuery).runSyncUnsafe().head
inserted mustEqual jsonbAstValue
}
}

"encodes and decodes optional json ast" - {
val jsonJoe = Json.Obj(Chunk("age" -> Json.Num(123), "name" -> Json.Str("Joe")))
inline def jsonAstOptQuery = quote(querySchema[JsonAstOptEntity]("JsonEntity"))
inline def jsonbAstOptQuery = quote(querySchema[JsonbAstOptEntity]("JsonbEntity"))

val jsonbAstValue = JsonbAstEntity("JoeEntity", JsonbValue(jsonJoe))

"some json" in {
val value = JsonAstOptEntity("JoeEntity", Some(JsonValue(jsonJoe)))
testContext.run(jsonAstOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonAstOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"some jsonb" in {
val value = JsonbAstOptEntity("JoeEntity", Some(JsonbValue(jsonJoe)))
testContext.run(jsonbAstOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonbAstOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"none json" in {
val value = JsonAstOptEntity("JoeEntity", None)
testContext.run(jsonAstOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonAstOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

"none jsonb" in {
val value = JsonbAstOptEntity("JoeEntity", None)
testContext.run(jsonbAstOptQuery.insertValue(lift(value))).runSyncUnsafe()
val inserted = testContext.run(jsonbAstOptQuery).runSyncUnsafe().head
inserted mustEqual value
}

}

}

0 comments on commit 8c9afd3

Please sign in to comment.