zio / zio-quill

Compile-time Language Integrated Queries for Scala
https://zio.dev/zio-quill
Apache License 2.0
2.15k stars 348 forks source link

Returning generated and embedded case classes #2725

Open ex0ns opened 1 year ago

ex0ns commented 1 year ago

This template isn't a strict requirement to open issues, but please try to provide as much information as possible.

Version: 4.6.0 Module: quill-jdbc Database: oracle, h2 Scala: 2.13.10

Expected behavior

A query using embedded case classes and returningGenerated used to work in 4.4.1, it does not anymore (4.6.0). I was able to reproduce the issue within quill, and the commit can be found https://github.com/ex0ns/zio-quill/commit/648923770451de743be9711e77b0382b450ed1a0

The tests fails with

Ex 5 insert.returning(_.subClass.generatedColumn) mod *** FAILED ***
[info]   org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "productid" not found; SQL statement:
[info] INSERT INTO ReturnedProduct (date,description,sku) VALUES ($1, $2, $3) [42122-212]
[info]   at org.h2.message.DbException.getJdbcSQLException(DbException.java:502)
[info]   at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
[info]   at org.h2.message.DbException.get(DbException.java:223)
[info]   at org.h2.message.DbException.get(DbException.java:199)
[info]   at org.h2.command.CommandContainer.executeUpdateWithGeneratedKeys(CommandContainer.java:228)
[info]   at org.h2.command.CommandContainer.update(CommandContainer.java:168)
[info]   at org.h2.command.Command.executeUpdate(Command.java:252)
[info]   at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:209)
[info]   at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:169)
[info]   at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeUpdate(ProxyPreparedStatement.java:61)

It seems that somehow the name of the embedded case class get prefixed product in this case, in front of the column. I have also tested with a SchemaMeta and/or UpdateMeta, and product always get prefixed, even if I explicitly set a mapping.

Actual behavior

It should return the generated id of an embedded case class

Steps to reproduce the behavior

This commit contains the changes to reproduce

https://github.com/ex0ns/zio-quill/commit/648923770451de743be9711e77b0382b450ed1a0

Workaround

@getquill/maintainers

ex0ns commented 1 year ago

A bit more information regarding this issue in case anyone with a bit more knowledge would happens to read that.

As of now what I was able to track down is that the doTranslate is called and end up in the case other where other is product.report_id (Property.Opinionated in the AST). And the .token call on it converts it to productreport_id

Looking down in the Property tokenizer, there is a big comment that seems to address exactly that, however for some reasons product.report_id is reported as a ExternalIdent.Opinionated and is hence "flattened" but not treated as an ExternalIdent as it probably should (according to the comment).

So my guess is that there is something wrong when mixing embedded case classes and external mapping, we only unpack the Opinionated part but do not consider the inner ExternalIdent. I am stuck there now and might try to have a deeper look at that later.

https://github.com/zio/zio-quill/blob/master/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala#L430 https://github.com/zio/zio-quill/blob/master/quill-core/src/main/scala/io/getquill/quotation/Parsing.scala#L586 For reference, here is the Action SQL that I was able to log:

 Action SQL:
  |  ReturningGenerated(
  |    Insert(
  |      Entity("ReturnedProduct", List(PropertyAlias(List("product", "id"), "id"))),
  |      List(
  |        Assignment(
  |          Id("v"),
  |          v.returnedDate,
  |          ScalarValueLift(
  |            "Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.returnedDate",
  |            UnparsedProperty("Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.returnedDate")
  |          )
  |        ),
  |        Assignment(
  |          Id("v"),
  |          v.product.description,
  |          ScalarValueLift(
  |            "Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.product.description",
  |            UnparsedProperty("Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.product.description")
  |          )
  |        ),
  |        Assignment(
  |          Id("v"),
  |          v.product.sku,
  |          ScalarValueLift(
  |            "Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.product.sku",
  |            UnparsedProperty("Ex 5 insert.returning(_.subClass.generatedColumn) mod.this.returnedProduct.product.sku")
  |          )
  |        )
  |      )
  |    ),
  |    Id("x11"),
  |    x11.product.`id`
  |  )