Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix query cancellation with cancelable #2079

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
run: sbt +update

- name: Start up Postgres
run: docker-compose up -d
run: docker compose up -d

- name: Check Headers
run: sbt '++ ${{ matrix.scala }}' headerCheckAll
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ ThisBuild / tlSonatypeUseLegacyHost := false
ThisBuild / githubWorkflowJavaVersions := Seq(JavaSpec.temurin("11"))
ThisBuild / githubWorkflowBuildPreamble ++= Seq(
WorkflowStep.Run(
commands = List("docker-compose up -d"),
commands = List("docker compose up -d"),
name = Some("Start up Postgres")
),
WorkflowStep.Sbt(
Expand Down Expand Up @@ -426,6 +426,7 @@ lazy val hikari = project
libraryDependencies ++= Seq(
// needs to be excluded, otherwise coursier may resolve slf4j-api 2 if > Java 11
"com.zaxxer" % "HikariCP" % hikariVersion exclude ("org.slf4j", "slf4j-api"),
"org.postgresql" % "postgresql" % postgresVersion % "test",
"com.h2database" % "h2" % h2Version % "test",
"org.slf4j" % "slf4j-api" % slf4jVersion,
"org.slf4j" % "slf4j-nop" % slf4jVersion % "test"
Expand Down
6 changes: 5 additions & 1 deletion modules/core/src/main/scala/doobie/hi/connection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@ object connection {
}

createLogged
.bracket(ps => IFC.embed(ps, prepLogged *> execAndProcessLogged))(ps => IFC.embed(ps, IFPS.close))
.bracket(ps =>
WeakAsyncConnectionIO.cancelable(
IFC.embed(ps, prepLogged *> execAndProcessLogged),
IFC.embed(ps, IFPS.close)
))(IFC.embed(_, IFPS.close))
}

/** Execute a PreparedStatement query and provide rows from the ResultSet in chunks
Expand Down
17 changes: 11 additions & 6 deletions modules/core/src/test/scala/doobie/util/QueryLogSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package doobie.util

import cats.syntax.all.*
import cats.effect.{IO, IOLocal}
import cats.effect.{IO, Ref}
import doobie.free.connection.ConnectionIO
import doobie.implicits.*
import doobie.util.log.Parameters.NonBatch
Expand All @@ -18,19 +18,24 @@ class QueryLogSuite extends munit.FunSuite with QueryLogSuitePlatform {

import cats.effect.unsafe.implicits.global

val ioLocal: IOLocal[LogEvent] =
IOLocal[LogEvent](null).unsafeRunSync()
val logEventRef: Ref[IO, LogEvent] =
Ref.of[IO, LogEvent](null).unsafeRunSync()

val xa = Transactor.fromDriverManager[IO](
"org.h2.Driver",
"jdbc:h2:mem:queryspec;DB_CLOSE_DELAY=-1",
"sa",
"",
logHandler = Some(ev => ioLocal.set(ev))
logHandler = Some(ev => logEventRef.set(ev))
)

def eventForCIO[A](cio: ConnectionIO[A]): LogEvent =
cio.transact(xa).attempt.flatMap(_ => ioLocal.get).unsafeRunSync()
def eventForCIO[A](cio: ConnectionIO[A]): LogEvent = {
for {
_ <- logEventRef.set(null)
_ <- cio.transact(xa).attempt
log <- logEventRef.get
} yield log
}.unsafeRunSync()

def successEventForCIO[A](cio: ConnectionIO[A]): Success =
eventForCIO(cio) match {
Expand Down
32 changes: 17 additions & 15 deletions modules/core/src/test/scala/doobie/util/UpdateLogSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

package doobie.util

import cats.effect.{IO, IOLocal}
import cats.effect.{IO, Ref}
import cats.syntax.all.*
import doobie.*
import doobie.implicits.*
Expand All @@ -18,28 +18,30 @@ class UpdateLogSuite extends munit.FunSuite {

import cats.effect.unsafe.implicits.global

val ioLocal: IOLocal[LogEvent] =
IOLocal[LogEvent](null).unsafeRunSync()
val logEventRef: Ref[IO, LogEvent] =
Ref.of[IO, LogEvent](null).unsafeRunSync()
Comment on lines +21 to +22
Copy link
Contributor Author

@TalkingFoxMid TalkingFoxMid Aug 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IOLocal is no longer works in logHandler because there is cancelable which forks a fiber in a middle of query processing and IOLocal context isn't shared between two fibers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we guarantee that IOLocal works with logHandler ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ouch yes we probably need to. One of our docuemented examples also uses IOLocal to pass additional context to LogHandler.
In a perfect world, I do feel like IO.cancelable implementation should pass on the IOLocal though..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a perfect world, I do feel like IO.cancelable implementation should pass on the IOLocal though..

In general, you cannot make these kinds of assumptions when using IOLocal with combinators. Further reading:

Copy link
Contributor Author

@TalkingFoxMid TalkingFoxMid Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a lot of problems with maintaining this feature:

  1. It is not very functional. I think the most appropriate way to do the same - use writer monads, and I suppose that is possible in current setup.
  2. It restricts ours capabilities to rely on CE3 functional combinators in Doobie code

To be honest, I think that IOLocal should be used exclusively in the internal code (as it is impure) of an application (or library). The API of a functional library should not provide (or at least guarantee that it will work fine) the use of this tool to users.

So I wish to stop maintaining this feature, remove code example and migrate users to more suitable instruments (like a writer monad or a reader with Ref in context). Is it possible?

Copy link
Contributor Author

@TalkingFoxMid TalkingFoxMid Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jatcwang

Please give a reference to place in the documentation where IOLocal is used. IOLocal can be used to pass context to LogHandler. But It can't be used to modify this context by logHandler if we use cancelable (in tests loghandler modifies IOLocal context, it puts log in IOLocal). I've found this example in doc:

import cats.effect.{IOLocal, Ref}
import doobie.util.log.Success

def users = List.range(0, 4).map(n => s"user-$n")

def program: IO[List[String]] =
  for {
    // define an IOLocal where we store the user which caused the query to be run  
    currentUser <- IOLocal("")
    // store all successful sql here, for all users
    successLogsRef <- Ref[IO].of(List.empty[String])
    xa = Transactor.fromDriverManager[IO](
        driver = "org.h2.Driver",
        url = "jdbc:h2:mem:queryspec;DB_CLOSE_DELAY=-1",
        user = "sa", 
        password = "",
        logHandler = Some(new LogHandler[IO] {
          def run(logEvent: LogEvent): IO[Unit] =
            currentUser.get.flatMap(user => successLogsRef.update(logs => s"sql for user $user: '${logEvent.sql}'" :: logs))
        })
      )
    // run a bunch of queries
    _ <- users.parTraverse(user =>
      for {
        _ <- currentUser.set(user)
        _ <- sql"select 1".query[Int].unique.transact(xa)
      } yield ()
    )
    // return collected log messages
    logs <- successLogsRef.get
  } yield logs

program.unsafeRunSync().sorted

It will work because child fibers inherit context of parents. So if we set up context in parrent fiber it can be accessed in loghandler. But if we modify context in loghandler this modification can't be propagated to a parent fiber. In tests there is modification of context in child fiber

Copy link
Contributor Author

@TalkingFoxMid TalkingFoxMid Aug 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider these two examples:

import cats.effect.{IO, IOApp, IOLocal}

object IOCancelTest extends IOApp.Simple {

  def childTask(local: IOLocal[Int]): IO[Unit] =
    for {
      _ <- local.get.flatTap(IO.println) // 42 inherits from parent fiber
    } yield ()

  override def run: IO[Unit] = for {
    local <- IOLocal(42)
    _ <- childTask(local).cancelable(IO(println("some finalizer")))
  } yield ()
}
import cats.effect.{IO, IOApp, IOLocal}

object IOCancelTest2 extends IOApp.Simple {

  def childTask(local: IOLocal[Int]): IO[Unit] =
    for {
      _ <- local.set(64)
    } yield ()

  override def run: IO[Unit] = for {
    local <- IOLocal(42)
    _ <- childTask(local).cancelable(IO(println("some finalizer")))
    _ <- local.get.flatTap(IO.println) // 42 even after update in child fiber
  } yield ()
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep you've found the example in the docs 👍

I think trying to modify IOLocal within a ConnectionIO should be quite rare. Since this is a general issue with IOLocal propagation it's not something we can solve here.

Thanks for updating the tests to use Ref 👍


val xa = Transactor.fromDriverManager[IO](
"org.h2.Driver",
"jdbc:h2:mem:queryspec;DB_CLOSE_DELAY=-1",
"sa",
"",
logHandler = Some(ev => ioLocal.set(ev))
logHandler = Some(ev => logEventRef.set(ev))
)

def eventForCIO[A](cio: ConnectionIO[A]): LogEvent =
(
sql"create table if not exists foo (c1 integer, c2 varchar)".update.run *> cio
)
.transact(xa)
.attempt
.flatMap { res =>
val _ = res
ioLocal.get
}
.unsafeRunSync()
def eventForCIO[A](cio: ConnectionIO[A]): LogEvent = {
logEventRef.set(null) *>
(
sql"create table if not exists foo (c1 integer, c2 varchar)".update.run *> cio
)
.transact(xa)
.attempt
.flatMap { res =>
val _ = res
logEventRef.get
}
}
.unsafeRunSync()

def successEventForCIO[A](cio: ConnectionIO[A]): Success =
eventForCIO(cio) match {
Expand Down
3 changes: 3 additions & 0 deletions modules/free/src/main/scala/doobie/WeakAsync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import scala.concurrent.duration.FiniteDuration
trait WeakAsync[F[_]] extends Sync[F] {
def fromFuture[A](fut: F[Future[A]]): F[A]
def fromFutureCancelable[A](fut: F[(Future[A], F[Unit])]): F[A]
def cancelable[A](fa: F[A], fin: F[Unit]): F[A]
}

object WeakAsync {
Expand All @@ -39,6 +40,8 @@ object WeakAsync {
override def onCancel[A](fa: F[A], fin: F[Unit]): F[A] = F.onCancel(fa, fin)
override def fromFuture[A](fut: F[Future[A]]): F[A] = F.fromFuture(fut)
override def fromFutureCancelable[A](fut: F[(Future[A], F[Unit])]): F[A] = F.fromFutureCancelable(fut)

override def cancelable[A](fa: F[A], fin: F[Unit]): F[A] = F.cancelable(fa, fin)
}

/** Create a natural transformation for lifting an `Async` effect `F` into a `WeakAsync` effect `G`
Expand Down
6 changes: 6 additions & 0 deletions modules/free/src/main/scala/doobie/free/blob.scala
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ object blob { module =>
def onCancel[A](fa: BlobIO[A], fin: BlobIO[Unit]): F[A]
def fromFuture[A](fut: BlobIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: BlobIO[(Future[A], BlobIO[Unit])]): F[A]
def cancelable[A](fa: BlobIO[A], fin: BlobIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// Blob
Expand Down Expand Up @@ -119,6 +120,9 @@ object blob { module =>
case class FromFutureCancelable[A](fut: BlobIO[(Future[A], BlobIO[Unit])]) extends BlobOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: BlobIO[A], fin: BlobIO[Unit]) extends BlobOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends BlobOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -181,6 +185,7 @@ object blob { module =>
def onCancel[A](fa: BlobIO[A], fin: BlobIO[Unit]) = FF.liftF[BlobOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: BlobIO[Future[A]]) = FF.liftF[BlobOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: BlobIO[(Future[A], BlobIO[Unit])]) = FF.liftF[BlobOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: BlobIO[A], fin: BlobIO[Unit]) = FF.liftF[BlobOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[BlobOp, Unit](PerformLogging(event))

// Smart constructors for Blob-specific operations.
Expand Down Expand Up @@ -216,6 +221,7 @@ object blob { module =>
override def onCancel[A](fa: BlobIO[A], fin: BlobIO[Unit]): BlobIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: BlobIO[Future[A]]): BlobIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: BlobIO[(Future[A], BlobIO[Unit])]): BlobIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: BlobIO[A], fin: BlobIO[Unit]): BlobIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidBlobIO[A : Monoid]: Monoid[BlobIO[A]] = new Monoid[BlobIO[A]] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ object callablestatement { module =>
def onCancel[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]): F[A]
def fromFuture[A](fut: CallableStatementIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: CallableStatementIO[(Future[A], CallableStatementIO[Unit])]): F[A]
def cancelable[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// CallableStatement
Expand Down Expand Up @@ -362,6 +363,9 @@ object callablestatement { module =>
case class FromFutureCancelable[A](fut: CallableStatementIO[(Future[A], CallableStatementIO[Unit])]) extends CallableStatementOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]) extends CallableStatementOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends CallableStatementOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -1090,6 +1094,7 @@ object callablestatement { module =>
def onCancel[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]) = FF.liftF[CallableStatementOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: CallableStatementIO[Future[A]]) = FF.liftF[CallableStatementOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: CallableStatementIO[(Future[A], CallableStatementIO[Unit])]) = FF.liftF[CallableStatementOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]) = FF.liftF[CallableStatementOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[CallableStatementOp, Unit](PerformLogging(event))

// Smart constructors for CallableStatement-specific operations.
Expand Down Expand Up @@ -1347,6 +1352,7 @@ object callablestatement { module =>
override def onCancel[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]): CallableStatementIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: CallableStatementIO[Future[A]]): CallableStatementIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: CallableStatementIO[(Future[A], CallableStatementIO[Unit])]): CallableStatementIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: CallableStatementIO[A], fin: CallableStatementIO[Unit]): CallableStatementIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidCallableStatementIO[A : Monoid]: Monoid[CallableStatementIO[A]] = new Monoid[CallableStatementIO[A]] {
Expand Down
6 changes: 6 additions & 0 deletions modules/free/src/main/scala/doobie/free/clob.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object clob { module =>
def onCancel[A](fa: ClobIO[A], fin: ClobIO[Unit]): F[A]
def fromFuture[A](fut: ClobIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: ClobIO[(Future[A], ClobIO[Unit])]): F[A]
def cancelable[A](fa: ClobIO[A], fin: ClobIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// Clob
Expand Down Expand Up @@ -124,6 +125,9 @@ object clob { module =>
case class FromFutureCancelable[A](fut: ClobIO[(Future[A], ClobIO[Unit])]) extends ClobOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: ClobIO[A], fin: ClobIO[Unit]) extends ClobOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends ClobOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -192,6 +196,7 @@ object clob { module =>
def onCancel[A](fa: ClobIO[A], fin: ClobIO[Unit]) = FF.liftF[ClobOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: ClobIO[Future[A]]) = FF.liftF[ClobOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: ClobIO[(Future[A], ClobIO[Unit])]) = FF.liftF[ClobOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: ClobIO[A], fin: ClobIO[Unit]) = FF.liftF[ClobOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[ClobOp, Unit](PerformLogging(event))

// Smart constructors for Clob-specific operations.
Expand Down Expand Up @@ -229,6 +234,7 @@ object clob { module =>
override def onCancel[A](fa: ClobIO[A], fin: ClobIO[Unit]): ClobIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: ClobIO[Future[A]]): ClobIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: ClobIO[(Future[A], ClobIO[Unit])]): ClobIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: ClobIO[A], fin: ClobIO[Unit]): ClobIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidClobIO[A : Monoid]: Monoid[ClobIO[A]] = new Monoid[ClobIO[A]] {
Expand Down
6 changes: 6 additions & 0 deletions modules/free/src/main/scala/doobie/free/connection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ object connection { module =>
def onCancel[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]): F[A]
def fromFuture[A](fut: ConnectionIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: ConnectionIO[(Future[A], ConnectionIO[Unit])]): F[A]
def cancelable[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// Connection
Expand Down Expand Up @@ -183,6 +184,9 @@ object connection { module =>
case class FromFutureCancelable[A](fut: ConnectionIO[(Future[A], ConnectionIO[Unit])]) extends ConnectionOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]) extends ConnectionOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends ConnectionOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -392,6 +396,7 @@ object connection { module =>
def onCancel[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]) = FF.liftF[ConnectionOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: ConnectionIO[Future[A]]) = FF.liftF[ConnectionOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: ConnectionIO[(Future[A], ConnectionIO[Unit])]) = FF.liftF[ConnectionOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]) = FF.liftF[ConnectionOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[ConnectionOp, Unit](PerformLogging(event))

// Smart constructors for Connection-specific operations.
Expand Down Expand Up @@ -476,6 +481,7 @@ object connection { module =>
override def onCancel[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]): ConnectionIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: ConnectionIO[Future[A]]): ConnectionIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: ConnectionIO[(Future[A], ConnectionIO[Unit])]): ConnectionIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: ConnectionIO[A], fin: ConnectionIO[Unit]): ConnectionIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidConnectionIO[A : Monoid]: Monoid[ConnectionIO[A]] = new Monoid[ConnectionIO[A]] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object databasemetadata { module =>
def onCancel[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]): F[A]
def fromFuture[A](fut: DatabaseMetaDataIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: DatabaseMetaDataIO[(Future[A], DatabaseMetaDataIO[Unit])]): F[A]
def cancelable[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// DatabaseMetaData
Expand Down Expand Up @@ -290,6 +291,9 @@ object databasemetadata { module =>
case class FromFutureCancelable[A](fut: DatabaseMetaDataIO[(Future[A], DatabaseMetaDataIO[Unit])]) extends DatabaseMetaDataOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]) extends DatabaseMetaDataOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends DatabaseMetaDataOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -856,6 +860,7 @@ object databasemetadata { module =>
def onCancel[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]) = FF.liftF[DatabaseMetaDataOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: DatabaseMetaDataIO[Future[A]]) = FF.liftF[DatabaseMetaDataOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: DatabaseMetaDataIO[(Future[A], DatabaseMetaDataIO[Unit])]) = FF.liftF[DatabaseMetaDataOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]) = FF.liftF[DatabaseMetaDataOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[DatabaseMetaDataOp, Unit](PerformLogging(event))

// Smart constructors for DatabaseMetaData-specific operations.
Expand Down Expand Up @@ -1059,6 +1064,7 @@ object databasemetadata { module =>
override def onCancel[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]): DatabaseMetaDataIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: DatabaseMetaDataIO[Future[A]]): DatabaseMetaDataIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: DatabaseMetaDataIO[(Future[A], DatabaseMetaDataIO[Unit])]): DatabaseMetaDataIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: DatabaseMetaDataIO[A], fin: DatabaseMetaDataIO[Unit]): DatabaseMetaDataIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidDatabaseMetaDataIO[A : Monoid]: Monoid[DatabaseMetaDataIO[A]] = new Monoid[DatabaseMetaDataIO[A]] {
Expand Down
6 changes: 6 additions & 0 deletions modules/free/src/main/scala/doobie/free/driver.scala
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object driver { module =>
def onCancel[A](fa: DriverIO[A], fin: DriverIO[Unit]): F[A]
def fromFuture[A](fut: DriverIO[Future[A]]): F[A]
def fromFutureCancelable[A](fut: DriverIO[(Future[A], DriverIO[Unit])]): F[A]
def cancelable[A](fa: DriverIO[A], fin: DriverIO[Unit]): F[A]
def performLogging(event: LogEvent): F[Unit]

// Driver
Expand Down Expand Up @@ -118,6 +119,9 @@ object driver { module =>
case class FromFutureCancelable[A](fut: DriverIO[(Future[A], DriverIO[Unit])]) extends DriverOp[A] {
def visit[F[_]](v: Visitor[F]) = v.fromFutureCancelable(fut)
}
case class Cancelable[A](fa: DriverIO[A], fin: DriverIO[Unit]) extends DriverOp[A] {
def visit[F[_]](v: Visitor[F]) = v.cancelable(fa, fin)
}
case class PerformLogging(event: LogEvent) extends DriverOp[Unit] {
def visit[F[_]](v: Visitor[F]) = v.performLogging(event)
}
Expand Down Expand Up @@ -168,6 +172,7 @@ object driver { module =>
def onCancel[A](fa: DriverIO[A], fin: DriverIO[Unit]) = FF.liftF[DriverOp, A](OnCancel(fa, fin))
def fromFuture[A](fut: DriverIO[Future[A]]) = FF.liftF[DriverOp, A](FromFuture(fut))
def fromFutureCancelable[A](fut: DriverIO[(Future[A], DriverIO[Unit])]) = FF.liftF[DriverOp, A](FromFutureCancelable(fut))
def cancelable[A](fa: DriverIO[A], fin: DriverIO[Unit]) = FF.liftF[DriverOp, A](Cancelable(fa, fin))
def performLogging(event: LogEvent) = FF.liftF[DriverOp, Unit](PerformLogging(event))

// Smart constructors for Driver-specific operations.
Expand Down Expand Up @@ -199,6 +204,7 @@ object driver { module =>
override def onCancel[A](fa: DriverIO[A], fin: DriverIO[Unit]): DriverIO[A] = module.onCancel(fa, fin)
override def fromFuture[A](fut: DriverIO[Future[A]]): DriverIO[A] = module.fromFuture(fut)
override def fromFutureCancelable[A](fut: DriverIO[(Future[A], DriverIO[Unit])]): DriverIO[A] = module.fromFutureCancelable(fut)
override def cancelable[A](fa: DriverIO[A], fin: DriverIO[Unit]): DriverIO[A] = module.cancelable(fa, fin)
}

implicit def MonoidDriverIO[A : Monoid]: Monoid[DriverIO[A]] = new Monoid[DriverIO[A]] {
Expand Down
Loading