skunk

package skunk

'''Skunk''' is a functional data access layer for Postgres.

Design principles:

  • Skunk doesn't use JDBC. It speaks the Postgres wire protocol. It will not work with any other database back end.
  • Skunk is asynchronous all the way down, via cats-effect, fs2, and ultimately nio. The high-level network layers (Protocol and Session) are safe to use concurrently.
  • Serialization to and from schema types is not typeclass-based, so there are no implicit derivations. Codecs are explicit, like parser combinators.
  • I'm not sweating arity abstraction that much. Pass a ~ b ~ c for three args and Void if there are no args. This may change in the future but it's fine for now.
  • Skunk uses Resource for lifetime-managed objects, which means it takes some discipline to avoid leaks, especially when working concurrently. May or may not end up being problematic.
  • I'm trying to write good Scaladoc this time.

A minimal example follows. We construct a Resource that yields a Session, then use it.

package example

import cats.effect._
import skunk._
import skunk.implicits._
import skunk.codec.numeric._

object Minimal extends IOApp {

 val session: Resource[IO, Session[IO]] =
   Session.single(
     host     = "localhost",
     port     = 5432,
     user     = "postgres",
     database = "world",
   )

 def run(args: List[String]): IO[ExitCode] =
   session.use { s =>
     for {
       n <- s.unique(sql"select 42".query(int4))
       _ <- IO(println(s"The answer is $n."))
     } yield ExitCode.Success
   }

}

Continue reading for an overview of the library. It's pretty small.

Attributes

Members list

Concise view

Queries and Commands

Skunk recognizes two classes of statements: Query, for statements that return rows; and Command, for statements that do not return rows. These values can be constructed directly but typically arise via the sql interpolator.

 val q = sql"""
   SELECT id, name
   FROM   employee
   WHERE  age > $int2
 """.query(int4 ~ varchar) // Query[Short, Long ~ String]

In the above example note that query parameters are specified by interpolated Encoders and column types are specified by a Decoder passed to .query. The ~ syntax constructs left-associated HLists of types and values via nested pairs. These are all described in more detail a bit further down. Commands are constructed in a similar way but have no output columns and thus no Decoder is needed.

 val c = sql"""
   UPDATE employee
   SET    salary = salary * 1.05
   WHERE  id = $int8
 """.command // Command[Long]

The interpolator also permits nested Fragments and interpolated constants like table names. See StringContextOps for more information on the interpolator.

final case class Command[A](sql: String, origin: Origin, encoder: Encoder[A]) extends Statement[A]

SQL and parameter encoder for a statement that returns no rows. We assume that sql has the same number of placeholders of the form $1, $2, etc., as the number of slots encoded by encoder, and that the parameter types specified by encoder are consistent with the schema. The check methods on Session provide a means to verify this assumption.

SQL and parameter encoder for a statement that returns no rows. We assume that sql has the same number of placeholders of the form $1, $2, etc., as the number of slots encoded by encoder, and that the parameter types specified by encoder are consistent with the schema. The check methods on Session provide a means to verify this assumption.

You can construct a Command directly, although it is more typical to use the sql interpolator.

sql"INSERT INTO foo VALUES ($int2, $varchar)".command // Command[(Short, String)]

Attributes

encoder

An encoder for all parameters $1, $2, etc., in sql.

sql

A SQL statement returning no rows.

See also:

StringContextOps for information on the sql interpolator.

Session for information on executing a Command.

Companion:
object
Source:
Command.scala
Graph
Supertypes
trait Serializable
trait Product
trait Equals
trait Statement[A]
class Object
trait Matchable
class Any
final case class Fragment[A](parts: List[Either[String, State[Int, String]]], encoder: Encoder[A], origin: Origin) extends A => AppliedFragment

A composable, embeddable hunk of SQL and typed parameters (common precursor to Command and Query). Although it is possible to construct a Fragment directly it is more typical to use the sql interpolator.

A composable, embeddable hunk of SQL and typed parameters (common precursor to Command and Query). Although it is possible to construct a Fragment directly it is more typical to use the sql interpolator.

Attributes

Companion:
object
Source:
Fragment.scala
Graph
Supertypes
trait Serializable
trait Product
trait Equals
trait A => AppliedFragment
class Object
trait Matchable
class Any
final case class Query[A, B](sql: String, origin: Origin, encoder: Encoder[A], decoder: Decoder[B], isDynamic: Boolean) extends Statement[A]

SQL, parameter encoder, and row decoder for a statement that returns rows. We assume that sql has the same number of placeholders of the form $1, $2, etc., as the number of slots encoded by encoder, that sql selects the same number of columns are the number of slots decoded by decoder, and that the parameter and column types specified by encoder and decoder are consistent with the schema. The check methods on Session provide a means to verify this assumption.

SQL, parameter encoder, and row decoder for a statement that returns rows. We assume that sql has the same number of placeholders of the form $1, $2, etc., as the number of slots encoded by encoder, that sql selects the same number of columns are the number of slots decoded by decoder, and that the parameter and column types specified by encoder and decoder are consistent with the schema. The check methods on Session provide a means to verify this assumption.

You can construct a Query directly, although it is more typical to use the sql interpolator.

sql"SELECT name, age FROM person WHERE age > $int2".query(varchar *: int2) // Query[Short, (String, Short)]

Attributes

decoder

A decoder for selected columns.

encoder

An encoder for all parameters $1, $2, etc., in sql.

origin

The Origin where the sql was defined, if any.

sql

A SQL statement returning no rows.

See also:

StringContextOps for information on the sql interpolator.

Session for information on executing a Query.

Companion:
object
Source:
Query.scala
Graph
Supertypes
trait Serializable
trait Product
trait Equals
trait Statement[A]
class Object
trait Matchable
class Any

Session Values

Skunk's central abstraction is the Session, which represents a connection to Postgres. From the Session we can produce prepared statements, cursors, and other resources that ultimately depend on their originating Session.

trait Channel[F[_], A, B] extends (F, A) => Unit

A '''channel''' that can be used for inter-process communication, implemented in terms of LISTEN and NOTIFY. All instances start life as a Channel[F, String, Notification] but can be mapped out to different input and output types. See the linked documentation for more information on the transactional semantics of these operations.

A '''channel''' that can be used for inter-process communication, implemented in terms of LISTEN and NOTIFY. All instances start life as a Channel[F, String, Notification] but can be mapped out to different input and output types. See the linked documentation for more information on the transactional semantics of these operations.

Attributes

See also:
Companion:
object
Source:
Channel.scala
Graph
Supertypes
trait Stream[F, A] => Stream[F, Unit]
class Object
trait Matchable
class Any
Self type
Channel[F, A, B]
trait Cursor[F[_], A]

An open cursor from which rows can be fetched, valid during the lifetime its defining Session. You can use this mechanism to implement chunked reads and paged results, although it is ofen more pleasant to use a Cursor-backed Stream, as produced by PreparedQuery#stream.

An open cursor from which rows can be fetched, valid during the lifetime its defining Session. You can use this mechanism to implement chunked reads and paged results, although it is ofen more pleasant to use a Cursor-backed Stream, as produced by PreparedQuery#stream.

Attributes

Companion:
object
Source:
Cursor.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Cursor[F, A]
trait PreparedCommand[F[_], A]

A prepared command, valid for the life of its defining Session.

A prepared command, valid for the life of its defining Session.

Attributes

Companion:
object
Source:
PreparedCommand.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
trait PreparedQuery[F[_], A, B]

A prepared query, valid for the life of its originating Session.

A prepared query, valid for the life of its originating Session.

Attributes

Companion:
object
Source:
PreparedQuery.scala
Graph
Supertypes
class Object
trait Matchable
class Any
trait Session[F[_]]

Represents a live connection to a Postgres database. Operations provided here are safe to use concurrently. Note that this is a lifetime-managed resource and as such is invalid outside the scope of its owning Resource, as are any streams constructed here. If you start an operation be sure to join its Fiber before releasing the resource.

Represents a live connection to a Postgres database. Operations provided here are safe to use concurrently. Note that this is a lifetime-managed resource and as such is invalid outside the scope of its owning Resource, as are any streams constructed here. If you start an operation be sure to join its Fiber before releasing the resource.

See the companion object for information on obtaining a pooled or single-use instance.

Attributes

Companion:
object
Source:
Session.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class Impl[F]

Codecs

When you construct a statement each parameter is specified via an Encoder, and row data is specified via a Decoder. In some cases encoders and decoders are symmetric and are defined together, as a Codec. There are many variants of this pattern in functional Scala libraries; this is closest in spirit to the strategy adopted by scodec.

trait Codec[A] extends Encoder[A] with Decoder[A]

Symmetric encoder and decoder of Postgres text-format data to and from Scala types.

Symmetric encoder and decoder of Postgres text-format data to and from Scala types.

Attributes

Companion:
object
Source:
Codec.scala
Graph
Supertypes
trait Decoder[A]
trait Encoder[A]
class Object
trait Matchable
class Any
Self type
trait Decoder[A]

Decoder of Postgres text-format data into Scala types.

Decoder of Postgres text-format data into Scala types.

Attributes

Companion:
object
Source:
Decoder.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
trait Codec[A]
Self type
trait Encoder[A]

Encoder of Postgres text-format data from Scala types.

Encoder of Postgres text-format data from Scala types.

Attributes

Companion:
object
Source:
Encoder.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
trait Codec[A]
Self type
sealed trait Void

A singly-inhabited type representing arguments to a parameterless statement.

A singly-inhabited type representing arguments to a parameterless statement.

Attributes

Companion:
object
Source:
Void.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
object Void.type

HLists

This idea was borrowed from scodec. We use ~ to build left-associated nested pairs of values and types, and can destructure with ~ the same way.

 val a: Int ~ String ~ Boolean =
   1 ~ "foo" ~ true
 a match {
   case n ~ s ~ b => ...
 }

Note that the ~ operation for Codec, Encoder, and Decoder is lifted. This is usually what you want. If you do need an HList of encoders you can use Tuple2.

 val c: Encoder[Int ~ String ~ Boolean]
   int4 ~ bpchar ~ bit
 // Unusual, but for completeness you can do it thus:
 val d: Encoder[Int] ~ Encoder[String] ~ Encoder[Boolean] =
   ((int4, bpchar), bit)

It is possible that we will end up switching to shapeless.HList but this is good for now.

object ~

Companion providing unapply for ~ such that (x ~ y ~ z) match { case a ~ b ~ c => ... }.

Companion providing unapply for ~ such that (x ~ y ~ z) match { case a ~ b ~ c => ... }.

Attributes

Source:
package.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
~.type
type ~[+A, +B] = (A, B)

Infix alias for (A, B) that provides convenient syntax for left-associated HLists.

Infix alias for (A, B) that provides convenient syntax for left-associated HLists.

Attributes

Source:
package.scala

Companion Objects

Companion objects for the traits and classes above.

object Channel

Attributes

Companion:
trait
Source:
Channel.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Channel.type
object Codec extends TwiddleSyntax[Codec]

Attributes

Companion:
trait
Source:
Codec.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Codec.type
object Command

Attributes

Companion:
class
Source:
Command.scala
Graph
Supertypes
trait Product
trait Mirror
class Object
trait Matchable
class Any
Self type
Command.type
object Cursor

Attributes

Companion:
trait
Source:
Cursor.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Cursor.type
object Decoder extends TwiddleSyntax[Decoder]

Attributes

Companion:
trait
Source:
Decoder.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Decoder.type
object Encoder extends TwiddleSyntax[Encoder]

Attributes

Companion:
trait
Source:
Encoder.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Encoder.type

Attributes

Companion:
class
Source:
Fragment.scala
Graph
Supertypes
trait Product
trait Mirror
class Object
trait Matchable
class Any
Self type

Attributes

Companion:
trait
Source:
PreparedCommand.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type

Attributes

Companion:
trait
Source:
PreparedQuery.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
object Query

Attributes

Companion:
class
Source:
Query.scala
Graph
Supertypes
trait Product
trait Mirror
class Object
trait Matchable
class Any
Self type
Query.type
object Session

Attributes

Companion:
trait
Source:
Session.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
Session.type
case object Void extends Void

Attributes

Companion:
trait
Source:
Void.scala
Graph
Supertypes
trait Singleton
trait Product
trait Mirror
trait Serializable
trait Product
trait Equals
trait Void
class Object
trait Matchable
class Any
Self type
Void.type

Type members

Classlikes

sealed trait AppliedFragment

A fragment applied to its argument, yielding an existentially-typed fragment + argument pair that can be useful when constructing dynamic queries in application code. Applied fragments must be deconstructed in order to prepare and execute.

A fragment applied to its argument, yielding an existentially-typed fragment + argument pair that can be useful when constructing dynamic queries in application code. Applied fragments must be deconstructed in order to prepare and execute.

Attributes

Companion:
object
Source:
AppliedFragment.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type

Attributes

Companion:
trait
Source:
AppliedFragment.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
abstract class SSL

Attributes

Companion:
object
Source:
SSL.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
object None.type
object System.type
object Trusted.type
Self type
object SSL

Attributes

Companion:
class
Source:
SSL.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
SSL.type
enum SqlState(val code: String)

Enumerated type of Postgres error codes. These can be used as extractors for error handling, for example:

Enumerated type of Postgres error codes. These can be used as extractors for error handling, for example:

doSomething.recoverWith { case SqlState.ForeignKeyViolation(ex) => ... }

Attributes

See also:
Source:
SqlState.scala
Graph
Supertypes
trait Enum
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
trait Statement[A]

Attributes

Companion:
object
Source:
Statement.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class Command[A]
class Query[A, B]
object Statement

Attributes

Companion:
trait
Source:
Statement.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
trait Transaction[F[_]]

Control methods for use within a transaction block. An instance is provided when you call Session.transaction(...).use.

Control methods for use within a transaction block. An instance is provided when you call Session.transaction(...).use.

Attributes

See also:

Session#transaction for information on default commit/rollback behavior

Companion:
object
Source:
Transaction.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type

Attributes

Companion:
trait
Source:
Transaction.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
object feature

Attributes

Source:
feature.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
feature.type

Attributes

Source:
feature.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Self type
object implicits extends ToAllOps

Attributes

Source:
package.scala
Graph
Supertypes
trait ToAllOps
trait ToListOps
trait ToIdOps
class Object
trait Matchable
class Any
Self type

Types

type SessionPool[F[_]] = Resource[F, Resource[F, Session[F]]]

Attributes

Source:
package.scala

Attributes

Source:
package.scala

Value members

Concrete fields

val Strategy: Strategy.type

Attributes

Source:
package.scala