From b5e84b88e98bf62d2cfd5792e6ce68b1ce72f4a5 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 20:31:01 -0500 Subject: [PATCH 01/14] Remove codegen-tests for now since they break local-builds. Will add back in a different build or project. --- build.sbt | 77 ++++++------ .../getquill/codegen/H2CodegenTestCases.scala | 110 ----------------- .../codegen/MysqlCodegenTestCases.scala | 110 ----------------- .../codegen/OracleCodegenTestCases.scala | 111 ------------------ .../codegen/PostgresCodegenTestCases.scala | 110 ----------------- .../codegen/SqlServerCodegenTestCases.scala | 110 ----------------- .../codegen/SqliteCodegenTestCases.scala | 54 --------- .../getquill/codegen/util/WithContext.scala | 42 ------- .../getquill/codegen/util/WithH2Context.scala | 84 ------------- .../codegen/util/WithMysqlContext.scala | 84 ------------- .../codegen/util/WithOracleContext.scala | 88 -------------- .../codegen/util/WithPostgresContext.scala | 84 ------------- .../codegen/util/WithSqlServerContext.scala | 84 ------------- .../codegen/util/WithSqliteContext.scala | 39 ------ 14 files changed, 39 insertions(+), 1148 deletions(-) delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/H2CodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/MysqlCodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/OracleCodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/PostgresCodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/SqlServerCodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/SqliteCodegenTestCases.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithContext.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithH2Context.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithMysqlContext.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithOracleContext.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithPostgresContext.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqlServerContext.scala delete mode 100644 quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqliteContext.scala diff --git a/build.sbt b/build.sbt index 323041cb3d..a97fd508d4 100644 --- a/build.sbt +++ b/build.sbt @@ -53,8 +53,7 @@ lazy val dbModules = Seq[sbt.ClasspathDep[sbt.ProjectReference]]( lazy val codegenModules = Seq[sbt.ClasspathDep[sbt.ProjectReference]]( `quill-codegen`, - `quill-codegen-jdbc`, - `quill-codegen-tests` + `quill-codegen-jdbc` ) lazy val bigdataModules = Seq[sbt.ClasspathDep[sbt.ProjectReference]]( @@ -93,7 +92,7 @@ lazy val filteredModules = { val moduleStrings = ListSet( modulesStr - .getOrElse("all") + .getOrElse("nocodegen") .split(",") .map(_.trim): _* ) @@ -121,8 +120,10 @@ lazy val filteredModules = { println("SBT =:> Invoking Aggregate Project") Seq[sbt.ClasspathDep[sbt.ProjectReference]]() case _ | "all" => - println("SBT =:> Compiling All Modules") - allModules + println("Compiling Not-Code Generator Modules") + baseModules ++ dbModules ++ bigdataModules + // println("SBT =:> Compiling All Modules") + // allModules } val selectedModules = { @@ -264,39 +265,39 @@ lazy val `quill-codegen-jdbc` = .dependsOn(`quill-codegen` % "compile->compile;test->test") .dependsOn(`quill-jdbc` % "compile->compile") -lazy val `quill-codegen-tests` = - (project in file("quill-codegen-tests")) - .settings(commonSettings: _*) - .settings( - publish / skip := true, - libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Test, - Test / fork := true, - (Test / sourceGenerators) += Def.task { - def recursiveList(file: JFile): List[JFile] = - if (file.isDirectory) - Option(file.listFiles()).map(_.flatMap(child => recursiveList(child)).toList).toList.flatten - else - List(file) - val r = (Compile / runner).value - val s = streams.value.log - val sourcePath = sourceManaged.value - val classPath = (`quill-codegen-jdbc` / Test / fullClasspath).value.map(_.data) - - // We could put the code generated products directly in the `sourcePath` directory but for some reason - // intellij doesn't like it unless there's a `main` directory inside. - val fileDir = new File(sourcePath, "main").getAbsoluteFile - val dbs = Seq("testH2DB", "testMysqlDB", "testPostgresDB", "testSqliteDB", "testSqlServerDB", "testOracleDB") - println(s"Running code generation for DBs: ${dbs.mkString(", ")}") - r.run( - "io.getquill.codegen.integration.CodegenTestCaseRunner", - classPath, - fileDir.getAbsolutePath +: dbs, - s - ) - recursiveList(fileDir) - }.tag(CodegenTag) - ) - .dependsOn(`quill-codegen-jdbc` % "compile->test") +//lazy val `quill-codegen-tests` = +// (project in file("quill-codegen-tests")) +// .settings(commonSettings: _*) +// .settings( +// publish / skip := true, +// libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Test, +// Test / fork := true, +// (Test / sourceGenerators) += Def.task { +// def recursiveList(file: JFile): List[JFile] = +// if (file.isDirectory) +// Option(file.listFiles()).map(_.flatMap(child => recursiveList(child)).toList).toList.flatten +// else +// List(file) +// val r = (Compile / runner).value +// val s = streams.value.log +// val sourcePath = sourceManaged.value +// val classPath = (`quill-codegen-jdbc` / Test / fullClasspath).value.map(_.data) +// +// // We could put the code generated products directly in the `sourcePath` directory but for some reason +// // intellij doesn't like it unless there's a `main` directory inside. +// val fileDir = new File(sourcePath, "main").getAbsoluteFile +// val dbs = Seq("testH2DB", "testMysqlDB", "testPostgresDB", "testSqliteDB", "testSqlServerDB", "testOracleDB") +// println(s"Running code generation for DBs: ${dbs.mkString(", ")}") +// r.run( +// "io.getquill.codegen.integration.CodegenTestCaseRunner", +// classPath, +// fileDir.getAbsolutePath +: dbs, +// s +// ) +// recursiveList(fileDir) +// }.tag(CodegenTag) +// ) +// .dependsOn(`quill-codegen-jdbc` % "compile->test") val excludeTests = sys.props.getOrElse("excludeTests", "false") match { diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/H2CodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/H2CodegenTestCases.scala deleted file mode 100644 index 6173cd3a25..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/H2CodegenTestCases.scala +++ /dev/null @@ -1,110 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestH2DB -import io.getquill.codegen.util._ -import org.scalatest.matchers.should.Matchers._ - -class H2CodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.h2._ - - type Prefix = TestH2DB - val prefix = TestH2DB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - - "3-comp-stereo-oneschema" in WithContext[Prefix, `3-comp-stereo-oneschema`].run { ctx => - import `3-comp-stereo-oneschema-lib`.public._ - import ctx._ - - (ctx.run(PublicSchema.PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(PublicSchema.AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "4-comp-stereo-twoschema" in WithContext[Prefix, `4-comp-stereo-twoschema`].run { ctx => - import `4-comp-stereo-twoschema-lib`.common._ - import `4-comp-stereo-twoschema-lib`.public._ - import ctx._ - - (ctx.run(PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "5 - non-stereotyped multiple schemas" in WithContext[Prefix, `5-comp-non-stereo-allschema`].run { ctx => - import `5-comp-non-stereo-allschema-lib`.alpha._ - import `5-comp-non-stereo-allschema-lib`.public._ - import ctx._ - - (ctx.run(ctx.AlphaSchema.PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, "blah", 55, "Wonkles"), - Person(2, "Jack", "Ripper", 33, "blah", 66, "Ginkles") - ) - ) - (ctx.run(ctx.PublicSchema.AddressDao.query.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/MysqlCodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/MysqlCodegenTestCases.scala deleted file mode 100644 index 5aa6bb1990..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/MysqlCodegenTestCases.scala +++ /dev/null @@ -1,110 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestMysqlDB -import io.getquill.codegen.util._ -import org.scalatest.matchers.should.Matchers._ - -class MysqlCodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.mysql._ - - type Prefix = TestMysqlDB - val prefix = TestMysqlDB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - - "3-comp-stereo-oneschema" in WithContext[Prefix, `3-comp-stereo-oneschema`].run { ctx => - import `3-comp-stereo-oneschema-lib`.public._ - import ctx._ - - (ctx.run(PublicSchema.PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(PublicSchema.AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "4-comp-stereo-twoschema" in WithContext[Prefix, `4-comp-stereo-twoschema`].run { ctx => - import `4-comp-stereo-twoschema-lib`.common._ - import `4-comp-stereo-twoschema-lib`.public._ - import ctx._ - - (ctx.run(PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "5 - non-stereotyped multiple schemas" in WithContext[Prefix, `5-comp-non-stereo-allschema`].run { ctx => - import `5-comp-non-stereo-allschema-lib`.alpha._ - import `5-comp-non-stereo-allschema-lib`.public._ - import ctx._ - - (ctx.run(ctx.AlphaSchema.PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, "blah", 55, "Wonkles"), - Person(2, "Jack", "Ripper", 33, "blah", 66, "Ginkles") - ) - ) - (ctx.run(ctx.PublicSchema.AddressDao.query.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/OracleCodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/OracleCodegenTestCases.scala deleted file mode 100644 index 07301cafe2..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/OracleCodegenTestCases.scala +++ /dev/null @@ -1,111 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestOracleDB -import io.getquill.codegen.util.{WithOracleContextStandalone => WithContext, _} -import WithContext._ -import org.scalatest.matchers.should.Matchers._ - -class OracleCodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.oracle._ - - type Prefix = TestOracleDB - val prefix = TestOracleDB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - - "3-comp-stereo-oneschema" in WithContext[Prefix, `3-comp-stereo-oneschema`].run { ctx => - import `3-comp-stereo-oneschema-lib`.public._ - import ctx._ - - (ctx.run(PublicSchema.PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(PublicSchema.AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "4-comp-stereo-twoschema" in WithContext[Prefix, `4-comp-stereo-twoschema`].run { ctx => - import `4-comp-stereo-twoschema-lib`.common._ - import `4-comp-stereo-twoschema-lib`.public._ - import ctx._ - - (ctx.run(PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "5 - non-stereotyped multiple schemas" in WithContext[Prefix, `5-comp-non-stereo-allschema`].run { ctx => - import `5-comp-non-stereo-allschema-lib`.alpha._ - import `5-comp-non-stereo-allschema-lib`.public._ - import ctx._ - - (ctx.run(ctx.AlphaSchema.PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, "blah", 55, "Wonkles"), - Person(2, "Jack", "Ripper", 33, "blah", 66, "Ginkles") - ) - ) - (ctx.run(ctx.PublicSchema.AddressDao.query.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/PostgresCodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/PostgresCodegenTestCases.scala deleted file mode 100644 index b2e147c66a..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/PostgresCodegenTestCases.scala +++ /dev/null @@ -1,110 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestPostgresDB -import io.getquill.codegen.util._ -import org.scalatest.matchers.should.Matchers._ - -class PostgresCodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.postgres._ - - type Prefix = TestPostgresDB - val prefix = TestPostgresDB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - - "3-comp-stereo-oneschema" in WithContext[Prefix, `3-comp-stereo-oneschema`].run { ctx => - import `3-comp-stereo-oneschema-lib`.public._ - import ctx._ - - (ctx.run(PublicSchema.PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(PublicSchema.AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "4-comp-stereo-twoschema" in WithContext[Prefix, `4-comp-stereo-twoschema`].run { ctx => - import `4-comp-stereo-twoschema-lib`.common._ - import `4-comp-stereo-twoschema-lib`.public._ - import ctx._ - - (ctx.run(PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "5 - non-stereotyped multiple schemas" in WithContext[Prefix, `5-comp-non-stereo-allschema`].run { ctx => - import `5-comp-non-stereo-allschema-lib`.alpha._ - import `5-comp-non-stereo-allschema-lib`.public._ - import ctx._ - - (ctx.run(ctx.AlphaSchema.PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, "blah", 55, "Wonkles"), - Person(2, "Jack", "Ripper", 33, "blah", 66, "Ginkles") - ) - ) - (ctx.run(ctx.PublicSchema.AddressDao.query.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqlServerCodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqlServerCodegenTestCases.scala deleted file mode 100644 index 73d7cb8a8e..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqlServerCodegenTestCases.scala +++ /dev/null @@ -1,110 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestSqlServerDB -import io.getquill.codegen.util._ -import org.scalatest.matchers.should.Matchers._ - -class SqlServerCodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.postgres._ - - type Prefix = TestSqlServerDB - val prefix = TestSqlServerDB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - - "3-comp-stereo-oneschema" in WithContext[Prefix, `3-comp-stereo-oneschema`].run { ctx => - import `3-comp-stereo-oneschema-lib`.public._ - import ctx._ - - (ctx.run(PublicSchema.PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(PublicSchema.AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "4-comp-stereo-twoschema" in WithContext[Prefix, `4-comp-stereo-twoschema`].run { ctx => - import `4-comp-stereo-twoschema-lib`.common._ - import `4-comp-stereo-twoschema-lib`.public._ - import ctx._ - - (ctx.run(PersonDao.alphaPerson.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, 55L, "Wonkles"), - Person(2, "Jack", "Ripper", 33, 66L, "Ginkles") - ) - ) - (ctx.run(AddressDao.publicAddress.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - - "5 - non-stereotyped multiple schemas" in WithContext[Prefix, `5-comp-non-stereo-allschema`].run { ctx => - import `5-comp-non-stereo-allschema-lib`.alpha._ - import `5-comp-non-stereo-allschema-lib`.public._ - import ctx._ - - (ctx.run(ctx.AlphaSchema.PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs ( - List( - Person(1, "Joe", "Bloggs", 22, "blah", 55, "Wonkles"), - Person(2, "Jack", "Ripper", 33, "blah", 66, "Ginkles") - ) - ) - (ctx.run(ctx.PublicSchema.AddressDao.query.filter(_.personFk == 1))) should contain theSameElementsAs ( - List( - Address(1, "123 Someplace", 1001), - Address(1, "678 Blah", 2002) - ) - ) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqliteCodegenTestCases.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqliteCodegenTestCases.scala deleted file mode 100644 index ea88986229..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/SqliteCodegenTestCases.scala +++ /dev/null @@ -1,54 +0,0 @@ -package io.getquill.codegen - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.TestSqliteDB -import io.getquill.codegen.util._ -import org.scalatest.matchers.should.Matchers._ - -class SqliteCodegenTestCases extends CodegenSpec { - import io.getquill.codegen.generated.postgres._ - - type Prefix = TestSqliteDB - val prefix = TestSqliteDB - - "trivial generator tests" - { - "use trivial snake case schema" in WithContext[Prefix, `1-simple-snake`].run { ctx => - import `1-simple-snake-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - "use trivial literal schema" in WithContext[Prefix, `2-simple-literal`].run { ctx => - import `2-simple-literal-lib`.public._ - import ctx._ - - val results = ctx.run(query[Person].filter(_.age > 11)).toSeq - results should contain theSameElementsAs - (List(Person(1, "Joe", "Bloggs", 22), Person(2, "Jack", "Ripper", 33))) - } - } - - "composable generator" - { - - "1-comp-sanity" in WithContext[Prefix, `1-comp-sanity`].run { ctx => - import `1-comp-sanity-lib`.public._ - import ctx._ - ctx.run(query[Person].filter(_.age > 11)) should contain theSameElementsAs List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - ) - } - - "2-comp-stereo-single" in WithContext[Prefix, `2-comp-stereo-single`].run { ctx => - import `2-comp-stereo-single-lib`.public._ - import ctx._ - (ctx.run(PersonDao.query.filter(_.age > 11))) should contain theSameElementsAs - (List( - Person(1, "Joe", "Bloggs", 22), - Person(2, "Jack", "Ripper", 33) - )) - } - } -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithContext.scala deleted file mode 100644 index 42a082ac49..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithContext.scala +++ /dev/null @@ -1,42 +0,0 @@ -package io.getquill.codegen.util -import java.io.Closeable - -import io.getquill.codegen.integration.CodegenTestCases -import io.getquill.context.jdbc.JdbcContext -import javax.sql.DataSource - -trait WithContext[Prefix <: ConfigPrefix, CTest <: CodegenTestCases] extends SchemaMaker { - type QuillContext <: JdbcContext[_, _] - - def ctest: CTest - def dbPrefix: Prefix - - protected def makeContext(ds: DataSource with Closeable): QuillContext - def run(testCode: QuillContext => Any): Unit = { - withContext(ctest.schemaMakerCoordinates(dbPrefix))(testCode) - () - } -} - -abstract class WithContextBase[Prefix <: ConfigPrefix, CTest <: CodegenTestCases]( - val dbPrefix: Prefix, - val ctest: CTest -) extends WithContext[Prefix, CTest] - -// Cannot include WithOracleContext here since it is not being compiled in all builds -object WithContext - extends WithContextAux - with WithH2Context - with WithMysqlContext - with WithPostgresContext - with WithSqliteContext - with WithSqlServerContext - -trait WithContextAux { - type Aux[Prefix <: ConfigPrefix, CTest <: CodegenTestCases, Ret] = WithContext[Prefix, CTest] { - type QuillContext = Ret - } - def apply[Prefix <: ConfigPrefix, CTest <: CodegenTestCases](implicit - cft: WithContext[Prefix, CTest] - ): Aux[Prefix, CTest, cft.QuillContext] = cft -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithH2Context.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithH2Context.scala deleted file mode 100644 index 0ec8c0fa1f..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithH2Context.scala +++ /dev/null @@ -1,84 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.{H2Dialect => TheDialect, H2JdbcContext => TheContext, Literal, SnakeCase} -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestH2DB => TheDB} -import javax.sql.DataSource - -trait WithH2Context extends WithContextAux { - import io.getquill.codegen.generated.h2._ - - implicit def h2SimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def h2SimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def h2ContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def h2ContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } - - implicit def h2ContextForTest3: Aux[TheDB, `3-comp-stereo-oneschema`, TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal]] = - new WithContextBase[TheDB, `3-comp-stereo-oneschema`](TheDB, `3-comp-stereo-oneschema`) { - override type QuillContext = TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[Literal](Literal, ds) - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - } - - implicit def h2ContextForTest4: Aux[ - TheDB, - `4-comp-stereo-twoschema`, - TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `4-comp-stereo-twoschema`](TheDB, `4-comp-stereo-twoschema`) { - override type QuillContext = TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - } - - implicit def h2ContextForTest5: Aux[ - TheDB, - `5-comp-non-stereo-allschema`, - TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `5-comp-non-stereo-allschema`](TheDB, `5-comp-non-stereo-allschema`) { - override type QuillContext = TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - } - -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithMysqlContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithMysqlContext.scala deleted file mode 100644 index 9a543154f6..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithMysqlContext.scala +++ /dev/null @@ -1,84 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.{MySQLDialect => TheDialect, MysqlJdbcContext => TheContext, Literal, SnakeCase} -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestMysqlDB => TheDB} -import javax.sql.DataSource - -trait WithMysqlContext extends WithContextAux { - import io.getquill.codegen.generated.mysql._ - - implicit def mysqlSimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def mysqlSimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def mysqlContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def mysqlContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } - - implicit def mysqlContextForTest3: Aux[TheDB, `3-comp-stereo-oneschema`, TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal]] = - new WithContextBase[TheDB, `3-comp-stereo-oneschema`](TheDB, `3-comp-stereo-oneschema`) { - override type QuillContext = TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[Literal](Literal, ds) - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - } - - implicit def mysqlContextForTest4: Aux[ - TheDB, - `4-comp-stereo-twoschema`, - TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `4-comp-stereo-twoschema`](TheDB, `4-comp-stereo-twoschema`) { - override type QuillContext = TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - } - - implicit def mysqlContextForTest5: Aux[ - TheDB, - `5-comp-non-stereo-allschema`, - TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `5-comp-non-stereo-allschema`](TheDB, `5-comp-non-stereo-allschema`) { - override type QuillContext = TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - } - -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithOracleContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithOracleContext.scala deleted file mode 100644 index 50f5f21f6f..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithOracleContext.scala +++ /dev/null @@ -1,88 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestOracleDB => TheDB} -import io.getquill.{Literal, SnakeCase, OracleDialect => TheDialect, OracleJdbcContext => TheContext} -import javax.sql.DataSource - -// Need separate object for Oracle contexts since WithOracleContext itself is excluded in builds -// that are not compiling with Oracle -object WithOracleContextStandalone extends WithContextAux with WithOracleContext - -trait WithOracleContext extends WithContextAux { - import io.getquill.codegen.generated.oracle._ - - implicit def oracleSimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def oracleSimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def oracleContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def oracleContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } - - implicit def oracleContextForTest3: Aux[TheDB, `3-comp-stereo-oneschema`, TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal]] = - new WithContextBase[TheDB, `3-comp-stereo-oneschema`](TheDB, `3-comp-stereo-oneschema`) { - override type QuillContext = TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[Literal](Literal, ds) - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - } - - implicit def oracleContextForTest4: Aux[ - TheDB, - `4-comp-stereo-twoschema`, - TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `4-comp-stereo-twoschema`](TheDB, `4-comp-stereo-twoschema`) { - override type QuillContext = TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - } - - implicit def oracleContextForTest5: Aux[ - TheDB, - `5-comp-non-stereo-allschema`, - TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `5-comp-non-stereo-allschema`](TheDB, `5-comp-non-stereo-allschema`) { - override type QuillContext = TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - } - -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithPostgresContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithPostgresContext.scala deleted file mode 100644 index f27b69ae2e..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithPostgresContext.scala +++ /dev/null @@ -1,84 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestPostgresDB => TheDB} -import io.getquill.{Literal, SnakeCase, PostgresDialect => TheDialect, PostgresJdbcContext => TheContext} -import javax.sql.DataSource - -trait WithPostgresContext extends WithContextAux { - import io.getquill.codegen.generated.postgres._ - - implicit def postgresSimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def postgresSimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def postgresContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def postgresContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } - - implicit def postgresContextForTest3: Aux[TheDB, `3-comp-stereo-oneschema`, TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal]] = - new WithContextBase[TheDB, `3-comp-stereo-oneschema`](TheDB, `3-comp-stereo-oneschema`) { - override type QuillContext = TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[Literal](Literal, ds) - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - } - - implicit def postgresContextForTest4: Aux[ - TheDB, - `4-comp-stereo-twoschema`, - TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `4-comp-stereo-twoschema`](TheDB, `4-comp-stereo-twoschema`) { - override type QuillContext = TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - } - - implicit def postgresContextForTest5: Aux[ - TheDB, - `5-comp-non-stereo-allschema`, - TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `5-comp-non-stereo-allschema`](TheDB, `5-comp-non-stereo-allschema`) { - override type QuillContext = TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - } - -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqlServerContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqlServerContext.scala deleted file mode 100644 index 263a9c2f4a..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqlServerContext.scala +++ /dev/null @@ -1,84 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestSqlServerDB => TheDB} -import io.getquill.{Literal, SnakeCase, SQLServerDialect => TheDialect, SqlServerJdbcContext => TheContext} -import javax.sql.DataSource - -trait WithSqlServerContext extends WithContextAux { - import io.getquill.codegen.generated.sqlserver._ - - implicit def sqlServerSimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqlServerSimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqlServerContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqlServerContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } - - implicit def sqlServerContextForTest3: Aux[TheDB, `3-comp-stereo-oneschema`, TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal]] = - new WithContextBase[TheDB, `3-comp-stereo-oneschema`](TheDB, `3-comp-stereo-oneschema`) { - override type QuillContext = TheContext[Literal] - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[Literal](Literal, ds) - with `3-comp-stereo-oneschema-lib`.public.PublicExtensions[TheDialect, Literal] - } - - implicit def sqlServerContextForTest4: Aux[ - TheDB, - `4-comp-stereo-twoschema`, - TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `4-comp-stereo-twoschema`](TheDB, `4-comp-stereo-twoschema`) { - override type QuillContext = TheContext[Literal] - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `4-comp-stereo-twoschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `4-comp-stereo-twoschema-lib`.common.CommonExtensions[TheDialect, Literal] - } - - implicit def sqlServerContextForTest5: Aux[ - TheDB, - `5-comp-non-stereo-allschema`, - TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - ] = new WithContextBase[TheDB, `5-comp-non-stereo-allschema`](TheDB, `5-comp-non-stereo-allschema`) { - override type QuillContext = TheContext[Literal] - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - override protected def makeContext(ds: DataSource with Closeable) = - new TheContext[Literal](Literal, ds) - with `5-comp-non-stereo-allschema-lib`.public.PublicExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.alpha.AlphaExtensions[TheDialect, Literal] - with `5-comp-non-stereo-allschema-lib`.bravo.BravoExtensions[TheDialect, Literal] - } - -} diff --git a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqliteContext.scala b/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqliteContext.scala deleted file mode 100644 index 5c0198082c..0000000000 --- a/quill-codegen-tests/src/test/scala/io/getquill/codegen/util/WithSqliteContext.scala +++ /dev/null @@ -1,39 +0,0 @@ -package io.getquill.codegen.util - -import java.io.Closeable - -import io.getquill.codegen.integration.CodegenTestCases._ -import io.getquill.codegen.util.ConfigPrefix.{TestSqliteDB => TheDB} -import io.getquill.{SnakeCase, SqliteDialect => TheDialect, SqliteJdbcContext => TheContext} -import javax.sql.DataSource - -trait WithSqliteContext extends WithContextAux { - import io.getquill.codegen.generated.sqlite._ - - implicit def sqliteSimpleContextForTest1: Aux[TheDB, `1-simple-snake`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-simple-snake`](TheDB, `1-simple-snake`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqliteSimpleContextForTest2: Aux[TheDB, `2-simple-literal`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `2-simple-literal`](TheDB, `2-simple-literal`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqliteContextForTest1: Aux[TheDB, `1-comp-sanity`, TheContext[SnakeCase]] = - new WithContextBase[TheDB, `1-comp-sanity`](TheDB, `1-comp-sanity`) { - override type QuillContext = TheContext[SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new QuillContext(SnakeCase, ds) - } - - implicit def sqliteContextForTest2: Aux[TheDB, `2-comp-stereo-single`, TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase]] = - new WithContextBase[TheDB, `2-comp-stereo-single`](TheDB, `2-comp-stereo-single`) { - override type QuillContext = TheContext[SnakeCase] - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - override protected def makeContext(ds: DataSource with Closeable) = new TheContext[SnakeCase](SnakeCase, ds) - with `2-comp-stereo-single-lib`.public.PublicExtensions[TheDialect, SnakeCase] - } -} From 276fcd779253db63dc8fb9e9570ab4452ba62ab6 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 20:32:22 -0500 Subject: [PATCH 02/14] Fix build scripts. Was using bad sqlserver version. --- build/Dockerfile-sqlserver | 6 ++++++ docker-compose.yml | 6 +++++- scripts/start_containers.sh | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 build/Dockerfile-sqlserver create mode 100755 scripts/start_containers.sh diff --git a/build/Dockerfile-sqlserver b/build/Dockerfile-sqlserver new file mode 100644 index 0000000000..748b293e61 --- /dev/null +++ b/build/Dockerfile-sqlserver @@ -0,0 +1,6 @@ +# Builds a ubuntu-based postgres image whose latency can be modified to be highe +# for performance experimentation. +FROM mcr.microsoft.com/mssql/server:2022-CU13-ubuntu-22.04 +USER root +RUN apt-get update && \ + apt-get install iproute2 iputils-ping -y diff --git a/docker-compose.yml b/docker-compose.yml index 9d7997bfa2..f3aef64359 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,12 +42,16 @@ services: init: true sqlserver: - image: mcr.microsoft.com/azure-sql-edge:latest # use this because it supports ARM64 architecture for M1 Mac + build: + context: . + dockerfile: ./build/Dockerfile-sqlserver ports: - "11433:1433" environment: - ACCEPT_EULA=Y - SA_PASSWORD=QuillRocks! + cap_add: + - NET_ADMIN oracle: image: quillbuilduser/oracle-18-xe-micro-sq diff --git a/scripts/start_containers.sh b/scripts/start_containers.sh new file mode 100755 index 0000000000..240f75b7d7 --- /dev/null +++ b/scripts/start_containers.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# From 'All In One' of Quill CONTRIBUTING.md +docker compose down && docker compose build && docker compose run --rm --service-ports setup + +# echo "Adding 50ms latency to protoquill_postgres_1" +# docker exec protoquill_postgres_1 tc qdisc add dev eth0 root netem delay 50ms + +# echo "Adding 50ms latency to protoquill_mysql_1" +# docker exec protoquill_mysql_1 tc qdisc add dev eth0 root netem delay 50ms From 71c20205889ed64c3ec96c8e95be88609d2d9acb Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 20:32:53 -0500 Subject: [PATCH 03/14] Better raw query support. Do not wrap top-level infixes. --- .../io/getquill/context/ContextMacro.scala | 10 ++- .../scala/io/getquill/util/ExceptionOps.scala | 15 +++++ .../main/scala/io/getquill/sql/SqlQuery.scala | 62 +++++++++++++------ .../io/getquill/sql/idiom/SqlIdiom.scala | 2 + .../getquill/sql/idiom/VerifySqlQuery.scala | 2 + .../sql/norm/AddDropToNestedOrderBy.scala | 2 + .../sql/norm/RemoveUnusedSelects.scala | 3 +- .../sql/norm/StatelessQueryTransformer.scala | 3 +- .../context/orientdb/OrientDBIdiom.scala | 9 ++- .../getquill/context/spark/SparkDialect.scala | 4 +- .../getquill/context/sql/SqlQuerySpec.scala | 29 +++++++-- .../context/sql/idiom/OracleDialectSpec.scala | 2 +- .../context/sql/idiom/SqlIdiomSpec.scala | 2 +- .../sql/norm/ExpandNestedQueriesSpec.scala | 13 +++- 14 files changed, 123 insertions(+), 35 deletions(-) create mode 100644 quill-core/src/main/scala/io/getquill/util/ExceptionOps.scala diff --git a/quill-core/src/main/scala/io/getquill/context/ContextMacro.scala b/quill-core/src/main/scala/io/getquill/context/ContextMacro.scala index a1163a5b00..0e24fe86e7 100644 --- a/quill-core/src/main/scala/io/getquill/context/ContextMacro.scala +++ b/quill-core/src/main/scala/io/getquill/context/ContextMacro.scala @@ -8,6 +8,8 @@ import io.getquill.util.MacroContextExt._ import io.getquill.{IdiomContext, NamingStrategy} import io.getquill.idiom._ import io.getquill.quat.Quat +import io.getquill.util.ExceptionOps.ThrowableOpsMethods + import scala.util.Failure import scala.util.Success @@ -89,9 +91,15 @@ trait ContextMacro extends Quotation { ) } catch { case e: Exception => - c.fail(s"Query compilation failed. ${e.getMessage}") + c.fail( + s"""Query compilation failed due to: ${e.getMessage} + |================= Error Stack: ================= + |${e.stackTraceToString} + |""".stripMargin + ) } } + } private def translateStatic(ast: Ast, topLevelQuat: Quat, batchAlias: Option[String]): Tree = { diff --git a/quill-core/src/main/scala/io/getquill/util/ExceptionOps.scala b/quill-core/src/main/scala/io/getquill/util/ExceptionOps.scala new file mode 100644 index 0000000000..867985a00e --- /dev/null +++ b/quill-core/src/main/scala/io/getquill/util/ExceptionOps.scala @@ -0,0 +1,15 @@ +package io.getquill.util + +import java.io.ByteArrayOutputStream + +object ExceptionOps { + implicit final class ThrowableOpsMethods(private val t: Throwable) extends AnyVal { + def stackTraceToString: String = { + val stream = new ByteArrayOutputStream() + val writer = new java.io.BufferedWriter(new java.io.OutputStreamWriter(stream)) + t.printStackTrace(new java.io.PrintWriter(writer)) + writer.flush() + stream.toString + } + } +} diff --git a/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala b/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala index 61cfc9b85c..ccc6aebe69 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala @@ -67,6 +67,14 @@ final case class UnaryOperationSqlQuery( override def quat: Quat = quatType } +// A top-level infix query. This needs special rendering because we don't +// want to tokenize column names, e.g. sql"select foo, bar from baz".as[Person] +// should become just "select foo, bar from baz", not the fully expanded form +// "select p.name, p.age from (select foo, bar from baz) as Person p" +final case class TopInfixQuery(ast: Infix) extends SqlQuery { + override def quat: Quat = ast.quat +} + final case class SelectValue(ast: Ast, alias: Option[String] = None, concat: Boolean = false) extends PseudoAst { override def toString: String = s"${ast.toString}${alias.map("->" + _).getOrElse("")}" } @@ -109,40 +117,54 @@ class SqlQueryApply(traceConfig: TraceConfig) { val interp: Interpolator = new Interpolator(TraceType.SqlQueryConstruct, traceConfig, 1) import interp._ - def apply(query: Ast): SqlQuery = + def apply(query: Ast) = build(query, true) + + private def build(query: Ast, isTopLevel: Boolean = false): SqlQuery = query match { case Union(a, b) => trace"Construct SqlQuery from: Union" andReturn { - SetOperationSqlQuery(apply(a), UnionOperation, apply(b))(query.quat) + SetOperationSqlQuery(build(a), UnionOperation, build(b))(query.quat) } case UnionAll(a, b) => trace"Construct SqlQuery from: UnionAll" andReturn { - SetOperationSqlQuery(apply(a), UnionAllOperation, apply(b))(query.quat) + SetOperationSqlQuery(build(a), UnionAllOperation, build(b))(query.quat) } case UnaryOperation(op, q: Query) => trace"Construct SqlQuery from: UnaryOperation" andReturn { - UnaryOperationSqlQuery(op, apply(q))(query.quat) + UnaryOperationSqlQuery(op, build(q))(query.quat) } case _: Operation | _: Value => trace"Construct SqlQuery from: Operation/Value" andReturn { FlattenSqlQuery(select = List(SelectValue(query)))(query.quat) } + // i.e. a transparent map that looks like `map(x => x)` + // in this case, don't even consider it a nesting (i.e. if it was top-level, it's still top-level) case Map(q, a, b) if a == b => trace"Construct SqlQuery from: Map" andReturn { - apply(q) + build(q, isTopLevel) } case TakeDropFlatten(q, limit, offset) => trace"Construct SqlQuery from: TakeDropFlatten" andReturn { flatten(q, "x").copy(limit = limit, offset = offset)(q.quat) } + // In the case of sql"select foo, bar from baz".as[Query[...]].nested + // make it only double nested (i.e. FlattenSqlQuery(from: InfixContext)) + // instead of triple: (i.e. FlattenSqlQuery(from: FlattenSqlQuery(from: InfixContext))) + case Nested(infix: Infix) => + trace"Construct SqlQuery from: Nested(Infix)" andReturn { + flatten(infix, "x") + } + case infix: Infix => + if (isTopLevel) + TopInfixQuery(infix) + else + trace"Construct SqlQuery from: Infix" andReturn { + flatten(infix, "x") + } case q: Query => trace"Construct SqlQuery from: Query" andReturn { flatten(q, "x") } - case infix: Infix => - trace"Construct SqlQuery from: Infix" andReturn { - flatten(infix, "x") - } case other => trace"Construct SqlQuery from: other" andReturn { fail(s"Query not properly normalized. Please open a bug report. Ast: '$other'") @@ -213,8 +235,8 @@ class SqlQueryApply(traceConfig: TraceConfig) { // case Map(_, _, _) => // trace"base| Nesting Map(a=>ContainsImpurities(a)) $q" andReturn nest(source(q, alias)) - case Nested(q) => trace"base| Nesting Nested $q" andReturn nest(QueryContext(apply(q), alias)) - case q: ConcatMap => trace"base| Nesting ConcatMap $q" andReturn nest(QueryContext(apply(q), alias)) + case Nested(q) => trace"base| Nesting Nested $q" andReturn nest(QueryContext(build(q), alias)) + case q: ConcatMap => trace"base| Nesting ConcatMap $q" andReturn nest(QueryContext(build(q), alias)) case Join(tpe, a, b, iA, iB, on) => trace"base| Collecting join aliases $q" andReturn { val ctx = source(q, alias) @@ -355,7 +377,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { else trace"Flattening| Map(Ident) [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, select = selectValues(p) )(quat) @@ -372,7 +394,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { else trace"Flattening| Filter(Ident) [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, where = Some(p), select = select(alias, quat) )(quat) @@ -390,7 +412,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { else trace"Flattening| SortBy(Ident) [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, orderBy = criteria, select = select(alias, quat) )(quat) @@ -408,7 +430,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { case other => trace"Flattening| Aggregation(Query) [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, select = List( SelectValue(Aggregation(op, Ident("*", quat))) ) // Quat of a * aggregation is same as for the entire query @@ -430,7 +452,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { else trace"Flattening| Take [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, limit = Some(n), select = select(alias, quat) )(quat) @@ -443,7 +465,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { else trace"Flattening| Drop [Complex]" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, offset = Some(n), select = select(alias, quat) )(quat) @@ -477,7 +499,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { case _ => trace"Flattening| DistinctOn" andReturn FlattenSqlQuery( - from = QueryContext(apply(q), alias) :: Nil, + from = QueryContext(build(q), alias) :: Nil, select = select(alias, quat), distinct = DistinctKind.DistinctOn(distinctList) )(quat) @@ -502,8 +524,8 @@ class SqlQueryApply(traceConfig: TraceConfig) { case infix: Infix => InfixContext(infix, alias) case Join(t, a, b, ia, ib, on) => JoinContext(t, source(a, ia.name), source(b, ib.name), on) case FlatJoin(t, a, ia, on) => FlatJoinContext(t, source(a, ia.name), on) - case Nested(q) => QueryContext(apply(q), alias) - case other => QueryContext(apply(other), alias) + case Nested(q) => QueryContext(build(q), alias) + case other => QueryContext(build(other), alias) } private def orderByCriteria(ast: Ast, ordering: Ast, from: List[FromContext]): List[OrderByCriteria] = diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala index 9f6d4af794..136524f971 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala @@ -249,6 +249,8 @@ trait SqlIdiom extends Idiom { strategy: NamingStrategy, idiomContext: IdiomContext ): Tokenizer[SqlQuery] = Tokenizer[SqlQuery] { + case q: TopInfixQuery => + q.ast.token case q: FlattenSqlQuery => new FlattenSqlQueryTokenizerHelper(q).apply case SetOperationSqlQuery(a, op, b) => diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/VerifySqlQuery.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/VerifySqlQuery.scala index 3c33064475..79636ac957 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/VerifySqlQuery.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/VerifySqlQuery.scala @@ -29,6 +29,8 @@ object VerifySqlQuery { private def verify(query: SqlQuery): Option[InvalidSqlQuery] = query match { + // if it's just an infix query no more verification to do + case p: TopInfixQuery => None case q: FlattenSqlQuery => verify(q) case SetOperationSqlQuery(a, op, b) => verify(a).orElse(verify(b)) case UnaryOperationSqlQuery(op, q) => verify(q) diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/AddDropToNestedOrderBy.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/AddDropToNestedOrderBy.scala index de14e9932d..ce721b00be 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/AddDropToNestedOrderBy.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/AddDropToNestedOrderBy.scala @@ -21,6 +21,7 @@ object AddDropToNestedOrderBy { case SetOperationSqlQuery(a, op, b) => SetOperationSqlQuery(applyInner(a), op, applyInner(b))(q.quat) case UnaryOperationSqlQuery(op, a) => UnaryOperationSqlQuery(op, applyInner(a))(q.quat) + case q: TopInfixQuery => q } private def applyInner(f: FromContext): FromContext = @@ -36,5 +37,6 @@ object AddDropToNestedOrderBy { case q: FlattenSqlQuery => q.copy(from = q.from.map(applyInner(_)))(q.quat) case SetOperationSqlQuery(a, op, b) => SetOperationSqlQuery(applyInner(a), op, applyInner(b))(q.quat) case UnaryOperationSqlQuery(op, a) => UnaryOperationSqlQuery(op, applyInner(a))(q.quat) + case q: TopInfixQuery => q } } diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/RemoveUnusedSelects.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/RemoveUnusedSelects.scala index dcdb7adc25..ccf1c9acad 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/RemoveUnusedSelects.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/RemoveUnusedSelects.scala @@ -1,7 +1,7 @@ package io.getquill.sql.norm import io.getquill.ast.{Ast, CollectAst, Ident, Property, StatefulTransformer} -import io.getquill.context.sql.{DistinctKind, FlatJoinContext, FlattenSqlQuery, FromContext, InfixContext, JoinContext, QueryContext, SelectValue, SetOperationSqlQuery, SqlQuery, TableContext, UnaryOperationSqlQuery} +import io.getquill.context.sql.{DistinctKind, FlatJoinContext, FlattenSqlQuery, FromContext, InfixContext, JoinContext, TopInfixQuery, QueryContext, SelectValue, SetOperationSqlQuery, SqlQuery, TableContext, UnaryOperationSqlQuery} import io.getquill.norm.PropertyMatryoshka import io.getquill.quat.Quat @@ -61,6 +61,7 @@ object RemoveUnusedSelects { SetOperationSqlQuery(apply(a, references), op, apply(b, references))(q.quat) case UnaryOperationSqlQuery(op, q) => UnaryOperationSqlQuery(op, apply(q, references))(q.quat) + case p: TopInfixQuery => p } private def filterUnused(select: List[SelectValue], references: Set[Property]): List[SelectValue] = { diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/StatelessQueryTransformer.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/StatelessQueryTransformer.scala index 205207c26a..4527d444b5 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/StatelessQueryTransformer.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/StatelessQueryTransformer.scala @@ -1,6 +1,6 @@ package io.getquill.sql.norm -import io.getquill.context.sql.{FlatJoinContext, FlattenSqlQuery, FromContext, InfixContext, JoinContext, QueryContext, SetOperationSqlQuery, SqlQuery, TableContext, UnaryOperationSqlQuery} +import io.getquill.context.sql.{FlatJoinContext, FlattenSqlQuery, FromContext, InfixContext, JoinContext, TopInfixQuery, QueryContext, SetOperationSqlQuery, SqlQuery, TableContext, UnaryOperationSqlQuery} import io.getquill.quat.Quat sealed trait QueryLevel { @@ -34,6 +34,7 @@ trait StatelessQueryTransformer { protected def apply(q: SqlQuery, level: QueryLevel): SqlQuery = q match { + case p: TopInfixQuery => p case q: FlattenSqlQuery => expandNested(q, level) case SetOperationSqlQuery(a, op, b) => diff --git a/quill-orientdb/src/main/scala/io/getquill/context/orientdb/OrientDBIdiom.scala b/quill-orientdb/src/main/scala/io/getquill/context/orientdb/OrientDBIdiom.scala index 8ae11c5934..205a821a94 100644 --- a/quill-orientdb/src/main/scala/io/getquill/context/orientdb/OrientDBIdiom.scala +++ b/quill-orientdb/src/main/scala/io/getquill/context/orientdb/OrientDBIdiom.scala @@ -5,7 +5,7 @@ import io.getquill.idiom.StatementInterpolator._ import io.getquill.context.sql.norm._ import io.getquill.ast.{AggregationOperator, External, _} import io.getquill.context.sql._ -import io.getquill.{IdiomContext, NamingStrategy} +import io.getquill.{AstPrinter, IdiomContext, NamingStrategy} import io.getquill.context.{CannotReturn, ExecutionType} import io.getquill.util.Messages.{fail, trace} import io.getquill.idiom._ @@ -129,6 +129,9 @@ trait OrientDBIdiom extends Idiom { strategy: NamingStrategy, idiomContext: IdiomContext ): Tokenizer[SqlQuery] = Tokenizer[SqlQuery] { + case q: TopInfixQuery => + q.ast.token + case FlattenSqlQuery(from, where, groupBy, orderBy, limit, offset, select, distinct) => val distinctTokenizer = (if (distinct == DistinctKind.Distinct) "DISTINCT" else "").token @@ -183,8 +186,8 @@ trait OrientDBIdiom extends Idiom { case SetOperationSqlQuery(a, op, b) => val str = f"SELECT $$c LET $$a = (${a.token}), $$b = (${b.token}), $$c = UNIONALL($$a, $$b)" str.token - case _ => - fail("Other operators are not supported yet. Please raise a ticket to support more operations") + case other => + fail("This operator is not supported (below). Please raise a ticket to support more operations\n" + new AstPrinter()(other)) } implicit def operationTokenizer(implicit diff --git a/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala b/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala index af47573162..d1459f9f94 100644 --- a/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala +++ b/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala @@ -3,7 +3,7 @@ package io.getquill.context.spark import io.getquill.{IdiomContext, NamingStrategy} import io.getquill.ast.{Ast, BinaryOperation, CaseClass, Constant, ExternalIdent, Ident, Operation, Property, Query, StringOperator, Tuple, Value} import io.getquill.context.spark.norm.EscapeQuestionMarks -import io.getquill.context.sql.{FlattenSqlQuery, SelectValue, SetOperationSqlQuery, SqlQuery, SqlQueryApply, UnaryOperationSqlQuery} +import io.getquill.context.sql.{FlattenSqlQuery, SelectValue, SetOperationSqlQuery, SqlQuery, SqlQueryApply, TopInfixQuery, UnaryOperationSqlQuery} import io.getquill.context.sql.idiom.SqlIdiom import io.getquill.context.sql.norm.SqlNormalize import io.getquill.idiom.StatementInterpolator._ @@ -123,6 +123,8 @@ trait SparkIdiom extends SqlIdiom with CannotReturn { self => strategy: NamingStrategy, idiomContext: IdiomContext ): Tokenizer[SqlQuery] = Tokenizer[SqlQuery] { + case q: TopInfixQuery => + q.ast.token case q: FlattenSqlQuery => new SparkFlattenSqlQueryTokenizerHelper(q).apply case SetOperationSqlQuery(a, op, b) => diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/SqlQuerySpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/SqlQuerySpec.scala index dc9d4fc7e8..735b480eca 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/SqlQuerySpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/SqlQuerySpec.scala @@ -345,17 +345,38 @@ class SqlQuerySpec extends Spec { "raw queries with sql" - { "using tuples" in { val q = quote { - sql"""SELECT t.s AS "_1", t.i AS "_2" FROM TestEntity t""".as[Query[(String, Int)]] + sql"""SELECT foo, bar FROM baz""".as[Query[(String, Int)]] } testContext.run(q).string mustEqual - """SELECT x._1, x._2 FROM (SELECT t.s AS "_1", t.i AS "_2" FROM TestEntity t) AS x""" + """SELECT foo, bar FROM baz""" + } + "using tuples - pure" in { + val q = quote { + sql"""SELECT foo, bar FROM baz""".pure.as[Query[(String, Int)]] + } + testContext.run(q).string mustEqual + """SELECT foo, bar FROM baz""" + } + "using tuples - nested" in { + val q = quote { + sql"""SELECT foo, bar FROM baz""".as[Query[(String, Int)]].nested + } + testContext.run(q).string mustEqual + """SELECT x._1, x._2 FROM (SELECT foo, bar FROM baz) AS x""" } "using single value" in { val q = quote { - sql"""SELECT t.i FROM TestEntity t""".as[Query[Int]] + sql"""SELECT foo, bar FROM baz""".as[Query[Int]] + } + testContext.run(q).string mustEqual + """SELECT foo, bar FROM baz""" + } + "using single value - nested" in { + val q = quote { + sql"""SELECT foo, bar FROM baz""".as[Query[Int]].nested } testContext.run(q).string mustEqual - """SELECT x.* FROM (SELECT t.i FROM TestEntity t) AS x""" + """SELECT x.* FROM (SELECT foo, bar FROM baz) AS x""" } } diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/OracleDialectSpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/OracleDialectSpec.scala index 9549f3a779..421e8ccb8a 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/OracleDialectSpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/OracleDialectSpec.scala @@ -189,7 +189,7 @@ class OracleDialectSpec extends Spec { case class Person(name: String, age: Int) "No 'AS' aliases" in { ctx.run(sql"SELECT name, age FROM Person p".as[Query[Person]]).string mustEqual - "SELECT x.name, x.age FROM (SELECT name, age FROM Person p) x" + "SELECT name, age FROM Person p" } case class Document(filename: String) diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/SqlIdiomSpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/SqlIdiomSpec.scala index 4cce884d8c..26f1237054 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/SqlIdiomSpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/idiom/SqlIdiomSpec.scala @@ -949,7 +949,7 @@ class SqlIdiomSpec extends Spec { } "full infix query" in { testContext.run(sql"SELECT * FROM TestEntity".as[Query[TestEntity]]).string mustEqual - "SELECT x.s, x.i, x.l, x.o, x.b FROM (SELECT * FROM TestEntity) AS x" + "SELECT * FROM TestEntity" } "full infix action" in { testContext.run(sql"DELETE FROM TestEntity".as[Action[TestEntity]]).string mustEqual diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandNestedQueriesSpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandNestedQueriesSpec.scala index a5aa55e99b..184de81a45 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandNestedQueriesSpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandNestedQueriesSpec.scala @@ -701,17 +701,26 @@ class ExpandNestedQueriesSpec extends Spec { sql"fromSomewhere()".as[Query[Person]] } testContext.run(q).string mustEqual - "SELECT x.first_name AS firstName, x.last_name AS lastName FROM (fromSomewhere()) AS x" + "fromSomewhere()" } "should be handled correctly in a regular schema - nested" in { + case class Person(firstName: String, lastName: String) + val q = quote { + sql"fromSomewhere()".as[Query[Person]].nested + } + testContext.run(q).string mustEqual + "SELECT x.first_name AS firstName, x.last_name AS lastName FROM (fromSomewhere()) AS x" + } + + "should be handled correctly in a regular schema - multi-level" in { case class Name(firstName: String, lastName: String) case class Person(name: Name, theAge: Int) val q = quote { sql"fromSomewhere()".as[Query[Person]] } testContext.run(q).string mustEqual - "SELECT x.first_name AS firstName, x.last_name AS lastName, x.the_age AS theAge FROM (fromSomewhere()) AS x" + "fromSomewhere()" } } From 35e1f4961fc5f1f579383585dcf17632aa9ba5af Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 21:19:17 -0500 Subject: [PATCH 04/14] Fix Spark cases --- .../src/main/scala/io/getquill/sql/SqlQuery.scala | 4 ++-- .../io/getquill/context/spark/SparkDialect.scala | 13 ++++++++++--- .../context/spark/QuillSparkContextSpec.scala | 2 ++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala b/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala index ccc6aebe69..0aa9187b76 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/SqlQuery.scala @@ -112,7 +112,7 @@ object CaseClassMake { } } -class SqlQueryApply(traceConfig: TraceConfig) { +class SqlQueryApply(traceConfig: TraceConfig, allowTopLevelInfix: Boolean = true) { val interp: Interpolator = new Interpolator(TraceType.SqlQueryConstruct, traceConfig, 1) import interp._ @@ -155,7 +155,7 @@ class SqlQueryApply(traceConfig: TraceConfig) { flatten(infix, "x") } case infix: Infix => - if (isTopLevel) + if (allowTopLevelInfix && isTopLevel) TopInfixQuery(infix) else trace"Construct SqlQuery from: Infix" andReturn { diff --git a/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala b/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala index d1459f9f94..81664c879c 100644 --- a/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala +++ b/quill-spark/src/main/scala/io/getquill/context/spark/SparkDialect.scala @@ -21,6 +21,10 @@ trait SparkIdiom extends SqlIdiom with CannotReturn { self => def liftingPlaceholder(index: Int): String = "?" + // Do not allow top-level infix queries in spark by default because then run(liftQuery(myDataset)) will fail + // power-users may want to override this in rare cases. + def allowTopLevelInfix = false + override def prepareForProbing(string: String) = string override implicit def externalIdentTokenizer(implicit @@ -39,7 +43,7 @@ trait SparkIdiom extends SqlIdiom with CannotReturn { self => val token = normalizedAst match { case q: Query => - val sql = new SqlQueryApply(idiomContext.config.traceConfig)(q) + val sql = new SqlQueryApply(idiomContext.config.traceConfig, allowTopLevelInfix)(q) trace("sql")(sql) val expanded = SimpleNestedExpansion(sql) trace("expanded sql")(expanded) @@ -123,14 +127,17 @@ trait SparkIdiom extends SqlIdiom with CannotReturn { self => strategy: NamingStrategy, idiomContext: IdiomContext ): Tokenizer[SqlQuery] = Tokenizer[SqlQuery] { - case q: TopInfixQuery => - q.ast.token case q: FlattenSqlQuery => new SparkFlattenSqlQueryTokenizerHelper(q).apply case SetOperationSqlQuery(a, op, b) => stmt"(${a.token}) ${op.token} (${b.token})" case UnaryOperationSqlQuery(op, q) => stmt"SELECT ${op.token} (${q.token})" + // Technically top-level infix queries are not allowed in Spark because + // run(liftQuery(myDataset)) then won't work but in case the user + // wants to override it, provide the translation. + case q: TopInfixQuery => + q.ast.token } override implicit def propertyTokenizer(implicit diff --git a/quill-spark/src/test/scala/io/getquill/context/spark/QuillSparkContextSpec.scala b/quill-spark/src/test/scala/io/getquill/context/spark/QuillSparkContextSpec.scala index 7c372cfd31..fb7fcd624d 100644 --- a/quill-spark/src/test/scala/io/getquill/context/spark/QuillSparkContextSpec.scala +++ b/quill-spark/src/test/scala/io/getquill/context/spark/QuillSparkContextSpec.scala @@ -1,6 +1,8 @@ package io.getquill.context.spark import io.getquill.base.Spec +import io.getquill.{Test => _, _} + import scala.util.Success class QuillSparkContextSpec extends Spec { From 95359cac072b60532c8ffeacc8a28ea855a670ae Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 21:32:04 -0500 Subject: [PATCH 05/14] Another fix --- .../scala/io/getquill/context/orientdb/OrientDBIdiomSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBIdiomSpec.scala b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBIdiomSpec.scala index 6ba79c1b82..855b2f9f35 100644 --- a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBIdiomSpec.scala +++ b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBIdiomSpec.scala @@ -330,7 +330,7 @@ class OrientDBIdiomSpec extends Spec { sql"SELECT MODE(i) FROM TestEntity".as[Query[Int]] } ctx.run(q).string mustEqual - "SELECT * FROM (SELECT MODE(i) FROM TestEntity)" + "SELECT MODE(i) FROM TestEntity" } } "action" - { From 0d4e9845b9cd15057c5ce780df8c0040f7eded52 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 22:00:18 -0500 Subject: [PATCH 06/14] Fix another tests --- .../scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala index 731e5b56b3..85d1112c2b 100644 --- a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala +++ b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala @@ -229,7 +229,7 @@ class OrientDBQuerySpec extends Spec { intercept[IllegalStateException]( t.token(UnaryOperationSqlQuery(BooleanOperator.`!`, e)(Quat.Value)) - ).getMessage mustBe "Other operators are not supported yet. Please raise a ticket to support more operations" + ).getMessage must contain("This operator is not supported") } "operation" in { val t = implicitly[Tokenizer[Operation]] From 32d2ff91d19f560535374b6c0f80c2a6d145d258 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Thu, 5 Dec 2024 22:22:28 -0500 Subject: [PATCH 07/14] fix test --- .../scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala index 85d1112c2b..531e22a6b5 100644 --- a/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala +++ b/quill-orientdb/src/test/scala/io/getquill/context/orientdb/OrientDBQuerySpec.scala @@ -229,7 +229,7 @@ class OrientDBQuerySpec extends Spec { intercept[IllegalStateException]( t.token(UnaryOperationSqlQuery(BooleanOperator.`!`, e)(Quat.Value)) - ).getMessage must contain("This operator is not supported") + ).getMessage must include("This operator is not supported") } "operation" in { val t = implicitly[Tokenizer[Operation]] From f6ec555b68f9a82e0ca9c8ea12be887a38bce760 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Fri, 6 Dec 2024 10:32:24 -0500 Subject: [PATCH 08/14] Starting to work on caching Normalization and Query Compiler --- .../scala/io/getquill/HasStatefulCache.scala | 60 +++++++++++++++++++ .../io/getquill/ast/StatefulTransformer.scala | 14 ++++- .../io/getquill/norm/AdHocReduction.scala | 7 ++- .../scala/io/getquill/norm/Normalize.scala | 6 +- .../norm/NormalizeNestedStructures.scala | 7 ++- .../scala/io/getquill/norm/OrderTerms.scala | 6 +- .../io/getquill/norm/StabilizeLifts.scala | 2 + .../io/getquill/norm/SymbolicReduction.scala | 6 +- .../norm/capture/AvoidAliasConflict.scala | 29 +++++---- .../getquill/norm/capture/AvoidCapture.scala | 5 +- .../io/getquill/norm/capture/Dealias.scala | 30 +++++----- 11 files changed, 133 insertions(+), 39 deletions(-) create mode 100644 quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala diff --git a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala new file mode 100644 index 0000000000..2f58e5e248 --- /dev/null +++ b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala @@ -0,0 +1,60 @@ +package io.getquill + +import io.getquill.ast.{Ast, Query, StatefulTransformer} + +trait StatefulCache[State] { + def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) + def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) +} +object StatefulCache { + class NoCache[State] extends StatefulCache[State] { + override def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) = default + override def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) = default + } + object NoCache { + def apply[State]() = new NoCache[State] + } +} + +trait StatelessCache { + def getOrCache(q: Ast): Ast + def getOrCache(q: Query): Query +} + +trait HasStatelessCache { + def cache: StatelessCache + def cached(q: Ast)(default: => Ast): Ast = cache.getOrCache(q) + def cached(q: Query)(default: => Query): Query = cache.getOrCache(q) +} + +trait StatelessCacheOpt { + def getOrCache(q: Ast): Option[Ast] + def getOrCache(q: Query): Option[Query] +} + +trait HasStatelessCacheOpt { + def cache: StatelessCacheOpt + def cached(q: Ast)(default: => Option[Ast]): Option[Ast] = cache.getOrCache(q) + def cached(q: Query)(default: => Option[Query]): Option[Query] = cache.getOrCache(q) +} + +trait HasStatefulCache[T] { + def cache: StatefulCache[T] + def state: T + + def cached(q: Ast)(default: => (Ast, StatefulTransformer[T])): (Ast, StatefulTransformer[T]) = + cache.getOrCache( + q, + this.state, + // If cache is a miss, evaluate "default" and write it together with the state to the cache + default + ) + + def cached(q: Query)(default: => (Query, StatefulTransformer[T])): (Query, StatefulTransformer[T]) = + cache.getOrCache( + q, + this.state, + // If cache is a miss, evaluate "default" and write it together with the state to the cache + default + ) +} diff --git a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala index 64135919cf..dc19bb0fff 100644 --- a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala +++ b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala @@ -1,10 +1,16 @@ package io.getquill.ast -trait StatefulTransformer[T] { +import io.getquill.{HasStatefulCache, NoCache, StatefulCache} + +trait StatefulTransformer[T] extends HasStatefulCache[T] { val state: T - def apply(e: Ast): (Ast, StatefulTransformer[T]) = + private val defaultCache = new NoCache[T] + // Override this value in implementations in order to use caching + override def cache: StatefulCache[T] = defaultCache + + def apply(e: Ast): (Ast, StatefulTransformer[T]) = cached(e) { e match { case e: Query => apply(e) case e: Operation => apply(e) @@ -52,6 +58,7 @@ trait StatefulTransformer[T] { case o: Ordering => (o, this) } + } def apply(o: OptionOperation): (OptionOperation, StatefulTransformer[T]) = o match { @@ -146,7 +153,7 @@ trait StatefulTransformer[T] { (ListContains(at, ct), ctt) } - def apply(e: Query): (Query, StatefulTransformer[T]) = + def apply(e: Query): (Query, StatefulTransformer[T]) = cached(e) { e match { case e: Entity => (e, this) case Filter(a, b, c) => @@ -217,6 +224,7 @@ trait StatefulTransformer[T] { val (at, att) = apply(a) (Nested(at), att) } + } def apply(e: Assignment): (Assignment, StatefulTransformer[T]) = e match { diff --git a/quill-engine/src/main/scala/io/getquill/norm/AdHocReduction.scala b/quill-engine/src/main/scala/io/getquill/norm/AdHocReduction.scala index 800aee833b..35e3e58c2f 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/AdHocReduction.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/AdHocReduction.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{HasStatelessCacheOpt, StatelessCache, StatelessCacheOpt} import io.getquill.ast.BinaryOperation import io.getquill.ast.BooleanOperator import io.getquill.ast.Filter @@ -10,9 +11,9 @@ import io.getquill.ast.Union import io.getquill.ast.UnionAll import io.getquill.util.TraceConfig -class AdHocReduction(traceConfig: TraceConfig) { +class AdHocReduction(val cache: StatelessCacheOpt, traceConfig: TraceConfig) extends HasStatelessCacheOpt { - def unapply(q: Query) = + def unapply(q: Query) = cached(q) { q match { // --------------------------- @@ -49,5 +50,5 @@ class AdHocReduction(traceConfig: TraceConfig) { case other => None } - + } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala index 05d97cf020..2567aa6e0f 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{NoCache, StatefulCache} import io.getquill.ast.Ast import io.getquill.ast.Query import io.getquill.ast.StatelessTransformer @@ -11,6 +12,8 @@ import io.getquill.util.Messages.TraceType.Normalizations import scala.annotation.tailrec +// TODO cache this whole thing? (need to stablize lifts first) +// perhaps rename this to NormalizeUnsafe, the safe version always stablizes the lifts first class Normalize(config: TranspileConfig) extends StatelessTransformer { val traceConf = config.traceConfig @@ -19,7 +22,7 @@ class Normalize(config: TranspileConfig) extends StatelessTransformer { // These are the actual phases of core-normalization. Highlighting them as such. lazy val NormalizeReturningPhase = new NormalizeReturning(this) - lazy val DealiasPhase = new DealiasApply(traceConf) + lazy val DealiasPhase = new DealiasApply(traceConf, StatefulCache.NoCache()) lazy val AvoidAliasConflictPhase = new AvoidAliasConflictApply(traceConf) val NormalizeNestedStructuresPhase = new NormalizeNestedStructures(this) val SymbolicReductionPhase = new SymbolicReduction(traceConf) @@ -58,6 +61,7 @@ class Normalize(config: TranspileConfig) extends StatelessTransformer { } } + // TODO cache this? (need to stablize lifts first) @tailrec private def norm(q: Query): Query = q match { diff --git a/quill-engine/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala b/quill-engine/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala index a6ed275eb2..ac981fc2ff 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/NormalizeNestedStructures.scala @@ -1,10 +1,11 @@ package io.getquill.norm +import io.getquill.{HasStatelessCacheOpt, StatelessCacheOpt} import io.getquill.ast._ -class NormalizeNestedStructures(normalize: Normalize) { +class NormalizeNestedStructures(normalize: Normalize, val cache: StatelessCacheOpt) extends HasStatelessCacheOpt { - def unapply(q: Query): Option[Query] = + def unapply(q: Query): Option[Query] = cached(q) { q match { case e: Entity => None case Map(a, b, c) => apply(a, c)(Map(_, b, _)) @@ -37,6 +38,7 @@ class NormalizeNestedStructures(normalize: Normalize) { case (a, b, on) => Some(Join(t, a, b, iA, iB, on)) } } + } private def apply(a: Ast)(f: Ast => Query) = (normalize(a)) match { @@ -49,4 +51,5 @@ class NormalizeNestedStructures(normalize: Normalize) { case (`a`, `b`) => None case (a, b) => Some(f(a, b)) } + } diff --git a/quill-engine/src/main/scala/io/getquill/norm/OrderTerms.scala b/quill-engine/src/main/scala/io/getquill/norm/OrderTerms.scala index 1b371a082c..1389b03562 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/OrderTerms.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/OrderTerms.scala @@ -1,11 +1,12 @@ package io.getquill.norm +import io.getquill.{HasStatelessCacheOpt, StatelessCacheOpt} import io.getquill.ast._ import io.getquill.util.TraceConfig -class OrderTerms(traceConfig: TraceConfig) { +class OrderTerms(val cache: StatelessCacheOpt, traceConfig: TraceConfig) extends HasStatelessCacheOpt { - def unapply(q: Query) = + def unapply(q: Query) = cached(q) { q match { case Take(Map(a: GroupBy, b, c), d) => None @@ -27,4 +28,5 @@ class OrderTerms(traceConfig: TraceConfig) { case other => None } + } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/StabilizeLifts.scala b/quill-engine/src/main/scala/io/getquill/norm/StabilizeLifts.scala index 84a73345ec..727342f7ae 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/StabilizeLifts.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/StabilizeLifts.scala @@ -3,6 +3,8 @@ package io.getquill.norm import io.getquill.ast._ import scala.collection.immutable.{Map => IMap} +// TODO this needs to also stablize UIDs of ScalarTag etc... and produce maps of them +// TODO include this in the compile-time version of the transformations private[getquill] object StabilizeLifts { def stabilize(ast: Ast): (Ast, State) = { diff --git a/quill-engine/src/main/scala/io/getquill/norm/SymbolicReduction.scala b/quill-engine/src/main/scala/io/getquill/norm/SymbolicReduction.scala index 29c14768d5..690ee00f18 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/SymbolicReduction.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/SymbolicReduction.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{HasStatelessCacheOpt, StatelessCacheOpt} import io.getquill.ast.{Filter, FlatMap, Query, Union, UnionAll} import io.getquill.util.TraceConfig @@ -14,9 +15,9 @@ import io.getquill.util.TraceConfig * whereas in Quill they are characterized as list comprehensions i.e. * `P.flatMap(x => ...)`. */ -class SymbolicReduction(traceConfig: TraceConfig) { +class SymbolicReduction(val cache: StatelessCacheOpt, traceConfig: TraceConfig) extends HasStatelessCacheOpt { - def unapply(q: Query) = + def unapply(q: Query) = cached(q) { q match { /* @@ -68,4 +69,5 @@ class SymbolicReduction(traceConfig: TraceConfig) { case other => None } + } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala index 0ce9cee323..43babdea73 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala @@ -1,5 +1,6 @@ package io.getquill.norm.capture +import io.getquill.StatefulCache import io.getquill.ast.{Entity, Filter, FlatJoin, FlatMap, GroupBy, Ident, Join, Map, Query, SortBy, StatefulTransformer, _} import io.getquill.ast.Implicits._ import io.getquill.norm.{BetaReduction, Normalize} @@ -44,12 +45,15 @@ import scala.collection.immutable.Set * called once from the top-level inside `SqlNormalize` at the very end of the * transformation pipeline. */ -private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: Boolean, traceConfig: TraceConfig) +private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: Boolean, override val cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig) extends StatefulTransformer[Set[IdentName]] { val interp = new Interpolator(TraceType.AvoidAliasConflict, traceConfig, 3) import interp._ + def AvoidAliasConflict(state: Set[IdentName]) = + new AvoidAliasConflict(state, detemp, cache, traceConfig) + object Unaliased { private def isUnaliased(q: Ast): Boolean = @@ -94,7 +98,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B BetaReduction(body, alias -> fresh) }(pr => pr != body) - (f(query, fresh, pr), AvoidAliasConflict(state + fresh.idName, detemp, traceConfig)) + (f(query, fresh, pr), AvoidAliasConflict(state + fresh.idName)) }(_._1 != elem) private def applyBodies[T <: Query](pairs: List[(Ident, Ast)]): (List[(Ident, Ast)], List[IdentName]) = @@ -137,7 +141,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B val ((toId1, mapBody1), s2) = apply(mapId, mapBody)((_, _)) ( GroupByMap(q, byId1, byBody1, toId1, mapBody1), - new AvoidAliasConflict(s1.state ++ s2.state, detemp, traceConfig) + AvoidAliasConflict(s1.state ++ s2.state) ) case DistinctOn(Unaliased(q), x, p) => @@ -165,7 +169,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B applyBodies(List(m1.byAlias -> m1.byBody, m1.mapAlias -> m1.mapBody)) ( GroupByMap(m1.query, byAlias, byBody, mapAlias, mapBody), - new AvoidAliasConflict(newTrans.state ++ state, detemp, traceConfig) + AvoidAliasConflict(newTrans.state ++ state) ) case m @ DistinctOn(CanRealias(), _, _) => @@ -188,7 +192,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B }(_ != o) val (orr, orrt) = trace"Uncapturing Join: Recurse with state: ${brt.state} + $freshA + $freshB".andReturnIf { - AvoidAliasConflict(brt.state + freshA.idName + freshB.idName, detemp, traceConfig)(or) + AvoidAliasConflict(brt.state + freshA.idName + freshB.idName)(or) }(_._1 != or) (Join(t, ar, br, freshA, freshB, orr), orrt) @@ -204,7 +208,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B }(_ != o) val (orr, orrt) = trace"Uncapturing FlatJoin: Recurse with state: ${art.state} + $freshA".andReturnIf { - AvoidAliasConflict(art.state + freshA.idName, detemp, traceConfig)(or) + new AvoidAliasConflict(art.state + freshA.idName, detemp, cache, traceConfig)(or) }(_._1 != or) (FlatJoin(t, ar, freshA, orr), orrt) @@ -225,7 +229,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B }(_ != p) val (prr, t) = trace"Uncapture Apply Recurse".andReturnIf { - AvoidAliasConflict(state + fresh.idName, detemp, traceConfig)(pr) + new AvoidAliasConflict(state + fresh.idName, detemp, cache, traceConfig)(pr) }(_._1 != pr) (f(fresh, prr), t) @@ -275,7 +279,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B case ((body, state, newParams), param) => { val fresh = freshIdent(param) val pr = BetaReduction(body, param -> fresh) - val (prr, t) = AvoidAliasConflict(state + fresh.idName, false, traceConfig)(pr) + val (prr, t) = AvoidAliasConflict(state + fresh.idName, false, cache, traceConfig)(pr) (prr, t.state, newParams :+ fresh) } } @@ -285,14 +289,14 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B private def applyForeach(f: Foreach): Foreach = { val fresh = freshIdent(f.alias) val pr = BetaReduction(f.body, f.alias -> fresh) - val (prr, _) = AvoidAliasConflict(state + fresh.idName, false, traceConfig)(pr) + val (prr, _) = new AvoidAliasConflict(state + fresh.idName, false, cache, traceConfig)(pr) Foreach(f.query, fresh, prr) } } -private[getquill] class AvoidAliasConflictApply(traceConfig: TraceConfig) { +private[getquill] class AvoidAliasConflictApply(cache: StatefulCache[Set[Ident]], traceConfig: TraceConfig) { def apply(q: Query, detemp: Boolean = false): Query = - AvoidAliasConflict(Set[IdentName](), detemp, traceConfig)(q) match { + new AvoidAliasConflict(Set[IdentName](), detemp, cache, traceConfig)(q) match { case (q, _) => q } } @@ -321,6 +325,9 @@ private[getquill] object AvoidAliasConflict { def sanitizeVariables(f: Foreach, dangerousVariables: Set[IdentName], traceConfig: TraceConfig): Foreach = AvoidAliasConflict(dangerousVariables, false, traceConfig).applyForeach(f) + // TODO trying to move AvoidAliasConflict into the tests + // since it only does two things and we want less indirection. + // the interesting question is whether to pass a dealias cache into this function. def sanitizeQuery(q: Query, dangerousVariables: Set[IdentName], normalize: Normalize): Query = AvoidAliasConflict(dangerousVariables, false, normalize.traceConf).apply(q) match { // Propagate aliasing changes to the rest of the query diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala index ddbd6f32f2..6c12ba3d1b 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala @@ -1,10 +1,13 @@ package io.getquill.norm.capture +import io.getquill.NoCache import io.getquill.ast.Query import io.getquill.util.TraceConfig +// This is not actually used anywhere but there is a test that tests +// the general functionality of dealiasing. Move this to there object AvoidCapture { def apply(q: Query, traceConfig: TraceConfig): Query = - Dealias(AvoidAliasConflict(q, false, traceConfig))(traceConfig) + Dealias(AvoidAliasConflict(q, false, traceConfig))(traceConfig, new NoCache) } diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/Dealias.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/Dealias.scala index 5426511852..a327c2dd4b 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/Dealias.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/Dealias.scala @@ -1,11 +1,13 @@ package io.getquill.norm.capture +import io.getquill.StatefulCache import io.getquill.ast._ import io.getquill.norm.BetaReduction import io.getquill.util.{Interpolator, TraceConfig} import io.getquill.util.Messages.TraceType -case class Dealias(state: Option[Ident], traceConfig: TraceConfig) extends StatefulTransformer[Option[Ident]] { +case class Dealias(state: Option[Ident], traceConfig: TraceConfig, override val cache: StatefulCache[Option[Ident]]) extends StatefulTransformer[Option[Ident]] { + def Dealias(state: Option[Ident]) = new Dealias(state, traceConfig, cache) val interp = new Interpolator(TraceType.Standard, traceConfig, 3) import interp._ @@ -34,12 +36,12 @@ case class Dealias(state: Option[Ident], traceConfig: TraceConfig) extends State dealias(a, b, c)(GroupBy) case g @ GroupByMap(qry, b, c, d, e) => apply(qry) match { - case (an, t @ Dealias(Some(alias), traceConfig)) => + case (an, t @ Dealias(Some(alias), _, _)) => val b1 = alias.copy(quat = b.quat) val d1 = alias.copy(quat = d.quat) (GroupByMap(an, b1, BetaReduction(c, b -> b1), d1, BetaReduction(e, d -> d1)), t) case other => - (g, Dealias(Some(b), traceConfig)) + (g, Dealias(Some(b))) } case DistinctOn(a, b, c) => dealias(a, b, c)(DistinctOn) @@ -52,43 +54,43 @@ case class Dealias(state: Option[Ident], traceConfig: TraceConfig) extends State case Union(a, b) => val (an, _) = apply(a) val (bn, _) = apply(b) - (Union(an, bn), Dealias(None, traceConfig)) + (Union(an, bn), Dealias(None)) case UnionAll(a, b) => val (an, _) = apply(a) val (bn, _) = apply(b) - (UnionAll(an, bn), Dealias(None, traceConfig)) + (UnionAll(an, bn), Dealias(None)) case Join(t, a, b, iA, iB, o) => val ((an, iAn, on), _) = dealias(a, iA, o)((_, _, _)) val ((bn, iBn, onn), _) = dealias(b, iB, on)((_, _, _)) - (Join(t, an, bn, iAn, iBn, onn), Dealias(None, traceConfig)) + (Join(t, an, bn, iAn, iBn, onn), Dealias(None)) case FlatJoin(t, a, iA, o) => val ((an, iAn, on), ont) = dealias(a, iA, o)((_, _, _)) - (FlatJoin(t, an, iAn, on), Dealias(Some(iA), traceConfig)) + (FlatJoin(t, an, iAn, on), Dealias(Some(iA))) case _: Entity | _: Distinct | _: Aggregation | _: Nested => - (q, Dealias(None, traceConfig)) + (q, Dealias(None)) } private def dealias[T](a: Ast, b: Ident, c: Ast)(f: (Ast, Ident, Ast) => T) = apply(a) match { - case (an, t @ Dealias(Some(alias), traceConfig)) => + case (an, t @ Dealias(Some(alias), _, _)) => val retypedAlias = alias.copy(quat = b.quat) trace"Dealias $b into $retypedAlias".andLog() (f(an, retypedAlias, BetaReduction(c, b -> retypedAlias)), t) case other => - (f(a, b, c), Dealias(Some(b), traceConfig)) + (f(a, b, c), Dealias(Some(b))) } } object Dealias { - def apply(query: Query)(traceConfig: TraceConfig) = - new Dealias(None, traceConfig)(query) match { + def apply(query: Query)(traceConfig: TraceConfig, cache: StatefulCache[Option[Ident]]) = + new Dealias(None, traceConfig, cache)(query) match { case (q, _) => q } } -class DealiasApply(traceConfig: TraceConfig) { +class DealiasApply(traceConfig: TraceConfig, cache: StatefulCache[Option[Ident]]) { def apply(query: Query) = - new Dealias(None, traceConfig)(query) match { + new Dealias(None, traceConfig, cache)(query) match { case (q, _) => q } } From 26b8d31173a2e9932e164bd510c201df37b7bfbd Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Sun, 8 Dec 2024 17:14:34 -0500 Subject: [PATCH 09/14] continue --- .../scala/io/getquill/HasStatefulCache.scala | 1 + .../scala/io/getquill/norm/Normalize.scala | 38 ++++++++++++------- .../norm/capture/AvoidAliasConflict.scala | 2 +- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala index 2f58e5e248..2c0ac09abc 100644 --- a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala +++ b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala @@ -14,6 +14,7 @@ object StatefulCache { object NoCache { def apply[State]() = new NoCache[State] } + } trait StatelessCache { diff --git a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala index 2567aa6e0f..d08ddb5b60 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala @@ -1,20 +1,28 @@ package io.getquill.norm -import io.getquill.{NoCache, StatefulCache} -import io.getquill.ast.Ast -import io.getquill.ast.Query -import io.getquill.ast.StatelessTransformer +import io.getquill.{HasStatelessCache, StatefulCache, StatelessCache, StatelessCacheOpt} +import io.getquill.ast.{Action, Ast, Ident, IdentName, Query, StatelessTransformer} import io.getquill.norm.capture.{AvoidAliasConflictApply, DealiasApply} -import io.getquill.ast.Action import io.getquill.util.Interpolator import io.getquill.util.Messages.{TraceType, title, trace} import io.getquill.util.Messages.TraceType.Normalizations import scala.annotation.tailrec +case class NormalizeCaches( + mainCache: StatelessCache, + dealiasCache: StatefulCache[Option[Ident]], + avoidAliasCache: StatefulCache[Set[IdentName]], + normalizeNestedCache: StatelessCacheOpt, + symbolicReductionCache: StatelessCacheOpt, + adHocReductionCache: StatelessCacheOpt, + orderTermsCache: StatelessCacheOpt +) + // TODO cache this whole thing? (need to stablize lifts first) // perhaps rename this to NormalizeUnsafe, the safe version always stablizes the lifts first -class Normalize(config: TranspileConfig) extends StatelessTransformer { +class Normalize(caches: NormalizeCaches, config: TranspileConfig) extends StatelessTransformer with HasStatelessCache { + val cache: StatelessCache = caches.mainCache val traceConf = config.traceConfig val interp = new Interpolator(TraceType.Normalizations, traceConf, 1) @@ -22,22 +30,24 @@ class Normalize(config: TranspileConfig) extends StatelessTransformer { // These are the actual phases of core-normalization. Highlighting them as such. lazy val NormalizeReturningPhase = new NormalizeReturning(this) - lazy val DealiasPhase = new DealiasApply(traceConf, StatefulCache.NoCache()) - lazy val AvoidAliasConflictPhase = new AvoidAliasConflictApply(traceConf) - val NormalizeNestedStructuresPhase = new NormalizeNestedStructures(this) - val SymbolicReductionPhase = new SymbolicReduction(traceConf) - val AdHocReductionPhase = new AdHocReduction(traceConf) - val OrderTermsPhase = new OrderTerms(traceConf) + lazy val DealiasPhase = new DealiasApply(traceConf, caches.dealiasCache) + lazy val AvoidAliasConflictPhase = new AvoidAliasConflictApply(caches.avoidAliasCache, traceConf) + val NormalizeNestedStructuresPhase = new NormalizeNestedStructures(this, caches.normalizeNestedCache) + val SymbolicReductionPhase = new SymbolicReduction(caches.symbolicReductionCache, traceConf) + val AdHocReductionPhase = new AdHocReduction(caches.adHocReductionCache, traceConf) + val OrderTermsPhase = new OrderTerms(caches.orderTermsCache, traceConf) - override def apply(q: Ast): Ast = + override def apply(q: Ast): Ast = cached(q) { super.apply(BetaReduction(q)) + } override def apply(q: Action): Action = NormalizeReturningPhase(super.apply(q)) - override def apply(q: Query): Query = + override def apply(q: Query): Query = cached(q) { trace"Avoid Capture and Normalize $q into:" andReturn norm(DealiasPhase(AvoidAliasConflictPhase(q, false))) + } private def traceNorm[T](label: String) = trace[T](s"${label} (Normalize)", 1, Normalizations) diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala index 43babdea73..23b368b6cf 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala @@ -294,7 +294,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B } } -private[getquill] class AvoidAliasConflictApply(cache: StatefulCache[Set[Ident]], traceConfig: TraceConfig) { +private[getquill] class AvoidAliasConflictApply(cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig) { def apply(q: Query, detemp: Boolean = false): Query = new AvoidAliasConflict(Set[IdentName](), detemp, cache, traceConfig)(q) match { case (q, _) => q From c122594ce52a2c54edcc0eca9b4a2c3861b42376 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Sun, 8 Dec 2024 21:55:00 -0500 Subject: [PATCH 10/14] continue --- .../io/getquill/norm/AdHocReductionSpec.scala | 3 +- .../norm/NormalizeAggregationIdentSpec.scala | 2 +- .../getquill/norm/NormalizeCachingSpec.scala | 2 +- .../norm/NormalizeNestedStructuresSpec.scala | 3 +- .../io/getquill/norm/NormalizeSpec.scala | 2 +- .../io/getquill/norm/OrderTermsSpec.scala | 3 +- .../getquill/norm/SymbolicReductionSpec.scala | 3 +- .../AvoidAliasConflictComplexSpec.scala | 4 +- .../norm/capture/AvoidAliasConflictSpec.scala | 4 +- .../norm/capture/AvoidCaptureSpec.scala | 3 +- .../getquill/norm/capture/DealiasSpec.scala | 3 +- .../scala/io/getquill/HasStatefulCache.scala | 118 ++++++++++++++++-- .../main/scala/io/getquill/MirrorIdiom.scala | 7 +- .../io/getquill/ast/StatefulTransformer.scala | 3 +- .../context/cassandra/CqlNormalize.scala | 7 +- .../scala/io/getquill/norm/Normalize.scala | 23 ++++ .../norm/capture/AvoidAliasConflict.scala | 18 +-- .../getquill/norm/capture/AvoidCapture.scala | 9 +- .../io/getquill/sql/norm/SqlNormalize.scala | 11 +- 19 files changed, 183 insertions(+), 45 deletions(-) diff --git a/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala index e3053b98cf..886d895825 100644 --- a/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala @@ -5,11 +5,12 @@ import io.getquill.MirrorContexts.testContext.qr1 import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.{StatelessCache, StatelessCacheOpt} import io.getquill.util.TraceConfig class AdHocReductionSpec extends Spec { - val AdHocReduction = new AdHocReduction(TraceConfig.Empty) + val AdHocReduction = new AdHocReduction(StatelessCacheOpt.NoCache(), TraceConfig.Empty) "*.filter" - { "a.filter(b => c).filter(d => e)" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala index dfe5c9001b..fb78e6f037 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala @@ -15,6 +15,6 @@ class NormalizeAggregationIdentSpec extends Spec { p._1 -> p._2.map(x1 => x1.l).sum } } - new Normalize(TranspileConfig.Empty)(q.ast) mustEqual (n.ast) + new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty)(q.ast) mustEqual (n.ast) } } diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala index 88417f0b67..c24a2c00d0 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala @@ -4,7 +4,7 @@ import io.getquill.base.Spec class NormalizeCachingSpec extends Spec { - val normalize = new Normalize(TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) val cached = NormalizeCaching(normalize.apply) val gen = new QueryGenerator(1) diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala index e32e69adbb..5bae7f30f9 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala @@ -6,6 +6,7 @@ import io.getquill.MirrorContexts.testContext.qr1 import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.StatelessCacheOpt class NormalizeNestedStructuresSpec extends Spec { @@ -17,7 +18,7 @@ class NormalizeNestedStructuresSpec extends Spec { qr1.take(1).map(x => x.i).size } - val NormalizeNestedStructures = new NormalizeNestedStructures(new Normalize(TranspileConfig.Empty)) + val NormalizeNestedStructures = new NormalizeNestedStructures(new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty), StatelessCacheOpt.NoCache()) "returns Some if a nested structure changes" - { "flatMap" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala index 6c3bf54c67..7826fd898e 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala @@ -9,7 +9,7 @@ import io.getquill.MirrorContexts.testContext.unquote class NormalizeSpec extends Spec { - val normalize = new Normalize(TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) "normalizes random-generated queries" - { val gen = new QueryGenerator(1) diff --git a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala index 5486a119b6..38ed50395c 100644 --- a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala @@ -6,11 +6,12 @@ import io.getquill.MirrorContexts.testContext.qr1 import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.StatelessCacheOpt import io.getquill.util.TraceConfig class OrderTermsSpec extends Spec { - val OrderTerms = new OrderTerms(TraceConfig.Empty) + val OrderTerms = new OrderTerms(StatelessCacheOpt.NoCache(), TraceConfig.Empty) "doesn't reorder groupBy.map" in { val q = quote { diff --git a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala index c2bb9a11bd..223e8afdc1 100644 --- a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala @@ -6,11 +6,12 @@ import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.qr3 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.StatelessCacheOpt import io.getquill.util.TraceConfig class SymbolicReductionSpec extends Spec { // hello - def symbolicReduction = (new SymbolicReduction(TraceConfig.Empty).unapply _).andThen(o => o.map(replaceTempIdent(_))) + def symbolicReduction = (new SymbolicReduction(StatelessCacheOpt.NoCache(), TraceConfig.Empty).unapply _).andThen(o => o.map(replaceTempIdent(_))) "a.filter(b => c).flatMap(d => e.$)" - { "e is an entity" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala index 7cbce54fb5..46d5d730dd 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala @@ -3,11 +3,11 @@ package io.getquill.norm.capture import io.getquill.MirrorContexts.testContext._ import io.getquill.Query import io.getquill.base.Spec -import io.getquill.norm.{Normalize, TranspileConfig} +import io.getquill.norm.{Normalize, NormalizeCaches, TranspileConfig} class AvoidAliasConflictComplexSpec extends Spec { - val normalize = new Normalize(TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) "properly aliases explicit join sets" - { import io.getquill.norm.Normalize diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala index 39a8d9f9d8..d85df2e67c 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala @@ -1,13 +1,13 @@ package io.getquill.norm.capture import io.getquill.MirrorContexts.testContext._ -import io.getquill.Query +import io.getquill.{Query, StatefulCache} import io.getquill.base.Spec import io.getquill.util.TraceConfig class AvoidAliasConflictSpec extends Spec { - val AvoidAliasConflict = new AvoidAliasConflictApply(TraceConfig.Empty) + val AvoidAliasConflict = new AvoidAliasConflictApply(StatefulCache.NoCache(), TraceConfig.Empty) "renames alias to avoid conflict between entities during normalization" - { "flatMap" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala index 689c71f115..dd1f8fa528 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala @@ -6,6 +6,7 @@ import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.qr3 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.StatefulCache import io.getquill.util.TraceConfig class AvoidCaptureSpec extends Spec { @@ -17,6 +18,6 @@ class AvoidCaptureSpec extends Spec { val n = quote { qr1.filter(u => u.s == "s1").flatMap(u => qr2.filter(u1 => u1.s == "s1")).flatMap(u1 => qr3.map(u2 => u2.s)) } - AvoidCapture(q.ast, TraceConfig(List.empty)) mustEqual n.ast + AvoidCapture(q.ast, StatefulCache.NoCache(), TraceConfig(List.empty)) mustEqual n.ast } } diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala index 514b39f685..224c274386 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala @@ -6,11 +6,12 @@ import io.getquill.MirrorContexts.testContext.qr1 import io.getquill.MirrorContexts.testContext.qr2 import io.getquill.MirrorContexts.testContext.quote import io.getquill.MirrorContexts.testContext.unquote +import io.getquill.StatefulCache import io.getquill.util.TraceConfig class DealiasSpec extends Spec { - val Dealias = new DealiasApply(TraceConfig.Empty) + val Dealias = new DealiasApply(TraceConfig.Empty, StatefulCache.NoCache()) "ensures that each entity is referenced by the same alias" - { "flatMap" in { diff --git a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala index 2c0ac09abc..a9cd1238ae 100644 --- a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala +++ b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala @@ -1,6 +1,9 @@ package io.getquill import io.getquill.ast.{Ast, Query, StatefulTransformer} +import io.getquill.quat.Quat + +import scala.collection.mutable trait StatefulCache[State] { def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) @@ -14,29 +17,126 @@ object StatefulCache { object NoCache { def apply[State]() = new NoCache[State] } - + // Make sure to store the Quat of the Ast since we don't want to cache the same Ast with different Quats + // e.g. Cache ident(x, Quat(Person)) should not be the same as ident(x, Quat(Address)) in the caching process + // because incorrect queries will be generated. + case class Unlimited[State]( + // TODO rebuild and try again + astMap: mutable.Map[(Ast, Quat, State), (Ast, StatefulTransformer[State])] = mutable.Map.empty[(Ast, Quat), (Ast, StatefulTransformer[State])], + queryMap: mutable.Map[(Query, Quat, State), (Query, StatefulTransformer[State])] = mutable.Map.empty[(Query, Quat), (Query, StatefulTransformer[State])] + ) extends StatefulCache[State] { + override def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) = { + val key = (q, q.quat, s) + astMap.get(key) match { + case Some(value) => value + case None => + val (ast, transformer) = default + astMap.put(key, (ast, transformer)) + (ast, transformer) + } + } + override def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) = { + val key = (q, q.quat) + queryMap.get(key) match { + case Some(value) => value + case None => + val (query, transformer) = default + queryMap.put(key, (query, transformer)) + (query, transformer) + } + } + } } trait StatelessCache { - def getOrCache(q: Ast): Ast - def getOrCache(q: Query): Query + def getOrCache(q: Ast, default: => Ast): Ast + def getOrCache(q: Query, default: => Query): Query +} +object StatelessCache { + class NoCache extends StatelessCache { + override def getOrCache(q: Ast, default: => Ast): Ast = default + override def getOrCache(q: Query, default: => Query): Query = default + } + object NoCache { + def apply() = new NoCache + } + case class Unlimited( + astCache: mutable.Map[(Ast, Quat), Ast] = mutable.Map.empty, + queryCache: mutable.Map[(Query, Quat), Query] = mutable.Map.empty + ) extends StatelessCache { + override def getOrCache(q: Ast, default: => Ast): Ast = { + val key = (q, q.quat) + astCache.get(key) match { + case Some(value) => value + case None => + val defaultVal = default + astCache.put(key, defaultVal) + defaultVal + } + } + override def getOrCache(q: Query, default: => Query): Query = { + val key = (q, q.quat) + queryCache.get(key) match { + case Some(value) => value + case None => + val defaultVal = default + queryCache.put(key, defaultVal) + defaultVal + } + } + } } trait HasStatelessCache { def cache: StatelessCache - def cached(q: Ast)(default: => Ast): Ast = cache.getOrCache(q) - def cached(q: Query)(default: => Query): Query = cache.getOrCache(q) + def cached(q: Ast)(default: => Ast): Ast = cache.getOrCache(q, default) + def cached(q: Query)(default: => Query): Query = cache.getOrCache(q, default) } trait StatelessCacheOpt { - def getOrCache(q: Ast): Option[Ast] - def getOrCache(q: Query): Option[Query] + def getOrCache(q: Ast, default: => Option[Ast]): Option[Ast] + def getOrCache(q: Query, default: => Option[Query]): Option[Query] +} +object StatelessCacheOpt { + class NoCache extends StatelessCacheOpt { + override def getOrCache(q: Ast, default: => Option[Ast]): Option[Ast] = default + override def getOrCache(q: Query, default: => Option[Query]): Option[Query] = default + } + object NoCache { + def apply() = new NoCache + } + case class Unlimited( + astCache: mutable.Map[(Ast, Quat), Option[Ast]] = mutable.Map.empty, + queryCache: mutable.Map[(Query, Quat), Option[Query]] = mutable.Map.empty + ) extends StatelessCacheOpt { + override def getOrCache(q: Ast, default: => Option[Ast]): Option[Ast] = { + val key = (q, q.quat) + astCache.get(key) match { + case Some(value) => value + case None => + val defaultVal = default + astCache.put(key, defaultVal) + defaultVal + } + } + + override def getOrCache(q: Query, default: => Option[Query]): Option[Query] = { + val key = (q, q.quat) + queryCache.get(key) match { + case Some(value) => value + case None => + val defaultVal = default + queryCache.put(key, defaultVal) + defaultVal + } + } + } } trait HasStatelessCacheOpt { def cache: StatelessCacheOpt - def cached(q: Ast)(default: => Option[Ast]): Option[Ast] = cache.getOrCache(q) - def cached(q: Query)(default: => Option[Query]): Option[Query] = cache.getOrCache(q) + def cached(q: Ast)(default: => Option[Ast]): Option[Ast] = cache.getOrCache(q, default) + def cached(q: Query)(default: => Option[Query]): Option[Query] = cache.getOrCache(q, default) } trait HasStatefulCache[T] { diff --git a/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala b/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala index 4535f007a2..e405059a7d 100644 --- a/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala @@ -6,7 +6,7 @@ import io.getquill.ast.{Action => AstAction, Query => AstQuery, _} import io.getquill.context.{CanReturnClause, ExecutionType} import io.getquill.idiom.StatementInterpolator._ import io.getquill.idiom.{Idiom, SetContainsToken, Statement, Token} -import io.getquill.norm.{Normalize, NormalizeCaching} +import io.getquill.norm.{Normalize, NormalizeCaches, NormalizeCaching} import io.getquill.quat.Quat import io.getquill.util.Interleave @@ -33,7 +33,8 @@ trait MirrorIdiomBase extends Idiom { )(implicit naming: NamingStrategy ): (Ast, Statement, ExecutionType) = { - val normalize = new Normalize(idiomContext.config) + // plugging in NormalizeCaches.noCache() into `Normalize` because NormalizeCaching does that work + val normalize = new Normalize(NormalizeCaches.noCache(), idiomContext.config) val normalizedAst = NormalizeCaching(normalize.apply)(ast) (normalizedAst, stmt"${normalizedAst.token}", executionType) } @@ -41,7 +42,7 @@ trait MirrorIdiomBase extends Idiom { override final def translate(ast: Ast, topLevelQuat: Quat, executionType: ExecutionType, idiomContext: IdiomContext)( implicit naming: NamingStrategy ): (Ast, Statement, ExecutionType) = { - val normalize = new Normalize(idiomContext.config) + val normalize = new Normalize(NormalizeCaches.unlimitedCache(), idiomContext.config) val normalizedAst = normalize(ast) (normalizedAst, stmt"${normalizedAst.token}", executionType) } diff --git a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala index dc19bb0fff..e7499b4e62 100644 --- a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala +++ b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala @@ -1,6 +1,7 @@ package io.getquill.ast -import io.getquill.{HasStatefulCache, NoCache, StatefulCache} +import io.getquill.StatefulCache.NoCache +import io.getquill.{HasStatefulCache, StatefulCache} trait StatefulTransformer[T] extends HasStatefulCache[T] { diff --git a/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala index d207728635..259c604372 100644 --- a/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala @@ -1,14 +1,15 @@ package io.getquill.context.cassandra +import io.getquill.StatefulCache import io.getquill.ast._ import io.getquill.norm.ConcatBehavior.AnsiConcat import io.getquill.norm.EqualityBehavior.AnsiEquality import io.getquill.norm.capture.AvoidAliasConflict -import io.getquill.norm.{FlattenOptionOperation, Normalize, RenameProperties, SimplifyNullChecks, TranspileConfig} +import io.getquill.norm.{FlattenOptionOperation, Normalize, NormalizeCaches, RenameProperties, SimplifyNullChecks, TranspileConfig} import io.getquill.quat.Quat class CqlNormalize(transpileConfig: TranspileConfig) { - val NormalizePhase = new Normalize(transpileConfig) + val NormalizePhase = new Normalize(NormalizeCaches.noCache(), transpileConfig) def apply(ast: Ast) = normalize(ast) @@ -42,6 +43,6 @@ class CqlNormalize(transpileConfig: TranspileConfig) { .andThen { ast => // In the final stage of normalization, change all temporary aliases into // shorter ones of the form x[0-9]+. - NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, transpileConfig.traceConfig)) + NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, StatefulCache.NoCache(), transpileConfig.traceConfig)) } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala index d08ddb5b60..7638c4d008 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala @@ -18,6 +18,29 @@ case class NormalizeCaches( adHocReductionCache: StatelessCacheOpt, orderTermsCache: StatelessCacheOpt ) +object NormalizeCaches { + def noCache() = + NormalizeCaches( + StatelessCache.NoCache(), + StatefulCache.NoCache[Option[Ident]](), + StatefulCache.NoCache[Set[IdentName]](), + StatelessCacheOpt.NoCache(), + StatelessCacheOpt.NoCache(), + StatelessCacheOpt.NoCache(), + StatelessCacheOpt.NoCache() + ) + + def unlimitedCache() = + NormalizeCaches( + StatelessCache.NoCache(), + StatefulCache.Unlimited[Option[Ident]](), + StatefulCache.Unlimited[Set[IdentName]](), + StatelessCacheOpt.Unlimited(), + StatelessCacheOpt.Unlimited(), + StatelessCacheOpt.Unlimited(), + StatelessCacheOpt.Unlimited() + ) +} // TODO cache this whole thing? (need to stablize lifts first) // perhaps rename this to NormalizeUnsafe, the safe version always stablizes the lifts first diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala index 23b368b6cf..a1dbc0595b 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala @@ -51,7 +51,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B val interp = new Interpolator(TraceType.AvoidAliasConflict, traceConfig, 3) import interp._ - def AvoidAliasConflict(state: Set[IdentName]) = + def AvoidAliasConflict(state: Set[IdentName], detemp: Boolean = this.detemp) = new AvoidAliasConflict(state, detemp, cache, traceConfig) object Unaliased { @@ -279,7 +279,7 @@ private[getquill] case class AvoidAliasConflict(state: Set[IdentName], detemp: B case ((body, state, newParams), param) => { val fresh = freshIdent(param) val pr = BetaReduction(body, param -> fresh) - val (prr, t) = AvoidAliasConflict(state + fresh.idName, false, cache, traceConfig)(pr) + val (prr, t) = AvoidAliasConflict(state + fresh.idName, false)(pr) (prr, t.state, newParams :+ fresh) } } @@ -303,13 +303,13 @@ private[getquill] class AvoidAliasConflictApply(cache: StatefulCache[Set[IdentNa private[getquill] object AvoidAliasConflict { - def Ast(q: Ast, detemp: Boolean = false, traceConfig: TraceConfig): Ast = - new AvoidAliasConflict(Set[IdentName](), detemp, traceConfig)(q) match { + def Ast(q: Ast, detemp: Boolean = false, cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig): Ast = + new AvoidAliasConflict(Set[IdentName](), detemp, cache, traceConfig)(q) match { case (q, _) => q } - def apply(q: Query, detemp: Boolean = false, traceConfig: TraceConfig): Query = - AvoidAliasConflict(Set[IdentName](), detemp, traceConfig)(q) match { + def apply(q: Query, detemp: Boolean = false, cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig): Query = + AvoidAliasConflict(Set[IdentName](), detemp, cache, traceConfig)(q) match { case (q, _) => q } @@ -319,17 +319,17 @@ private[getquill] object AvoidAliasConflict { * transforming and queries encountered. */ def sanitizeVariables(f: Function, dangerousVariables: Set[IdentName], traceConfig: TraceConfig): Function = - AvoidAliasConflict(dangerousVariables, false, traceConfig).applyFunction(f) + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), traceConfig).applyFunction(f) /** Same is `sanitizeVariables` but for Foreach * */ def sanitizeVariables(f: Foreach, dangerousVariables: Set[IdentName], traceConfig: TraceConfig): Foreach = - AvoidAliasConflict(dangerousVariables, false, traceConfig).applyForeach(f) + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), traceConfig).applyForeach(f) // TODO trying to move AvoidAliasConflict into the tests // since it only does two things and we want less indirection. // the interesting question is whether to pass a dealias cache into this function. def sanitizeQuery(q: Query, dangerousVariables: Set[IdentName], normalize: Normalize): Query = - AvoidAliasConflict(dangerousVariables, false, normalize.traceConf).apply(q) match { + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), normalize.traceConf).apply(q) match { // Propagate aliasing changes to the rest of the query case (q, _) => normalize(q) } diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala index 6c12ba3d1b..cc6b0658a1 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala @@ -1,13 +1,14 @@ package io.getquill.norm.capture -import io.getquill.NoCache -import io.getquill.ast.Query +import io.getquill.StatefulCache +import io.getquill.StatefulCache.NoCache +import io.getquill.ast.{IdentName, Query} import io.getquill.util.TraceConfig // This is not actually used anywhere but there is a test that tests // the general functionality of dealiasing. Move this to there object AvoidCapture { - def apply(q: Query, traceConfig: TraceConfig): Query = - Dealias(AvoidAliasConflict(q, false, traceConfig))(traceConfig, new NoCache) + def apply(q: Query, cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig): Query = + Dealias(AvoidAliasConflict(q, false, cache, traceConfig))(traceConfig, NoCache()) } diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala index 9d1622f686..2e51a523ee 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala @@ -24,7 +24,8 @@ class SqlNormalize( transpileConfig: TranspileConfig ) { - val NormalizePhase = new Normalize(transpileConfig) + val caches = NormalizeCaches.unlimitedCache() + val NormalizePhase = new Normalize(caches, transpileConfig) val traceConfig = transpileConfig.traceConfig private def demarcate(heading: String) = @@ -68,9 +69,13 @@ class SqlNormalize( .andThen { ast => // In the final stage of normalization, change all temporary aliases into // shorter ones of the form x[0-9]+. - NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, transpileConfig.traceConfig)) + NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, caches.avoidAliasCache, transpileConfig.traceConfig)) } .andThen(demarcate("Normalize")) - def apply(ast: Ast) = normalize(ast) + def apply(ast: Ast) = { + val (stableAst, state) = StabilizeLifts.stabilize(ast) + val outputAst = normalize(stableAst) + StabilizeLifts.revert(outputAst, state) + } } From 898b96c960104c6fb0bafb5d403a5f54231243ba Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Tue, 10 Dec 2024 20:31:52 -0500 Subject: [PATCH 11/14] continue --- .../context/ContextVerbTranslate.scala | 6 +- .../io/getquill/norm/AdHocReductionSpec.scala | 2 +- .../norm/FlattenOptionOperationSpec.scala | 87 +++++------ .../norm/NormalizeAggregationIdentSpec.scala | 2 +- .../getquill/norm/NormalizeCachingSpec.scala | 2 +- .../norm/NormalizeNestedStructuresSpec.scala | 2 +- .../io/getquill/norm/NormalizeSpec.scala | 2 +- .../io/getquill/norm/OrderTermsSpec.scala | 2 +- .../norm/SimplifyNullChecksSpec.scala | 5 +- .../getquill/norm/SymbolicReductionSpec.scala | 2 +- .../AvoidAliasConflictComplexSpec.scala | 2 +- .../norm/capture/AvoidAliasConflictSpec.scala | 2 +- .../norm/capture/AvoidCaptureSpec.scala | 2 +- .../getquill/norm/capture/DealiasSpec.scala | 2 +- .../scala/io/getquill/HasStatefulCache.scala | 34 ++--- .../main/scala/io/getquill/MirrorIdiom.scala | 4 +- .../io/getquill/ast/StatefulTransformer.scala | 2 +- .../io/getquill/context/RowContext.scala | 7 +- .../context/cassandra/CqlNormalize.scala | 12 +- .../norm/FlattenOptionOperation.scala | 143 +++++++++--------- .../scala/io/getquill/norm/Normalize.scala | 18 +-- .../io/getquill/norm/RenameProperties.scala | 9 +- .../io/getquill/norm/SimplifyNullChecks.scala | 6 +- .../norm/capture/AvoidAliasConflict.scala | 6 +- .../getquill/norm/capture/AvoidCapture.scala | 2 +- .../io/getquill/sql/norm/ExpandDistinct.scala | 6 +- .../io/getquill/sql/norm/ExpandJoin.scala | 7 +- .../io/getquill/sql/norm/SqlNormalize.scala | 38 ++++- .../context/sql/norm/ExpandDistinctSpec.scala | 4 +- .../context/sql/norm/ExpandJoinSpec.scala | 5 +- 30 files changed, 234 insertions(+), 189 deletions(-) diff --git a/quill-core/src/main/scala/io/getquill/context/ContextVerbTranslate.scala b/quill-core/src/main/scala/io/getquill/context/ContextVerbTranslate.scala index fb6772fb62..de17cbc333 100644 --- a/quill-core/src/main/scala/io/getquill/context/ContextVerbTranslate.scala +++ b/quill-core/src/main/scala/io/getquill/context/ContextVerbTranslate.scala @@ -109,7 +109,11 @@ trait ContextTranslateProto { )(executionInfo: ExecutionInfo, dc: Runner): List[String] = groups.flatMap { group => (group.prepare zip group.liftings).map { case (_, liftings) => - translateQuery(group.string, options = options, liftings = liftings)(executionInfo, dc) + if (liftings.forall(_.isInstanceOf[ScalarLift])) { + translateQuery(group.string, options = options, liftings = liftings.asInstanceOf[List[ScalarLift]])(executionInfo, dc) + } else { + throw new IllegalArgumentException("All liftings in Scala2-Quill must be ScalarLifts") + } } } diff --git a/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala index 886d895825..24f31de6b9 100644 --- a/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/AdHocReductionSpec.scala @@ -10,7 +10,7 @@ import io.getquill.util.TraceConfig class AdHocReductionSpec extends Spec { - val AdHocReduction = new AdHocReduction(StatelessCacheOpt.NoCache(), TraceConfig.Empty) + val AdHocReduction = new AdHocReduction(StatelessCacheOpt.NoCache, TraceConfig.Empty) "*.filter" - { "a.filter(b => c).filter(d => e)" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/FlattenOptionOperationSpec.scala b/quill-core/src/test/scala/io/getquill/norm/FlattenOptionOperationSpec.scala index 4cc67239cf..02c4d41970 100644 --- a/quill-core/src/test/scala/io/getquill/norm/FlattenOptionOperationSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/FlattenOptionOperationSpec.scala @@ -5,6 +5,7 @@ import io.getquill.MirrorContexts.testContext._ import io.getquill.ast.Implicits._ import io.getquill.norm.ConcatBehavior.{AnsiConcat, NonAnsiConcat} import io.getquill.MoreAstOps._ +import io.getquill.StatelessCache import io.getquill.base.Spec import io.getquill.util.TraceConfig @@ -24,7 +25,7 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.getOrElse(1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, o, c1) } "orElse" - { @@ -32,14 +33,14 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.orElse(Option(1)) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, o, c1) } "with forall" in { val q = quote { (o: Option[Int]) => o.orElse(Option(1)).forall(_ == 2) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((o +==+ c2) +||+ (IsNullCheck(o) +&&+ (c1 +==+ c2)) +||+ (IsNullCheck(o) +&&+ IsNullCheck(c1))) } @@ -47,7 +48,7 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.orElse(Option(1)).exists(_ == 2) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((o +==+ c2 +&&+ IsNotNullCheck(o)) +||+ (c1 +==+ c2 +&&+ IsNotNullCheck(c1))) } } @@ -56,27 +57,27 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Option[Int]]) => o.flatten.map(i => i + 1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 } "possible-fallthrough operation" in { val q = quote { (o: Option[Option[String]]) => o.flatten.map(s => s + "foo") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ Constant.auto("foo") - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExistElseNull(o, o +++ cFoo) } "never-fallthrough operation" in { val q = quote { (o: Option[Option[String]]) => o.flatten.map(s => if (s == "value") "foo" else "bar") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue) } } @@ -85,27 +86,27 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Option[Int]]) => o.flatMap(i => i.map(j => j + 1)) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 } "possible-fallthrough operation" in { val q = quote { (o: Option[Option[String]]) => o.flatMap(s => s.map(j => j + "foo")) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ cFoo - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExistElseNull(o, IfExistElseNull(o, o +++ cFoo)) } "never-fallthrough operation" in { val q = quote { (o: Option[Option[String]]) => o.flatMap(s => s.map(j => if (j == "value") "foo" else "bar")) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue), NullValue) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue), NullValue) } } @@ -113,34 +114,34 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Option[Row]]) => o.flatMap(i => i.map(j => j)) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o } "map" - { "regular operation" in { val q = quote { (o: Option[Int]) => o.map(i => i + 1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ c1 } "possible-fallthrough operation" in { val q = quote { (o: Option[String]) => o.map(s => s + "foo") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o +++ Constant.auto("foo") - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExistElseNull(o, o +++ cFoo) } "never-fallthrough operation" in { val q = quote { (o: Option[String]) => o.map(s => if (s == "value") "foo" else "bar") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual IfExist(o, If(o +==+ cValue, cFoo, cBar), NullValue) } } @@ -148,13 +149,13 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Row]) => o.map(i => i) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual o } "map + getOrElse(true)" in { val q = quote { (o: Option[Int]) => o.map(_ < 1).getOrElse(true) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)( + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)( q.ast.body: Ast ).toString mustEqual "((o < 1) && (o != null)) || (true && (o == null))" } @@ -162,7 +163,7 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.map(_ < 1).getOrElse(false) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)( + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)( q.ast.body: Ast ).toString mustEqual "((o < 1) && (o != null)) || (false && (o == null))" } @@ -171,31 +172,31 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.forall(i => i != 1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((o +!=+ c1) +||+ IsNullCheck(o)) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((o +!=+ c1) +||+ IsNullCheck(o)) } "possible-fallthrough operation" in { val q = quote { (o: Option[String]) => o.forall(s => s + "foo" == "bar") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (((o +++ cFoo) +==+ cBar) +||+ IsNullCheck(o)) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((((o +++ cFoo) +==+ cBar) +&&+ IsNotNullCheck(o)) +||+ IsNullCheck(o)) } "never-fallthrough operation" in { val q = quote { (o: Option[String]) => o.forall(s => if (s + "foo" == "bar") true else false) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((If( (o +++ cFoo) +==+ cBar, Constant.auto(true), Constant.auto(false) ) +&&+ IsNotNullCheck(o)) +||+ IsNullCheck(o)) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((If( (o +++ cFoo) +==+ cBar, Constant.auto(true), @@ -208,16 +209,16 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[TestEntity]) => o.map(_.i).forall(i => i != 1) && true } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((((Property(o, "i") +!=+ c1)) +||+ IsNullCheck(Property(o, "i"))) +&&+ Constant.auto(true)) } "possible-fallthrough operation" in { val q = quote { (o: Option[TestEntity2]) => o.map(_.s).forall(s => s + "foo" == "bar") && true } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((((Property(o, "s") +++ cFoo) +==+ cBar) +||+ IsNullCheck(Property(o, "s")) +&&+ Constant.auto(true))) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (((((Property(o, "s") +++ cFoo) +==+ cBar) +&&+ IsNotNullCheck(Property(o, "s"))) +||+ IsNullCheck(Property(o, "s"))) +&&+ Constant.auto(true)) } } @@ -226,27 +227,27 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Int]) => o.exists(i => i > 1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (o +>+ c1) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (o +>+ c1) } "possible-fallthrough operation" in { val q = quote { (o: Option[String]) => o.exists(s => s + "foo" == "bar") } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual ((o +++ cFoo) +==+ cBar) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (((o +++ cFoo) +==+ cBar) +&&+ IsNotNullCheck(o)) } "never-fallthrough operation" in { val q = quote { (o: Option[String]) => o.exists(s => if (s + "foo" == "bar") true else false) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (If((o +++ cFoo) +==+ cBar, Constant.auto(true), Constant.auto(false)) +&&+ IsNotNullCheck(o)) - new FlattenOptionOperation(NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, NonAnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (If((o +++ cFoo) +==+ cBar, Constant.auto(true), Constant.auto(false)) +&&+ IsNotNullCheck(o)) } } @@ -254,13 +255,13 @@ class FlattenOptionOperationSpec extends Spec { val q = quote { (o: Option[Row]) => o.exists(r => r.id != 1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (Property(o, "id") +!=+ c1) + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual (Property(o, "id") +!=+ c1) } "contains" in { val q = quote { (o: Option[Int]) => o.contains(1) } - new FlattenOptionOperation(AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual + new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, TraceConfig.Empty)(q.ast.body: Ast) mustEqual BinaryOperation(Ident("o"), EqualityOperator.`_==`, Constant.auto(1)) } } diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala index fb78e6f037..67f5319069 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeAggregationIdentSpec.scala @@ -15,6 +15,6 @@ class NormalizeAggregationIdentSpec extends Spec { p._1 -> p._2.map(x1 => x1.l).sum } } - new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty)(q.ast) mustEqual (n.ast) + new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty)(q.ast) mustEqual (n.ast) } } diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala index c24a2c00d0..4a4d069e64 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeCachingSpec.scala @@ -4,7 +4,7 @@ import io.getquill.base.Spec class NormalizeCachingSpec extends Spec { - val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty) val cached = NormalizeCaching(normalize.apply) val gen = new QueryGenerator(1) diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala index 5bae7f30f9..8c3ba32cef 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeNestedStructuresSpec.scala @@ -18,7 +18,7 @@ class NormalizeNestedStructuresSpec extends Spec { qr1.take(1).map(x => x.i).size } - val NormalizeNestedStructures = new NormalizeNestedStructures(new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty), StatelessCacheOpt.NoCache()) + val NormalizeNestedStructures = new NormalizeNestedStructures(new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty), StatelessCacheOpt.NoCache) "returns Some if a nested structure changes" - { "flatMap" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala b/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala index 7826fd898e..8ae8945b7d 100644 --- a/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/NormalizeSpec.scala @@ -9,7 +9,7 @@ import io.getquill.MirrorContexts.testContext.unquote class NormalizeSpec extends Spec { - val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty) "normalizes random-generated queries" - { val gen = new QueryGenerator(1) diff --git a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala index 38ed50395c..fc5aa878fe 100644 --- a/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/OrderTermsSpec.scala @@ -11,7 +11,7 @@ import io.getquill.util.TraceConfig class OrderTermsSpec extends Spec { - val OrderTerms = new OrderTerms(StatelessCacheOpt.NoCache(), TraceConfig.Empty) + val OrderTerms = new OrderTerms(StatelessCacheOpt.NoCache, TraceConfig.Empty) "doesn't reorder groupBy.map" in { val q = quote { diff --git a/quill-core/src/test/scala/io/getquill/norm/SimplifyNullChecksSpec.scala b/quill-core/src/test/scala/io/getquill/norm/SimplifyNullChecksSpec.scala index 43b8255193..7dafa0f26f 100644 --- a/quill-core/src/test/scala/io/getquill/norm/SimplifyNullChecksSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/SimplifyNullChecksSpec.scala @@ -6,6 +6,7 @@ import io.getquill.base.Spec import io.getquill.norm.EqualityBehavior.{AnsiEquality, NonAnsiEquality} import io.getquill.MirrorContexts.testContext.{quote, unquote} import io.getquill.MirrorContexts.testContext.extras._ +import io.getquill.StatelessCache class SimplifyNullChecksSpec extends Spec { @@ -17,8 +18,8 @@ class SimplifyNullChecksSpec extends Spec { val it = Ident("t") val ca = Constant.auto("a") - val SimplifyNullChecksAnsi = new SimplifyNullChecks(AnsiEquality) - val SimplifyNullChecksNonAnsi = new SimplifyNullChecks(NonAnsiEquality) + val SimplifyNullChecksAnsi = new SimplifyNullChecks(StatelessCache.NoCache, AnsiEquality) + val SimplifyNullChecksNonAnsi = new SimplifyNullChecks(StatelessCache.NoCache, NonAnsiEquality) "center rule must" - { "apply when conditionals same" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala index 223e8afdc1..a2afc6008f 100644 --- a/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/SymbolicReductionSpec.scala @@ -11,7 +11,7 @@ import io.getquill.util.TraceConfig class SymbolicReductionSpec extends Spec { // hello - def symbolicReduction = (new SymbolicReduction(StatelessCacheOpt.NoCache(), TraceConfig.Empty).unapply _).andThen(o => o.map(replaceTempIdent(_))) + def symbolicReduction = (new SymbolicReduction(StatelessCacheOpt.NoCache, TraceConfig.Empty).unapply _).andThen(o => o.map(replaceTempIdent(_))) "a.filter(b => c).flatMap(d => e.$)" - { "e is an entity" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala index 46d5d730dd..6457832015 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictComplexSpec.scala @@ -7,7 +7,7 @@ import io.getquill.norm.{Normalize, NormalizeCaches, TranspileConfig} class AvoidAliasConflictComplexSpec extends Spec { - val normalize = new Normalize(NormalizeCaches.noCache(), TranspileConfig.Empty) + val normalize = new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty) "properly aliases explicit join sets" - { import io.getquill.norm.Normalize diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala index d85df2e67c..4373173fd9 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidAliasConflictSpec.scala @@ -7,7 +7,7 @@ import io.getquill.util.TraceConfig class AvoidAliasConflictSpec extends Spec { - val AvoidAliasConflict = new AvoidAliasConflictApply(StatefulCache.NoCache(), TraceConfig.Empty) + val AvoidAliasConflict = new AvoidAliasConflictApply(StatefulCache.NoCache, TraceConfig.Empty) "renames alias to avoid conflict between entities during normalization" - { "flatMap" in { diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala index dd1f8fa528..7ba877a941 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/AvoidCaptureSpec.scala @@ -18,6 +18,6 @@ class AvoidCaptureSpec extends Spec { val n = quote { qr1.filter(u => u.s == "s1").flatMap(u => qr2.filter(u1 => u1.s == "s1")).flatMap(u1 => qr3.map(u2 => u2.s)) } - AvoidCapture(q.ast, StatefulCache.NoCache(), TraceConfig(List.empty)) mustEqual n.ast + AvoidCapture(q.ast, StatefulCache.NoCache, TraceConfig(List.empty)) mustEqual n.ast } } diff --git a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala index 224c274386..aab1d8e400 100644 --- a/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala +++ b/quill-core/src/test/scala/io/getquill/norm/capture/DealiasSpec.scala @@ -11,7 +11,7 @@ import io.getquill.util.TraceConfig class DealiasSpec extends Spec { - val Dealias = new DealiasApply(TraceConfig.Empty, StatefulCache.NoCache()) + val Dealias = new DealiasApply(TraceConfig.Empty, StatefulCache.NoCache) "ensures that each entity is referenced by the same alias" - { "flatMap" in { diff --git a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala index a9cd1238ae..4677a2f151 100644 --- a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala +++ b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala @@ -10,20 +10,24 @@ trait StatefulCache[State] { def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) } object StatefulCache { - class NoCache[State] extends StatefulCache[State] { - override def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) = default - override def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) = default - } - object NoCache { - def apply[State]() = new NoCache[State] - } + + private val noCacheInstance = + new StatefulCache[Any] { + override def getOrCache(q: Ast, s: Any, default: => (Ast, StatefulTransformer[Any])): (Ast, StatefulTransformer[Any]) = default + override def getOrCache(q: Query, s: Any, default: => (Query, StatefulTransformer[Any])): (Query, StatefulTransformer[Any]) = default + } + + // Since the sateless NoCache[T] just returns what it was passed in we can reuse the same instance + // (and typecast with impunity) since there is no internal logic + def NoCache[T]: StatefulCache[T] = noCacheInstance.asInstanceOf[StatefulCache[T]] + // Make sure to store the Quat of the Ast since we don't want to cache the same Ast with different Quats // e.g. Cache ident(x, Quat(Person)) should not be the same as ident(x, Quat(Address)) in the caching process // because incorrect queries will be generated. case class Unlimited[State]( // TODO rebuild and try again - astMap: mutable.Map[(Ast, Quat, State), (Ast, StatefulTransformer[State])] = mutable.Map.empty[(Ast, Quat), (Ast, StatefulTransformer[State])], - queryMap: mutable.Map[(Query, Quat, State), (Query, StatefulTransformer[State])] = mutable.Map.empty[(Query, Quat), (Query, StatefulTransformer[State])] + astMap: mutable.Map[(Ast, Quat, State), (Ast, StatefulTransformer[State])] = mutable.Map.empty[(Ast, Quat, State), (Ast, StatefulTransformer[State])], + queryMap: mutable.Map[(Query, Quat, State), (Query, StatefulTransformer[State])] = mutable.Map.empty[(Query, Quat, State), (Query, StatefulTransformer[State])] ) extends StatefulCache[State] { override def getOrCache(q: Ast, s: State, default: => (Ast, StatefulTransformer[State])): (Ast, StatefulTransformer[State]) = { val key = (q, q.quat, s) @@ -36,7 +40,7 @@ object StatefulCache { } } override def getOrCache(q: Query, s: State, default: => (Query, StatefulTransformer[State])): (Query, StatefulTransformer[State]) = { - val key = (q, q.quat) + val key = (q, q.quat, s) queryMap.get(key) match { case Some(value) => value case None => @@ -53,13 +57,10 @@ trait StatelessCache { def getOrCache(q: Query, default: => Query): Query } object StatelessCache { - class NoCache extends StatelessCache { + object NoCache extends StatelessCache { override def getOrCache(q: Ast, default: => Ast): Ast = default override def getOrCache(q: Query, default: => Query): Query = default } - object NoCache { - def apply() = new NoCache - } case class Unlimited( astCache: mutable.Map[(Ast, Quat), Ast] = mutable.Map.empty, queryCache: mutable.Map[(Query, Quat), Query] = mutable.Map.empty @@ -98,13 +99,10 @@ trait StatelessCacheOpt { def getOrCache(q: Query, default: => Option[Query]): Option[Query] } object StatelessCacheOpt { - class NoCache extends StatelessCacheOpt { + object NoCache extends StatelessCacheOpt { override def getOrCache(q: Ast, default: => Option[Ast]): Option[Ast] = default override def getOrCache(q: Query, default: => Option[Query]): Option[Query] = default } - object NoCache { - def apply() = new NoCache - } case class Unlimited( astCache: mutable.Map[(Ast, Quat), Option[Ast]] = mutable.Map.empty, queryCache: mutable.Map[(Query, Quat), Option[Query]] = mutable.Map.empty diff --git a/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala b/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala index e405059a7d..9aacb16c5e 100644 --- a/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/MirrorIdiom.scala @@ -33,8 +33,8 @@ trait MirrorIdiomBase extends Idiom { )(implicit naming: NamingStrategy ): (Ast, Statement, ExecutionType) = { - // plugging in NormalizeCaches.noCache() into `Normalize` because NormalizeCaching does that work - val normalize = new Normalize(NormalizeCaches.noCache(), idiomContext.config) + // plugging in NormalizeCaches.noCache into `Normalize` because NormalizeCaching does that work + val normalize = new Normalize(NormalizeCaches.noCache, idiomContext.config) val normalizedAst = NormalizeCaching(normalize.apply)(ast) (normalizedAst, stmt"${normalizedAst.token}", executionType) } diff --git a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala index e7499b4e62..c0a0e0f6b2 100644 --- a/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala +++ b/quill-engine/src/main/scala/io/getquill/ast/StatefulTransformer.scala @@ -7,7 +7,7 @@ trait StatefulTransformer[T] extends HasStatefulCache[T] { val state: T - private val defaultCache = new NoCache[T] + private val defaultCache = NoCache[T] // Override this value in implementations in order to use caching override def cache: StatefulCache[T] = defaultCache diff --git a/quill-engine/src/main/scala/io/getquill/context/RowContext.scala b/quill-engine/src/main/scala/io/getquill/context/RowContext.scala index cdbdcb722b..e305733854 100644 --- a/quill-engine/src/main/scala/io/getquill/context/RowContext.scala +++ b/quill-engine/src/main/scala/io/getquill/context/RowContext.scala @@ -1,7 +1,7 @@ package io.getquill.context import io.getquill.ReturnAction -import io.getquill.ast.ScalarLift +import io.getquill.ast.{External, ScalarLift} trait RowContext { type PrepareRow @@ -11,8 +11,9 @@ trait RowContext { private val _identityExtractor: Extractor[Any] = (rr: ResultRow, _: Session) => rr protected def identityExtractor[T]: Extractor[T] = _identityExtractor.asInstanceOf[Extractor[T]] - case class BatchGroup(string: String, prepare: List[Prepare], liftings: List[List[ScalarLift]]) - case class BatchGroupReturning(string: String, returningBehavior: ReturnAction, prepare: List[Prepare], liftings: List[List[ScalarLift]]) + // TODO need this for backwards compat + case class BatchGroup(string: String, prepare: List[Prepare], liftings: List[List[External]]) + case class BatchGroupReturning(string: String, returningBehavior: ReturnAction, prepare: List[Prepare], liftings: List[List[External]]) type Prepare = (PrepareRow, Session) => (List[Any], PrepareRow) type Extractor[T] = (ResultRow, Session) => T diff --git a/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala index 259c604372..c3b984be7f 100644 --- a/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/context/cassandra/CqlNormalize.scala @@ -1,6 +1,6 @@ package io.getquill.context.cassandra -import io.getquill.StatefulCache +import io.getquill.{StatefulCache, StatelessCache} import io.getquill.ast._ import io.getquill.norm.ConcatBehavior.AnsiConcat import io.getquill.norm.EqualityBehavior.AnsiEquality @@ -9,7 +9,7 @@ import io.getquill.norm.{FlattenOptionOperation, Normalize, NormalizeCaches, Ren import io.getquill.quat.Quat class CqlNormalize(transpileConfig: TranspileConfig) { - val NormalizePhase = new Normalize(NormalizeCaches.noCache(), transpileConfig) + val NormalizePhase = new Normalize(NormalizeCaches.noCache, transpileConfig) def apply(ast: Ast) = normalize(ast) @@ -28,9 +28,9 @@ class CqlNormalize(transpileConfig: TranspileConfig) { qry } - val RenamePropertiesPhase = new RenameProperties(transpileConfig.traceConfig) - val FlattenOptionOperationPhase = new FlattenOptionOperation(AnsiConcat, transpileConfig.traceConfig) - val SimplifyNullChecksPhase = new SimplifyNullChecks(AnsiEquality) + val RenamePropertiesPhase = new RenameProperties(StatelessCache.NoCache, transpileConfig.traceConfig) + val FlattenOptionOperationPhase = new FlattenOptionOperation(StatelessCache.NoCache, AnsiConcat, transpileConfig.traceConfig) + val SimplifyNullChecksPhase = new SimplifyNullChecks(StatelessCache.NoCache, AnsiEquality) private[this] val normalize = (identity[Ast] _) @@ -43,6 +43,6 @@ class CqlNormalize(transpileConfig: TranspileConfig) { .andThen { ast => // In the final stage of normalization, change all temporary aliases into // shorter ones of the form x[0-9]+. - NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, StatefulCache.NoCache(), transpileConfig.traceConfig)) + NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, StatefulCache.NoCache, transpileConfig.traceConfig)) } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/FlattenOptionOperation.scala b/quill-engine/src/main/scala/io/getquill/norm/FlattenOptionOperation.scala index f2c55d50c8..02dda5677b 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/FlattenOptionOperation.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/FlattenOptionOperation.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{HasStatelessCache, StatelessCache} import io.getquill.ast._ import io.getquill.ast.Implicits._ import io.getquill.norm.ConcatBehavior.NonAnsiConcat @@ -7,7 +8,7 @@ import io.getquill.quat.QuatOps.HasBooleanQuat import io.getquill.util.Messages.TraceType import io.getquill.util.{Interpolator, TraceConfig} -class FlattenOptionOperation(concatBehavior: ConcatBehavior, traceConfig: TraceConfig) extends StatelessTransformer { +class FlattenOptionOperation(val cache: StatelessCache, concatBehavior: ConcatBehavior, traceConfig: TraceConfig) extends StatelessTransformer with HasStatelessCache { val interp = new Interpolator(TraceType.FlattenOptionOperation, traceConfig, 2) import interp._ @@ -49,95 +50,97 @@ class FlattenOptionOperation(concatBehavior: ConcatBehavior, traceConfig: TraceC case BinaryOperation(_, StringOperator.`+`, _) if (concatBehavior == NonAnsiConcat) => true }.nonEmpty - override def apply(ast: Ast): Ast = trace"Flattening option clause $ast ".andReturnIf { - ast match { + override def apply(ast: Ast): Ast = cached(ast) { + trace"Flattening option clause $ast ".andReturnIf { + ast match { - case OptionTableFlatMap(ast, alias, body) => - uncheckedReduction(ast, alias, body, _ => false) + case OptionTableFlatMap(ast, alias, body) => + uncheckedReduction(ast, alias, body, _ => false) - case OptionTableMap(ast, alias, body) => - uncheckedReduction(ast, alias, body, _ => false) + case OptionTableMap(ast, alias, body) => + uncheckedReduction(ast, alias, body, _ => false) - case OptionTableExists(ast, alias, body) => - uncheckedReduction(ast, alias, body, _ => false) + case OptionTableExists(ast, alias, body) => + uncheckedReduction(ast, alias, body, _ => false) - case OptionTableForall(ast, alias, body) => - uncheckedForall(ast, alias, body, _ => false) + case OptionTableForall(ast, alias, body) => + uncheckedForall(ast, alias, body, _ => false) - case OptionFlatten(ast) => - apply(ast) + case OptionFlatten(ast) => + apply(ast) - case OptionSome(ast) => - apply(ast) + case OptionSome(ast) => + apply(ast) - case OptionApply(ast) => - apply(ast) + case OptionApply(ast) => + apply(ast) - case OptionOrNull(ast) => - apply(ast) + case OptionOrNull(ast) => + apply(ast) - case OptionGetOrNull(ast) => - apply(ast) + case OptionGetOrNull(ast) => + apply(ast) - case OptionGetOrElse(HasBooleanQuat(OptionMap(ast, alias, body)), HasBooleanQuat(alternative)) => - val expr = BetaReduction(body, alias -> ast) - val output: Ast = (expr +&&+ IsNotNullCheck(ast)) +||+ (alternative +&&+ IsNullCheck(ast)) - apply(output) + case OptionGetOrElse(HasBooleanQuat(OptionMap(ast, alias, body)), HasBooleanQuat(alternative)) => + val expr = BetaReduction(body, alias -> ast) + val output: Ast = (expr +&&+ IsNotNullCheck(ast)) +||+ (alternative +&&+ IsNullCheck(ast)) + apply(output) - case OptionGetOrElse(ast, body) => - apply(If(IsNotNullCheck(ast), ast, body)) + case OptionGetOrElse(ast, body) => + apply(If(IsNotNullCheck(ast), ast, body)) - case OptionOrElse(ast, body) => - apply(If(IsNotNullCheck(ast), ast, body)) + case OptionOrElse(ast, body) => + apply(If(IsNotNullCheck(ast), ast, body)) - case OptionFlatMap(ast, alias, body) => - uncheckedReduction(ast, alias, body, containsNonFallthroughElement) + case OptionFlatMap(ast, alias, body) => + uncheckedReduction(ast, alias, body, containsNonFallthroughElement) - case OptionMap(ast, alias, body) => - uncheckedReduction(ast, alias, body, containsNonFallthroughElement) + case OptionMap(ast, alias, body) => + uncheckedReduction(ast, alias, body, containsNonFallthroughElement) - // a.orElse(b).forAll(alias => body) becomes: - // body(->a) || a==null && body(->b) || a==null && b==null - // - // Note that since all of the clauses are boolean this a.orElse(...) can be replaced - // by a||(b && a==null). If the clause was actually returning value (e.g. if(a) foo else bar) - // then these kinds of reductions would not be possible. - // Leaving the ||a==null clause without reversing for now despite the fact that ==null - // clauses shuold generally be the 2nd in the order because of the <> issue. - case OptionForall(OptionOrElse(a, b), alias, body) => - val reducedA = BetaReduction(body, alias -> a) - val reducedB = BetaReduction(body, alias -> b) - val isNullA = IsNullCheck(a) - val isNullB = IsNullCheck(b) + // a.orElse(b).forAll(alias => body) becomes: + // body(->a) || a==null && body(->b) || a==null && b==null + // + // Note that since all of the clauses are boolean this a.orElse(...) can be replaced + // by a||(b && a==null). If the clause was actually returning value (e.g. if(a) foo else bar) + // then these kinds of reductions would not be possible. + // Leaving the ||a==null clause without reversing for now despite the fact that ==null + // clauses shuold generally be the 2nd in the order because of the <> issue. + case OptionForall(OptionOrElse(a, b), alias, body) => + val reducedA = BetaReduction(body, alias -> a) + val reducedB = BetaReduction(body, alias -> b) + val isNullA = IsNullCheck(a) + val isNullB = IsNullCheck(b) - apply(reducedA) +||+ apply((isNullA +&&+ reducedB): Ast) +||+ apply((isNullA +&&+ isNullB): Ast) + apply(reducedA) +||+ apply((isNullA +&&+ reducedB): Ast) +||+ apply((isNullA +&&+ isNullB): Ast) - case OptionForall(ast, alias, body) => - uncheckedForall(ast, alias, body, containsNonFallthroughElement) + case OptionForall(ast, alias, body) => + uncheckedForall(ast, alias, body, containsNonFallthroughElement) - case OptionExists(OptionOrElse(a, b), alias, body) => - val reducedA = BetaReduction(body, alias -> a) - val reducedB = BetaReduction(body, alias -> b) - apply((reducedA +&&+ IsNotNullCheck(a)): Ast) +||+ apply((reducedB +&&+ IsNotNullCheck(b)): Ast) + case OptionExists(OptionOrElse(a, b), alias, body) => + val reducedA = BetaReduction(body, alias -> a) + val reducedB = BetaReduction(body, alias -> b) + apply((reducedA +&&+ IsNotNullCheck(a)): Ast) +||+ apply((reducedB +&&+ IsNotNullCheck(b)): Ast) - case OptionExists(ast, alias, body) => - validateContainsOrElse( - containsNonFallthroughElement(body), - () => { - val reduction = BetaReduction(body, alias -> ast) - apply(reduction +&&+ IsNotNullCheck(ast): Ast) - }, - () => apply(BetaReduction(body, alias -> ast)) - ) + case OptionExists(ast, alias, body) => + validateContainsOrElse( + containsNonFallthroughElement(body), + () => { + val reduction = BetaReduction(body, alias -> ast) + apply(reduction +&&+ IsNotNullCheck(ast): Ast) + }, + () => apply(BetaReduction(body, alias -> ast)) + ) - case OptionContains(ast, body) => - apply((ast +==+ body): Ast) + case OptionContains(ast, body) => + apply((ast +==+ body): Ast) - case FilterIfDefined(ast, alias, body) => - uncheckedForall(ast, alias, body, containsNonFallthroughElement) + case FilterIfDefined(ast, alias, body) => + uncheckedForall(ast, alias, body, containsNonFallthroughElement) - case other => - super.apply(other) - } - }(_ != ast) + case other => + super.apply(other) + } + }(_ != ast) + } } diff --git a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala index 7638c4d008..f5ac087baa 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala @@ -19,20 +19,20 @@ case class NormalizeCaches( orderTermsCache: StatelessCacheOpt ) object NormalizeCaches { - def noCache() = + val noCache = NormalizeCaches( - StatelessCache.NoCache(), - StatefulCache.NoCache[Option[Ident]](), - StatefulCache.NoCache[Set[IdentName]](), - StatelessCacheOpt.NoCache(), - StatelessCacheOpt.NoCache(), - StatelessCacheOpt.NoCache(), - StatelessCacheOpt.NoCache() + StatelessCache.NoCache, + StatefulCache.NoCache, + StatefulCache.NoCache, + StatelessCacheOpt.NoCache, + StatelessCacheOpt.NoCache, + StatelessCacheOpt.NoCache, + StatelessCacheOpt.NoCache ) def unlimitedCache() = NormalizeCaches( - StatelessCache.NoCache(), + StatelessCache.NoCache, StatefulCache.Unlimited[Option[Ident]](), StatefulCache.Unlimited[Set[IdentName]](), StatelessCacheOpt.Unlimited(), diff --git a/quill-engine/src/main/scala/io/getquill/norm/RenameProperties.scala b/quill-engine/src/main/scala/io/getquill/norm/RenameProperties.scala index 1aa5908d70..b72d28c8ad 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/RenameProperties.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/RenameProperties.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{HasStatelessCache, StatelessCache} import io.getquill.ast._ import io.getquill.quat.Quat import io.getquill.util.{Interpolator, TraceConfig} @@ -29,14 +30,17 @@ import io.getquill.util.Messages.{TraceType, title} * transformations with straightforward operations since the majority of the * logic actually lives within the Quats themselves. */ -class RenameProperties(traceConfig: TraceConfig) { +class RenameProperties(val cache: StatelessCache, traceConfig: TraceConfig) extends HasStatelessCache { private def demarcate(heading: String) = ((ast: Ast) => title(heading)(ast)) val ApplyRenamesToPropsPhase = new ApplyRenamesToProps(traceConfig) val RepropagateQuatsPhase = new RepropagateQuats(traceConfig) - def apply(ast: Ast) = + // Note that caching every single sub-transform of this phase might be dangerous + // because identifier equality does not (yet) take upcoming renames into account. + // If we want more in-depth caching of this phase we need to think of a strategy for that. + def apply(ast: Ast) = cached(ast) { (identity[Ast] _) .andThen(SeedRenames.apply(_: Ast)) // Stage field renames into the Quats of entities .andThen(demarcate("SeedRenames")) @@ -48,6 +52,7 @@ class RenameProperties(traceConfig: TraceConfig) { .andThen(demarcate("ApplyRenamesToProps")) // Go through the Quats and 'commit' the renames .andThen(CompleteRenames.apply(_: Ast)) .andThen(demarcate("CompleteRenames"))(ast) // Quats can be invalid in between this phase and the previous one + } } object CompleteRenames extends StatelessTransformer { diff --git a/quill-engine/src/main/scala/io/getquill/norm/SimplifyNullChecks.scala b/quill-engine/src/main/scala/io/getquill/norm/SimplifyNullChecks.scala index a85e4b223e..76f9d3d166 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/SimplifyNullChecks.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/SimplifyNullChecks.scala @@ -1,5 +1,6 @@ package io.getquill.norm +import io.getquill.{HasStatelessCache, StatelessCache} import io.getquill.ast._ import io.getquill.ast.Implicits._ import io.getquill.norm.EqualityBehavior.AnsiEquality @@ -30,9 +31,9 @@ import io.getquill.norm.EqualityBehavior.AnsiEquality * 'foo')` because a user may use `Option[T].flatMap` and explicitly transform a * particular value to `null`. */ -class SimplifyNullChecks(equalityBehavior: EqualityBehavior) extends StatelessTransformer { +class SimplifyNullChecks(val cache: StatelessCache, equalityBehavior: EqualityBehavior) extends StatelessTransformer with HasStatelessCache { - override def apply(ast: Ast): Ast = + override def apply(ast: Ast): Ast = cached(ast) { ast match { // Center rule @@ -76,6 +77,7 @@ class SimplifyNullChecks(equalityBehavior: EqualityBehavior) extends StatelessTr case other => super.apply(other) } + } object `== or !=` { def unapply(ast: Ast): Option[(Ast, Ast)] = ast match { diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala index a1dbc0595b..e579861ed0 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidAliasConflict.scala @@ -319,17 +319,17 @@ private[getquill] object AvoidAliasConflict { * transforming and queries encountered. */ def sanitizeVariables(f: Function, dangerousVariables: Set[IdentName], traceConfig: TraceConfig): Function = - AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), traceConfig).applyFunction(f) + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache, traceConfig).applyFunction(f) /** Same is `sanitizeVariables` but for Foreach * */ def sanitizeVariables(f: Foreach, dangerousVariables: Set[IdentName], traceConfig: TraceConfig): Foreach = - AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), traceConfig).applyForeach(f) + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache, traceConfig).applyForeach(f) // TODO trying to move AvoidAliasConflict into the tests // since it only does two things and we want less indirection. // the interesting question is whether to pass a dealias cache into this function. def sanitizeQuery(q: Query, dangerousVariables: Set[IdentName], normalize: Normalize): Query = - AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache(), normalize.traceConf).apply(q) match { + AvoidAliasConflict(dangerousVariables, false, StatefulCache.NoCache, normalize.traceConf).apply(q) match { // Propagate aliasing changes to the rest of the query case (q, _) => normalize(q) } diff --git a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala index cc6b0658a1..e707ea650e 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/capture/AvoidCapture.scala @@ -10,5 +10,5 @@ import io.getquill.util.TraceConfig object AvoidCapture { def apply(q: Query, cache: StatefulCache[Set[IdentName]], traceConfig: TraceConfig): Query = - Dealias(AvoidAliasConflict(q, false, cache, traceConfig))(traceConfig, NoCache()) + Dealias(AvoidAliasConflict(q, false, cache, traceConfig))(traceConfig, NoCache) } diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandDistinct.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandDistinct.scala index 818c11bfd0..3243e222e0 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandDistinct.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandDistinct.scala @@ -1,5 +1,6 @@ package io.getquill.context.sql.norm +import io.getquill.{HasStatelessCache, StatelessCache} import io.getquill.ast.Visibility.Hidden import io.getquill.ast._ import io.getquill.quat.Quat @@ -7,12 +8,12 @@ import io.getquill.quat.QuatNestingHelper._ import io.getquill.util.{Interpolator, TraceConfig} import io.getquill.util.Messages.TraceType -class ExpandDistinct(traceConfig: TraceConfig) { +class ExpandDistinct(val cache: StatelessCache, traceConfig: TraceConfig) extends HasStatelessCache { val interp = new Interpolator(TraceType.ExpandDistinct, traceConfig, 3) import interp._ - def apply(q: Ast): Ast = + def apply(q: Ast): Ast = cached(q) { q match { case Distinct(q) => trace"ExpandDistinct Distinct(inside)" andReturn @@ -85,4 +86,5 @@ class ExpandDistinct(traceConfig: TraceConfig) { Map(Distinct(newMap), newIdent, Property(newIdent, "_1")) } } + } } diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandJoin.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandJoin.scala index c8d4932477..6d61685147 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandJoin.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/ExpandJoin.scala @@ -1,5 +1,6 @@ package io.getquill.context.sql.norm +import io.getquill.{HasStatelessCache, StatelessCache} import io.getquill.ast._ import io.getquill.norm.BetaReduction import io.getquill.norm.Normalize @@ -17,9 +18,11 @@ import io.getquill.norm.Normalize * `ExpandNestedQueries` does not yet use Quat fields for expansion. Once this * is changed, using that implementation here should be reconsidered. */ -class ExpandJoin(normalize: Normalize) { +class ExpandJoin(val cache: StatelessCache, normalize: Normalize) extends HasStatelessCache { - def apply(q: Ast) = expand(q, None) + def apply(q: Ast) = cached(q) { + expand(q, None) + } def expand(q: Ast, id: Option[Ident]) = Transform(q) { diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala index 2e51a523ee..f7f0ec7652 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala @@ -1,5 +1,6 @@ package io.getquill.context.sql.norm +import io.getquill.StatelessCache import io.getquill.norm.{SimplifyNullChecks, _} import io.getquill.ast.Ast import io.getquill.norm.ConcatBehavior.AnsiConcat @@ -18,25 +19,47 @@ object SqlNormalize { new SqlNormalize(concatBehavior, equalityBehavior, transpileConfig)(ast) } +case class SqlNormalizeCaches( + expandJoinCache: StatelessCache, + renamePropertiesCache: StatelessCache, + expandDistinctCache: StatelessCache, + flattenOptionCache: StatelessCache, + simplifyNullChecksCache: StatelessCache +) +object SqlNormalizeCaches { + def unlimitedCache() = + SqlNormalizeCaches( + StatelessCache.Unlimited(), + StatelessCache.Unlimited(), + StatelessCache.Unlimited(), + StatelessCache.Unlimited(), + StatelessCache.Unlimited() + ) +} + class SqlNormalize( concatBehavior: ConcatBehavior, equalityBehavior: EqualityBehavior, transpileConfig: TranspileConfig ) { - val caches = NormalizeCaches.unlimitedCache() + val mainCache = StatelessCache.Unlimited() + val caches = NormalizeCaches.unlimitedCache() + val sqlNormCaches = SqlNormalizeCaches.unlimitedCache() + val NormalizePhase = new Normalize(caches, transpileConfig) val traceConfig = transpileConfig.traceConfig private def demarcate(heading: String) = ((ast: Ast) => title(heading, TraceType.SqlNormalizations)(ast)) - val ExpandJoinPhase = new ExpandJoin(NormalizePhase) - val RenamePropertiesPhase = new RenameProperties(traceConfig) - val ExpandDistinctPhase = new ExpandDistinct(traceConfig) + val ExpandJoinPhase = new ExpandJoin(sqlNormCaches.expandJoinCache, NormalizePhase) + val RenamePropertiesPhase = new RenameProperties(sqlNormCaches.renamePropertiesCache, traceConfig) // can't really cache this because renames are not on the quat comparison + val ExpandDistinctPhase = new ExpandDistinct(sqlNormCaches.expandDistinctCache, traceConfig) + // TODO want to get rid of this stage val SheathLeafClausesPhase = new SheathLeafClausesApply(traceConfig) - val FlattenOptionOperationPhase = new FlattenOptionOperation(concatBehavior, transpileConfig.traceConfig) - val SimplifyNullChecksPhase = new SimplifyNullChecks(equalityBehavior) + val FlattenOptionOperationPhase = new FlattenOptionOperation(sqlNormCaches.flattenOptionCache, concatBehavior, transpileConfig.traceConfig) + val SimplifyNullChecksPhase = new SimplifyNullChecks(sqlNormCaches.simplifyNullChecksCache, equalityBehavior) private val normalize = (identity[Ast] _) @@ -75,7 +98,8 @@ class SqlNormalize( def apply(ast: Ast) = { val (stableAst, state) = StabilizeLifts.stabilize(ast) - val outputAst = normalize(stableAst) + + val outputAst = mainCache.getOrCache(stableAst, normalize(stableAst)) StabilizeLifts.revert(outputAst, state) } } diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandDistinctSpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandDistinctSpec.scala index c8aff9ed6f..31a3651756 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandDistinctSpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandDistinctSpec.scala @@ -1,6 +1,6 @@ package io.getquill.context.sql.norm -import io.getquill.Ord +import io.getquill.{Ord, StatelessCache} import io.getquill.base.Spec import io.getquill.context.sql.testContext.qr1 import io.getquill.context.sql.testContext.quote @@ -8,7 +8,7 @@ import io.getquill.context.sql.testContext.unquote import io.getquill.util.TraceConfig class ExpandDistinctSpec extends Spec { - val ExpandDistinct = new ExpandDistinct(TraceConfig.Empty) + val ExpandDistinct = new ExpandDistinct(StatelessCache.NoCache, TraceConfig.Empty) "expands distinct map" - { "simple" in { diff --git a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandJoinSpec.scala b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandJoinSpec.scala index 08060be0e6..076b4a7433 100644 --- a/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandJoinSpec.scala +++ b/quill-sql-test/src/test/scala/io/getquill/context/sql/norm/ExpandJoinSpec.scala @@ -1,16 +1,17 @@ package io.getquill.context.sql.norm +import io.getquill.StatelessCache import io.getquill.base.Spec import io.getquill.context.sql.testContext.qr1 import io.getquill.context.sql.testContext.qr2 import io.getquill.context.sql.testContext.qr3 import io.getquill.context.sql.testContext.quote import io.getquill.context.sql.testContext.unquote -import io.getquill.norm.{Normalize, TranspileConfig} +import io.getquill.norm.{Normalize, NormalizeCaches, TranspileConfig} class ExpandJoinSpec extends Spec { - val ExpandJoin = new ExpandJoin(new Normalize(TranspileConfig.Empty)) + val ExpandJoin = new ExpandJoin(StatelessCache.NoCache, new Normalize(NormalizeCaches.noCache, TranspileConfig.Empty)) "expands the outer join by mapping the result" - { "simple" in { From 716e4f2a5163aeb539967caaf5256434d44cdce8 Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Wed, 11 Dec 2024 13:44:32 -0500 Subject: [PATCH 12/14] Disable parts of caching --- .../scala/io/getquill/HasStatefulCache.scala | 1 - .../scala/io/getquill/ast/CollectAst.scala | 49 +++++++++++ .../sql/idiom/BooleanLiteralSupport.scala | 4 +- .../io/getquill/sql/idiom/SqlIdiom.scala | 2 +- .../io/getquill/sql/norm/SqlNormalize.scala | 85 ++++++++++++------- 5 files changed, 108 insertions(+), 33 deletions(-) diff --git a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala index 4677a2f151..07d5f6102e 100644 --- a/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala +++ b/quill-engine/src/main/scala/io/getquill/HasStatefulCache.scala @@ -25,7 +25,6 @@ object StatefulCache { // e.g. Cache ident(x, Quat(Person)) should not be the same as ident(x, Quat(Address)) in the caching process // because incorrect queries will be generated. case class Unlimited[State]( - // TODO rebuild and try again astMap: mutable.Map[(Ast, Quat, State), (Ast, StatefulTransformer[State])] = mutable.Map.empty[(Ast, Quat, State), (Ast, StatefulTransformer[State])], queryMap: mutable.Map[(Query, Quat, State), (Query, StatefulTransformer[State])] = mutable.Map.empty[(Query, Quat, State), (Query, StatefulTransformer[State])] ) extends StatefulCache[State] { diff --git a/quill-engine/src/main/scala/io/getquill/ast/CollectAst.scala b/quill-engine/src/main/scala/io/getquill/ast/CollectAst.scala index e04b1a4652..c90249b356 100644 --- a/quill-engine/src/main/scala/io/getquill/ast/CollectAst.scala +++ b/quill-engine/src/main/scala/io/getquill/ast/CollectAst.scala @@ -3,6 +3,55 @@ package io.getquill.ast import scala.collection.immutable.Queue import scala.reflect.ClassTag +case class VisitAst(p: PartialFunction[Ast, Unit]) extends StatelessTransformer { + override def apply(a: Ast) = { + p.lift(a).getOrElse(()) + super.apply(a) + } +} + +case class QueryStats( + hasOptionOps: Boolean, + hasFlatMaps: Boolean, + hasDistincts: Boolean, + hasRenames: Boolean, + hasJoins: Boolean, + hasNullValues: Boolean, + hasInfixes: Boolean, + hasReturning: Boolean +) + +class CollectStats { + var hasOptionOps: Boolean = false + var hasFlatMaps: Boolean = false + var hasDistincts: Boolean = false + var hasRenames: Boolean = false + var hasJoins: Boolean = false + var hasNullValues: Boolean = false + var hasInfixes: Boolean = false + var hasReturning: Boolean = false + + def apply(ast: Ast): QueryStats = { + VisitAst { + case _: OptionOperation => hasOptionOps = true + case _: FlatMap => hasFlatMaps = true + case _: Distinct => hasDistincts = true + case _: Join => hasJoins = true + case _: NullValue.type => hasNullValues = true + case _: Infix => hasInfixes = true + case _: Returning => hasReturning = true + case _: ReturningGenerated => hasReturning = true + case e: Entity if (e.properties.nonEmpty) => + hasRenames = true + }.apply(ast) + + QueryStats(hasOptionOps, hasFlatMaps, hasDistincts, hasRenames, hasJoins, hasNullValues, hasInfixes, hasReturning) + } +} +object CollectStats { + def apply(ast: Ast): QueryStats = new CollectStats().apply(ast) +} + /** * The collection is treated as immutable internally but an ArrayBuffer is more * efficient then Collection.list at appending which is mostly what the diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala index 48537d4196..3409d67ea5 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala @@ -3,7 +3,7 @@ package io.getquill.sql.idiom import io.getquill.NamingStrategy import io.getquill.ast._ import io.getquill.context.sql.idiom.SqlIdiom -import io.getquill.context.sql.norm.SqlNormalize +import io.getquill.context.sql.norm.{SqlNormalize, SqlNormalizeCaches} import io.getquill.idiom.StatementInterpolator._ import io.getquill.idiom.StringToken import io.getquill.norm.{ConcatBehavior, EqualityBehavior} @@ -20,7 +20,7 @@ trait BooleanLiteralSupport extends SqlIdiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = { - val norm = SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior) + val norm = SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.Global) if (Messages.smartBooleans) VendorizeBooleans(norm) else diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala index 136524f971..befc9a68cb 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala @@ -42,7 +42,7 @@ trait SqlIdiom extends Idiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = - SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior) + SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.Global) def querifyAst(ast: Ast, traceConfig: TraceConfig) = new SqlQueryApply(traceConfig)(ast) diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala index f7f0ec7652..6f89db003b 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala @@ -2,7 +2,7 @@ package io.getquill.context.sql.norm import io.getquill.StatelessCache import io.getquill.norm.{SimplifyNullChecks, _} -import io.getquill.ast.Ast +import io.getquill.ast.{Ast, CollectStats, QueryStats} import io.getquill.norm.ConcatBehavior.AnsiConcat import io.getquill.norm.EqualityBehavior.AnsiEquality import io.getquill.norm.capture.{AvoidAliasConflict, DemarcateExternalAliases} @@ -14,21 +14,22 @@ object SqlNormalize { ast: Ast, transpileConfig: TranspileConfig, concatBehavior: ConcatBehavior = AnsiConcat, - equalityBehavior: EqualityBehavior = AnsiEquality + equalityBehavior: EqualityBehavior = AnsiEquality, + caches: SqlNormalizeCaches = SqlNormalizeCaches.unlimitedLocal() ) = - new SqlNormalize(concatBehavior, equalityBehavior, transpileConfig)(ast) + new SqlNormalize(concatBehavior, equalityBehavior, transpileConfig, caches)(ast) } -case class SqlNormalizeCaches( +case class CompilePhaseCaches( expandJoinCache: StatelessCache, renamePropertiesCache: StatelessCache, expandDistinctCache: StatelessCache, flattenOptionCache: StatelessCache, simplifyNullChecksCache: StatelessCache ) -object SqlNormalizeCaches { +object CompilePhaseCaches { def unlimitedCache() = - SqlNormalizeCaches( + CompilePhaseCaches( StatelessCache.Unlimited(), StatelessCache.Unlimited(), StatelessCache.Unlimited(), @@ -37,17 +38,35 @@ object SqlNormalizeCaches { ) } +trait SqlNormalizeCaches { + def mainCache: StatelessCache + def normCaches: NormalizeCaches + def phaseCaches: CompilePhaseCaches +} +object SqlNormalizeCaches { + def unlimitedLocal() = + new SqlNormalizeCaches { + val mainCache = StatelessCache.NoCache + val normCaches = NormalizeCaches.unlimitedCache() + val phaseCaches = CompilePhaseCaches.unlimitedCache() + } + + object Global extends SqlNormalizeCaches { + val mainCache = StatelessCache.NoCache + val normCaches = NormalizeCaches.noCache + val phaseCaches = CompilePhaseCaches.unlimitedCache() + } +} + class SqlNormalize( concatBehavior: ConcatBehavior, equalityBehavior: EqualityBehavior, - transpileConfig: TranspileConfig + transpileConfig: TranspileConfig, + caches: SqlNormalizeCaches ) { - val mainCache = StatelessCache.Unlimited() - val caches = NormalizeCaches.unlimitedCache() - val sqlNormCaches = SqlNormalizeCaches.unlimitedCache() - - val NormalizePhase = new Normalize(caches, transpileConfig) + val sqlNormCaches = caches.phaseCaches + val NormalizePhase = new Normalize(caches.normCaches, transpileConfig) val traceConfig = transpileConfig.traceConfig private def demarcate(heading: String) = @@ -61,45 +80,53 @@ class SqlNormalize( val FlattenOptionOperationPhase = new FlattenOptionOperation(sqlNormCaches.flattenOptionCache, concatBehavior, transpileConfig.traceConfig) val SimplifyNullChecksPhase = new SimplifyNullChecks(sqlNormCaches.simplifyNullChecksCache, equalityBehavior) - private val normalize = + private def normalize(stats: QueryStats) = (identity[Ast] _) .andThen(demarcate("original")) - .andThen(DemarcateExternalAliases.apply _) + .andThen(if (stats.hasReturning) DemarcateExternalAliases.apply _ else identity[Ast] _) .andThen(demarcate("DemarcateReturningAliases")) - .andThen(FlattenOptionOperationPhase.apply _) + .andThen(if (stats.hasOptionOps) FlattenOptionOperationPhase.apply _ else identity[Ast] _) .andThen(demarcate("FlattenOptionOperation")) - .andThen(SimplifyNullChecksPhase.apply _) + .andThen(if (stats.hasOptionOps || stats.hasNullValues) SimplifyNullChecksPhase.apply _ else identity[Ast] _) .andThen(demarcate("SimplifyNullChecks")) .andThen(NormalizePhase.apply _) .andThen(demarcate("Normalize")) // Need to do RenameProperties before ExpandJoin which normalizes-out all the tuple indexes // on which RenameProperties relies // .andThen(RenameProperties.apply _) - .andThen(RenamePropertiesPhase.apply _) + .andThen(if (stats.hasRenames) RenamePropertiesPhase.apply _ else identity[Ast] _) .andThen(demarcate("RenameProperties")) - .andThen(ExpandDistinctPhase.apply _) - .andThen(demarcate("ExpandDistinct")) - .andThen(NormalizePhase.apply _) - .andThen(demarcate("Normalize")) // Needed only because ExpandDistinct introduces an alias. - .andThen(NormalizePhase.apply _) - .andThen(demarcate("Normalize")) - .andThen(ExpandJoinPhase.apply _) + .andThen { ast => + if (stats.hasOptionOps) { + val expanded = ExpandDistinctPhase(ast) + demarcate("ExpandDistinct") + // Normalize is only needed only because ExpandDistinct introduces an alias. + // why are two normalize phases needed here??? + val e1 = NormalizePhase(expanded) + demarcate("Normalize") + val e2 = NormalizePhase(expanded) + demarcate("Normalize") + e2 + } else + ast + } + .andThen(if (stats.hasJoins) ExpandJoinPhase.apply _ else identity[Ast] _) .andThen(demarcate("ExpandJoin")) - .andThen(ExpandMappedInfix.apply _) - .andThen(demarcate("ExpandMappedInfix")) + .andThen(ExpandMappedInfix.apply _) // TODO disable if this has no infixes + .andThen(if (stats.hasInfixes) demarcate("ExpandMappedInfix") else identity[Ast] _) .andThen(SheathLeafClausesPhase.apply _) .andThen(demarcate("SheathLeaves")) .andThen { ast => // In the final stage of normalization, change all temporary aliases into // shorter ones of the form x[0-9]+. - NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, caches.avoidAliasCache, transpileConfig.traceConfig)) + NormalizePhase.apply(AvoidAliasConflict.Ast(ast, true, caches.normCaches.avoidAliasCache, transpileConfig.traceConfig)) } .andThen(demarcate("Normalize")) def apply(ast: Ast) = { val (stableAst, state) = StabilizeLifts.stabilize(ast) - - val outputAst = mainCache.getOrCache(stableAst, normalize(stableAst)) + val stats = CollectStats(stableAst) + val outputAst = caches.mainCache.getOrCache(stableAst, normalize(stats)(stableAst)) StabilizeLifts.revert(outputAst, state) } } From 05eeb1759921a0929c2d7d30b1c76b3a743f48bc Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Sun, 22 Dec 2024 18:57:29 -0500 Subject: [PATCH 13/14] Remove global, use local --- .../io/getquill/sql/idiom/BooleanLiteralSupport.scala | 2 +- .../main/scala/io/getquill/sql/idiom/SqlIdiom.scala | 2 +- .../main/scala/io/getquill/sql/norm/SqlNormalize.scala | 10 +--------- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala index 3409d67ea5..ffd36980a1 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala @@ -20,7 +20,7 @@ trait BooleanLiteralSupport extends SqlIdiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = { - val norm = SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.Global) + val norm = SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.unlimitedLocal()) if (Messages.smartBooleans) VendorizeBooleans(norm) else diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala index befc9a68cb..14e6b9947e 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala @@ -42,7 +42,7 @@ trait SqlIdiom extends Idiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = - SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.Global) + SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.unlimitedLocal()) def querifyAst(ast: Ast, traceConfig: TraceConfig) = new SqlQueryApply(traceConfig)(ast) diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala index 6f89db003b..1036367ed1 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala @@ -39,23 +39,15 @@ object CompilePhaseCaches { } trait SqlNormalizeCaches { - def mainCache: StatelessCache def normCaches: NormalizeCaches def phaseCaches: CompilePhaseCaches } object SqlNormalizeCaches { def unlimitedLocal() = new SqlNormalizeCaches { - val mainCache = StatelessCache.NoCache val normCaches = NormalizeCaches.unlimitedCache() val phaseCaches = CompilePhaseCaches.unlimitedCache() } - - object Global extends SqlNormalizeCaches { - val mainCache = StatelessCache.NoCache - val normCaches = NormalizeCaches.noCache - val phaseCaches = CompilePhaseCaches.unlimitedCache() - } } class SqlNormalize( @@ -126,7 +118,7 @@ class SqlNormalize( def apply(ast: Ast) = { val (stableAst, state) = StabilizeLifts.stabilize(ast) val stats = CollectStats(stableAst) - val outputAst = caches.mainCache.getOrCache(stableAst, normalize(stats)(stableAst)) + val outputAst = normalize(stats)(stableAst) StabilizeLifts.revert(outputAst, state) } } From f247b6cb50c2424bf58f92b77ad8eb9d7c5424ad Mon Sep 17 00:00:00 2001 From: Alexander Ioffe Date: Sun, 22 Dec 2024 22:11:55 -0500 Subject: [PATCH 14/14] Refining cache support --- .../scala/io/getquill/norm/Normalize.scala | 2 +- .../sql/idiom/BooleanLiteralSupport.scala | 2 +- .../io/getquill/sql/idiom/SqlIdiom.scala | 8 +++++++- .../io/getquill/sql/norm/SqlNormalize.scala | 19 +++++++++++++++++-- .../scala/io/getquill/util/Messages.scala | 6 ++++++ 5 files changed, 32 insertions(+), 5 deletions(-) diff --git a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala index f5ac087baa..2ad87e641e 100644 --- a/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala +++ b/quill-engine/src/main/scala/io/getquill/norm/Normalize.scala @@ -19,7 +19,7 @@ case class NormalizeCaches( orderTermsCache: StatelessCacheOpt ) object NormalizeCaches { - val noCache = + val NoCache = NormalizeCaches( StatelessCache.NoCache, StatefulCache.NoCache, diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala index ffd36980a1..8fe8fdcb92 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/BooleanLiteralSupport.scala @@ -20,7 +20,7 @@ trait BooleanLiteralSupport extends SqlIdiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = { - val norm = SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.unlimitedLocal()) + val norm = SqlNormalize(ast, idiomContext.config, makeCache(), concatBehavior, equalityBehavior) if (Messages.smartBooleans) VendorizeBooleans(norm) else diff --git a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala index 14e6b9947e..02422af211 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/idiom/SqlIdiom.scala @@ -25,6 +25,12 @@ trait SqlIdiom extends Idiom { def useActionTableAliasAs: ActionTableAliasBehavior = ActionTableAliasBehavior.UseAs + def makeCache() = + if (Messages.cacheNormalization) + SqlNormalizeCaches.unlimitedLocal() + else + SqlNormalizeCaches.NoCache + override def prepareForProbing(string: String): String protected def concatBehavior: ConcatBehavior = AnsiConcat @@ -42,7 +48,7 @@ trait SqlIdiom extends Idiom { equalityBehavior: EqualityBehavior, idiomContext: IdiomContext ) = - SqlNormalize(ast, idiomContext.config, concatBehavior, equalityBehavior, SqlNormalizeCaches.unlimitedLocal()) + SqlNormalize(ast, idiomContext.config, makeCache(), concatBehavior, equalityBehavior) def querifyAst(ast: Ast, traceConfig: TraceConfig) = new SqlQueryApply(traceConfig)(ast) diff --git a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala index 1036367ed1..81081da835 100644 --- a/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala +++ b/quill-engine/src/main/scala/io/getquill/sql/norm/SqlNormalize.scala @@ -13,9 +13,9 @@ object SqlNormalize { def apply( ast: Ast, transpileConfig: TranspileConfig, + caches: SqlNormalizeCaches, concatBehavior: ConcatBehavior = AnsiConcat, - equalityBehavior: EqualityBehavior = AnsiEquality, - caches: SqlNormalizeCaches = SqlNormalizeCaches.unlimitedLocal() + equalityBehavior: EqualityBehavior = AnsiEquality ) = new SqlNormalize(concatBehavior, equalityBehavior, transpileConfig, caches)(ast) } @@ -28,6 +28,15 @@ case class CompilePhaseCaches( simplifyNullChecksCache: StatelessCache ) object CompilePhaseCaches { + val NoCache = + CompilePhaseCaches( + StatelessCache.NoCache, + StatelessCache.NoCache, + StatelessCache.NoCache, + StatelessCache.NoCache, + StatelessCache.NoCache + ) + def unlimitedCache() = CompilePhaseCaches( StatelessCache.Unlimited(), @@ -43,6 +52,12 @@ trait SqlNormalizeCaches { def phaseCaches: CompilePhaseCaches } object SqlNormalizeCaches { + val NoCache = + new SqlNormalizeCaches { + val normCaches = NormalizeCaches.NoCache + val phaseCaches = CompilePhaseCaches.NoCache + } + def unlimitedLocal() = new SqlNormalizeCaches { val normCaches = NormalizeCaches.unlimitedCache() diff --git a/quill-engine/src/main/scala/io/getquill/util/Messages.scala b/quill-engine/src/main/scala/io/getquill/util/Messages.scala index 32b7098bba..adf24ccec7 100644 --- a/quill-engine/src/main/scala/io/getquill/util/Messages.scala +++ b/quill-engine/src/main/scala/io/getquill/util/Messages.scala @@ -45,6 +45,12 @@ object Messages { cache("quill.trace.ast.simple", variable("quill.trace.ast.simple", "quill_trace_ast_simple", "false").toBoolean) def traceQuats = cache("quill.trace.quat", QuatTrace(variable("quill.trace.quat", "quill_trace_quat", QuatTrace.None.value))) + + def cacheNormalization = cache( + "quill.cache.norm", + variable("quill.cache.norm", "quill_cache_norm", "true").toBoolean + ) + def cacheDynamicQueries = cache( "quill.query.cacheDynamic", variable("quill.query.cacheDynamic", "query_query_cacheDynamic", "true").toBoolean