zio-jdbc
zio-jdbc copied to clipboard
Replace Schema-based derivation of encoder / decoder with ZIO Schema Deriver-based derivation
Currently, ZIO JDBC directly converts from Schema to encoders / decoders by generating the appropriate type class instances (JdbcEncoder, JdbcDecoder).
This is not ideal, because it means a user does not have any ability to define a custom encoder / decoder for any part of their ADT.
In order to solve this problem, we can switch from defining Schema => Decoder / Encoder
, and instead use the Deriver
mechanism built into ZIO Schema, which is designed to solve this precise problem.
Using Deriver
to derive JdbcEncoder / JdbcDecoder, we can both automatically support derivation, as well as allow user-defined overrides for parts of a larger data type.
/bounty $250
💎 $250 bounty • ZIO
Steps to solve:
-
Start working: Comment
/attempt #144
with your implementation plan -
Submit work: Create a pull request including
/claim #144
in the PR body to claim the bounty - Receive payment: 100% of the bounty is received 2-5 days post-reward. Make sure you are eligible for payouts
Additional opportunities:
-
🔴 Livestream on Algora TV while solving this bounty & earn $200 upon merge! Make sure to have your camera and microphone on. Comment
/livestream
once live
Thank you for contributing to zio/zio-jdbc!
Add a bounty • Share on socials
Attempt | Started (GMT+0) | Solution |
---|---|---|
🔴 @FVelasquezM | Jun 17, 2023, 2:03:36 AM | WIP |
🔴 @Andrapyre | May 13, 2024, 2:54:25 AM | WIP |
Hi, I'd like to try this one /attempt #144
@FVelasquezM: Reminder that in 7 days the bounty will become up for grabs, so please submit a pull request before then 🙏
The bounty is up for grabs! Everyone is welcome to /attempt #144
🙌
I have been playing with this for a few hours but my lack of experience with zio-schema shows and I could not find non-trivial examples of using Deriver
. Could I, please, ask someone to help me verify that the following approach could be the right one?
deriveRecord
collects values of columns decoded (1) by JdbcDecoders (fields
) in Chunk[Any]
and then passes them into record.construct
to create record (2). Besides that, used column indices are tracked. So far, it reuses existing decoders for primitives.
Thank you.
val drv: Deriver[JdbcDecoder] = new Deriver[JdbcDecoder] {
override def deriveRecord[A](
record: Schema.Record[A],
fields: => Chunk[WrappedF[zio.jdbc.JdbcDecoder, ?]],
summoned: => Option[JdbcDecoder[A]]
): JdbcDecoder[A] =
new JdbcDecoder[A] {
override def unsafeDecode(columnIndex: Int, rs: ResultSet): (Int, A) = {
val (nextColumn, fieldValues) = fields
.foldLeft((columnIndex, Chunk.empty[Any])) { case ((col, values), decoder) =>
decoder
.unwrap
.decode(col, rs) // 1
.fold(
e => throw new JdbcDecoderError("Cannot decode value", e, rs.getMetaData(), rs.getRow()),
(c, value) => (c + 1, values :+ value)
)
}
Unsafe
.unsafe(record.construct(fieldValues)) // 2
.fold(
e => throw new JdbcDecoderError("Cannot construct record", new Exception(e), rs.getMetaData(), rs.getRow()),
a => (nextColumn - 1, a)
)
}
}
…
}.autoAcceptSummoned
users
:
id | name | age |
---|---|---|
1 | Pierre | 100 |
2 | John | 20 |
3 | Nelly | 17 |
case class User(name: String, age: Int)
case class Id(value: Int)
case class Rec(id: Int, user: User, id2: Id)
given Schema[Rec] = DeriveSchema.gen
given JdbcDecoder[Rec] = Derive.derive(drv)
// query
sql"SELECT id, name, age, id * 2 FROM users".query[Rec].selectAll
/*
Rec(1, User(Pierre, 100), Id(2))
Rec(2, User(John, 20), Id(4))
Rec(3, User(Nelly, 17), Id(6))
*/
// with custom decoder of inner record
given JdbcDecoder[Id] = JdbcDecoder.intDecoder.map(n => Id(n + 1000))
/*
Rec(1, User(Pierre, 100), Id(1002))
Rec(2, User(John, 20), Id(1004))
Rec(3, User(Nelly, 17), Id(1006))
*/
My further experiments and attempts to use zio-jdbc in one of my projects bring me to another question: should the codecs work based on position or names? As for now, they seem to work based on names for records (case classes). This has issue with nested case classes (how to name columns in result set and how to refer to nested values). My attempt above uses positional decoding, I think the example I provided would not be possible with name-based decoding.
/attempt #144
Algora profile | Completed bounties | Tech | Active attempts | Options |
---|---|---|---|---|
@Andrapyre | 1 ZIO bounty | TypeScript, Scala, Rust & more |
zio/zio-json#1055 |
Cancel attempt |
@Andrapyre: Reminder that in 7 days the bounty will become up for grabs, so please submit a pull request before then 🙏
The bounty is up for grabs! Everyone is welcome to /attempt #144
🙌