diff --git a/TODO.md b/TODO.md index f5f20a2..3a02ea7 100644 --- a/TODO.md +++ b/TODO.md @@ -1,4 +1,8 @@ + +jsonb + +- include/exclude tables - override types - paging diff --git a/build.sc b/build.sc index d7ba3bf..a0b809e 100644 --- a/build.sc +++ b/build.sc @@ -12,30 +12,40 @@ import io.kipp.mill.ci.release.CiReleaseModule import ba.sake.millhepek.MillHepekModule val scala213 = "2.13.14" - -object squery extends CommonScalaModule with CiReleaseModule { - - def pomSettings = PomSettings( - organization = "ba.sake", - url = "https://github.com/sake92/squery", - licenses = Seq(License.Common.Apache2), - versionControl = VersionControl.github("sake92", "squery"), - description = "Squery SQL library", - developers = Seq( - Developer("sake92", "Sakib Hadžiavdić", "https://sake.ba") - ) +val scala3 = "3.4.2" +val pomSettingsValue = PomSettings( + organization = "ba.sake", + url = "https://github.com/sake92/squery", + licenses = Seq(License.Common.Apache2), + versionControl = VersionControl.github("sake92", "squery"), + description = "Squery SQL library", + developers = Seq( + Developer("sake92", "Sakib Hadžiavdić", "https://sake.ba") ) +) +val millVersion = "0.11.12" +object squery extends ScalaModule with ScalafmtModule with CiReleaseModule { def artifactName = "squery" - + def pomSettings = pomSettingsValue + def scalaVersion = scala3 def ivyDeps = Agg( ivy"com.typesafe.scala-logging::scala-logging:3.9.4", ivy"com.github.jsqlparser:jsqlparser:4.7", ivy"org.scala-lang.modules::scala-collection-contrib:0.3.0" ) - def scalacOptions = Seq("-Ywarn-unused", "-deprecation", "-feature") + object `postgres-jawn` extends ScalaModule with ScalafmtModule with CiReleaseModule { + def artifactName = "squery-postgres-jawn" + def pomSettings = pomSettingsValue + def scalaVersion = scala3 + def moduleDeps = Seq(squery) + def ivyDeps = Agg( + ivy"org.typelevel::jawn-ast::1.6.0" + ) + } + object test extends ScalaTests with TestModule.Munit { def ivyDeps = Agg( ivy"org.scalameta::munit:1.0.0", @@ -55,22 +65,10 @@ object squery extends CommonScalaModule with CiReleaseModule { } } -object generator extends ScalaModule with CiReleaseModule { +object generator extends ScalaModule with CiReleaseModule with ScalafmtModule { def artifactName = "squery-generator" - + def pomSettings = pomSettingsValue def scalaVersion = scala213 - - def pomSettings = PomSettings( - organization = "ba.sake", - url = "https://github.com/sake92/squery", - licenses = Seq(License.Common.Apache2), - versionControl = VersionControl.github("sake92", "squery"), - description = "Squery Generator library", - developers = Seq( - Developer("sake92", "Sakib Hadžiavdić", "https://sake.ba") - ) - ) - def ivyDeps = Agg( ivy"ba.sake::regenesca:0.1.0", ivy"com.typesafe.scala-logging::scala-logging:3.9.4", @@ -80,33 +78,18 @@ object generator extends ScalaModule with CiReleaseModule { } /* MILL PLUGIN */ -val millVersion = "0.11.12" object `mill-plugin` extends ScalaModule with CiReleaseModule with ScalafmtModule { - + def artifactName = + s"mill-squery-generator_mill${millBinaryVersion(millVersion)}" def millBinaryVersion(millVersion: String) = scalaNativeBinaryVersion(millVersion) - + def pomSettings = pomSettingsValue def scalaVersion = scala213 - - def artifactName = - s"mill-squery-generator_mill${millBinaryVersion(millVersion)}" - - def pomSettings = PomSettings( - description = "Mill plugin for generating squery source code", - organization = "ba.sake", - url = "https://github.com/sake92/squery", - licenses = Seq(License.`Apache-2.0`), - versionControl = VersionControl.github(owner = "sake92", repo = "squery"), - developers = Seq(Developer("sake92", "Sakib Hadziavdic", "https://github.com/sake92")) - ) - def compileIvyDeps = super.compileIvyDeps() ++ Agg( ivy"com.lihaoyi::mill-scalalib:${millVersion}" ) - def moduleDeps = Seq(generator) - def ivyDeps = Agg( ivy"com.h2database:h2:2.3.232", ivy"org.postgresql:postgresql:42.7.4", @@ -114,30 +97,19 @@ object `mill-plugin` extends ScalaModule with CiReleaseModule with ScalafmtModul ivy"org.mariadb.jdbc:mariadb-java-client:3.3.2", ivy"com.oracle.database.jdbc:ojdbc8:23.3.0.23.09" ) - def scalacOptions = Seq("-Ywarn-unused", "-deprecation") - } object `mill-plugin-itest` extends MillIntegrationTestModule { - def millTestVersion = millVersion - def pluginsUnderTest = Seq(`mill-plugin`) - def temporaryIvyModules = Seq(squery) - def testBase = millSourcePath / "src" - - def testInvocations: T[Seq[(PathRef, Seq[TestInvocation.Targets])]] = - T { - Seq( - PathRef(testBase / "h2") -> Seq( - TestInvocation.Targets(Seq("verify"), noServer = true) - ) - ) - } - + def testInvocations: T[Seq[(PathRef, Seq[TestInvocation.Targets])]] = T { + Seq( + PathRef(testBase / "h2") -> Seq(TestInvocation.Targets(Seq("verify"), noServer = true)) + ) + } override def perTestResources = T.sources { os.write( T.dest / "versions.sc", @@ -150,12 +122,9 @@ object `mill-plugin-itest` extends MillIntegrationTestModule { } /* DOCS */ -object docs extends CommonScalaModule with MillHepekModule { +object docs extends ScalaModule with MillHepekModule with ScalafmtModule { + def scalaVersion = scala3 def ivyDeps = Agg( ivy"ba.sake::hepek:0.24.1" ) } - -trait CommonScalaModule extends ScalaModule with ScalafmtModule { - def scalaVersion = "3.4.0" -} diff --git a/docs/src/files/tutorials/CodeGen.scala b/docs/src/files/tutorials/CodeGen.scala index e7888d5..c2a4372 100644 --- a/docs/src/files/tutorials/CodeGen.scala +++ b/docs/src/files/tutorials/CodeGen.scala @@ -40,8 +40,13 @@ object CodeGen extends TutorialPage { import $$ivy.`org.postgresql:postgresql:42.7.4` import $$ivy.`com.zaxxer:HikariCP:5.1.0` import ba.sake.squery.generator.* + import com.zaxxer.hikari.HikariDataSource - val dataSource = com.zaxxer.hikari.HikariDataSource() + // if using Postgres JSONB + // import $$ivy.`ba.sake::squery-postgres-jawn:${Consts.ArtifactVersion}` + // import ba.sake.squery.postgres.jawn.* + + val dataSource = HikariDataSource() dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb") dataSource.setUsername("username") dataSource.setPassword("password") @@ -55,7 +60,6 @@ object CodeGen extends TutorialPage { ctx.run { MyTableCrudDao.findAll() } - """.md ) diff --git a/generator/src/ba/sake/squery/generator/PostgresDefExtractor.scala b/generator/src/ba/sake/squery/generator/PostgresDefExtractor.scala index 7352691..8a4a9a1 100644 --- a/generator/src/ba/sake/squery/generator/PostgresDefExtractor.scala +++ b/generator/src/ba/sake/squery/generator/PostgresDefExtractor.scala @@ -98,6 +98,8 @@ class PostgresDefExtractor(ds: DataSource) extends DbDefExtractor(ds) { case "timestamp with time zone" => ColumnType.Predefined(t"Instant") case "uuid" => ColumnType.Predefined(t"UUID") case "bytea" => ColumnType.Predefined(t"Array[Byte]") + case "json" => ColumnType.Predefined(t"org.typelevel.jawn.ast.JValue") + case "jsonb" => ColumnType.Predefined(t"org.typelevel.jawn.ast.JValue") case other => throw new RuntimeException(s"Unknown scalar type ${other}") } } diff --git a/generator/src/ba/sake/squery/generator/SqueryGenerator.scala b/generator/src/ba/sake/squery/generator/SqueryGenerator.scala index d9759c9..3f29937 100644 --- a/generator/src/ba/sake/squery/generator/SqueryGenerator.scala +++ b/generator/src/ba/sake/squery/generator/SqueryGenerator.scala @@ -35,6 +35,7 @@ class SqueryGenerator(ds: DataSource, config: SqueryGeneratorConfig = SqueryGene logger.info(s"Started generating schema '${schemaName}'") val (modelFiles, daoFiles) = generateSchema(schemaDef, dbType = dbDef.tpe, basePackage = "", fileGen = false) + // models first because of Ammonite eval order! val allFiles = modelFiles ++ daoFiles val allSources = generateBaseImports(dbDef.tpe).map(_.syntax) ++ allFiles.map(_.source.syntax) logger.info(s"Finished generating schema '${schemaName}'") @@ -86,7 +87,7 @@ class SqueryGenerator(ds: DataSource, config: SqueryGeneratorConfig = SqueryGene _.columnDefs.map(_.scalaType).collect { case e: ColumnType.Enumeration => e } - } + }.distinctBy(_.name) val enumFiles = enumDefs.map { enumDef => val enumCaseDefs = Defn.RepeatedEnumCase( List.empty, @@ -198,18 +199,20 @@ class SqueryGenerator(ds: DataSource, config: SqueryGeneratorConfig = SqueryGene """ } + // object goes first, coz class references PK type + // so that it works in ammonite where definitions are parsed 1 by 1 val source = if (fileGen) source""" package ${generatePkgSelect(s"${basePackage}.models")} ..${generateBaseImports(dbType)} - ${caseClassDefn} ${objectDefn} + ${caseClassDefn} """ else source""" - ${caseClassDefn} ${objectDefn} + ${caseClassDefn} """ GeneratedFileSource(Paths.get(s"${caseClassName}.scala"), source) } @@ -515,13 +518,13 @@ class SqueryGenerator(ds: DataSource, config: SqueryGeneratorConfig = SqueryGene source""" package ${generatePkgSelect(s"${basePackage}.daos")} ..${generateDaoImports(dbType, basePackage)} - ${daoClassDefn} ${daoObjectDefn} + ${daoClassDefn} """ else source""" - ${daoClassDefn} ${daoObjectDefn} + ${daoClassDefn} """ GeneratedFileSource(Paths.get(s"${daoClassName}.scala"), source) } diff --git a/squery/postgres-jawn/src/ba/sake/postgres/jawn/reads.scala b/squery/postgres-jawn/src/ba/sake/postgres/jawn/reads.scala new file mode 100644 index 0000000..f273b70 --- /dev/null +++ b/squery/postgres-jawn/src/ba/sake/postgres/jawn/reads.scala @@ -0,0 +1,13 @@ +package ba.sake.squery.postgres.jawn + +import java.{sql => jsql} +import org.typelevel.jawn.ast.* +import ba.sake.squery.read.* + +given SqlRead[JValue] with { + def readByName(jRes: jsql.ResultSet, colName: String): Option[JValue] = + SqlRead[String].readByName(jRes, colName).map(JParser.parseUnsafe) + + def readByIdx(jRes: jsql.ResultSet, colIdx: Int): Option[JValue] = + SqlRead[String].readByIdx(jRes, colIdx).map(JParser.parseUnsafe) +} diff --git a/squery/postgres-jawn/src/ba/sake/postgres/jawn/writes.scala b/squery/postgres-jawn/src/ba/sake/postgres/jawn/writes.scala new file mode 100644 index 0000000..f2d876c --- /dev/null +++ b/squery/postgres-jawn/src/ba/sake/postgres/jawn/writes.scala @@ -0,0 +1,15 @@ +package ba.sake.squery.postgres.jawn + +import java.{sql => jsql} +import org.typelevel.jawn.ast.* +import ba.sake.squery.write.* + +given SqlWrite[JValue] with { + def write( + ps: jsql.PreparedStatement, + idx: Int, + valueOpt: Option[JValue] + ): Unit = valueOpt match + case Some(value) => ps.setObject(idx, FastRenderer.render(value), jsql.Types.OTHER) + case None => ps.setNull(idx, jsql.Types.OTHER) +}