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
andSession
) 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 andVoid
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
Packages
Skunk network stack, starting with BitVectorSocket
at the bottom and ending with Protocol
at the top (Session
delegates all its work to Protocol
).
Skunk network stack, starting with BitVectorSocket
at the bottom and ending with Protocol
at the top (Session
delegates all its work to Protocol
). Everything is non-blocking.
Attributes
Grouped members
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 Encoder
s 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 Fragment
s and interpolated constants like table names. See StringContextOps
for more information on the interpolator.
SQL and parameter encoder for a statement that returns no rows.
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)]
Value parameters
- encoder
-
An encoder for all parameters
$1
,$2
, etc., insql
. - sql
-
A SQL statement returning no rows.
Attributes
- See also
-
StringContextOps for information on the
sql
interpolator.Session for information on executing a
Command
. - Companion
- object
- Source
- Command.scala
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait Statement[A]class Objecttrait Matchableclass AnyShow all
A composable, embeddable hunk of SQL and typed parameters (common precursor to Command
and Query
).
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
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
SQL, parameter encoder, and row decoder for a statement that returns rows.
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)]
Value parameters
- decoder
-
A decoder for selected columns.
- encoder
-
An encoder for all parameters
$1
,$2
, etc., insql
. - origin
-
The
Origin
where the sql was defined, if any. - sql
-
A SQL statement returning no rows.
Attributes
- See also
-
StringContextOps for information on the
sql
interpolator.Session for information on executing a
Query
. - Companion
- object
- Source
- Query.scala
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait Statement[A]class Objecttrait Matchableclass AnyShow all
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
.
A channel that can be used for inter-process communication, implemented in terms of LISTEN
and NOTIFY
.
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
- Supertypes
- Self type
-
An open cursor from which rows can be fetched, valid during the lifetime its defining Session
.
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
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
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
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
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
- Supertypes
-
class Objecttrait Matchableclass Any
Represents a live connection to a Postgres database.
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
- Supertypes
-
class Objecttrait Matchableclass 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.
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
- Supertypes
- Self type
-
Codec[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
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
-
trait Codec[A]
- Self type
-
Decoder[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
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
-
trait Codec[A]
- Self type
-
Encoder[A]
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
- Supertypes
-
class Objecttrait Matchableclass 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.
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
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
~.type
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.
Attributes
- Companion
- trait
- Source
- Channel.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Channel.type
Attributes
- Companion
- trait
- Source
- Codec.scala
- Supertypes
- Self type
-
Codec.type
Attributes
- Companion
- class
- Source
- Command.scala
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
Command.type
Attributes
- Companion
- trait
- Source
- Cursor.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Cursor.type
Attributes
- Companion
- trait
- Source
- Decoder.scala
- Supertypes
- Self type
-
Decoder.type
Attributes
- Companion
- trait
- Source
- Encoder.scala
- Supertypes
- Self type
-
Encoder.type
Attributes
- Companion
- class
- Source
- Fragment.scala
- Supertypes
-
trait Producttrait Mirrortrait TwiddleSyntax[Fragment]class Objecttrait Matchableclass AnyShow all
- Self type
-
Fragment.type
Attributes
- Companion
- trait
- Source
- PreparedCommand.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
PreparedCommand.type
Attributes
- Companion
- trait
- Source
- PreparedQuery.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
PreparedQuery.type
Attributes
- Companion
- class
- Source
- Query.scala
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
Query.type
Attributes
- Companion
- trait
- Source
- Session.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Session.type
Type members
Classlikes
A fragment applied to its argument, yielding an existentially-typed fragment + argument pair that can be useful when constructing dynamic queries in application code.
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
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
Attributes
- Companion
- trait
- Source
- AppliedFragment.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
AppliedFragment.type
Specifies how encoded values are redacted before being shown in exceptions and traces.
Specifies how encoded values are redacted before being shown in exceptions and traces.
Attributes
- Companion
- object
- Source
- RedactionStrategy.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
Attributes
- Companion
- trait
- Source
- RedactionStrategy.scala
- Supertypes
-
trait Sumtrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
RedactionStrategy.type
Enumerated type of Postgres error codes.
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
- Supertypes
-
trait Enumtrait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- object
- Source
- Statement.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
Attributes
- Companion
- trait
- Source
- Statement.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Statement.type
Control methods for use within a transaction
block.
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
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Transaction[F]
Attributes
- Companion
- trait
- Source
- Transaction.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Transaction.type
Attributes
- Source
- feature.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
feature.type
Attributes
- Source
- feature.scala
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
featureFlags.type
Attributes
- Source
- package.scala
- Supertypes
-
trait ToAllOpstrait ToDecoderOpstrait ToDecoderOpsLowtrait ToEncoderOpstrait ToEncoderOpsLowtrait ToCodecOpstrait ToCodecOpsLowtrait ToListOpstrait ToStringContextOpstrait ToIdOpsclass Objecttrait Matchableclass AnyShow all
- Self type
-
implicits.type
Value members
Concrete fields
Attributes
- Source
- package.scala