Skip to content

Regression in 4.8.6 (#3147): unwrapped top-level infix entity queries decode by table column order, not case-class field order #3403

Description

@eugkhp

Summary

Engine commit c334986f (Improve handling of raw-queries (#3147) — "Better raw query support. Do not wrap top-level infixes."), first released in quill-engine 4.8.6, changes the decode contract of entity-typed top-level raw queries:

val q = quote { sql"""SELECT b.* FROM blocked_periods b WHERE ...""".as[Query[BlockedPeriod]] }
ctx.run(q) // dynamic in our case (runtime #\$ splices)

Through 4.8.5, the engine wrapped such queries: SELECT x.id, x.home_id, ... FROM (<raw sql>) AS x — the generated extractor (which decodes positionally, in case-class field order) always matched. After #3147 the raw SQL passes through unwrapped, so the result set has the table's physical column order (DDL order incl. appended ALTER columns), while the extractor still decodes in field order.

Consequences when the orders differ:

  • decoders read neighboring columns' values — in our production case an enumeratum decoder got the next enum column's value: NoSuchElementException: OCCUPIED is not a member of Enum(...);
  • worse, when adjacent columns have compatible types/overlapping enum member names, the corruption is silent.

Bisection evidence

Verified single-variable on a large production codebase (Scala 3.8.4, ProtoQuill master + Postgres testcontainers suite):

engine spec result
4.8.5 green (71/71 suite)
4.8.5 + cherry-pick c334986f only red — exact production failure
4.8.6 / forks based on it red

ProtoQuill releases pin engine ≤4.8.5, which is why this seems unreported; downstream forks of master (e.g. org.li-nk 4.8.7-RC1) inherit it.

Suggested direction

Either restore the projecting wrapper when the infix is decoded into a Product (entity/case-class) type — positional decode requires a known projection — or expand the entity's fields into the wrapper only when Quat.Product is present, keeping the unwrapped passthrough for scalar/tuple raw queries that motivated #3147. Happy to test candidate patches against our reproducer suite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions