diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 976b37415..92c439f5e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,24 +16,24 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
fetch-depth: 0
- - name: Install JDK 11
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
+ - name: Install JDK 17
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: zulu
- java-version: '11'
+ java-version: '17'
java-package: jdk
- name: Install SBT
- uses: sbt/setup-sbt@93e926cbdb4a428e41b4ef754124ec82925ffdc2 # v1.1.23
+ uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
- name: Cache SBT
uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1
with:
- extraKey: v1
+ extraKey: v2
- name: Lint
run: sbt ++${{ matrix.scala }} scalafmtCheckAll
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c59084b65..2abee7cd1 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,22 +11,22 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- - name: Install JDK 11
- uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
+ - name: Install JDK 17
+ uses: actions/setup-java@1bcf9fb12cf4aa7d266a90ae39939e61372fe520 # v5.4.0
with:
distribution: zulu
- java-version: '11'
+ java-version: '17'
java-package: jdk
- name: Install SBT
- uses: sbt/setup-sbt@93e926cbdb4a428e41b4ef754124ec82925ffdc2 # v1.1.23
+ uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
- name: Cache SBT
uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d # v8.1.1
with:
- extraKey: v1
+ extraKey: v2
- name: Release
run: sbt ci-release
diff --git a/.jvmopts b/.jvmopts
new file mode 100644
index 000000000..db01e0f59
--- /dev/null
+++ b/.jvmopts
@@ -0,0 +1,2 @@
+# avoid stack overflow while compiling project/FreeGen2.scala
+-Xss2m
diff --git a/.sbtopts b/.sbtopts
index 2a21302fc..1110562c3 100644
--- a/.sbtopts
+++ b/.sbtopts
@@ -1 +1 @@
--mem 2048
+-J-Xmx2000m
diff --git a/README.md b/README.md
index df4bf5093..d5d20621a 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ Currently contains the following changes:
- PostGIS instances have been moved to the new `postgis` module and are available under `doobie.postgis.instances.{geography,geometry}`
- `LogHandler` has been removed. Override interpreters if you want to log things.
- removed `WeakAsync`, a `ConnectionIO` is pretty much always a database transaction and should not be mixed with an `IO` implementation
-- Requires Java 11
+- Requires Java 17
To use add at least the core module to your project:
```
diff --git a/build.sbt b/build.sbt
index 1433ab8e4..576d9c6d5 100644
--- a/build.sbt
+++ b/build.sbt
@@ -3,16 +3,16 @@ import FreeGen2.*
val catsVersion = "2.13.0"
val catsEffectVersion = "3.7.0"
-val circeVersion = "0.14.15"
+val circeVersion = "0.14.16"
val fs2Version = "3.13.0"
val h2Version = "2.4.240"
-val hikariVersion = "7.0.2"
+val hikariVersion = "7.1.0"
val magnoliaVersion = "1.1.10"
val munitVersion = "1.3.3"
val mysqlVersion = "9.7.0"
val openTelemetryVersion = "1.63.0"
val postgisVersion = "2025.1.1"
-val postgresVersion = "42.7.11"
+val postgresVersion = "42.7.12"
val scalatestVersion = "3.2.20"
val shapelessVersion = "2.3.12"
val slf4jVersion = "2.0.18"
@@ -21,7 +21,7 @@ val zioInteropCats = "23.1.0.5"
val zioVersion = "2.1.19"
val Scala213 = "2.13.18"
-val Scala3 = "3.3.7"
+val Scala3 = "3.3.8"
inThisBuild(Seq(
organization := "io.github.guymers",
@@ -42,7 +42,7 @@ lazy val commonSettings = Seq(
"-deprecation",
"-encoding", "UTF-8",
"-feature",
- "-release", "11",
+ "-release", "17",
"-unchecked",
),
scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
@@ -96,7 +96,7 @@ lazy val commonSettings = Seq(
),
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
- case Some((2, _)) => Seq(compilerPlugin("org.typelevel" % "kind-projector" % "0.13.4" cross CrossVersion.full))
+ case Some((2, _)) => Seq(compilerPlugin(("org.typelevel" % "kind-projector" % "0.13.4").cross(CrossVersion.full)))
case _ => Seq.empty
}),
@@ -166,7 +166,7 @@ lazy val foobie = project.in(file("."))
)
.aggregate(
modules, integrationTests,
- example, bench, docs,
+ example, bench,
)
lazy val modules = project.in(file("project/.root"))
@@ -418,52 +418,7 @@ lazy val bench = project.in(file("modules/bench"))
.enablePlugins(JmhPlugin)
.dependsOn(core, postgres)
-lazy val docs = project.in(file("modules/docs"))
- .dependsOn(core, postgres, postgis, h2, hikari, munit, scalatest, weaver)
- .settings(commonSettings)
- .settings(noPublishSettings)
- .settings(Compile / compile / wartremoverErrors := Nil)
- .enablePlugins(GhpagesPlugin)
- .enablePlugins(ParadoxPlugin)
- .enablePlugins(ParadoxSitePlugin)
- .enablePlugins(MdocPlugin)
- .settings(
- scalacOptions := (CrossVersion.partialVersion(scalaVersion.value) match {
- case Some((2, _)) => Seq("-Xsource:3", "-Wconf:cat=scala3-migration:silent")
- case Some((3, _)) => Seq("-Ykind-projector")
- case _ => Seq.empty
- }),
- libraryDependencies ++= Seq(
- "io.circe" %% "circe-core" % circeVersion,
- "io.circe" %% "circe-generic" % circeVersion,
- "io.circe" %% "circe-parser" % circeVersion,
- ),
- Test / fork := true,
-
- version := version.value.takeWhile(_ != '+'), // strip off the +3-f22dca22+20191110-1520-SNAPSHOT business
-
- git.remoteRepo := "git@github.com:guymers/foobie.git",
- ghpagesNoJekyll := true,
- paradoxTheme := Some(builtinParadoxTheme("generic")),
- paradoxProperties ++= Map(
- "scala-versions" -> {
- val crossVersions = (core / crossScalaVersions).value.flatMap(CrossVersion.partialVersion)
- val scala2Versions = crossVersions.filter(_._1 == 2).map(_._2).mkString("2.", "/", "") // 2.12/13
- val scala3 = crossVersions.find(_._1 == 3).map(_ => "3") // 3
- List(Some(scala2Versions), scala3).flatten.filter(_.nonEmpty).mkString(" and ") // 2.12/13 and 3
- },
- "org" -> organization.value,
- "scala.binary.version" -> CrossVersion.binaryScalaVersion(scalaVersion.value),
- "version" -> version.value,
- "catsVersion" -> catsVersion,
- "fs2Version" -> fs2Version,
- "shapelessVersion" -> shapelessVersion,
- "h2Version" -> h2Version,
- "postgresVersion" -> postgresVersion,
- "scalaVersion" -> scalaVersion.value,
- ),
-
- mdocIn := baseDirectory.value / "src" / "main" / "mdoc",
- Compile / paradox / sourceDirectory := mdocOut.value,
- makeSite := makeSite.dependsOn(mdoc.toTask("")).value,
- )
+Global / excludeLintKeys ++= Set(
+ com.github.sbt.git.SbtGit.GitKeys.gitDescribedVersion,
+ com.github.sbt.git.SbtGit.GitKeys.gitUncommittedChanges,
+)
diff --git a/modules/docs/src/main/mdoc/docs/01-Introduction.md b/modules/docs/src/main/mdoc/docs/01-Introduction.md
deleted file mode 100644
index de6096394..000000000
--- a/modules/docs/src/main/mdoc/docs/01-Introduction.md
+++ /dev/null
@@ -1,109 +0,0 @@
-
-
-
-## Introduction
-
-This is a very short book about **doobie**, which is a pure-functional JDBC layer for Scala.
-
-**doobie** provides low-level access to everything in `java.sql` (as of Java 8), allowing you to write any JDBC program in a pure functional style. However the focus of this book is the **high-level API**, which is where most users will spend their time.
-
-This book is organized cookbook-style: we demonstrate a common task and then explain how it works, perhaps in more detail than you want right now. The goal is to get you up and running quickly, but give you a handle on the deeper stuff if you need it later.
-
-
-### Target Audience
-
-This library is designed for people who are interested in typed, pure functional programming. If you are not a [Cats](https://github.com/typelevel/cats) user or are not familiar with functional I/O and monadic effects, you may need to go slowly and may want to spend some time reading [Functional Programming in Scala](http://manning.com/bjarnason/), which introduces all of the ideas that you will find when exploring **doobie**.
-
-Having said this, if you find yourself confused or frustrated by this documentation or the **doobie** API, *please* ask a question on [Gitter](https://gitter.im/tpolecat/doobie), file an [issue](https://github.com/tpolecat/doobie/issues) and ask for help. Both the library and the documentation are young and are changing quickly, and it is inevitable that some things will be unclear. Accordingly, **this book is updated continuously** to address problems and omissions.
-
-### The Setup
-
-This book is compiled as part of the build using the [tut](https://github.com/tpolecat/tut) tutorial generator, so the code examples are guaranteed to compile (and with luck should also work correctly). Each page stands on its own: if you copy and paste code samples starting from the top, it will work in your REPL as long as you have the proper setup, described here.
-
-#### Sample Database Setup
-
-The example code assumes a local [PostgreSQL](http://www.postgresql.org/) server with a `postgres` user with no password, [PostGIS](http://postgis.net/) extensions (optional), and the sample `world` database loaded up. If you're on a Mac you might check out the excellent [Postgres.app](http://postgresapp.com/) if you don't want to install PostgreSQL as a service. You can set up the user and sample database (and an `enum` we use in a few examples) as follows:
-
-```
-$ curl -O https://raw.githubusercontent.com/tpolecat/doobie/series/0.7.x/world.sql
-$ psql -c 'create user postgres createdb' postgres
-$ psql -c 'create database world;' -U postgres
-$ psql -c '\i world.sql' -d world -U postgres
-$ psql -d world -c "create type myenum as enum ('foo', 'bar')" -U postgres
-$ psql -d world -c "create extension postgis" -U postgres
-```
-
-Skip the last statement if you don't have PostGIS installed. Note that the final `ANALYZE` comand in the import will emit a few errors for system tables. This is expected and is fine. Try a query or two to double-check your setup:
-
-```
-$ psql -d world -U postgres
-psql (9.3.5)
-Type "help" for help.
-
-world=> select name, continent, population from country where name like 'U%';
- name | continent | population
---------------------------------------+---------------+------------
- United Arab Emirates | Asia | 2441000
- United Kingdom | Europe | 59623400
- Uganda | Africa | 21778000
- Ukraine | Europe | 50456000
- Uruguay | South America | 3337000
- Uzbekistan | Asia | 24318000
- United States | North America | 278357000
- United States Minor Outlying Islands | Oceania | 0
-(8 rows)
-
-world=> \q
-$
-```
-
-You can of course change this setup if you like, but you will need to adjust your JDBC connection information accordingly. Most examples will work with any compliant database, but in a few cases (noted in the text) we rely on vendor-specific behavior.
-
-#### Scala Setup
-
-On the Scala side you just need a console with the proper dependencies. A minimal `build.sbt` would look something like this.
-
-@@@vars
-```scala
-scalaVersion := "$scalaVersion$" // Scala $scalaVersion$
-
-lazy val doobieVersion = "$version$"
-
-libraryDependencies ++= Seq(
- "org.tpolecat" %% "doobie-core" % doobieVersion,
- "org.tpolecat" %% "doobie-postgres" % doobieVersion,
-)
-```
-@@@
-
-If you are not using PostgreSQL you can omit `doobie-postgres` and will need to add the appropriate JDBC driver as a dependency. Note that there is a `doobie-h2` add-on if you happen to be using [H2](http://www.h2database.com/).
-
-### Conventions
-
-Each page begins with some imports, like this.
-
-```scala mdoc:silent
-import cats.*
-import cats.data.*
-import cats.implicits.*
-import doobie.*
-```
-
-After that there is text interspersed with code examples. Sometimes definitions will stand alone.
-
-```scala mdoc:silent
-case class Person(name: String, age: Int)
-val nel = NonEmptyList.of(Person("Bob", 12), Person("Alice", 14))
-```
-
-And sometimes they will appear with output.
-
-```scala mdoc
-nel.head
-nel.tail
-```
-Sometimes we demonstrate that something doesn't compile. In such cases it will be clear from the context that this is expected, and not a problem with the documentation.
-
-```scala mdoc:fail
-woozle(nel) // doesn't compile
-```
diff --git a/modules/docs/src/main/mdoc/docs/02-Toolkit.md b/modules/docs/src/main/mdoc/docs/02-Toolkit.md
deleted file mode 100644
index 8e37643f8..000000000
--- a/modules/docs/src/main/mdoc/docs/02-Toolkit.md
+++ /dev/null
@@ -1,43 +0,0 @@
-## Toolkit
-
-Before we get into writing **doobie** programs it's worth spending a moment to explain the big picture. The short explanation is:
-
-> **doobie** programs are values. You can compose small programs to build larger programs. Once you have constructed a program you wish to run, you interpret it into an effectful target monad of your choice (`IO` for example) and drop it into your main application wherever you like.
-
-If you want a slightly longer explanation in video form, you might enjoy this lecture from Scala by the Bay 2015.
-
-
-
-
-
-### Nested Programs
-
-**doobie** is a monadic API that provides a number of data types that all work the same way but describe computations in different contexts. For example `ConnectionIO` describes computations that take place in a context where a `java.sql.Connection` is available. Similar data types exist for each major type in `java.sql`. Programs written in these contexts compose naturally via lifting, mirroring the lifecycles of each contexts's carrier type.
-
-
-
-Some patterns of composition are so common and generic that they can be provided for you, as **doobie** does with its high-level API. As a result many programs can be written entirely in terms of `ConnectionIO`. Some examples are:
-
-- Performing a query and reading the results as a stream.
-- Performing an update and returning updated rows as a stream.
-- Validating a query or update in terms of the schema and JDBC ⟷ Scala type mappings.
-
-This book largely focuses on these common interactions, but also explains their representation at lower levels in case you wish to do something other than what you get for free.
-
-### Low and High
-
-**doobie** provides two APIs intended for different audiences.
-
-The **low-level API** in `doobie.free` provides a direct 1:1 mapping to the underlying JDBC API, giving library implementors a pure functional wrapper for the full JDBC 4.0 API. This API provides no resource safety, `NULL` checking, or type mapping; and it directly exposes lifetime-managed JDBC objects; programs written at this level have desirable compositional properties but still require a great deal of care.
-
-The **high-level API** in `doobie.hi` (implemented entirely in terms of the low-level API) provides a safe subset of the JDBC API. Programs written with this API have no access to the underlying JDBC objects and cannot leak references. Features of this API include:
-
-- Typeclass-based mapping of scalar, array, and vendor-specific types to columns.
-- Typeclass-based mapping of product types to rows.
-- Trivial custom two-way row/column mapping via invariant functors.
-- Nullability via `Option` (SQL `NULL` cannot be observed).
-- Proper enumerated types for all JDBC constants.
-- Result sets as fs2 streams.
-- Typesafe string interpolator for SQL literals.
-
-The types used for both APIs are identical; the difference lies only in the exposed constructors. This means that a program otherwise written in the `doobie.hi` API can use constructors from `doobie.free` to implement advanced or vendor-specific behavior directly, without translation or lifting.
\ No newline at end of file
diff --git a/modules/docs/src/main/mdoc/docs/03-Connecting.md b/modules/docs/src/main/mdoc/docs/03-Connecting.md
deleted file mode 100644
index d52d8fef0..000000000
--- a/modules/docs/src/main/mdoc/docs/03-Connecting.md
+++ /dev/null
@@ -1,157 +0,0 @@
-## Connecting to a Database
-
-In this chapter we start from the beginning. First we write a program that connects to a database and returns a value, and then we run that program in the REPL. We also touch on composing small programs to construct larger ones.
-
-### Our First Program
-
-Before we can use **doobie** we need to import some symbols. We will use package imports here as a convenience; this will give us the most commonly-used symbols when working with the high-level API.
-
-```scala mdoc:silent
-import doobie.free.KleisliInterpreter
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.transactor.Transactor
-```
-
-Let's also bring in Cats.
-
-```scala mdoc:silent
-import cats.*
-import cats.effect.*
-import cats.implicits.*
-```
-
-In the **doobie** high level API the most common types we will deal with have the form `ConnectionIO[A]`, specifying computations that take place in a context where a `java.sql.Connection` is available, ultimately producing a value of type `A`.
-
-So let's start with a `ConnectionIO` program that simply returns a constant.
-
-```scala mdoc
-val program1 = 42.pure[ConnectionIO]
-```
-
-This is a perfectly respectable **doobie** program, but we can't run it as-is; we need a `Connection` first. There are several ways to do this, but here let's use a `Transactor`.
-
-```scala mdoc:silent
-import doobie.util.ExecutionContexts
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // JDBC driver classname
- "jdbc:postgresql:world", // Connect URL
- "postgres", // user
- "password" // password
-)
-```
-
-Please consult your JDBC driver's documentation for the driver class name and connection URL.
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-A `Transactor` is a data type that knows how to connect to a database, hand out connections, and clean them up; and with this knowledge it can transform `ConnectionIO ~> IO`, which gives us a program we can run. Specifically it gives us an `IO` that, when run, will connect to the database and execute single transaction.
-
-We are using `cats.effect.IO` as our final effect type, but you can use any monad `M[_]` given `cats.effect.Async[M]`. See *Using Your Own Target Monad* at the end of this chapter for more details.
-
-The `DriverManagerTransactor` simply delegates to the `java.sql.DriverManager` to allocate connections, which is fine for development but inefficient for production use. In a later chapter we discuss other approaches for connection management.
-
-And here we go.
-
-```scala mdoc
-val io = program1.transact(xa)
-io.unsafeRunSync()
-```
-
-Hooray! We have computed a constant. It's not very interesting because we never ask the database to perform any work, but it's a first step.
-
-> Keep in mind that all the code in this book is pure *except* the calls to `IO.unsafeRunSync`, which is the "end of the world" operation that typically appears only at your application's entry points. In the REPL we use it to force a computation to "happen".
-
-Right. Now let's try something more interesting.
-
-### Our Second Program
-
-Now let's use the `sql` string interpolator to construct a query that asks the *database* to compute a constant. We will cover this construction in great detail later on, but the meaning of `program2` is "run the query, interpret the resultset as a stream of `Int` values, and yield its one and only element."
-
-```scala mdoc
-val program2 = sql"select 42".query[Int].unique
-val io2 = program2.transact(xa)
-io2.unsafeRunSync()
-```
-
-Ok! We have now connected to a database to compute a constant. Considerably more impressive.
-
-### Our Third Program
-
-What if we want to do more than one thing in a transaction? Easy! `ConnectionIO` is a monad, so we can use a `for` comprehension to compose two smaller programs into one larger program.
-
-```scala mdoc:silent
-val program3: ConnectionIO[(Int, Double)] =
- for {
- a <- sql"select 42".query[Int].unique
- b <- sql"select random()".query[Double].unique
- } yield (a, b)
-```
-
-And behold!
-
-```scala mdoc
-program3.transact(xa).unsafeRunSync()
-```
-
-The astute among you will note that we don't actually need a monad to do this; an applicative functor is all we need here. So we could also write `program3` as:
-
-```scala mdoc:silent
-val program3a = {
- val a: ConnectionIO[Int] = sql"select 42".query[Int].unique
- val b: ConnectionIO[Double] = sql"select random()".query[Double].unique
- (a, b).tupled
-}
-```
-
-And lo, it was good:
-
-```scala mdoc
-program3a.transact(xa).unsafeRunSync()
-```
-
-And of course this composition can continue indefinitely.
-
-```scala mdoc
-val valuesList = program3a.replicateA(5)
-val result = valuesList.transact(xa)
-result.unsafeRunSync().foreach(println)
-```
-
-### Diving Deeper
-
-All of the **doobie** monads are implemented via `Free` and have no operational semantics; we can only "run" a **doobie** program by transforming `FooIO` (for some carrier type `java.sql.Foo`) to a monad that actually has some meaning.
-
-Out of the box **doobie** provides an interpreter from its free monads to `Kleisli[M, Foo, ?]` given `Async[M]`.
-
-```scala mdoc
-import cats.~>
-import cats.data.Kleisli
-import doobie.free.connection.ConnectionOp
-import java.sql.Connection
-
-val interpreter = KleisliInterpreter[IO].ConnectionInterpreter
-val kleisli = program1.foldMap(interpreter)
-val io3 = IO(null: java.sql.Connection) >>= kleisli.run
-io3.unsafeRunSync() // sneaky; program1 never looks at the connection
-```
-
-So the interpreter above is used to transform a `ConnectionIO[A]` program into a `Kleisli[IO, Connection, A]`. Then we construct an `IO[Connection]` (returning `null`) and bind it through the `Kleisli`, yielding our `IO[Int]`. This of course only works because `program1` is a pure value that does not look at the connection.
-
-The `Transactor` that we defined at the beginning of this chapter is basically a utility that allows us to do the same as above using `program1.transact(xa)`.
-
-There is a bit more going on when calling `transact` (we add commit/rollback handling and ensure that the connection is closed in all cases) but fundamentally it's just a natural transformation and a bind.
-
-#### Using Your Own Target Monad
-
-As mentioned earlier, you can use any monad `M[_]` given `cats.effect.Async[M]`.
diff --git a/modules/docs/src/main/mdoc/docs/04-Selecting.md b/modules/docs/src/main/mdoc/docs/04-Selecting.md
deleted file mode 100644
index e099cdc8e..000000000
--- a/modules/docs/src/main/mdoc/docs/04-Selecting.md
+++ /dev/null
@@ -1,226 +0,0 @@
-## Selecting Data
-
-In this chapter we will write some programs to read from the database, mapping rows to Scala types on the way. We also introduce YOLO mode for experimenting with **doobie** in the REPL.
-
-### Setting Up
-
-First let's get our imports out of the way and set up a `Transactor` as we did before. You can skip this step if you still have your REPL running from last chapter.
-
-```scala mdoc:silent
-import doobie.HC
-import doobie.free.preparedstatement.PreparedStatementIO
-import doobie.syntax.connectionio.*
-import doobie.syntax.stream.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-import fs2.Stream
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-We will be playing with the `country` table, shown here for reference. If you don't have the `world` database set up, go back to the [Introduction](01-Introduction.html) for instructions.
-
-```sql
-CREATE TABLE country (
- code character(3) NOT NULL,
- name text NOT NULL,
- population integer NOT NULL,
- gnp numeric(10,2)
- -- more columns, but we won't use them here
-)
-```
-
-### Reading Rows into Collections
-
-For our first query let's aim low and select some country names into a `List`, then print out the first few. There are several steps here so we have noted the types along the way.
-
-```scala mdoc
-sql"select name from country"
- .query[String] // Query0[String]
- .to[List] // ConnectionIO[List[String]]
- .transact(xa) // IO[List[String]]
- .unsafeRunSync() // List[String]
- .take(5) // List[String]
- .foreach(println) // Unit
-```
-
-Let's break this down a bit.
-
-- `sql"select name from country".query[String]` defines a `Query0[String]`, which is a one-column query that maps each returned row to a `String`. We will get to more interesting row types soon.
-- `.to[List]` is a convenience method that accumulates rows into a `List`, in this case yielding a `ConnectionIO[List[String]]`. It works with any collection type that has a `CanBuildFrom`. Similar methods are:
- - `.unique` which returns a single value, raising an exception if there is not exactly one row returned.
- - `.option` which returns an `Option`, raising an exception if there is more than one row returned.
- - `.nel` which returns an `NonEmptyList`, raising an exception if there are no rows returned.
- - See the Scaladoc for `Query0` for more information on these and other methods.
-- The rest is familar; `transact(xa)` yields a `IO[List[String]]` which we run, giving us a normal Scala `List[String]` that we print out.
-
-### Internal Streaming
-
-The example above is ok, but there's not much point reading all the results from the database when we only want the first five rows. So let's try a different approach.
-
-```scala mdoc
-sql"select name from country"
- .query[String] // Query0[String]
- .stream // Stream[ConnectionIO, String]
- .take(5) // Stream[ConnectionIO, String]
- .compile.toList // ConnectionIO[List[String]]
- .transact(xa) // IO[List[String]]
- .unsafeRunSync() // List[String]
- .foreach(println) // Unit
-```
-
-The difference here is that `stream` gives us an [fs2](https://github.com/functional-streams-for-scala/fs2) `Stream[ConnectionIO, String]`
-that emits rows as they arrive from the database. By applying `take(5)` we instruct the stream to shut everything down (and clean everything up) after five elements have been emitted. This is much more efficient than pulling all 239 rows and then throwing most of them away.
-
-Of course a server-side `LIMIT` would be an even better way to do this (for databases that support it), but in cases where you need client-side filtering or other custom postprocessing, `Stream` is a very general and powerful tool.
-For more information see the [fs2](https://github.com/functional-streams-for-scala/fs2) repo, which has a good list of learning resources.
-
-### YOLO Mode
-
-The API we have seen so far is ok, but it's tiresome to keep saying `transact(xa)` and doing `foreach(println)` to see what the results look like. So **just for REPL exploration** there is a module of extra syntax provided on your `Transactor` that you can import.
-
-```scala mdoc:silent
-val y = xa.yolo // a stable reference is required
-import y.*
-```
-
-We can now run our previous query in an abbreviated form.
-
-```scala mdoc
-sql"select name from country"
- .query[String] // Query0[String]
- .stream // Stream[ConnectionIO, String]
- .take(5) // Stream[ConnectionIO, String]
- .quick // IO[Unit]
- .unsafeRunSync()
-```
-
-This syntax allows you to quickly run a `Query0[A]` or `Stream[ConnectionIO, A]` and see the results printed to the console. This isn't a huge deal but it can save you some keystrokes when you're just messing around.
-
-- The `.quick` method sinks the stream to standard out (adding ANSI coloring for fun) and then calls `.transact`, yielding a `IO[Unit]`.
-- The `.check` method returns a `IO[Unit]` that performs a metadata analysis on the provided query and asserted types and prints out a report. This is covered in detail in the chapter on typechecking queries.
-
-### Multi-Column Queries
-
-We can select multiple columns, of course, and map them to a tuple. The `gnp` column in our table is nullable so we'll select that one into an `Option[Double]`. In a later chapter we'll see how to check the types to be sure they're sensible.
-
-```scala mdoc
-sql"select code, name, population, gnp from country"
- .query[(String, String, Int, Option[Double])]
- .stream
- .take(5)
- .quick
- .unsafeRunSync()
-```
-**doobie** supports row mappings for atomic column types, as well as options, tuples, and case classes thereof.
-
-Mapping rows to a case class.
-
-```scala mdoc:silent
-case class Country(code: String, name: String, pop: Int, gnp: Option[Double])
-object Country {
- implicit val read: Read[Country] = Read.derived
-}
-```
-
-```scala mdoc
-sql"select code, name, population, gnp from country"
- .query[Country]
- .stream
- .take(5)
- .quick
- .unsafeRunSync()
-```
-
-You can also nest case classes, and/or tuples arbitrarily as long as the eventual members are of supported columns types. For instance, here we map the same set of columns to a tuple of two case classes:
-
-```scala mdoc:silent
-case class Code(code: String)
-object Code {
- implicit val read: Read[Code] = Read.derived
-}
-case class Country2(name: String, pop: Int, gnp: Option[Double])
-object Country2 {
- implicit val read: Read[Country2] = Read.derived
-}
-```
-
-```scala mdoc
-sql"select code, name, population, gnp from country"
- .query[(Code, Country2)]
- .stream
- .take(5)
- .quick
- .unsafeRunSync()
-```
-
-And just for fun, since the `Code` values are constructed from the primary key, let's turn the results into a `Map`. Trivial but useful.
-
-```scala mdoc
-sql"select code, name, population, gnp from country"
- .query[(Code, Country2)]
- .stream.take(5)
- .compile.toList
- .map(_.toMap)
- .quick
- .unsafeRunSync()
-```
-
-### Final Streaming
-
-In the examples above we construct a `Stream[ConnectionIO, A]` and discharge it via `.compile.toList`, yielding a `ConnectionIO[List[A]]` which eventually becomes a `IO[List[A]]`. So the construction and execution of the `Stream` is entirely internal to the **doobie** program.
-
-However in some cases a stream is what we want as our "top level" type. For example, [http4s](https://github.com/http4s/http4s) can use a `Stream[IO, A]` directly as a response type, which could allow us to stream a resultset directly to the network socket. We can achieve this in **doobie** by calling `transact` directly on the `Stream[ConnectionIO, A]`.
-
-```scala mdoc
-val p: Stream[IO, Country2] = {
- sql"select name, population, gnp from country"
- .query[Country2] // Query0[Country2]
- .stream // Stream[ConnectionIO, Country2]
- .transact(xa) // Stream[IO, Country2]
-}
-
-p.take(5).compile.toVector.unsafeRunSync().foreach(println)
-```
-
-### Diving Deeper
-
-The `sql` interpolator is sugar for constructors defined in the `doobie.hi.connection` module, aliased as `HC` if you use the standard imports. Using these constructors directly, the above program would look like this:
-
-```scala mdoc
-
-val proc = HC.stream[(Code, Country2)](
- "select code, name, population, gnp from country", // statement
- ().pure[PreparedStatementIO], // prep (none)
- 512 // chunk size
-)
-
-proc.take(5) // Stream[ConnectionIO, (Code, Country2)]
- .compile.toList // ConnectionIO[List[(Code, Country2)]]
- .map(_.toMap) // ConnectionIO[Map[Code, Country2]]
- .quick
- .unsafeRunSync()
-```
-
-The `stream` combinator is parameterized on the element type and consumes a statement and a program in `PreparedStatementIO` that sets input parameters and any other pre-execution configuration. In this case the "prepare" program is a no-op.
diff --git a/modules/docs/src/main/mdoc/docs/05-Parameterized.md b/modules/docs/src/main/mdoc/docs/05-Parameterized.md
deleted file mode 100644
index 65abafb62..000000000
--- a/modules/docs/src/main/mdoc/docs/05-Parameterized.md
+++ /dev/null
@@ -1,205 +0,0 @@
-## Parameterized Queries
-
-In this chapter we learn how to construct parameterized queries, and introduce the `Put` and `Write` typeclasses.
-
-### Setting Up
-
-Same as last chapter, so if you're still set up you can skip this section. Otherwise let's set up a `Transactor` and YOLO mode.
-
-```scala mdoc:silent
-import doobie.FPS
-import doobie.Fragments
-import doobie.HC
-import doobie.HPS
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-We're still playing with the `country` table, shown here for reference.
-
-```sql
-CREATE TABLE country (
- code character(3) NOT NULL,
- name text NOT NULL,
- population integer NOT NULL,
- gnp numeric(10,2)
- -- more columns, but we won't use them here
-)
-```
-
-### Adding a Parameter
-
-Let's set up our Country class and re-run last chapter's query just to review.
-
-```scala mdoc:silent
-case class Country(code: String, name: String, pop: Int, gnp: Option[Double])
-object Country {
- implicit val read: Read[Country] = Read.derived
- implicit val write: Write[Country] = Write.derived
-}
-```
-
-```scala mdoc
-{
- sql"select code, name, population, gnp from country"
- .query[Country]
- .stream
- .take(5)
- .quick
- .unsafeRunSync()
-}
-```
-
-Still works. Ok.
-
-So let's factor our query into a method and add a parameter that selects only the countries with a population larger than some value the user will provide. We insert the `minPop` argument into our SQL statement as `$minPop`, just as if we were doing string interpolation.
-
-```scala mdoc:silent
-def biggerThan(minPop: Int) = sql"""
- select code, name, population, gnp
- from country
- where population > $minPop
-""".query[Country]
-```
-
-And when we run the query ... surprise, it works!
-
-```scala mdoc
-biggerThan(150_000_000).quick.unsafeRunSync() // Let's see them all
-```
-
-So what's going on? It looks like we're just dropping a string literal into our SQL string, but actually we're constructing a `PreparedStatement`, and the `minPop` value is ultimately set via a call to `setInt` (see "Diving Deeper" below).
-
-**doobie** allows you to interpolate values of any type (and options thereof) with a `Put` instance, which includes
-
-- any JVM type that has a target mapping defined by the JDBC specification,
-- vendor-specific types defined by extension packages,
-- custom column types that you define, and
-- single-member products (case classes, typically) of any of the above.
-
-We will discuss custom type mappings in a later chapter.
-
-### Multiple Parameters
-
-Multiple parameters work the same way. No surprises here.
-
-```scala mdoc
-def populationIn(range: Range) = sql"""
- select code, name, population, gnp
- from country
- where population > ${range.min}
- and population < ${range.max}
-""".query[Country]
-
-populationIn(150_000_000 to 200_000_000).quick.unsafeRunSync()
-```
-
-### Dealing with `IN` Clauses
-
-A common irritant when dealing with SQL literals is the desire to inline a *sequence* of arguments into an `IN` clause, but SQL does not support this notion (nor does JDBC do anything to assist). **doobie** supports this via *statement fragments* (see Chapter 8).
-
-```scala mdoc:silent
-def populationIn(range: Range, codes: NonEmptyList[String]) = {
- val q = fr"""
- select code, name, population, gnp
- from country
- where population > ${range.min}
- and population < ${range.max}
- and code IN (${Fragments.commas(codes)})"""
- q.query[Country]
-}
-```
-
-Note that the `IN` clause must be non-empty, so `codes` is a `NonEmptyList`.
-
-Running this query gives us the desired result.
-
-```scala mdoc
-populationIn(100_000_000 to 300_000_000, NonEmptyList.of("USA", "BRA", "PAK", "GBR")).quick.unsafeRunSync()
-```
-
-### Diving Deeper
-
-In the previous chapter's *Diving Deeper* we saw how a query constructed with the `sql` interpolator is just sugar for the `stream` constructor defined in the `doobie.hi.connection` module (aliased as `HC`). Here we see that the second parameter, a `PreparedStatementIO` program, is used to set the query parameters. The third parameter specifies a chunking factor; rows are buffered in chunks of the specified size.
-
-```scala mdoc:silent
-import fs2.Stream
-
-val q = """
- select code, name, population, gnp
- from country
- where population > ?
- and population < ?
- """
-
-def proc(range: Range): Stream[ConnectionIO, Country] =
- HC.stream[Country](q, HPS.set((range.min, range.max)), 512)
-```
-
-Which produces the same output.
-
-```scala mdoc
-proc(150_000_000 to 200_000_000).quick.unsafeRunSync()
-```
-
-But how does the `set` constructor work?
-
-When setting parameters in the high-level API, we require an instance of `Write[A]` for the input type. It is not immediately obvious when using the `sql` interpolator, but the parameters (each of which require a `Put` instance, to be discussed in a later chapter) are gathered into an `HList` and treated as a single writable parameter.
-
-`Write` instances are derived automatically for column types (and options thereof) that have `Put` instances, and for products of other writable types. We can summon their instances thus:
-
-```scala mdoc
-Write[(String, Boolean)]
-Write[Country]
-```
-
-The `set` constructor takes an argument of any type with a `Write` instance and returns a program that sets the unrolled sequence of values starting at parameter index 1 by default. Some other variations are shown here.
-
-```scala mdoc:silent
-// Set parameters as (String, Boolean) starting at index 1 (default)
-HPS.set(("foo", true))
-
-// Set parameters as (String, Boolean) starting at index 1 (explicit)
-HPS.set(1, ("foo", true))
-
-// Set parameters individually
-HPS.set(1, "foo") *> HPS.set(2, true)
-
-// Or out of order, who cares?
-HPS.set(2, true) *> HPS.set(1, "foo")
-```
-
-Using the low level `doobie.free` constructors there is no typeclass-driven type mapping, so each parameter type requires a distinct method, exactly as in the underlying JDBC API. The purpose of the `Put` typeclass (discussed in a later chapter) is to abstract away these differences.
-
-```scala mdoc:silent
-FPS.setString(1, "foo") *> FPS.setBoolean(2, true)
-
-```
diff --git a/modules/docs/src/main/mdoc/docs/06-Checking.md b/modules/docs/src/main/mdoc/docs/06-Checking.md
deleted file mode 100644
index 792ee11a4..000000000
--- a/modules/docs/src/main/mdoc/docs/06-Checking.md
+++ /dev/null
@@ -1,132 +0,0 @@
-## Typechecking Queries
-
-In this chapter we learn how to use YOLO mode to validate queries against the database schema and ensure that our type mappings are correct (and if not, get some hints on how to fix them).
-
-### Setting Up
-
-Our setup here is the same as last chapter, so if you're still running from last chapter you can skip this section. Otherwise: imports, `Transactor`, and YOLO mode.
-
-```scala mdoc:silent
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-And again, we're playing with the `country` table, shown here for reference.
-
-```sql
-CREATE TABLE country (
- code character(3) NOT NULL,
- name text NOT NULL,
- population integer NOT NULL,
- gnp numeric(10,2),
- indepyear smallint
- -- more columns, but we won't use them here
-)
-```
-
-### Checking a Query
-
-In order to create a query that's not quite right, let's redefine our `Country` class with slightly different types.
-
-```scala mdoc:silent
-case class Country(code: Int, name: String, pop: Int, gnp: Double)
-object Country {
- implicit val read: Read[Country] = Read.derived
- implicit val write: Write[Country] = Write.derived
-}
-```
-
-Here's our parameterized query from last chapter, but with the new `Country` definition and the `minPop` parameter changed to a `Short`.
-
-```scala mdoc:silent
-def biggerThan(minPop: Short) =
- sql"""
- select code, name, population, gnp, indepyear
- from country
- where population > $minPop
- """.query[Country]
-```
-
-Now let's try the `check` method provided by YOLO and see what happens.
-
-```scala mdoc
-biggerThan(0).check.unsafeRunSync()
-```
-
-Yikes, there are quite a few problems, in several categories. In this case **doobie** found
-
-- a parameter coercion that should always work but is not required to be supported by compliant drivers;
-- two column coercions that **are** supported by JDBC but are not recommended and can fail in some cases;
-- a column nullability mismatch, where a column that is *provably* nullable is read into a non-`Option` type;
-- and an unused column.
-
-If we fix all of these problems and try again, we get a clean bill of health.
-
-```scala mdoc:silent
-case class Country2(code: String, name: String, pop: Int, gnp: Option[BigDecimal])
-object Country2 {
- implicit val read: Read[Country2] = Read.derived
- implicit val write: Write[Country2] = Write.derived
-}
-
-def biggerThan2(minPop: Int) =
- sql"""
- select code, name, population, gnp
- from country
- where population > $minPop
- """.query[Country2]
-```
-
-```scala mdoc
-biggerThan2(0).check.unsafeRunSync()
-```
-
-**doobie** supports `check` for queries and updates in four ways: programmatically, via YOLO mode in the REPL, and via the test modules, which allow checking to become part of your test suite. We will investigate this in the chapter on testing.
-
-### Working Around Bad Metadata
-
-Some drivers do not implement the JDBC metadata specification very well, which limits the usefulness of the query checking feature. MySQL and MS-SQL do a particularly rotten job in this department. In some cases queries simply cannot be checked because no metadata is available for the prepared statement (manifested as an exception) or the returned metadata is obviously inaccurate.
-
-However a common case is that *parameter* metadata is unavailable but *output column* metadata is. And in these cases there is a workaround: use `checkOutput` rather than `check`. This instructs **doobie** to punt on the input parameters and only check output columns. Unsatisfying but better than nothing.
-
-```scala mdoc
-biggerThan(0).checkOutput.unsafeRunSync()
-```
-
-### Diving Deeper
-
-The `check` logic requires both a database connection and concrete `Get` and `Put` instances that define column-level JDBC mappings.
-
-The way this works is that a `Query` value has enough type information to describe all parameter and column mappings, as well as the SQL literal itself (with interpolated parameters erased into `?`). From here it is straightforward to prepare the statement, pull the `ResultsetMetaData` and `DatabaseMetaData` and work out whether things are aligned correctly (and if not, determine how misalignments might be fixed). The `Anaylsis` class consumes this metadata and is able to provide the following diagnostics:
-
-- SQL validity. The query must compile, which means it must be consistent with the schema.
-- Parameter and column arity. All query inputs and outputs must map 1:1 with parameters and columns.
-- Nullability. A parameter or column that is *provably* nullable must be mapped to a Scala `Option`. Note that this is a weak guarantee; columns introduced by an outer join might be nullable but JDBC will tend to report them as "might not be nullable" which isn't useful information.
-- Coercibility of types. Mapping of Scala types to JDBC types and JDBC types to vendor types, is asymmetric with respect to reading and writing, and the specification is quite terrible. **doobie** encodes the JDBC spec and combines this with vendor-specific metadata to determine whether a given asserted mapping is sensible or not, and if not, will suggest a fix via changing the Scala type, and another via changing the schema type.
diff --git a/modules/docs/src/main/mdoc/docs/07-Updating.md b/modules/docs/src/main/mdoc/docs/07-Updating.md
deleted file mode 100644
index 9e2263cda..000000000
--- a/modules/docs/src/main/mdoc/docs/07-Updating.md
+++ /dev/null
@@ -1,245 +0,0 @@
-## DDL, Inserting, and Updating
-
-In this chapter we examine operations that modify data in the database, and ways to retrieve the results of these updates.
-
-### Setting Up
-
-Again we set up a transactor and pull in YOLO mode, but this time we're not using the world database.
-
-```scala mdoc:silent
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import doobie.util.update.Update
-import doobie.util.update.Update0
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-### Data Definition
-
-It is uncommon to define database structures at runtime, but **doobie** handles it just fine and treats such operations like any other kind of update. And it happens to be useful here!
-
-Let's create a new table, which we will use for the examples to follow. This looks a lot like our prior usage of the `sql` interpolator, but this time we're using `update` rather than `query`. The `.run` method gives a `ConnectionIO[Int]` that yields the total number of rows modified, and the YOLO-mode `.quick` gives a `IO[Unit]` that prints out the row count.
-
-```scala mdoc:silent
-val drop =
- sql"""
- DROP TABLE IF EXISTS person
- """.update.run
-
-val create =
- sql"""
- CREATE TABLE person (
- id SERIAL,
- name VARCHAR NOT NULL UNIQUE,
- age SMALLINT
- )
- """.update.run
-```
-
-We can compose these and run them together, yielding the total number of affected rows.
-
-```scala mdoc
-(drop, create).mapN(_ + _).transact(xa).unsafeRunSync()
-```
-
-
-### Inserting
-
-
-Inserting is straightforward and works just as with selects. Here we define a method that constructs an `Update0` that inserts a row into the `person` table.
-
-```scala mdoc:silent
-def insert1(name: String, age: Option[Short]): Update0 =
- sql"insert into person (name, age) values ($name, $age)".update
-```
-
-Let's insert a few rows.
-
-```scala mdoc
-insert1("Alice", Some(12)).run.transact(xa).unsafeRunSync()
-insert1("Bob", None).quick.unsafeRunSync() // switch to YOLO mode
-```
-
-And read them back.
-
-```scala mdoc:silent
-case class Person(id: Long, name: String, age: Option[Short])
-object Person {
- implicit val read: Read[Person] = Read.derived
- implicit val write: Write[Person] = Write.derived
-}
-```
-
-```scala mdoc
-sql"select id, name, age from person".query[Person].quick.unsafeRunSync()
-```
-
-
-### Updating
-
-
-Updating follows the same pattern. Here we update Alice's age.
-
-```scala mdoc
-sql"update person set age = 15 where name = 'Alice'".update.quick.unsafeRunSync()
-sql"select id, name, age from person".query[Person].quick.unsafeRunSync()
-```
-
-### Retrieving Results
-
-When we insert we usually want the new row back, so let's do that. First we'll do it the hard way, by inserting, getting the last used key via `lastVal()`, then selecting the indicated row.
-
-```scala mdoc:silent
-def insert2(name: String, age: Option[Short]): ConnectionIO[Person] =
- for {
- _ <- sql"insert into person (name, age) values ($name, $age)".update.run
- id <- sql"select lastval()".query[Long].unique
- p <- sql"select id, name, age from person where id = $id".query[Person].unique
- } yield p
-```
-
-```scala mdoc
-insert2("Jimmy", Some(42)).quick.unsafeRunSync()
-```
-
-This is irritating but it is supported by all databases (although the "get the last used id" function will vary by vendor).
-
-Some database (like H2) allow you to return [only] the inserted id, allowing the above operation to be reduced to two statements (see below for an explanation of `withUniqueGeneratedKeys`).
-
-```scala mdoc:silent
-def insert2_H2(name: String, age: Option[Short]): ConnectionIO[Person] =
- for {
- id <- sql"insert into person (name, age) values ($name, $age)"
- .update
- .withUniqueGeneratedKeys[Int]("id")
- p <- sql"select id, name, age from person where id = $id"
- .query[Person]
- .unique
- } yield p
-```
-
-```scala mdoc
-insert2_H2("Ramone", Some(42)).quick.unsafeRunSync()
-```
-
-Other databases (including PostgreSQL) provide a way to do this in one shot by returning multiple specified columns from the inserted row.
-
-```scala mdoc:silent
-def insert3(name: String, age: Option[Short]): ConnectionIO[Person] = {
- sql"insert into person (name, age) values ($name, $age)"
- .update
- .withUniqueGeneratedKeys("id", "name", "age")
-}
-```
-
-The `withUniqueGeneratedKeys` specifies that we expect exactly one row back (otherwise an exception will be raised), and requires a list of columns to return. This isn't the most beautiful API but it's what JDBC gives us. And it does work.
-
-```scala mdoc
-insert3("Elvis", None).quick.unsafeRunSync()
-```
-
-This mechanism also works for updates, for databases that support it. In the case of multiple row updates we omit `unique` and get a `Stream[ConnectionIO, Person]` back.
-
-
-```scala mdoc:silent
-val up = {
- sql"update person set age = age + 1 where age is not null"
- .update
- .withGeneratedKeys[Person]("id", "name", "age")
-}
-```
-
-Running this process updates all rows with a non-`NULL` age and returns them.
-
-```scala mdoc
-up.quick.unsafeRunSync()
-up.quick.unsafeRunSync() // and again!
-```
-
-### Batch Updates
-
-**doobie** supports batch updating via the `updateMany` and `updateManyWithGeneratedKeys` operations on the `Update` data type (which we haven't seen before). An `Update0`, which is the type of an `sql"...".update` expression, represents a parameterized statement where the arguments are known. An `Update[A]` is more general, and represents a parameterized statement where the composite argument of type `A` is *not* known.
-
-```scala mdoc:silent
-// Given some values ...
-val a = 1; val b = "foo"
-
-// this expression ...
-sql"... $a $b ..."
-
-// is syntactic sugar for this one, which is an Update applied to (a, b)
-Update[(Int, String)]("... ? ? ...").run((a, b))
-```
-
-By using an `Update` directly we can apply *many* sets of arguments to the same statement, and execute it as a single batch operation.
-
-```scala mdoc:silent
-type PersonInfo = (String, Option[Short])
-
-def insertMany(ps: List[PersonInfo]): ConnectionIO[Int] = {
- val sql = "insert into person (name, age) values (?, ?)"
- Update[PersonInfo](sql).updateMany(ps)
-}
-
-// Some rows to insert
-val data = List[PersonInfo](
- ("Frank", Some(12)),
- ("Daddy", None))
-```
-
-Running this program yields the number of updated rows.
-
-```scala mdoc
-insertMany(data).quick.unsafeRunSync()
-```
-
-For databases that support it (such as PostgreSQL) we can use `updateManyWithGeneratedKeys` to return a stream of updated rows.
-
-```scala mdoc:silent
-import fs2.Stream
-
-def insertMany2(ps: List[PersonInfo]): Stream[ConnectionIO, Person] = {
- val sql = "insert into person (name, age) values (?, ?)"
- Update[PersonInfo](sql).updateManyWithGeneratedKeys[Person]("id", "name", "age")(ps)
-}
-
-// Some rows to insert
-val data2 = List[PersonInfo](
- ("Banjo", Some(39)),
- ("Skeeter", None),
- ("Jim-Bob", Some(12)))
-```
-
-Running this program yields the updated instances.
-
-```scala mdoc
-insertMany2(data2).quick.unsafeRunSync()
-```
diff --git a/modules/docs/src/main/mdoc/docs/08-Fragments.md b/modules/docs/src/main/mdoc/docs/08-Fragments.md
deleted file mode 100644
index 1821d6c93..000000000
--- a/modules/docs/src/main/mdoc/docs/08-Fragments.md
+++ /dev/null
@@ -1,156 +0,0 @@
-## Statement Fragments
-
-In this chapter we discuss how to construct SQL statements at runtime.
-
-### Setting Up
-
-Same as last chapter, so if you're still set up you can skip this section. Otherwise let's set up a `Transactor` and YOLO mode.
-
-```scala mdoc:silent
-import doobie.Fragments
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.fragment.Fragment
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-We're still playing with the `country` table, shown here for reference.
-
-```sql
-CREATE TABLE country (
- code character(3) NOT NULL,
- name text NOT NULL,
- population integer NOT NULL,
- gnp numeric(10,2)
- -- more columns, but we won't use them here
-)
-```
-
-### Composing SQL literals
-
-You can construct a SQL `Fragment` using the `fr` interpolator, which behaves just like the `sql` interpolator. Fragments are concatenated with `++`.
-
-```scala mdoc
-val a = fr"select name from country"
-val b = fr"where code = 'USA'"
-val c = a ++ b // concatenation by ++
-c.query[String].unique.quick.unsafeRunSync()
-```
-
-Fragments can capture arguments of any type with a `Put` instance, just as the `sql` interpolator does.
-
-```scala mdoc
-def whereCode(s: String) = fr"where code = $s"
-val fra = whereCode("FRA")
-(fr"select name from country" ++ fra).query[String].quick.unsafeRunSync()
-```
-
-You can lift an arbitrary string value via `Fragment.const`, which allows you to parameterize on things that aren't valid SQL parameters.
-
-```scala mdoc
-def count(table: String) = (fr"select count(*) from" ++ Fragment.const(table)).query[Int].unique
-count("city").quick.unsafeRunSync()
-```
-
-> Note that `Fragment.const` performs no escaping of passed strings. Passing user-supplied data is an **injection risk**.
->
-
-You can also use Fragments in `fr` interpolator directly. Both parameters and SQL literals will be
-substituted correctly.
-
-```scala mdoc
-val countryCode: String = "NZL"
-val whereFragment: Fragment = fr"WHERE code = $countryCode"
-
-val frag = fr"SELECT name FROM country $whereFragment"
-
-frag.query[String].option.quick.unsafeRunSync()
-```
-
-> As long as your individual fragments were constructed securely (i.e. Never call `Fragment.const` with user supplied input),
-> You can freely concatenate or interpolate fragments without worrying about SQL injection.
-
-### Whitespace handling
-
-The rendered SQL string for a `fr` or `const` fragment will have a single space character appended, which is usually what you want. Normally you don't need to worry about whitespace when composing fragments.
-
-If you do *not* want a fragment to have trailing space you can use the `fr0` interpolator or `const0` constructor. This is used here and there in the `Fragments` module to yield prettier SQL strings.
-
-```scala mdoc
-fr"IN (" ++ List(1, 2, 3).map(n => fr"$n").intercalate(fr",") ++ fr")"
-fr0"IN (" ++ List(1, 2, 3).map(n => fr0"$n").intercalate(fr",") ++ fr")"
-```
-Note that the `sql` interpolator is simply an alias for `fr0`.
-
-### The `Fragments` Module
-
-The `Fragments` module provides some combinators for common patterns when working with fragments. The following example illustrates a few of them. See the Scaladoc or source for more information.
-
-Here we define a query with a three optional filter conditions.
-
-```scala mdoc:silent
-
-// Country Info
-case class Info(name: String, code: String, population: Int)
-object Info {
- implicit val read: Read[Info] = Read.derived
- implicit val write: Write[Info] = Write.derived
-}
-
-// Construct a Query0 with some optional filter conditions and a configurable LIMIT.
-def select(name: Option[String], pop: Option[Int], codes: List[String], limit: Long) = {
-
- // Three Option[Fragment] filter conditions.
- val f1 = name.map(s => fr"name LIKE $s")
- val f2 = pop.map(n => fr"population > $n")
- val f3 = codes.toNel.map(cs => fr"code IN ${Fragments.commas(cs)}")
-
- // Our final query
- val q: Fragment = fr"""
- SELECT name, code, population FROM country
- ${Fragments.whereAndOpt(f1, f2, f3)}
- LIMIT $limit"
- """
-
- // Construct a Query0
- q.query[Info]
-
-}
-```
-
-We first construct three optional filters, the third of which uses the `in` combinator to construct an SQL `IN` clause. The final statement uses the `whereAndOpt` combinator that constructs a `WHERE` clause with the passed sequence of `Option[Fragment]` joined with `AND` if any are defined, otherwise it evaluates to the empty fragment. The end result is that the `WHERE` clause appears only if at least one filter is defined.
-
-Let's look at a few possibilities.
-
-```scala mdoc
-select(None, None, Nil, 10).check.unsafeRunSync() // no filters
-select(Some("U%"), None, Nil, 10).check.unsafeRunSync() // one filter
-select(Some("U%"), Some(12345), List("FRA", "GBR"), 10).check.unsafeRunSync() // three filters
-```
-
diff --git a/modules/docs/src/main/mdoc/docs/09-Error-Handling.md b/modules/docs/src/main/mdoc/docs/09-Error-Handling.md
deleted file mode 100644
index 965ae1489..000000000
--- a/modules/docs/src/main/mdoc/docs/09-Error-Handling.md
+++ /dev/null
@@ -1,148 +0,0 @@
-## Error Handling
-
-In this chapter we examine a set of combinators that allow us to construct programs that trap and handle exceptions.
-
-### Setting Up
-
-```scala mdoc:silent
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.applicativeerror.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-### About Exceptions
-
-Exceptions are a fact of life when interacting with databases, and they are largely nondeterministic; whether an operation will succeed or not depends on unpredictable factors like network health, the current contents of tables, locking state, and so on. So we must decide whether to compute everything in a disjunction like `EitherT[ConnectionIO, Throwable, A]` or allow exceptions to propagate until they are caught explicitly. **doobie** adopts the second strategy: exceptions are allowed to propagate and escape unless handled explicitly (exactly as `IO` works). This means when a **doobie** action (transformed to some target monad) is executed, exceptions can escape.
-
-There are three main types of exceptions that are likely to arise:
-
-1. Various types of `IOException` can happen with any kind of I/O, and these exceptions tend to be unrecoverable.
-1. Database exceptions, typically as a generic `SQLException` with a vendor-specific `SQLState` identifying the specific error, are raised for common situations such as key violations. Some vendors (PostgreSQL for instance) publish a table of error codes, and in these cases **doobie** can provide a matching set of exception-handling combinators. However in most cases the error codes must be passed down as folklore or discovered by experimentation. There exist the XOPEN and SQL:2003 standards, but it seems that no vendor adheres closely to these specifications. Some of these errors are recoverable and others aren't.
-1. **doobie** will raise an `InvariantViolation` in response to invalid type mappings, unknown JDBC constants returned by drivers, observed `NULL` values, and other violations of invariants that **doobie** assumes. These exceptions indicate programmer error or driver non-compliance and are generally unrecoverable.
-
-### `MonadError` and Derived Combinators
-
-All **doobie** monads provide an `Async` instance, which extends `MonadError[?[_], Throwable]`. This means `ConnectionIO`, etc., have the following primitive operations:
-
-- `.attempt` converts `M[A]` into `M[Either[Throwable, A]]`
-- `raiseError` constructs an `M[A]` that fails with a provided `Throwable`
-
-So any **doobie** program can be lifted into a disjunction simply by adding `.attempt`.
-
-```scala mdoc
-val p = 42.pure[ConnectionIO]
-p.attempt
-```
-
-From the `.attempt` and `raiseError` combinators we can derive many other operations, as described in the Cats documentation. In addition **doobie** provides the following specialized combinators that only pay attention to `SQLException`:
-
-- `attemptSql` is like `attempt` but only traps `SQLException`.
-- `attemptSomeSql` traps only specified `SQLException`s.
-- `exceptSql` recovers from an `SQLException` with a new action.
-- `onSqlException` executes an action on `SQLException` and discards its result.
-
-And finally we have a set of combinators that focus on `SQLState`s.
-
-- `attemptSqlState` is like `attemptSql` but yields `M[Either[SQLState, A]]`.
-- `attemptSomeSqlState` traps only specified `SQLState`s.
-- `exceptSqlState` recovers from an `SQLState` with a new action.
-- `exceptSomeSqlState` recovers from specified `SQLState`s with a new action.
-
-See the ScalaDoc for more information.
-
-### Example: Unique Constraint Violation
-
-Ok let's set up a `person` table again, using a slightly different formulation just for fun. Note that the `name` column is marked as being unique.
-
-```scala mdoc
-List(
- sql"""DROP TABLE IF EXISTS person""",
- sql"""CREATE TABLE person (
- id SERIAL,
- name VARCHAR NOT NULL UNIQUE
- )"""
-).traverse(_.update.quick).void.unsafeRunSync()
-```
-
-Alright, let's define a `Person` data type and a way to insert instances.
-
-```scala mdoc:silent
-case class Person(id: Int, name: String)
-object Person {
- implicit val read: Read[Person] = Read.derived
- implicit val write: Write[Person] = Write.derived
-}
-
-def insert(s: String): ConnectionIO[Person] = {
- sql"insert into person (name) values ($s)"
- .update
- .withUniqueGeneratedKeys("id", "name")
-}
-```
-
-The first insert will work.
-
-```scala mdoc
-insert("bob").quick.unsafeRunSync()
-```
-
-The second will fail with a unique constraint violation.
-
-```scala mdoc
-try {
- insert("bob").quick.unsafeRunSync()
-} catch {
- case e: java.sql.SQLException =>
- println(e.getMessage)
- println(e.getSQLState)
-}
-```
-
-So let's change our method to return an `Either[String, Person]` by using the `attemptSomeSql` combinator. This allows us to specify the `SQLState` value that we want to trap. In this case the culprit `"23505"` (yes, it's a string) is provided as a constant in the `doobie-postgres` add-on.
-
-
-```scala mdoc:silent
-import doobie.postgres.*
-
-def safeInsert(s: String): ConnectionIO[Either[String, Person]] =
- insert(s).attemptSomeSqlState {
- case sqlstate.class23.UNIQUE_VIOLATION => "Oops!"
- }
-```
-
-Given this definition we can safely attempt to insert duplicate records and get a helpful error message rather than an exception.
-
-
-```scala mdoc
-safeInsert("bob").quick.unsafeRunSync()
-
-safeInsert("steve").quick.unsafeRunSync()
-```
diff --git a/modules/docs/src/main/mdoc/docs/10-Logging.md b/modules/docs/src/main/mdoc/docs/10-Logging.md
deleted file mode 100644
index 6c54508e7..000000000
--- a/modules/docs/src/main/mdoc/docs/10-Logging.md
+++ /dev/null
@@ -1,32 +0,0 @@
-## Logging
-
-If for some reason you want to log SQL you can extend `ConnectionInterpreter`.
-
-```scala
-case class User(name: String)
-val currentUser = IOLocal[Option[User]](None).unsafeRunSync()
-
-def logSQL[T](sql: String, result: Kleisli[IO, Connection, T]): Kleisli[IO, Connection, T] = {
- result.tapWithF { case (_, t) =>
- for {
- user <- currentUser.get
- _ <- IO.delay {
- println(s"user $user; sql: '$sql'")
- }
- } yield t
- }
-}
-
-val i = KleisliInterpreter[IO]
-val LoggingConnectionInterpreter = new i.ConnectionInterpreter {
- override def prepareStatement(a: String) = logSQL(a, super.prepareStatement(a))
-}
-
-val loggingDb = db.copy(interpret0 = LoggingConnectionInterpreter)
-```
-
-### Caveats
-
-Note that the invocation is part of your `ConnectionIO` program, and it is called synchronously. Most back-end loggers are asynchronous so this is unlikely to be an issue, but do take care not to spend too much time in your handler.
-
-Further note that the handler is not transactional; anything your logger does stays done, even if the transaction is rolled back. This is only for diagnostics, not for business logic.
diff --git a/modules/docs/src/main/mdoc/docs/11-Arrays.md b/modules/docs/src/main/mdoc/docs/11-Arrays.md
deleted file mode 100644
index 679f16c32..000000000
--- a/modules/docs/src/main/mdoc/docs/11-Arrays.md
+++ /dev/null
@@ -1,100 +0,0 @@
-## SQL Arrays
-
-This chapter shows how we can map Scala sequence types to SQL `ARRAY` types, for vendors that support it. Note that although SQL array mappings are part of the JDBC specification, their behavior is vendor-specific and requires an add-on library; the code in this chapter requires `doobie-postgres`.
-
-### Setting Up
-
-Again we set up a transactor and pull in YOLO mode. We also need an import to get PostgreSQL-specific type mappings.
-
-```scala mdoc:silent
-import doobie.free.connection.ConnectionIO
-import doobie.postgres.instances.array.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-
-val y = xa.yolo
-import y.*
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-### Reading and Writing Arrays
-
-Let's create a new table with an SQL array column. Note that this is likely to work only for PostgreSQL; the syntax for arrays differs significantly from vendor to vendor.
-
-```scala mdoc:silent
-val drop = sql"DROP TABLE IF EXISTS person".update.quick
-
-val create =
- sql"""
- CREATE TABLE person (
- id SERIAL,
- name VARCHAR NOT NULL UNIQUE,
- pets VARCHAR[] NOT NULL
- )
- """.update.quick
-```
-
-```scala mdoc
-(drop *> create).unsafeRunSync()
-```
-
-**doobie** maps SQL array columns to `Array`, `List`, and `Vector` by default. No special handling is required, other than importing the vendor-specific array support above.
-
-```scala mdoc:silent
-case class Person(id: Long, name: String, pets: List[String])
-object Person {
- implicit val read: Read[Person] = Read.derived
- implicit val write: Write[Person] = Write.derived
-}
-
-def insert(name: String, pets: List[String]): ConnectionIO[Person] = {
- sql"insert into person (name, pets) values ($name, $pets)"
- .update
- .withUniqueGeneratedKeys("id", "name", "pets")
-}
-```
-
-Insert works fine, as does reading the result. No surprises.
-
-```scala mdoc
-insert("Bob", List("Nixon", "Slappy")).quick.unsafeRunSync()
-insert("Alice", Nil).quick.unsafeRunSync()
-```
-
-### Lamentations of `NULL`
-
-**doobie** maps nullable columns via `Option`, so `null` is never observed in programs that use the high-level API, and the typechecking feature discussed earlier will find mismatches. So this means if you have a nullable SQL `varchar[]` then you will be chided grimly if you don't map it as `Option[List[String]]` (or some other supported sequence type).
-
-However there is another axis of variation here: the *array cells* themselves may contain null values. And the query checker can't save you here because this is not reflected in the metadata provided by PostgreSQL or H2, and probably not by anyone.
-
-So there are actually four ways to map an array, and you should carefully consider which is appropriate for your schema. In the first two cases reading a `NULL` cell would result in a `NullableCellRead` exception.
-
-```scala mdoc
-sql"select array['foo','bar','baz']".query[List[String]].quick.unsafeRunSync()
-sql"select array['foo','bar','baz']".query[Option[List[String]]].quick.unsafeRunSync()
-sql"select array['foo',NULL,'baz']".query[List[Option[String]]].quick.unsafeRunSync()
-sql"select array['foo',NULL,'baz']".query[Option[List[Option[String]]]].quick.unsafeRunSync()
-```
diff --git a/modules/docs/src/main/mdoc/docs/12-Custom-Mappings.md b/modules/docs/src/main/mdoc/docs/12-Custom-Mappings.md
deleted file mode 100644
index 72086fa03..000000000
--- a/modules/docs/src/main/mdoc/docs/12-Custom-Mappings.md
+++ /dev/null
@@ -1,223 +0,0 @@
-## Custom Mappings
-
-**doobie** provides two families of abstractions that define mappings between Scala types and schema types. These are used when we pass query arguments to the database, and when we interpret results that come back. Many such mappings are provided for free but it is sometimes necessary to define your own, and this is the subject of this chapter.
-
-The most common kind of custom mapping operates on single column values, so we will examine this kind of mapping first. We will then talk about column vector mappings for "wide" structures.
-
-## Setup
-
-In this chapter we're importing the essentials from Cats and **doobie**, as well as some other odds and ends we'll discuss below.
-
-```scala mdoc:silent
-import doobie.util.Get
-import doobie.util.Put
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.meta.Meta
-import cats.*
-import cats.data.*
-import cats.implicits.*
-import io.circe.*
-import io.circe.jawn.*
-import io.circe.syntax.*
-import java.awt.Point
-import org.postgresql.util.PGobject
-```
-
-## When do I need a custom type mapping?
-
-Your first evidence that you need a new type mapping will likely be a type error. There are two common cases. The first case appears when you try to use an unmapped type as a statement parameter.
-
-```scala mdoc:fail
-def nope(msg: String, ex: Exception): ConnectionIO[Int] =
- sql"INSERT INTO log (message, detail) VALUES ($msg, $ex)".update.run
-```
-
-The second common case is when we try to read rows into a data type that includes an unmapped member type, such as this one.
-
-```scala mdoc:silent
-case class LogEntry(msg: String, ex: Exception)
-```
-
-When we attempt to define a `Query0[LogEntry]` we get a type error similar to the one above.
-
-```scala mdoc:fail
-sql"SELECT message, detail FROM log".query[LogEntry]
-```
-
-In both cases some hints are provided and refer you to this very chapter! So let's keep on going and look at type mappings in detail.
-
-## Single-Column Type Mappings
-
-JDBC defines mappings between JVM types like `Int` and `String` and standard schema types like `INTEGER` and `VARCHAR`. These suffice for most cases, and **doobie** provides them out of the box. We abstract over such mappings via the `Get` and `Put` typeclasses.
-
-- `Get[A]` describes a mapping from some non-nullable schema type to Scala type `A`, and from the equivalent nullable schema type to `Option[A]`. This lets us read column values and statement return values.
-- `Put[A]` describes a mapping from Scala type `A` to some non-nullable schema type, and from `Option[A]` to the equivalent nullable schema type. This lets us set statement parameters and update columns.
-
-Instances are provided for the following Scala types:
-
-- JVM numeric types `Byte`, `Short`, `Int`, `Long`, `Float`, and `Double`;
-- `BigDecimal` (both Java and Scala versions);
-- `Boolean`, `String`, and `Array[Byte]`;
-- `Date`, `Time`, and `Timestamp` from the `java.sql` package;
-- `Date` from the `java.util` package;
-- `Instant`, `LocalDate`, `LocalTime`, `LocalDateTime`, `OffsetTime` and `OffsetDateTime` from the `java.time` package; and
-- single-element case classes wrapping one of the above types.
-
-The `java.time` instances may require a separate import , dependent on your Database Driver . See the [doobie-faq](https://tpolecat.github.io/doobie/docs/17-FAQ.html#how-do-i-use-java-time-types-with-doobie-) for details
-
-The above cases are defined by the JDBC specification. See later chapters on vendor-specific additions, which provide mappings for some non-standard types such as `UUID`s and network addresses.
-
-#### Deriving Get and Put from Existing Instances
-
-If we don't have the `Get` or `Put` instance we need, we can often make one from an existing instance. Consider here a type `Nat` of natural numbers, along with a conversion to and from `Int`.
-
-```scala mdoc:silent
-object NatModule {
-
- sealed trait Nat
- case object Zero extends Nat
- case class Succ(n: Nat) extends Nat
-
- def toInt(n: Nat): Int = {
- def go(n: Nat, acc: Int): Int =
- n match {
- case Zero => acc
- case Succ(n) => go(n, acc + 1)
- }
- go(n, 0)
- }
-
- def fromInt(n: Int): Nat = {
- def go(n: Int, acc: Nat): Nat =
- if (n <= 0) acc else go(n - 1, Succ(acc))
- go(n, Zero)
- }
-
-}
-import NatModule.*
-```
-
-There is no direct schema mapping for `Nat`, but there *is* a schema mapping for `Int` that we get out of the box, and we can use it to define our mapping for `Nat`.
-
-```scala mdoc:silent
-// Bidirectional schema mapping for Nat, in terms of Int
-implicit val natGet: Get[Nat] = Get[Int].map(fromInt)
-implicit val natPut: Put[Nat] = Put[Int].contramap(toInt)
-```
-
-The `.map` and `.contramap` methods conform with the signatures of `Functor` and `Contravariant`, and indeed `Get` and `Put` are instances, respectively. However it's best to use the tagged versions `.tmap` and `.tcontramap` when possible because it makes the name of the type ("Nat" in this case) available to the query checker by requiring a `TypeTag` for the mapped type. This isn't always practical but it can result in better diagnostic messages so it should be preferred.
-
-```scala mdoc:silent
-// Prefer .tmap and .tcontramap when possible.
-implicit val natGet2: Get[Nat] = Get[Int].tmap(fromInt)
-implicit val natPut2: Put[Nat] = Put[Int].tcontramap(toInt)
-```
-
-#### Deriving Get and Put from Meta
-
-Because it is common to define bidirectional mappings there is also a `Meta` typeclass which serves to introduce both a `Get` and `Put` into implicit scope. If you have an implicit `Meta[A]` then you get an implicit `Put[A]` and a `Get[A]` for free.
-
-Because a `Meta` instance exists for `Int` and other base types this is often the most convenient way to define a bidirectional mapping.
-
-```scala mdoc:silent
-// Bidirectional schema mapping for Nat, in terms of Int
-implicit val natMeta: Meta[Nat] = Meta[Int].imap(fromInt)(toInt)
-```
-
-And as above, prefer `.timap` when possible.
-
-```scala mdoc:silent
-// Prefer .timap when possible.
-implicit val natMeta2: Meta[Nat] = Meta[Int].timap(fromInt)(toInt)
-```
-
-**Note:** it is important to understand that `Meta` exists only to introduce `Get`/`Put` pairs into implicit scope. You should never demand `Meta` as evidence in user code: instead demand `Get`, `Put`, or both.
-
-```scala
-def foo[A: Meta](...) // don't do this
-def foo[A: Get: Put](...) // ok
-```
-
-#### Defining Get and Put for Exotic Types
-
-In rare cases it is not possible to define a new mapping in terms of primitive JDBC types because the underlying schema type is vendor-specific or otherwise not part of the JDBC specification. In these cases it is necessary to define mappings explicitly.
-
-In this example we will create a mapping for PostgreSQL's `json` type, which is not part of the JDBC specification. On the Scala side we will use the `Json` type from [Circe](https://github.com/circe/circe). The PostgreSQL JDBC driver transfers `json` values via the JDBC type `OTHER`, with an uwrapped payload type `PGobject`. The only way to know this is by experimentation. You can expect to get this kind of mapping wrong a few times before it starts working. In any case the `OTHER` type is commonly used for nonstandard types and **doobie** provides a way to construct such mappings.
-
-```scala mdoc:silent
-implicit val showPGobject: Show[PGobject] = Show.show(_.getValue.take(250))
-
-implicit val jsonGet: Get[Json] =
- Get.Advanced.other[PGobject](NonEmptyList.of("json")).temap[Json] { o =>
- parse(o.getValue).leftMap(_.show)
- }
-```
-
-In the instance above we read a value via JDBC's `getOther` with schema type `json`, cast the result to `PGobject`, then parse its value (a `String`), returning the parse exception as a string on failure. Consider for a moment how comically optimistic this is. Many things have to work in order to get a `Json` value in hand, and if anything fails it's an unrecoverable error. Effective testing is essential when defining new mappings like this.
-
-The `Put` instance is less error-prone since we know the `Json` we start with is valid. Here we construct and return a new `PGobject` whose schema type and string value are filled in explicitly.
-
-```scala mdoc:silent
-implicit val jsonPut: Put[Json] =
- Put.Advanced.other[PGobject](NonEmptyList.of("json")).tcontramap[Json] { j =>
- val o = new PGobject
- o.setType("json")
- o.setValue(j.noSpaces)
- o
- }
-```
-
-As above, with bidirectional mappings it's usually more convenient to use `Meta`, which provides an `other` constructor allowing the operations above to be combined.
-
-```scala mdoc:silent
-implicit val jsonMeta: Meta[Json] =
- Meta.Advanced.other[PGobject]("json").timap[Json](
- a => parse(a.getValue).leftMap[Json](e => throw e).merge)(
- a => {
- val o = new PGobject
- o.setType("json")
- o.setValue(a.noSpaces)
- o
- }
- )
-```
-
-There are similar constructors for array types and other possibilities, but `other` is by far the most common in user code and we won't discuss the others here. See the Scaladoc for more information.
-
-
-## Column Vector Mappings
-
-The `Get` and `Put` typeclasses described above define mappings between Scala types and single-column schema types, however in general we need more than this. Queries return **heterogeneous vectors** of values that we wish to map to composite Scala data types, and similarly we may wish to map a composite Scala data type to a heterogeneous vector of schema values (when setting a `VALUES` clause in an update, for instance). Mappings for these "wide" data types are provided by the `Read` and `Write` typeclasses.
-
-- `Read[A]` describes a mapping from some vector of schema types to Scala type `A`. This lets us read rows as composite values.
-- `Write[A]` describes a mapping from Scala type `A` to some vector of schema types. This lets us set multiple statement parameters.
-
-As `Read` and `Write` instances are [logically] built from vectors of `Get` and `Put` instances we can construct them automatically in almost all cases. The base cases are:
-
-- We can `Read` and `Write` the zero-width types `Unit`.
-- We can `Read` or `Write` single-column types that have `Get` or `Put` instance, respectively; as well as `Option`s thereof.
-
-The inductive cases that build on the base cases above are:
-
-- We can `Read` or `Write` a product type (case class or tuple) if its tuple representation can be read or written, respectively.
-
-In addition, **doobie** provides `Read[Option[A]]` and `Write[Option[A]]` in the three cases above, mapping all columns to *nullable* schema types. This allows you to map the columns from an `OUTER JOIN` to an *optional* data type. For instance, reading parent/child pairs we might map output rows to the type `(Parent, Option[Child])`.
-
-The above rules allow you to map between column/parameter vectors and [nested] tuples and case classes, which covers most use cases.
-
-#### Deriving Read and Write from Existing Instances
-
-Although automatic derivation will suffice in most cases, it does not work with traits and non-case classes. In these cases we must provide a mapping between the unruly data type and a type that has a defined mapping.
-
-Consider the `Point` class from Java AWT, which is logically a pair of `Int`s but is not a case class and is thus not eligible for automatic derivation of `Read` and `Write` instances. We can define these by hand by mapping to and from the Scala type `(Int, Int)` which *does* have automatically-derived instances.
-
-```scala mdoc
-implicit val pointRead: Read[Point] =
- Read[(Int, Int)].map { case (x, y) => new Point(x, y) }
-
-implicit val pointWrite: Write[Point] =
- Write[(Int, Int)].contramap(p => (p.x, p.y))
-```
-
-There is no equivalent to `Meta` for bidirectional column vector mappings.
diff --git a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md b/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md
deleted file mode 100644
index 5cbb5c6d3..000000000
--- a/modules/docs/src/main/mdoc/docs/13-Unit-Testing.md
+++ /dev/null
@@ -1,153 +0,0 @@
-## Unit Testing
-
-The YOLO-mode query checking feature demonstrated in an earlier chapter is also available as a trait you can mix into your [ScalaTest](http://www.scalatest.org/), [MUnit](https://scalameta.org/munit) or [Weaver](https://disneystreaming.github.io/weaver-test/) unit tests.
-
-### Setting Up
-
-As with earlier chapters we set up a `Transactor` and YOLO mode. We will also use the `doobie-scalatest` module.
-
-```scala mdoc:silent
-import doobie.syntax.string.*
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.transactor.Transactor
-import doobie.util.update.Update0
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-And again we are playing with the `country` table, given here for reference.
-
-```sql
-CREATE TABLE country (
- code character(3) NOT NULL,
- name text NOT NULL,
- population integer NOT NULL,
- gnp numeric(10,2),
- indepyear smallint
- -- more columns, but we won't use them here
-)
-```
-
-So here are a few queries we would like to check. Note that we can only check values of type `Query0` and `Update0`; we can't check `Process` or `ConnectionIO` values, so a good practice is to define your queries in a DAO module and apply further operations at a higher level.
-
-```scala mdoc:silent
-case class Country(code: Int, name: String, pop: Int, gnp: Double)
-object Country {
- implicit val read: Read[Country] = Read.derived
- implicit val write: Write[Country] = Write.derived
-}
-
-val trivial =
- sql"""
- select 42, 'foo'::varchar
- """.query[(Int, String)]
-
-def biggerThan(minPop: Short) =
- sql"""
- select code, name, population, gnp, indepyear
- from country
- where population > $minPop
- """.query[Country]
-
-val update: Update0 =
- sql"""
- update country set name = "new" where name = "old"
- """.update
-
-```
-
-### The ScalaTest Package
-
-The `doobie-scalatest` add-on provides a mix-in trait that we can add to any `Assertions` implementation (like `AnyFunSuite`).
-
-```scala mdoc:silent
-import org.scalatest.*
-
-class AnalysisTestScalaCheck extends funsuite.AnyFunSuite with matchers.must.Matchers with doobie.scalatest.IOChecker {
-
- override val colors = doobie.util.Colors.None // just for docs
-
- val transactor = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", "jdbc:postgresql:world", "postgres", "password"
- )
-
- test("trivial") { check(trivial) }
- test("biggerThan") { checkOutput(biggerThan(0)) }
- test("update") { check(update) }
-
-}
-```
-
-Details are shown for failing tests.
-
-```scala mdoc
-// Run a test programmatically. Usually you would do this from sbt, bloop, etc.
-(new AnalysisTestScalaCheck).execute(color = false)
-```
-
-### The MUnit Package
-
-The `doobie-munit` add-on provides a mix-in trait that we can add to any `Assertions` implementation (like `FunSuite`) much like the ScalaTest package above.
-
-```scala mdoc:silent
-import _root_.munit.*
-
-class AnalysisTestSuite extends FunSuite with doobie.munit.IOChecker {
-
- override val colors = doobie.util.Colors.None // just for docs
-
- val transactor = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", "jdbc:postgresql:world", "postgres", "password"
- )
-
- test("trivial") { check(trivial) }
- test("biggerThan") { checkOutput(biggerThan(0)) }
- test("update") { check(update) }
-
-}
-```
-
-### The Weaver Package
-
-The `doobie-weaver` add-on provides a mix-in trait what we can add to any effectful test Suite.
-The `check` function takes an implicit `Transactor[F]` parameter. Since Weaver has its own way
-to manage shared resources, it is convenient to use that to allocate the transcator.
-
-```scala mdoc:silent
-import _root_.weaver.*
-import doobie.weaver.*
-
-object AnalysisTestSuite extends IOSuite with IOChecker {
-
- override type Res = Transactor[IO]
- override def sharedResource: Resource[IO,Res] =
- Resource.pure(Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", "jdbc:postgresql:world", "postgres", "password"
- ))
-
- test("trivial") { implicit transactor => check(trivial) }
- test("biggerThan") { implicit transactor => checkOutput(biggerThan(0)) }
- test("update") { implicit transactor => check(update) }
-
-}
-```
diff --git a/modules/docs/src/main/mdoc/docs/14-Managing-Connections.md b/modules/docs/src/main/mdoc/docs/14-Managing-Connections.md
deleted file mode 100644
index fc061b170..000000000
--- a/modules/docs/src/main/mdoc/docs/14-Managing-Connections.md
+++ /dev/null
@@ -1,168 +0,0 @@
-## Managing Connections
-
-In this chapter we discuss several ways to manage connections in applications that use **doobie**, including managed/pooled connections and re-use of existing connections. For this chapter we have a few imports and no other setup.
-
-```scala mdoc:silent
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-import doobie.util.transactor.Transactor
-```
-
-### About Transactors
-
-Most **doobie** programs are values of type `ConnectionIO[A]` or `Stream[ConnectionIO, A]` that describe computations requiring a database connection. By providing a means of acquiring a JDBC connection we can transform these programs into computations that can actually be executed. The most common way of performing this transformation is via a `Transactor`.
-
-A `Transactor[M]` consists of the following bits of information:
-
-- An arbitrary piece of configuration, called the `kernel`, particular to a given implementation (see below);
-- a source of connections, computed in `M` (typically `IO`);
-- an *interpreter* from `ConnectionOp ~> M`; and
-- a transaction `Strategy` that specifies a setup, error-handling, and cleanup strategy associated with each database interation.
-
-Given this information a `Transactor[M]` can provide the following transformations:
-
-- `trans: ConnectionIO ~> M` A natural transformation of a program in `ConnectionIO` to the target monad `M` that uses the given `Strategy` to wrap the given program with additional setup, error-handling and cleanup operations. This yields an independent program in `M`. This is the most common way to run a doobie program.
- - e.g., `xa.trans.apply(program1)`
- - you can also use the syntax `program1.transact(xa)`, which runs `xa.trans` under the hood
-- `rawTrans` natural transformation equivalent to `trans` but one that does not use the provided `Strategy` to wrap the given program with additional operations. This can be useful in cases where transactional handling is unsupported or undesired.
-- `rawTransP: Stream[ConnectionIO, ?] ~> Stream[M, ?]` equivalent to `rawTrans` but expressed using `Stream`.
-- `transP: Stream[ConnectionIO, ?] ~> Stream[M, ?]` equivalent to `trans` but expressed using `Stream`.
-- `exec: Kleisli[M, Connection, ?] ~> M` equivalent to `trans` except it transforms a `Kleisli` that expects a `java.sql.Connection` and not a `ConnectionIO`. This can be used in combination with the doobie interpreters, which can transform doobie programs (e.g., `ConnectionIO`) to `Kleisli` effects, in order to implement your own logic for running doobie programs.
-- `rawExec` natural transformation equivalent to `exec` but one that does not use the provided `Strategy` to wrap the given program with additional operations.
-
-So summarizing, once you have a `Transactor[M]` you have a way of discharging `ConnectionIO` and replacing it with some effectful `M` like `IO`. In effect this turns a **doobie** program into a "real" program value that you can integrate with the rest of your application; all doobieness is left behind.
-
-### About threading
-
-Throughout our doobie program we will run both non-blocking CPU-bound tasks or blocking JDBC operations.
-Both of these will be handled by the Cats-Effect runtime.
-
-Requesting and waiting for a connection from the connection pool is a blocking operation too, but this has to be handled by the `connectEC` ExecutionContext.
-This ExecutionContext should be **bounded**, as we do not want to create hundreds and thousands of threads (one for each request) waiting for database connection when the database is busy.
-The maximum thread limit for `connectEC` should be the same as your underlying JDBC connection pool, since any more threads are guaranteed to be blocked.
-
-Because these pools need to be shut down in order to exit cleanly it is typical to use `Resource` to manage their lifetimes. See below for examples.
-
-### Using the JDBC DriverManager
-
-JDBC provides a bare-bones connection provider via `DriverManager.getConnection`, which has the advantage of being extremely simple: there is no connection pooling and thus no configuration required. The disadvantage is that it is quite a bit slower than pooling connection managers, and provides no upper bound on the number of concurrent connections. It executes blocking operations on a similar unbounded pool of daemon threads.
-
-However, for test and for experimentation as described in this book (and for situations where you really do want to ensure that you get a truly fresh connection right away) the `DriverManager` is fine. Support in **doobie** is via `DriverManagerTransactor`. To construct one you must pass the name of the driver class and a connect URL. Normally you will also pass a user/password (the API provides several variants matching the `DriverManager` static API).
-
-```scala mdoc:silent
-import doobie.util.ExecutionContexts
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-### Using a HikariCP Connection Pool
-
-The `doobie-hikari` add-on provides a `Transactor` implementation backed by a [HikariCP](https://github.com/brettwooldridge/HikariCP) connection pool. The connection pool is a lifetime-managed object that must be shut down cleanly, so it is managed as a `Resource`. A program that uses `HikariTransactor` will typically use `IOApp`.
-
-```scala mdoc:silent:reset
-import cats.effect.*
-import cats.implicits.*
-import doobie.hikari.*
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-
-object HikariApp extends IOApp {
-
- // Resource yielding a transactor configured with a bounded connect EC and an unbounded
- // transaction EC. Everything will be closed and shut down cleanly after use.
- val transactor: Resource[IO, HikariTransactor[IO]] =
- for {
- ce <- ExecutionContexts.fixedThreadPool[IO](32) // our connect EC
- xa <- HikariTransactor.newHikariTransactor[IO](
- "org.h2.Driver", // driver classname
- "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", // connect URL
- "sa", // username
- "", // password
- ce // await connection here
- )
- } yield xa
-
-
- def run(args: List[String]): IO[ExitCode] =
- transactor.use { xa =>
-
- // Construct and run your server here!
- for {
- n <- sql"select 42".query[Int].unique.transact(xa)
- _ <- IO(println(n))
- } yield ExitCode.Success
-
- }
-
-}
-```
-
-And running this program gives us the desired result.
-
-```scala mdoc:silent
-HikariApp.main(Array())
-```
-
-### Using an existing DataSource
-
-If your application exposes an existing `javax.sql.DataSource` you can use it directly by using `Transactor.fromDataSource`. You still need to provide execution contexts.
-
-```scala mdoc:silent
-import doobie.util.transactor.Transactor
-import javax.sql.DataSource
-
-// Resource yielding a Transactor[IO] wrapping the given `DataSource`
-def transactor(ds: DataSource): Resource[IO, Transactor[IO]] =
- for {
- ce <- ExecutionContexts.fixedThreadPool[IO](32) // our connect EC
- } yield Transactor.fromDataSource[IO](ds, ce)
-```
-
-The `configure` method on `Transactor` provides access to the underlying `DataSource` if additional configuration is required.
-
-
-### Using an Existing JDBC Connection
-
-If your application exposes an existing `Connection` you can use it directly by wrapping it in a `Transactor`. You still need to provide an execution context for blocking operations. Note that you are responsible for closing the `Connection`.
-
-```scala mdoc:silent
-import java.sql.Connection
-
-// A Transactor[IO] wrapping the given `Connection`
-def transactor(c: Connection): Transactor[IO] =
- Transactor.fromConnection[IO](c)
-```
-
-### Customizing Transactors
-
-If the default `Transactor` behavior don't meet your needs you can replace any member with one that does what you need. See the Scaladoc for `Transactor` and `Strategy` for details on the structure. Lenses are provided to make it straightforward to replace just the piece you're interested in. For example, to create a transactor that is the same as `xa` but always rolls back (for testing perhaps) you can say:
-
-```scala
-val testXa = Transactor.after.set(xa, HC.rollback)
-```
-
-As another example, Hive's JDBC driver doesn't support transaction commit or rollback, you can create your own `Transactor` to accommodate that, like:
-
-```scala
-import doobie.free.connection.unit
-
-val hiveXa = Transactor.strategy.set(xa, Strategy.default.copy(after = unit, oops = unit))
-```
diff --git a/modules/docs/src/main/mdoc/docs/15-Extensions-PostgreSQL.md b/modules/docs/src/main/mdoc/docs/15-Extensions-PostgreSQL.md
deleted file mode 100644
index 384b10074..000000000
--- a/modules/docs/src/main/mdoc/docs/15-Extensions-PostgreSQL.md
+++ /dev/null
@@ -1,368 +0,0 @@
-## Extensions for PostgreSQL
-
-In this chapter we discuss the extended support that **doobie** offers for users of [PostgreSQL](http://www.postgresql.org/). To use these extensions you must add an additional dependency to your project:
-
-@@@ vars
-```scala
-libraryDependencies += "org.tpolecat" %% "doobie-postgres" % "$version$"
-```
-@@@
-
-
-This library pulls in [PostgreSQL JDBC Driver](https://jdbc.postgresql.org) as a transitive dependency.
-
-
-
-There are extensions available for dealing with JSON by using Circe, if you like to use those, include this dependency:
-
-@@@ vars
-
-```scala
-libraryDependencies += "org.tpolecat" %% "doobie-postgres-circe" % "$version$"
-```
-
-@@@
-
-Then, you will be able to import the implicits for dealing with JSON:
-
-@@@ vars
-
-```scala
-import doobie.postgres.circe.json.implicits
-import doobie.postgres.circe.jsonb.implicits
-
-```
-
-@@@
-
-
-### Setting Up
-
-The following examples require a few imports.
-
-```scala mdoc:silent
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.implicits.*
-import doobie.enumerated.SqlState
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.applicativeerror.*
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.meta.Meta
-import doobie.util.transactor.Transactor
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager and executes blocking operations
-// on an our synchronous EC. See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password" // password
-)
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-**doobie** adds support for a large number of extended types that are not supported directly by JDBC. All mappings (except postgis) are provided in the `pgtypes` module.
-
-```scala mdoc:silent
-import doobie.postgres.PFCM
-import doobie.postgres.PHC
-import doobie.postgres.instances.enumeration.*
-import doobie.postgres.sqlstate
-import doobie.postgres.syntax.applicativeerror.*
-import doobie.postgres.syntax.explain.*
-import doobie.postgres.syntax.fragment.*
-```
-
-### Java 8 Time Types (JSR310)
-
-To ensure **doobie** performs the conversion correctly between Java 8 time types and PostgreSQL Date/Time types when handling timezones or the lack thereof.
-The correct combination of date/time types should be used:
-
-- `TIMESTAMP` maps to `java.time.LocalDateTime`
-- `TIMESTAMPTZ` maps to `java.time.Instant` or `java.time.OffsetDateTime`
-- `DATE` maps to `java.time.LocalDate`
-- `TIME` maps to `java.time.LocalTime`
-
-### Array Types
-
-**doobie** supports single-dimensional arrays of the following types:
-
-- `bit[]` maps to `Array[Boolean]`
-- `int4[]` map to `Array[Int]`
-- `int8[]` maps to `Array[Long]`
-- `float4[]` maps to `Array[Float]`
-- `float8[]` maps to `Array[Double]`
-- `varchar[]`, `char[]`, `text[],` and `bpchar[]` all map to `Array[String]`.
-- `uuid[]` maps to `Array[UUID]`
-
-In addition to `Array` you can also map to `List` and `Vector`. Note that arrays of advanced types and structs are not supported by the driver; arrays of `Byte` are represented as `bytea`; and arrays of `int2` are incorrectly mapped by the driver as `Array[Int]` rather than `Array[Short]` and are not supported in **doobie**.
-
-See the previous chapter on **SQL Arrays** for usage examples.
-
-### Enum Types
-
-**doobie** supports mapping PostgreSQL `enum` types to Scala enumerated types, with the slight complication that Scala doesn't really support enumerated types as a first-class notion. We will examine three ways to construct mappings for the following PostgreSQL type:
-
-```sql
-create type myenum as enum ('foo', 'bar')
-```
-
-```java
-// This is Java code
-public enum MyJavaEnum { foo, bar; }
-```
-
-```scala
-implicit val MyJavaEnumMeta: Meta[MyJavaEnum] = pgJavaEnum[MyJavaEnum]("myenum")
-```
-
-And the final, most general construction simply requires evidence that your target type can be translated to and from `String`.
-
-```scala mdoc:silent
-sealed trait FooBar
-
-object FooBar {
-
- case object Foo extends FooBar
- case object Bar extends FooBar
-
- def toEnum(e: FooBar): String =
- e match {
- case Foo => "foo"
- case Bar => "bar"
- }
-
- def fromEnum(s: String): Option[FooBar] =
- Option(s) collect {
- case "foo" => Foo
- case "bar" => Bar
- }
-
-}
-
-implicit val FoobarMeta: Meta[FooBar] =
- pgEnumStringOpt("myenum", FooBar.fromEnum, FooBar.toEnum)
-```
-
-```scala mdoc
-sql"select 'foo'::myenum".query[FooBar].unique.transact(xa).unsafeRunSync()
-```
-
-
-### Geometric Types
-
-The following geometric types are supported, and map to driver-supplied types.
-
-- the `box` schema type maps to `org.postgresql.geometric.PGbox`
-- the `circle` schema type maps to `org.postgresql.geometric.PGcircle`
-- the `lseg` schema type maps to `org.postgresql.geometric.PGlseg`
-- the `path` schema type maps to `org.postgresql.geometric.PGpath`
-- the `point` schema type maps to `org.postgresql.geometric.PGpoint`
-- the `polygon` schema type maps to `org.postgresql.geometric.PGpolygon`
-
-It is expected that these will be mapped to application-specific types via `xmap` as described in **Custom Mappings**.
-
-### PostGIS Types
-
-**doobie** provides mappings for the top-level PostGIS geometric types provided by the `org.postgis` driver extension.
-
-Mappings for postgis are provided in the `postgis` module.
-
-```scala mdoc:silent
-import doobie.postgis.instances.geometry.*
-```
-
-- `PGgeometry`
-- `PGbox2d`
-- `PGbox3d`
-
-In addition to the general types above, **doobie** provides mappings for the following abstract and concrete fine-grained types carried by `PGgeometry`:
-
-- `Geometry`
-- `ComposedGeom`
-- `GeometryCollection`
-- `MultiLineString`
-- `MultiPolygon`
-- `PointComposedGeom`
-- `LineString`
-- `MultiPoint`
-- `Polygon`
-- `Point`
-
-[Geographic types](http://postgis.net/workshops/postgis-intro/geography.html) mappings are defined in a different object (`geography`), to allow geometric types using geodetic coordinates.
-
-```
-import doobie.postgis.instances.geography.*
-
-// or define the implicit conversion manually
-
-implicit val geographyPoint: Meta[Point] =
- doobie.postgres.pgisgeographyimplicits.PointType
-```
-- Point
-- Polygon
-- MultiPoint
-- LineString
-- PointComposedGeom
-- MultiPolygon
-- MultiLineString
-
-
-### Other Nonstandard Types
-
-- The `uuid` schema type is supported and maps to `java.util.UUID`.
-- The `inet` schema type is supported and maps to `java.net.InetAddress`.
-- The `hstore` schema type is supported and maps to both `java.util.Map[String, String]` and Scala `Map[String, String]`.
-
-### Extended Error Handling
-
-A complete table of SQLSTATE values is provided in the `doobie.postgres.sqlstate` module. Recovery combinators for each of these states (`onUniqueViolation` for example) are provided in `doobie.postgres.syntax`.
-
-```scala mdoc:silent
-val p = sql"oops".query[String].unique // this won't work
-```
-
-Some of the recovery combinators demonstrated:
-
-```scala mdoc
-p.attempt.transact(xa).unsafeRunSync() // attempt is provided by ApplicativeError instance
-
-p.attemptSqlState.transact(xa).unsafeRunSync() // this catches only SQL exceptions
-
-p.attemptSomeSqlState { case SqlState("42601") => "caught!" } .transact(xa).unsafeRunSync() // catch it
-
-p.attemptSomeSqlState { case sqlstate.class42.SYNTAX_ERROR => "caught!" } .transact(xa).unsafeRunSync() // same, w/constant
-
-p.exceptSomeSqlState { case sqlstate.class42.SYNTAX_ERROR => "caught!".pure[ConnectionIO] } .transact(xa).unsafeRunSync() // recover
-
-p.onSyntaxError("caught!".pure[ConnectionIO]).transact(xa).unsafeRunSync() // using recovery combinator
-```
-
-
-### Server-Side Statements
-
-PostgreSQL supports server-side caching of prepared statements after a certain number of executions, which can have desirable performance consequences for statements that only need to be planned once. Note that this caching happens only for `PreparedStatement` instances that are re-used within a single connection lifetime. **doobie** supports programmatic configuration of the prepare threshold:
-
-- For a given `Connection` you can set and query the prepare threshold with the `ConnectionIO` constructors `doobie.postgres.hi.connection.pgSetPrepareThreshold` and `pgGetPrepareThreshold`.
-- For a specific `PreparedStatement` you can set and query the prepare threshold with the `PreparedStatementIO` constructors `doobie.postgres.hi.preparedstatement.pgSetPrepareThreshold` and `pgGetPrepareThreshold`.
-
-See the [JDBC driver documentation](https://jdbc.postgresql.org/documentation/93/server-prepare.html) for more information.
-
-### `LISTEN` and `NOTIFY`
-
-PostgreSQL provides a simple transactional message queue that can be used to notify a connection that something interesting has happened. Such notifications can be tied to database triggers, which provides a way to notify clients that data has changed. Which is cool.
-
-**doobie** provides `ConnectionIO` constructors for SQL `LISTEN`, `UNLISTEN`, and `NOTIFY` in the `doobie.postgres.hi.connection` module. New notifications are retrieved (synchronously, sadly, that's all the driver provides) via `pgGetNotifications`. Note that all of the "listening" operations apply to the **current connection**, which must therefore be long-running and typically off to the side from normal transactional operations. Further note that you must `setAutoCommit(false)` on this connection or `commit` between each call in order to retrieve messages. The `examples` project includes a program that demonstrates how to present a channel as a `Stream[IO, PGNotification]`.
-
-### Large Objects
-
-PostgreSQL provides a facility for storing very large objects (up to 4TB each) in a single uniform storage, identified by unique numeric ID and accessed via fast byte-block transfer. Note that "normal" large object columns types such as `bytea` and `text` can store values as large as 1GB each, so the large object API is rarely used. However there are cases where the size and/or efficiency of large objects justifies the use of this API.
-
-**doobie** provides an algebra and free monads for the driver's `LargeObjectManager` and `LargeObject` types in the `doobie.postgres.free` package. There is also [the beginnings of] a high-level API that includes constructors for creating large objects from files and vice-versa. The `example` project contains a brief usage example.
-
-Please file an issue or ask questions on the [Gitter](https://gitter.im/tpolecat/doobie) channel if you need to use this API; it will evolve as use cases demand.
-
-### Copy Manager
-
-The PostgreSQL JDBC driver's [CopyManager](https://jdbc.postgresql.org/documentation/publicapi/org/postgresql/copy/CopyManager.html) API provides a pass-through for the SQL [`COPY`](http://www.postgresql.org/docs/9.3/static/sql-copy.html) statement, allowing very fast data transfer via `java.io` streams. Here we construct a program that dumps a table to `Console.out` in CSV format, with quoted values.
-
-```scala mdoc:silent
-val q = """
- copy country (name, code, population)
- to stdout (
- encoding 'utf-8',
- force_quote *,
- format csv
- )
- """
-
-val prog: ConnectionIO[Long] =
- PHC.pgGetCopyAPI(PFCM.copyOut(q, Console.out)) // return value is the row count
-```
-
-See the links above and sample code in the `examples/` project in the **doobie** GitHub repo for more information on this specialized API.
-
-
-Note: the following API was introduced in version 0.5.2 and is experimental. Community feedback and contributions (instances in particular) are encouraged.
-
-
-**doobie** also provides a specialized API for very fast batch inserts using upates of the form `COPY ... FROM STDIN` and a `Text` typeclass that defines how data types are encoded in Postgres text format (similar to `Put`; instances must be present for all fields in the data type to be inserted).
-
-First a temp table for our experiment.
-
-```scala mdoc:silent
-val create: ConnectionIO[Unit] =
- sql"""
- CREATE TEMPORARY TABLE food (
- name VARCHAR,
- vegetarian BOOLEAN,
- calories INTEGER
- )
- """.update.run.void
-```
-
-And some values to insert. `Text` instances are provided for all the data types we are using here.
-
-```scala mdoc:silent
-case class Food(name: String, isVegetarian: Boolean, caloriesPerServing: Int)
-
-val foods = List(
- Food("banana", true, 110),
- Food("cheddar cheese", true, 113),
- Food("Big Mac", false, 1120)
-)
-```
-
-Our insert statement must have the form `COPY ... FROM STDIN`, and we can insert any `Foldable`.
-
-```scala mdoc:silent
-def insert[F[_]: Foldable](fa: F[Food]): ConnectionIO[Long] =
- sql"COPY food (name, vegetarian, calories) FROM STDIN".copyIn(fa)
-```
-
-We can run it thus, yielding the number of affected rows.
-
-```scala mdoc
-(create *> insert(foods)).transact(xa).unsafeRunSync()
-```
-
-### EXPLAIN/EXPLAIN ANALYZE
-
-The PostgreSQL server can provide an analysis of any query, using the `EXPLAIN` keyword. **doobie** can run `EXPLAIN`
-on any `Query0` or `Query` object, as long as `doobie.postgres._` and `doobie.postgres.implicits._` have been imported.
-Using an example from earlier in the book:
-
-```scala mdoc
-sql"select name from country"
- .query[String] // Query0[String]
- .explain
- .transact(xa)
- .unsafeRunSync()
- .foreach(println)
-```
-
-Similary, `explainAnalyze` will analyze the query **and** run it, comparing the query planner's estimates with real
-performance. Using the example above again:
-
-```scala mdoc
-sql"select name from country"
- .query[String] // Query0[String]
- .explainAnalyze
- .transact(xa)
- .unsafeRunSync()
- .foreach(println)
-```
-
-`explain` and `explainAnalyze` both return a `ConnectinIO[List[String]]` result, where each member of the list is one
-row of the query planner's output.
diff --git a/modules/docs/src/main/mdoc/docs/16-Extensions-H2.md b/modules/docs/src/main/mdoc/docs/16-Extensions-H2.md
deleted file mode 100644
index e4afec8ec..000000000
--- a/modules/docs/src/main/mdoc/docs/16-Extensions-H2.md
+++ /dev/null
@@ -1,92 +0,0 @@
-## Extensions for H2
-
-In this chapter we discuss the extended support that **doobie** offers for users of [H2](http://www.h2database.com/html/main.html) . To use these extensions you must add an additional dependency to your project:
-
-@@@ vars
-```scala
-libraryDependencies += "org.tpolecat" %% "doobie-h2" % "$version$"
-```
-@@@
-
-This library pulls in H2 as a transitive dependency.
-
-There are extensions available for dealing with JSON by using Circe, if you like to use those, include this dependency:
-
-@@@ vars
-
-```scala
-libraryDependencies += "org.tpolecat" %% "doobie-h2-circe" % "$version$"
-```
-
-@@@
-
-Then, you will be able to import the implicits for dealing with JSON:
-
-@@@ vars
-
-```scala
-import doobie.h2.circe.json.implicits
-```
-
-@@@
-
-### Array Types
-
-**doobie** supports H2 arrays of the following types:
-
-- `Boolean`
-- `Int`
-- `Long`
-- `Float`
-- `Double`
-- `String`
-
-In addition to `Array` you can also map to `List` and `Vector`.
-
-See the previous chapter on **SQL Arrays** for usage examples.
-
-### Other Nonstandard Types
-
-- The `uuid` type is supported and maps to `java.util.UUID`.
-
-### H2 Connection Pool
-
-**doobie** provides a `Transactor` that wraps the connection pool provided by H2. Because the transactor has internal state, constructing one is a side-effect that must be captured (here by `IO`).
-
-```scala mdoc:silent:reset
-import cats.effect.*
-import cats.implicits.*
-import doobie.h2.H2Transactor
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-
-object H2App extends IOApp {
-
- // Resource yielding a transactor configured with a bounded connect EC and an unbounded
- // transaction EC. Everything will be closed and shut down cleanly after use.
- val transactor: Resource[IO, H2Transactor[IO]] =
- for {
- ce <- ExecutionContexts.fixedThreadPool[IO](32) // our connect EC
- xa <- H2Transactor.newH2Transactor[IO](
- "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", // connect URL
- "sa", // username
- "", // password
- ce, // await connection here
- )
- } yield xa
-
-
- def run(args: List[String]): IO[ExitCode] =
- transactor.use { xa =>
-
- // Construct and run your server here!
- for {
- n <- sql"select 42".query[Int].unique.transact(xa)
- _ <- IO(println(n))
- } yield ExitCode.Success
-
- }
-
-}
-```
diff --git a/modules/docs/src/main/mdoc/docs/17-FAQ.md b/modules/docs/src/main/mdoc/docs/17-FAQ.md
deleted file mode 100644
index b74721336..000000000
--- a/modules/docs/src/main/mdoc/docs/17-FAQ.md
+++ /dev/null
@@ -1,221 +0,0 @@
-## Frequently-Asked Questions
-
-In this chapter we address some frequently-asked questions, in no particular order. First a bit of set-up.
-
-```scala mdoc:silent
-import cats.*
-import cats.data.*
-import cats.effect.*
-import cats.effect.implicits.*
-import cats.implicits.*
-import doobie.FC
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.string.*
-import doobie.util.ExecutionContexts
-import doobie.util.Read
-import doobie.util.Write
-import doobie.util.meta.Meta
-import doobie.util.query.Query0
-import doobie.util.transactor.Transactor
-import java.awt.geom.Point2D
-import java.util.UUID
-
-// This is just for testing. Consider using cats.effect.IOApp instead of calling
-// unsafe methods directly.
-import cats.effect.unsafe.implicits.global
-
-// A transactor that gets connections from java.sql.DriverManager.
-// See the chapter on connection handling for more info.
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", // driver classname
- "jdbc:postgresql:world", // connect URL (driver-specific)
- "postgres", // user
- "password", // password
-)
-```
-
-```scala mdoc:invisible
-implicit val mdocColors: doobie.util.Colors = doobie.util.Colors.None
-```
-
-### How do I do an `IN` clause?
-
-This used to be very irritating, but as of 0.4.0 there is a good solution. See the section on `IN` clauses in [Chapter 5](05-Parameterized.html) and [Chapter 8](08-Fragments.html) on statement fragments.
-
-### How do I ascribe an SQL type to an interpolated parameter?
-
-Interpolated parameters are replaced with `?` placeholders, so if you need to ascribe an SQL type you can use vendor-specific syntax in conjunction with the interpolated value. For example, in PostgreSQL you use `:: type`:
-
-```scala mdoc
-{
- val y = xa.yolo
- import y.*
- val s = "foo"
- sql"select $s".query[String].check.unsafeRunSync()
- sql"select $s :: char".query[String].check.unsafeRunSync()
-}
-```
-
-### How do I do several things in the same transaction?
-
-You can use a `for` comprehension to compose any number of `ConnectionIO` programs, and then call `.transact(xa)` on the result. All of the composed programs will run in the same transaction. For this reason it's useful for your APIs to expose values in `ConnectionIO`, so higher-level code can place transaction boundaries as needed.
-
-### How do I run something outside of a transaction?
-
-`Transactor.transact` takes a `ConnectionIO` and constructs an `IO` or similar that will run it in a single transaction, but it is also possible to include transaction boundaries *within* a `ConnectionIO`, and to disable transaction handling altogether. Some kinds of DDL statements may require this for some databases. You can define a combinator to do this for you.
-
-```scala mdoc:silent
-/**
- * Take a program `p` and return an equivalent one that first commits
- * any ongoing transaction, runs `p` without transaction handling, then
- * starts a new transaction.
- */
-def withoutTransaction[A](p: ConnectionIO[A]): ConnectionIO[A] =
- FC.setAutoCommit(true).bracket(_ => p)(_ => FC.setAutoCommit(false))
-```
-
-Note that you need both of these operations if you are using a `Transactor` because it will always start a transaction and will try to commit on completion.
-
-
-### How do I turn an arbitrary SQL string into a `Query0/Update0`?
-
-As of **doobie** 0.4.0 this is done via [statement fragments](08-Fragments.html). Here we choose the sort order dynamically.
-
-```scala mdoc:silent
-case class Code(country: String)
-object Code {
- implicit val read: Read[Code] = Read.derived
- implicit val write: Write[Code] = Write.derived
-}
-case class City(code: Code, name: String, population: Int)
-object City {
- implicit val read: Read[City] = Read.derived
-}
-
-def cities(code: Code, asc: Boolean): Query0[City] = {
- val ord = if (asc) fr"ASC" else fr"DESC"
- val sql = fr"""
- SELECT countrycode, name, population
- FROM city
- WHERE countrycode = $code
- ORDER BY name""" ++ ord
- sql.query[City]
-}
-```
-
-We can check the resulting `Query0` as expected.
-
-```scala mdoc
-{
- val y = xa.yolo
- import y.*
- cities(Code("USA"), true).check.unsafeRunSync()
-}
-```
-
-And it works!
-
-```scala mdoc
-{
- val y = xa.yolo
- import y.*
- cities(Code("USA"), true).stream.take(5).quick.unsafeRunSync()
- cities(Code("USA"), false).stream.take(5).quick.unsafeRunSync()
-}
-```
-
-### How do I handle outer joins?
-
-With an outer join you end up with set of nullable columns, which you typically want to map to a single `Option` of some composite type, which doobie can do for you. If all columns are null you will get back `None`.
-
-```scala mdoc:silent
-case class Country(name: String, code: String)
-object Country {
- implicit val read: Read[Country] = Read.derived
-}
-case class City2(name: String, district: String)
-object City2 {
- implicit val read: Read[City2] = Read.derived
-}
-
-val join =
- sql"""
- select c.name, c.code,
- k.name, k.district
- from country c
- left outer join city k
- on c.capital = k.id
- """.query[(Country, Option[City2])]
-```
-
-Some examples, filtered for size.
-
-```scala mdoc
-{
- val y = xa.yolo
- import y.*
- join.stream.filter(_._1.name.startsWith("United")).quick.unsafeRunSync()
-}
-```
-
-### How do I log the SQL produced for my query after interpolation?
-
-As of **doobie** 0.4 there is a reasonable solution to the logging/instrumentation question. See [Chapter 10](10-Logging.html) for more details.
-
-### Why is there no `Get` or `Put` for `SQLXML`?
-
-There are a lot of ways to handle `SQLXML` so there is no pre-defined strategy, but here is one that maps `scala.xml.Elem` to `SQLXML` via streaming.
-
-```scala mdoc:silent
-import doobie.enumerated.JdbcType.Other
-import java.sql.SQLXML
-import scala.xml.{ XML, Elem }
-
-implicit val XmlMeta: Meta[Elem] =
- Meta.Advanced.one[Elem](
- Other,
- NonEmptyList.of("xml"),
- (rs, n) => XML.load(rs.getObject(n).asInstanceOf[SQLXML].getBinaryStream),
- (ps, n, e) => {
- val sqlXml = ps.getConnection.createSQLXML
- val osw = new java.io.OutputStreamWriter(sqlXml.setBinaryStream)
- XML.write(osw, e, "UTF-8", false, null)
- osw.close
- ps.setObject(n, sqlXml)
- },
- (_, _, _) => sys.error("update not supported, sorry")
- )
-```
-
-## How do I set the chunk size for streaming results?
-
-By default streams constructed with the `sql` interpolator are fetched `Query.DefaultChunkSize` rows at a time (currently 512). If you wish to change this chunk size you can use `streamWithChunkSize` for queries, and `withGeneratedKeysWithChunkSize` for updates that return results.
-
-## My Postgres domains are all type checking as DISTINCT! How can I get my Yolo tests to pass?
-
-Domains with check constraints will type check as DISTINCT. For Doobie later than 0.4.4, in order to get the type checks to pass, you can define a `Meta` of with target type Distinct and `xmap` that instances. For example,
-
-```scala mdoc:silent
-import cats.data.NonEmptyList
-import doobie.*
-import doobie.enumerated.JdbcType
-
-object distinct {
-
- def string(name: String): Meta[String] =
- Meta.Advanced.many(
- NonEmptyList.of(JdbcType.Distinct, JdbcType.VarChar),
- NonEmptyList.of(name),
- _ getString _,
- _.setString(_, _),
- _.updateString(_, _)
- )
-}
-
-case class NonEmptyString(value: String)
-
-// If the domain for NonEmptyStrings is nes
-implicit val nesMeta: Meta[NonEmptyString] = {
- distinct.string("nes").imap(NonEmptyString.apply)(_.value)
-}
-```
diff --git a/modules/docs/src/main/mdoc/docs/index.md b/modules/docs/src/main/mdoc/docs/index.md
deleted file mode 100644
index 6f6d1aabe..000000000
--- a/modules/docs/src/main/mdoc/docs/index.md
+++ /dev/null
@@ -1,23 +0,0 @@
-@@@ index
-* [Introduction](01-Introduction.md)
-* [Toolkit](02-Toolkit.md)
-* [Connecting](03-Connecting.md)
-* [Selecting](04-Selecting.md)
-* [Parameterized](05-Parameterized.md)
-* [Checking](06-Checking.md)
-* [Updating](07-Updating.md)
-* [Fragments](08-Fragments.md)
-* [Error-Handling](09-Error-Handling.md)
-* [Logging](10-Logging.md)
-* [Arrays](11-Arrays.md)
-* [Custom-Mappings](12-Custom-Mappings.md)
-* [Unit-Testing](13-Unit-Testing.md)
-* [Managing-Connections](14-Managing-Connections.md)
-* [Extensions-PostgreSQL](15-Extensions-PostgreSQL.md)
-* [Extensions-H2](16-Extensions-H2.md)
-* [FAQ](17-FAQ.md)
-@@@
-
-# Book of Doobie
-
-@@toc { depth=1 }
\ No newline at end of file
diff --git a/modules/docs/src/main/mdoc/img/navbar_brand.png b/modules/docs/src/main/mdoc/img/navbar_brand.png
deleted file mode 100644
index 4fa4d8a8a..000000000
Binary files a/modules/docs/src/main/mdoc/img/navbar_brand.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/img/navbar_brand.svg b/modules/docs/src/main/mdoc/img/navbar_brand.svg
deleted file mode 100644
index 9c1bb768c..000000000
--- a/modules/docs/src/main/mdoc/img/navbar_brand.svg
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
diff --git a/modules/docs/src/main/mdoc/img/navbar_brand2x.png b/modules/docs/src/main/mdoc/img/navbar_brand2x.png
deleted file mode 100644
index 180fddc35..000000000
Binary files a/modules/docs/src/main/mdoc/img/navbar_brand2x.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/img/nesting.png b/modules/docs/src/main/mdoc/img/nesting.png
deleted file mode 100644
index 5634c14ea..000000000
Binary files a/modules/docs/src/main/mdoc/img/nesting.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/img/sidebar_brand.png b/modules/docs/src/main/mdoc/img/sidebar_brand.png
deleted file mode 100644
index dbc38dd95..000000000
Binary files a/modules/docs/src/main/mdoc/img/sidebar_brand.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/img/sidebar_brand2x.png b/modules/docs/src/main/mdoc/img/sidebar_brand2x.png
deleted file mode 100644
index 180fddc35..000000000
Binary files a/modules/docs/src/main/mdoc/img/sidebar_brand2x.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/img/yeah.png b/modules/docs/src/main/mdoc/img/yeah.png
deleted file mode 100644
index b4463c2f6..000000000
Binary files a/modules/docs/src/main/mdoc/img/yeah.png and /dev/null differ
diff --git a/modules/docs/src/main/mdoc/index.md b/modules/docs/src/main/mdoc/index.md
deleted file mode 100644
index 057007094..000000000
--- a/modules/docs/src/main/mdoc/index.md
+++ /dev/null
@@ -1,115 +0,0 @@
-@@@ index
-* [Infographic](infographic.md)
-* [Book of Doobie](docs/index.md)
-@@@
-
-
-# doobie
-
-**doobie** is a pure functional JDBC layer for Scala and [**Cats**](http://typelevel.org/cats/). It is not an ORM, nor is it a relational algebra; it simply provides a functional way to construct programs (and higher-level libraries) that use JDBC. For common use cases **doobie** provides a minimal but expressive high-level API:
-
-```scala mdoc:silent
-import doobie.free.connection.ConnectionIO
-import doobie.syntax.connectionio.*
-import doobie.syntax.string.*
-import doobie.util.Read
-import doobie.util.transactor.Transactor
-import cats.effect.IO
-import scala.concurrent.ExecutionContext
-
-import cats.effect.unsafe.implicits.global
-
-val xa = Transactor.fromDriverManager[IO](
- "org.postgresql.Driver", "jdbc:postgresql:world", "postgres", "password"
-)
-
-case class Country(code: String, name: String, population: Long)
-object Country {
- implicit val read: Read[Country] = Read.derived
-}
-
-def find(n: String): ConnectionIO[Option[Country]] =
- sql"select code, name, population from country where name = $n".query[Country].option
-```
-
-And then …
-
-```scala mdoc
-find("France").transact(xa).unsafeRunSync()
-```
-
-**doobie** is a [**Typelevel**](http://typelevel.org/) project. This means we embrace pure, typeful, functional programming, and provide a safe and friendly environment for teaching, learning, and contributing as described in the Scala [**Code of Conduct**](http://scala-lang.org/conduct.html).
-
-## Quick Start
-
-The current version is **$version$** for **Scala $scala-versions$** with
-
-- [**cats**](http://typelevel.org/cats/) $catsVersion$
-- [**fs2**](https://github.com/functional-streams-for-scala/fs2) $fs2Version$
-
-
-
-
-
-To use **doobie** you need to add the following to your `build.sbt`. If you're not using the Postgres or H2 add-ons you'll also need to provide a JDBC driver for the database you're using.
-
-@@@ vars
-```scala
-libraryDependencies ++= Seq(
-
- // Start with this one
- "org.tpolecat" %% "doobie-core" % "$version$",
-
- // And add any of these as needed
- "org.tpolecat" %% "doobie-h2" % "$version$", // H2 driver $h2Version$ + type mappings.
- "org.tpolecat" %% "doobie-hikari" % "$version$", // HikariCP transactor.
- "org.tpolecat" %% "doobie-postgres" % "$version$", // Postgres driver $postgresVersion$ + type mappings.
- "org.tpolecat" %% "doobie-scalatest" % "$version$" % "test" // ScalaTest support for typechecking statements.
-
-)
-```
-@@@
-
-See the [**documentation**](docs/01-Introduction.html) for more information on these add-ons.
-
-Note that **doobie** is pre-1.0 software and is still undergoing active development. New versions are **not** binary compatible with prior versions, although in most cases user code will be source compatible. Starting with the 0.5.x we're trying to be a bit more careful about versioning.
-
-## Documentation and Support
-
-- Behold the sparkly [**documentation**](docs/01-Introduction.html) ← start here
-- The [**Scaladoc**](https://www.javadoc.io/doc/org.tpolecat/doobie-core_$scala.binary.version$) will be handy once you get your feet wet.
-- See [**releases**](https://github.com/tpolecat/doobie/releases) for an overview of changes in this and previous versions.
-- The [**Gitter Channel**](https://gitter.im/tpolecat/doobie) is a great place to chat!
-- There is a [**Scala Exercises**](https://www.scala-exercises.org/) module, courtesy of our friends at 47 Degrees!
-- There is also the [**source**](https://github.com/tpolecat/doobie). Check out the examples too.
-- If you have comments or run into trouble, please file an issue.
-
-## Presentations, Blog Posts, etc.
-
-Listed newest first. If you have given a presentation or have written a blog post that includes **doobie**, let me know and I'll add it to this list.
-
-- [Learn Doobie for the Greater Good](https://blog.rockthejvm.com/doobie/) by Daniel Ciocîrlan, Rock the JVM Blog, 28-Dec 2021
-- [Typechecking SQL queries with doobie](https://blog.godatadriven.com/doobie-monix-jdbc-example) by Bas Beelen, GoDataDriven, 13-Feb 2018
-- [Doobie - Feedback from the Trenches](http://fr.slideshare.net/normation/doobie-feedbacks-from-the-trenches-scalaio-2016) by François Armand, ScalaIO, October 2016
-- [Pure Functional Database Programming with Fixpoint Types](https://www.youtube.com/watch?v=7xSfLPD6tiQ) by Rob Norris - Scala World, 2016 - [slides](http://tpolecat.github.io/presentations/sw2016/slides.html#1)
-- [The Functional Web Stack](http://www.lyranthe.org/presentations/http4s_doobie_circe.pdf) by Gary Coady - Dublin Scala Users Group, April 2016
-- [End to End and On The Level](https://www.youtube.com/watch?v=lMW_yMkxX4Q&list=PL_5uJkfWNxdkQd7FbN1whrTOsJPMgHgLg&index=2) by Dave Gurnell - Typelevel Summit, Philadelphia, March 2016
-- [Programs as Values: JDBC Programming with doobie](https://www.youtube.com/watch?v=M5MF6M7FHPo) by Rob Norris - Scala by the Bay, 2015 - [slides](http://tpolecat.github.io/assets/sbtb-slides.pdf)
-- [Typechecking SQL in Slick and doobie](http://underscore.io/blog/posts/2015/05/28/typechecking-sql.html) by Richard Dallaway
-- [DB to JSON with a Microservice](http://da_terry.bitbucket.org/slides/presentation-scalasyd-functional-jdbc-http/#/) by Da Terry - [code](https://bitbucket.org/da_terry/scalasyd-doobie-http4s)
-
-## Testing
-
-If you want to build and run the tests for yourself, you'll need a local postgresql database. The easiest way to do this is to run `docker-compose up` from the project root.
-
-## Building the Doc Site
-
-I have to look this up every time. So here's the dance.
-
-```
-% sbt
-sbt:doobie> project docs
-sbt:docs> clean
-sbt:docs> makeSite
-sbt:doce> ghpagesPushSite
-```
diff --git a/modules/docs/src/main/mdoc/infographic.md b/modules/docs/src/main/mdoc/infographic.md
deleted file mode 100644
index d72f26ec0..000000000
--- a/modules/docs/src/main/mdoc/infographic.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Infographic
-
-This is an embedded SVG image. Click it to visit the [repository](https://github.com/tpolecat/doobie-infographic), which also has a PDF export and the OmniGraffle source if you care to contribute. This is very much a **work in progress** and feedback in the form of issues and/or stars would be helpful!
-
-
-
diff --git a/project/FreeGen2.scala b/project/FreeGen2.scala
index 89b96f773..075646f5d 100644
--- a/project/FreeGen2.scala
+++ b/project/FreeGen2.scala
@@ -10,19 +10,21 @@ object FreeGen2 {
lazy val freeGen2Dir = settingKey[File]("directory where free algebras go")
lazy val freeGen2Package = settingKey[String]("package where free algebras go")
lazy val freeGen2Renames = settingKey[Map[Class[?], String]]("map of imports that must be renamed")
- lazy val freeGen2 = taskKey[Seq[File]]("generate free algebras")
+ @transient lazy val freeGen2 = taskKey[List[File]]("generate free algebras")
lazy val freeGen2Settings = Seq(
freeGen2Classes := Nil,
freeGen2Dir := (Compile / sourceManaged).value,
freeGen2Package := "doobie.free",
freeGen2Renames := Map(classOf[java.sql.Array] -> "SqlArray"),
- freeGen2 := new FreeGen2(
- freeGen2Classes.value,
- freeGen2Package.value,
- freeGen2Renames.value,
- state.value.log,
- ).gen(freeGen2Dir.value),
+ freeGen2 := {
+ new FreeGen2(
+ freeGen2Classes.value,
+ freeGen2Package.value,
+ freeGen2Renames.value,
+ streams.value.log,
+ ).gen(freeGen2Dir.value)
+ },
)
}
@@ -405,7 +407,7 @@ class FreeGen2(managed: List[Class[?]], pkg: String, renames: Map[Class[?], Stri
|sealed trait Embedded[A]
|
|object Embedded {
- | ${managed.map(ClassTag(_)).map(embed(_)).mkString("\n ")}
+ | ${managed.map(c => embed(using ClassTag(c))).mkString("\n ")}
|}
|
|// Typeclass for embeddable pairs (J, F)
@@ -476,7 +478,7 @@ class FreeGen2(managed: List[Class[?]], pkg: String, renames: Map[Class[?], Stri
|import cats.free.Free
|import scala.concurrent.duration.FiniteDuration
|
- |${managed.map(ClassTag(_)).flatMap(imports(_)).distinct.sorted.mkString("\n")}
+ |${managed.flatMap(c => imports(using ClassTag(c))).distinct.sorted.mkString("\n")}
|
|${managed.map(_.getSimpleName).map(c => s"import ${pkg}.${c.toLowerCase}.{ ${c}IO, ${c}Op }").mkString("\n")}
|
@@ -532,16 +534,16 @@ class FreeGen2(managed: List[Class[?]], pkg: String, renames: Map[Class[?], Stri
| }
|
| // Interpreters
- |${managed.map(ClassTag(_)).map(interp(_)).mkString("\n")}
+ |${managed.map(c => interp(using ClassTag(c))).mkString("\n")}
|
|}
|""".trim.stripMargin
- def gen(base: File): Seq[File] = {
+ def gen(base: File): List[File] = {
log.info("Generating free algebras into " + base)
val fs = managed.map { c =>
base.mkdirs
- val mod = module(ClassTag(c))
+ val mod = module(using ClassTag(c))
val file = printToFile(base, s"${c.getSimpleName.toLowerCase}.scala", mod)
log.info(s"${c.getName} -> ${file.getName}")
file
diff --git a/project/build.properties b/project/build.properties
index dabdb1590..312809a98 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=1.12.11
+sbt.version=2.0.1
diff --git a/project/build.sbt b/project/build.sbt
index 0c049b3d9..3e16ec35a 100644
--- a/project/build.sbt
+++ b/project/build.sbt
@@ -1,3 +1,3 @@
// Required for the freegen definition for postgres in ../build.sbt
-val postgresVersion = "42.7.11"
+val postgresVersion = "42.7.12"
libraryDependencies += "org.postgresql" % "postgresql" % postgresVersion
diff --git a/project/plugins.sbt b/project/plugins.sbt
index e839f2a62..6ff487df2 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,14 +1,9 @@
-addSbtPlugin("com.github.sbt" % "sbt-ghpages" % "0.9.0")
-addSbtPlugin("com.github.sbt" % "sbt-site-paradox" % "1.7.0")
-addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.9.0")
-
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.8")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.6.1")
-addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.5.8")
+addSbtPlugin("org.wartremover" % "sbt-wartremover" % "3.6.1")
addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.2")
-addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.5")
+addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.6")
-//addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
-//addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.3.1")
+addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.7.0")
diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt
index 83e0d9cb1..6573bbd47 100644
--- a/project/project/plugins.sbt
+++ b/project/project/plugins.sbt
@@ -1,2 +1,2 @@
// reload plugins; dependencyUpdates
-//addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
+//addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.7.0")