diff --git a/build.sbt b/build.sbt index 0d27ae2..f17c0fa 100644 --- a/build.sbt +++ b/build.sbt @@ -6,15 +6,8 @@ scalaVersion := "3.3.3" crossScalaVersions := List("2.12.19", "2.13.13", "3.3.3") -libraryDependencies += { - if (scalaBinaryVersion.value == "3") { - "com.typesafe.slick" %% "slick" % "3.5.0-RC1" - } else { - "com.typesafe.slick" %% "slick" % "3.4.1" - } -} - libraryDependencies ++= Seq( + "com.typesafe.slick" %% "slick" % "3.5.0", "com.dimafeng" %% "testcontainers-scala" % "0.41.3" % "test", "org.testcontainers" % "mysql" % "1.19.7" % "test", "com.mysql" % "mysql-connector-j" % "8.3.0" % "test", @@ -33,6 +26,14 @@ publishTo := { scalacOptions := Seq("-deprecation", "-feature") +scalacOptions ++= { + if (scalaBinaryVersion.value != "3") { + Seq("-Xsource:3") + } else { + Nil + } +} + Test / scalacOptions ++= { if (scalaBinaryVersion.value == "3") { Seq("-source:3.0-migration") diff --git a/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingOracleDriver.scala b/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingOracleDriver.scala deleted file mode 100644 index fc2488d..0000000 --- a/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingOracleDriver.scala +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.takezoe.slick.blocking - -import slick.jdbc.OracleProfile - -object BlockingOracleDriver extends OracleProfile with BlockingJdbcProfile diff --git a/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingProfile.scala b/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingProfile.scala deleted file mode 100644 index f92c301..0000000 --- a/src/main/scala-2/com/github/takezoe/slick/blocking/BlockingProfile.scala +++ /dev/null @@ -1,344 +0,0 @@ -package com.github.takezoe.slick.blocking - -import java.sql.Connection -import slick.ast.Node -import slick.basic.BasicAction -import slick.basic.BasicStreamingAction -import slick.dbio._ -import slick.jdbc.ActionBasedSQLInterpolation -import slick.jdbc.JdbcBackend -import slick.jdbc.JdbcProfile -import slick.lifted.RunnableCompiled -import slick.relational._ -import scala.language.existentials -import scala.language.higherKinds -import scala.language.implicitConversions -import scala.util._ - -trait BlockingRelationalProfile extends RelationalProfile { - trait BlockingAPI extends API {} -} - -trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { - val blockingApi = new BlockingAPI {} - - trait BlockingAPI extends super.BlockingAPI with ImplicitColumnTypes with slick.JdbcProfileBlockingSession { - - implicit def actionBasedSQLInterpolation(s: StringContext): ActionBasedSQLInterpolation = - new ActionBasedSQLInterpolation(s) - private class BlockingJdbcActionContext(s: JdbcBackend#Session) extends backend.JdbcActionContext { - val useSameThread = true - override def session = s.asInstanceOf[backend.Session] - override def connection: Connection = s.conn - } - - /** - * Extends DDL to add methods to create and drop tables immediately. - */ - implicit class DDLInvoker(schema: DDL) { - def create(implicit s: JdbcBackend#Session): Unit = { - createSchemaActionExtensionMethods(schema).create - .asInstanceOf[SynchronousDatabaseAction[Unit, NoStream, Backend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - - def remove(implicit s: JdbcBackend#Session): Unit = { - createSchemaActionExtensionMethods(schema).drop - .asInstanceOf[SynchronousDatabaseAction[Unit, NoStream, Backend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - } - - implicit class RepQueryExecutor[E](rep: Rep[E]) { - private val invoker = new QueryInvoker[E](queryCompiler.run(Query(rep)(slick.lifted.RepShape).toNode).tree, ()) - - def run(implicit s: JdbcBackend#Session): E = invoker.first - def selectStatement: String = invoker.selectStatement - } - implicit class QueryExecutor[U, C[_]](q: Query[?, U, C]) { - private val invoker = new QueryInvoker[U](queryCompiler.run(q.toNode).tree, ()) - - def run(implicit s: JdbcBackend#Session): Seq[U] = invoker.results(0).right.get.toSeq - def selectStatement: String = invoker.selectStatement - } - - implicit class RunnableCompiledQueryExecutor[U, C[_]](c: RunnableCompiled[? <: Query[?, ?, C], C[U]]) { - private val invoker = new QueryInvoker[U](c.compiledQuery, c.param) - - def run(implicit s: JdbcBackend#Session): Seq[U] = invoker.invoker.results(0).right.get.toSeq - def selectStatement: String = invoker.selectStatement - } - - /** - * Extends QueryInvokerImpl to add selectStatement method. - */ - class QueryInvoker[R](tree: Node, param: Any) extends QueryInvokerImpl[R](tree, param, null) { - def selectStatement: String = getStatement - } - - class BlockingQueryInvoker[U](tree: Node, param: Any) { - def selectStatement: String = { - val invoker = new QueryInvoker[U](tree, param) - invoker.selectStatement - } - def list(implicit s: JdbcBackend#Session): List[U] = { - val invoker = new QueryInvoker[U](tree, param) - invoker.results(0).right.get.toList - } - - def first(implicit s: JdbcBackend#Session): U = { - val invoker = new QueryInvoker[U](tree, param) - invoker.first - } - - def firstOption(implicit s: JdbcBackend#Session): Option[U] = { - val invoker = new QueryInvoker[U](tree, param) - invoker.firstOption - } - - def foreach(b: U => Unit)(implicit s: JdbcBackend#Session): Unit = { - val invoker = new QueryInvoker[U](tree, param) - invoker.results(0).right.get.foreach(b) - } - - def foldLeft[R](z: R)(f: (R, U) => R)(implicit s: JdbcBackend#Session): R = { - val invoker = new QueryInvoker[U](tree, param) - invoker.results(0).right.get.foldLeft(z)(f) - } - } - implicit def queryToQueryInvoker[U, C[_]](q: Query[?, U, C]): BlockingQueryInvoker[U] = - new BlockingQueryInvoker[U](queryCompiler.run(q.toNode).tree, ()) - implicit def compiledToQueryInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[?, ?, C], C[U]] - ): BlockingQueryInvoker[U] = - new BlockingQueryInvoker[U](c.compiledQuery, c.param) - - class BlockingDeleteInvoker(protected val tree: Node, param: Any) { - def deleteStatement = createDeleteActionExtensionMethods(tree, param).delete.statements.head - - def delete(implicit s: JdbcBackend#Session): Int = { - createDeleteActionExtensionMethods(tree, param).delete - .asInstanceOf[SynchronousDatabaseAction[Int, NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - - def deleteInvoker: this.type = this - } - implicit def queryToDeleteInvoker[U, C[_]](q: Query[?, U, C]): BlockingDeleteInvoker = - new BlockingDeleteInvoker(deleteCompiler.run(q.toNode).tree, ()) - implicit def compiledToDeleteInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[?, ?, C], C[U]] - ): BlockingDeleteInvoker = - new BlockingDeleteInvoker(c.compiledDelete, c.param) - - class MapInvoker[A, B](tree: slick.ast.Node, param: Any) { - def selectStatement: String = { - val invoker = new QueryInvoker[(A, B)](tree, param) - invoker.selectStatement - } - - def toMap(implicit s: JdbcBackend#Session): Map[A, B] = { - val invoker = new QueryInvoker[(A, B)](tree, param) - invoker.results(0).right.get.toMap - } - } - implicit def mapInvoker[A, B, C[_]](q: Query[?, (A, B), C]): MapInvoker[A, B] = - new MapInvoker[A, B](queryCompiler.run(q.toNode).tree, ()) - implicit def compiledMapInvoker[A, B, C[_]]( - c: RunnableCompiled[? <: Query[?, ?, C], C[(A, B)]] - ): MapInvoker[A, B] = - new MapInvoker[A, B](c.compiledQuery, c.param) - - class BlockingUpdateInvoker[U](tree: Node, param: Any) { - def updateStatement = createUpdateActionExtensionMethods(tree, param).updateStatement - - def update(value: U)(implicit s: JdbcBackend#Session): Int = { - createUpdateActionExtensionMethods(tree, param) - .update(value) - .asInstanceOf[SynchronousDatabaseAction[Int, NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - - def updateInvoker: this.type = this - } - implicit def queryToUpdateInvoker[U, C[_]](q: Query[?, U, C]): BlockingUpdateInvoker[U] = - new BlockingUpdateInvoker[U](updateCompiler.run(q.toNode).tree, ()) - implicit def compiledToUpdateInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[?, ?, C], C[U]] - ): BlockingUpdateInvoker[U] = - new BlockingUpdateInvoker[U](c.compiledUpdate, c.param) - - class BlockingInsertInvoker[U](compiled: CompiledInsert) { - - def +=(value: U)(implicit session: JdbcBackend#Session): Int = insert(value) - - def insert(value: U)(implicit s: JdbcBackend#Session): Int = { - createInsertActionExtensionMethods(compiled) - .+=(value) - .asInstanceOf[SynchronousDatabaseAction[Int, NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - - def ++=(values: Iterable[U])(implicit s: JdbcBackend#Session): Int = insertAll(values.toSeq: _*) - - def insertAll(values: U*)(implicit s: JdbcBackend#Session): Int = { - createInsertActionExtensionMethods(compiled) - .++=(values) - .asInstanceOf[SynchronousDatabaseAction[Option[Int], NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - .getOrElse(0) - } - - def insertOrUpdate(value: U)(implicit s: JdbcBackend#Session): Int = { - createInsertActionExtensionMethods(compiled) - .insertOrUpdate(value) - .asInstanceOf[SynchronousDatabaseAction[Int, NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - - def insertInvoker: this.type = this - } - implicit def queryToInsertInvoker[U, C[_]](q: Query[?, U, C]): BlockingInsertInvoker[U] = - new BlockingInsertInvoker[U](compileInsert(q.toNode)) - implicit def compiledToInsertInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[?, ?, C], C[U]] - ): BlockingInsertInvoker[U] = - new BlockingInsertInvoker[U](c.compiledInsert.asInstanceOf[CompiledInsert]) - - implicit class ReturningInsertActionComposer2[T, R](a: ReturningInsertActionComposer[T, R]) { - - def +=(value: T)(implicit s: JdbcBackend#Session): R = insert(value) - - def insert(value: T)(implicit s: JdbcBackend#Session): R = { - (a += value) match { - case a: SynchronousDatabaseAction[R, ?, JdbcBackend, ?] @unchecked => { - a.run(new BlockingJdbcActionContext(s)) - } - } - } - - def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq: _*) - - def insertAll(values: T*)(implicit s: JdbcBackend#Session): Seq[R] = { - (a ++= values) match { - case a: SynchronousDatabaseAction[Seq[R], ?, JdbcBackend, ?] @unchecked => { - a.run(new BlockingJdbcActionContext(s)) - } - } - } - - } - - implicit class IntoInsertActionComposer2[T, R](a: IntoInsertActionComposer[T, R]) { - def +=(value: T)(implicit s: JdbcBackend#Session): R = insert(value) - - def insert(value: T)(implicit s: JdbcBackend#Session): R = { - (a += value) match { - case a: SynchronousDatabaseAction[R, ?, JdbcBackend, ?] @unchecked => { - a.run(new BlockingJdbcActionContext(s)) - } - } - } - - def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq: _*) - - def insertAll(values: T*)(implicit s: JdbcBackend#Session): Seq[R] = { - (a ++= values) match { - case a: SynchronousDatabaseAction[Seq[R], ?, JdbcBackend, ?] @unchecked => { - a.run(new BlockingJdbcActionContext(s)) - } - } - } - - } - - /** - * Extends Database to add methods for session management. - */ - implicit class BlockingDatabase(db: JdbcBackend#DatabaseDef) { - - def withSession[T](f: (JdbcBackend#Session) => T): T = { - val session = db.createSession() - try { - f(session) - } finally { - session.close() - } - } - - def withTransaction[T](f: (JdbcBackend#Session) => T): T = - withSession { s => s.withTransaction(f(s)) } - } - - implicit class BasicStreamingActionInvoker[R, E <: Effect](action: BasicStreamingAction[Vector[R], R, E]) { - def first(implicit s: JdbcBackend#Session): R = { - action.head - .asInstanceOf[SynchronousDatabaseAction[R, NoStream, JdbcBackend, E]] - .run(new BlockingJdbcActionContext(s)) - } - def firstOption(implicit s: JdbcBackend#Session): Option[R] = { - action.headOption - .asInstanceOf[SynchronousDatabaseAction[Option[R], NoStream, JdbcBackend, E]] - .run(new BlockingJdbcActionContext(s)) - } - def list(implicit s: JdbcBackend#Session): List[R] = { - action - .asInstanceOf[SynchronousDatabaseAction[Vector[R], Streaming[R], JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - .toList - } - } - - implicit class BasicActionInvoker[R](action: BasicAction[R, NoStream, Effect]) { - def execute(implicit s: JdbcBackend#Session): R = { - action - .asInstanceOf[SynchronousDatabaseAction[R, NoStream, JdbcBackend, Effect]] - .run(new BlockingJdbcActionContext(s)) - } - } - - /** - * Extends plain db queries - */ - implicit class RichDBIOAction[R, E <: Effect](action: DBIOAction[R, NoStream, E]) { - - def executeAction[T]( - action: DBIOAction[T, NoStream, E], - ctx: backend.JdbcActionContext, - streaming: Boolean, - topLevel: Boolean - ): T = action match { - case a: SynchronousDatabaseAction[?, ?, JdbcBackend, Effect] => a.run(ctx).asInstanceOf[T] - case FlatMapAction(base, f, ec) => - val result = executeAction(base, ctx, false, topLevel) - executeAction(f(result), ctx, streaming, false) - case AndThenAction(actions) => - val last = actions.length - 1 - val results = actions.zipWithIndex.map { case (action, pos) => - executeAction(action, ctx, streaming && pos == last, pos == 0) - } - results.last.asInstanceOf[T] - case SequenceAction(dbios) => dbios.map(dbio => executeAction(dbio, ctx, streaming, topLevel)).asInstanceOf[T] - case CleanUpAction(base, f, keepFailure, ec) => - val t1 = Try(executeAction(base, ctx, streaming, topLevel)) - - val a2 = f(t1 match { - case Success(_) => None - case Failure(t) => Some(t) - }) - val t2 = Try(executeAction(a2, ctx, streaming, topLevel)) - - t2 match { - case Failure(e) if t1.isSuccess || !keepFailure => throw e - case _ => - t1 match { - case Success(r) => r - case Failure(e) => throw e - } - } - } - - def run(implicit s: JdbcBackend#Session): R = executeAction(action, new BlockingJdbcActionContext(s), false, true) - } - } -} diff --git a/src/main/scala-3/com/github/takezoe/slick/blocking/BlockingProfile.scala b/src/main/scala/com/github/takezoe/slick/blocking/BlockingProfile.scala similarity index 96% rename from src/main/scala-3/com/github/takezoe/slick/blocking/BlockingProfile.scala rename to src/main/scala/com/github/takezoe/slick/blocking/BlockingProfile.scala index 816b88f..d790a67 100644 --- a/src/main/scala-3/com/github/takezoe/slick/blocking/BlockingProfile.scala +++ b/src/main/scala/com/github/takezoe/slick/blocking/BlockingProfile.scala @@ -61,7 +61,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { def selectStatement: String = invoker.selectStatement } - implicit class RunnableCompiledQueryExecutor[U, C[_]](c: RunnableCompiled[? <: Query[_, _, C], C[U]]) { + implicit class RunnableCompiledQueryExecutor[U, C[_]](c: RunnableCompiled[? <: Query[?, ?, C], C[U]]) { private val invoker = new QueryInvoker[U](c.compiledQuery, c.param) def run(implicit s: JdbcBackend#Session): Seq[U] = invoker.invoker.results(0).right.get.toSeq @@ -108,7 +108,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { implicit def queryToQueryInvoker[U, C[_]](q: Query[?, U, C]): BlockingQueryInvoker[U] = new BlockingQueryInvoker[U](queryCompiler.run(q.toNode).tree, ()) implicit def compiledToQueryInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[_, _, C], C[U]] + c: RunnableCompiled[? <: Query[?, ?, C], C[U]] ): BlockingQueryInvoker[U] = new BlockingQueryInvoker[U](c.compiledQuery, c.param) @@ -126,7 +126,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { implicit def queryToDeleteInvoker[U, C[_]](q: Query[?, U, C]): BlockingDeleteInvoker = new BlockingDeleteInvoker(deleteCompiler.run(q.toNode).tree, ()) implicit def compiledToDeleteInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[_, _, C], C[U]] + c: RunnableCompiled[? <: Query[?, ?, C], C[U]] ): BlockingDeleteInvoker = new BlockingDeleteInvoker(c.compiledDelete, c.param) @@ -146,7 +146,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { new MapInvoker[A, B](queryCompiler.run(q.toNode).tree, ()) implicit def compiledMapInvoker[A, B, C[_]]( - c: RunnableCompiled[? <: Query[_, _, C], C[(A, B)]] + c: RunnableCompiled[? <: Query[?, ?, C], C[(A, B)]] ): MapInvoker[A, B] = new MapInvoker[A, B](c.compiledQuery, c.param) @@ -165,7 +165,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { implicit def queryToUpdateInvoker[U, C[_]](q: Query[?, U, C]): BlockingUpdateInvoker[U] = new BlockingUpdateInvoker[U](updateCompiler.run(q.toNode).tree, ()) implicit def compiledToUpdateInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[_, _, C], C[U]] + c: RunnableCompiled[? <: Query[?, ?, C], C[U]] ): BlockingUpdateInvoker[U] = new BlockingUpdateInvoker[U](c.compiledUpdate, c.param) @@ -180,7 +180,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { .run(new BlockingJdbcActionContext(s)) } - def ++=(values: Iterable[U])(implicit s: JdbcBackend#Session): Int = insertAll(values.toSeq*) + def ++=(values: Iterable[U])(implicit s: JdbcBackend#Session): Int = insertAll(values.toSeq *) def insertAll(values: U*)(implicit s: JdbcBackend#Session): Int = { createInsertActionExtensionMethods(compiled) @@ -202,7 +202,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { implicit def queryToInsertInvoker[U, C[_]](q: Query[?, U, C]): BlockingInsertInvoker[U] = new BlockingInsertInvoker[U](compileInsert(q.toNode)) implicit def compiledToInsertInvoker[U, C[_]]( - c: RunnableCompiled[? <: Query[_, _, C], C[U]] + c: RunnableCompiled[? <: Query[?, ?, C], C[U]] ): BlockingInsertInvoker[U] = new BlockingInsertInvoker[U](c.compiledInsert.asInstanceOf[CompiledInsert]) @@ -218,7 +218,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { } } - def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq*) + def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq *) def insertAll(values: T*)(implicit s: JdbcBackend#Session): Seq[R] = { (a ++= values) match { @@ -241,7 +241,7 @@ trait BlockingJdbcProfile extends JdbcProfile with BlockingRelationalProfile { } } - def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq*) + def ++=(values: Iterable[T])(implicit s: JdbcBackend#Session): Seq[R] = insertAll(values.toSeq *) def insertAll(values: T*)(implicit s: JdbcBackend#Session): Seq[R] = { (a ++= values) match { diff --git a/src/test/scala/com/github/takezoe/slick/blocking/SlickBlockingAPISpec.scala b/src/test/scala/com/github/takezoe/slick/blocking/SlickBlockingAPISpec.scala index 2c53213..f11075c 100644 --- a/src/test/scala/com/github/takezoe/slick/blocking/SlickBlockingAPISpec.scala +++ b/src/test/scala/com/github/takezoe/slick/blocking/SlickBlockingAPISpec.scala @@ -177,15 +177,27 @@ abstract class SlickBlockingAPISpec(p: BlockingJdbcProfile) extends AnyFunSuite test("insert multiple returning") { testWithSession { implicit session => - val id = Users.returning(Users.map(_.id)) insertAll (UsersRow(1, "takezoe", None), UsersRow(2, "mrfyda", None)) - assert(id == List(1, 2)) + val id = + Users + .returning(Users.map(_.id)) + .insertAll( + Seq( + UsersRow(1, "takezoe", None), + UsersRow(2, "mrfyda", None) + ) + ) + .run assert(Users.length.run == 2) - val u = (Users.returning(Users.map(_.id)).into((u, id) => u.copy(id = id))) insertAll (UsersRow( - 3, - "takezoe", - None - ), UsersRow(4, "mrfyda", None)) - assert(u.map(_.id) == List(3, 4)) + val u = Users + .returning(Users.map(_.id)) + .into((u, id) => u.copy(id = id)) + .insertAll( + Seq( + UsersRow(3, "takezoe", None), + UsersRow(4, "mrfyda", None) + ) + ) + .run assert(Users.length.run == 4) } }