zio / zio-quill

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

Using Context as Trait Member causes Macro Expansion Error #1033

Closed deusaquilus closed 6 years ago

deusaquilus commented 6 years ago

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

Version: 2.3.2 Module: quill-sql, quill-jdbc Database: All

A nice way to be able to be able to compose contexts would be to define them as values of a trait, and then extend that trait later with an actual context and run the query.

For example, let's define a trait:

trait ContextTrait[Dialect <: SqlIdiom, Naming <: NamingStrategy] {
  def context: io.getquill.context.Context[Dialect, Naming]

  def query = {
    val ctx = context
    import ctx._

    quote {
      querySchema[Person]("some.table")
    }
  }
}

Now let's define our main method where we extend it with an actual context and try to run a query:

  def main(args:Array[String]):Unit = {
    val context = new SqlMirrorContext(MirrorSqlDialect, Literal)
    import context._

    class TraitImpl extends ContextTrait[MirrorSqlDialect, Literal] {
      override def context: Context[MirrorSqlDialect, Literal] = context
    }
    val traitImpl = new TraitImpl()

    println( run(traitImpl.query).string )
  }

This causes the following error at run(traitImpl.query)

exception during macro expansion: 
scala.reflect.macros.TypecheckException: class type required but $anon found
    at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:32)
    at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$2(Typers.scala:26)
    at scala.reflect.macros.contexts.Typers.doTypecheck$1(Typers.scala:25)

You can see this issue on Scastie here: https://scastie.scala-lang.org/deusaquilus/GWGSaFJxSLSjuK5nFsw7cA

Alternatively, even if you use a trait with the context as a global import the same problem happens:

trait ContextTrait[Dialect <: SqlIdiom, Naming <: NamingStrategy] {
  def context: io.getquill.context.Context[Dialect, Naming]

  def query = {
    val ctx = context
    import ctx._

    quote {
      querySchema[Person]("some.table")
    }
  }
}

You can see this second case on Scastie here: https://scastie.scala-lang.org/deusaquilus/Qr6CAEIaT3ChsLVKjA4O4Q

Similarly, if you decide to try to define a context via a class import, it also does not work although I am not sure whether this last example should actually work or not.

Class importing a context:

class SchemaContainer[Dialect <: SqlIdiom, Naming <: NamingStrategy](
  val context: io.getquill.context.Context[Dialect, Naming]
) {

  def query = {
    import context._

    quote {
      querySchema[Person]("some.table")
    }
  }
}

Later it gets used like this:

object DefContextCakeExample {
  def main(args:Array[String]):Unit = {
    val context = new SqlMirrorContext(MirrorSqlDialect, Literal)
    import context._

    val schemaContainer = new SchemaContainer(context)

    println( run(schemaContainer.query).string )
}

This causes the following:

exception during macro expansion: 
scala.reflect.macros.TypecheckException: class type required but schemaContainer.context.Quoted[schemaContainer.context.EntityQuery[Person]]{def quoted: io.getquill.ast.Entity; def ast: io.getquill.ast.Entity; def id1630926838(): Unit; val liftings: Object} found
    at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:32)
    at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$2(Typers.scala:26)
    at scala.reflect.macros.contexts.Typers.doTypecheck$1(Typers.scala:25)

You can find an example of this latter issue here: https://scastie.scala-lang.org/deusaquilus/tpfNL6puSXyV74vcYPAwJg

Workaround

None that I know of. This kind of cake-pattern usage does not work with Quill.

@getquill/maintainers

Edit: One more example.

mosyp commented 6 years ago

Hello, You need to make context trait member to be abstract val:

trait SomeSchema {
  val ctx: SqlContext[_, _]

  import ctx._

  def select = quote {
    query[Person].filter(p => p.age > 21)
  }
}

And the use it in successors as:

class Test(val ctx: SqlMirrorContext[MirrorSqlDialect, SnakeCase]) extends SomeSchema {
  import ctx._

  def runSelect = run(select)
}

Working example: https://scastie.scala-lang.org/j9k22KuATQqNftgv4xjNwA

Feel free to re-open this issue if this is not something you'd like to achieve