hpgsql
Safe HaskellNone
LanguageHaskell2010

Hpgsql

Description

Hpgsql is a PostgreSQL driver written entirely in Haskell, with no bindings to libpq. It communicates with the database using the PostgreSQL wire protocol directly.

Quick start

Connect to the database and run a simple query:

{-# LANGUAGE QuasiQuotes #-}
import Hpgsql
import Hpgsql.Connection (withConnection, ConnectionString(..))
import Hpgsql.Query (sql)

main :: IO ()
main = do
  let connstr = ConnectionString
        { hostname = "localhost"
        , port     = 5432
        , user     = "postgres"
        , password = ""
        , database = "mydb"
        , options  = ""
        }
  -- Connect with a 10-second timeout
  withConnection connstr 10 $ \conn -> do
    rows <- query conn [sql|SELECT 1 + 1|]
    print (rows :: [Only Int])
    -- [Only {fromOnly = 2}]

Building queries

Use the [sql|...|] quasiquoter from Hpgsql.Query to build queries. Interpolate Haskell values with #{}:

let name = "Alice" :: Text
rows <- query conn [sql|SELECT id, email FROM users WHERE name = #{name}|]
print (rows :: [(Int, Text)])

Values interpolated with #{} are sent as query parameters, so they are safe from SQL injection.

To embed a Query fragment (e.g. an identifier or a sub-query), use ^{}:

let tableName = escapeIdentifier "users"
rows <- query conn [sql|SELECT * FROM ^{tableName}|]

You can also use mkQuery with dollar-numbered query arguments:

query conn (mkQuery "SELECT * FROM users WHERE age BETWEEN $1 AND $2" (18 :: Int, 60 :: Int))

And finally, you can use the [sqlPrep|...|] quasiquoter to build prepared statements.

Fetching results

  • query returns all rows as a list.
  • queryS returns all rows as a Stream, streaming directly from the socket (without cursors).
  • query1 returns exactly one row, throwing if zero or more than one row is returned.
  • queryMay returns zero or one row, throwing if more than one is returned.
  • execute runs a statement and returns the count of affected rows.
  • execute_ runs a statement and discards the result.

All these methods have equivalents in Hpgsql.Pipeline that can be composed together to reduce the number of round-trips.

Encoders and decoders

To define your own encoder and decoder instances, take a look at Hpgsql.Encoding.

Handling errors

  • Hpgsql is interruption-safe (with one exception for COPY inside transactions; see Hpgsql.Copy), so a query can be interrupted by asynchronous exceptions and you should still be able to run new queries on the same connection without any other side-effects. Naturally, it is up to you to determine which queries ran or not to completion, since they might have side-effects.
  • Hpgsql will throw either PostgresError or IrrecoverableHpgsqlError, and:
  • If you receive a IrrecoverableHpgsqlError, Hpgsql makes no promises about which statements ran to completion and what connection state is, and you should closeForcefully the connection without running any other queries. These errors should only be thrown for "obvious" developer mistakes from which usually there would be no way to proceed, anyway.
  • If you receive a PostgresError exception, postgres and Hpgsql's states are synced and you can issue new queries afterwards.

What's in this module

This module re-exports some of the essentials: querying and the core types. We recommend importing from this module instead of others whenever possible. For more functionality, see:

Synopsis

Query

query :: FromPgRow a => HPgConnection -> Query -> IO [a] Source #

Fetches any number of rows from a query.

query conn "SELECT * FROM table"

queryWith :: RowDecoder a -> HPgConnection -> Query -> IO [a] Source #

Fetches any number of rows from a query with a custom row decoder.

queryWith rowDecoder conn "SELECT * FROM table"

queryMWith :: RowDecoderMonadic a -> HPgConnection -> Query -> IO [a] Source #

Prefer to use query and queryWith, because RowDecoder can typecheck PostgreSQL results even when no rows are returned by queries, and RowDecoderMonadic cannot.

queryS :: FromPgRow a => HPgConnection -> Query -> IO (Stream (Of a) IO ()) Source #

Runs a query and streams results directly from the connection's socket, i.e. without using cursors.

Note on thread safety: it is important to note the same thread that runs this must be the thread that consumes the returned Stream, and the returned Stream must be consumed completely (up to the last row or a postgres error) before you are able to run other queries.

querySWith :: RowDecoder a -> HPgConnection -> Query -> IO (Stream (Of a) IO ()) Source #

Runs a query and streams results directly from the connection's socket, i.e. without using cursors.

Note on thread safety: it is important to note the same thread that runs this must be the thread that consumes the returned Stream, and the returned Stream must be consumed completely (up to the last row or a postgres error) before you are able to run other queries.

querySMWith :: RowDecoderMonadic a -> HPgConnection -> Query -> IO (Stream (Of a) IO ()) Source #

Runs a query and streams results directly from the connection's socket, i.e. without using cursors.

Prefer to use queryS and querySWith, because RowDecoder can typecheck PostgreSQL results even when no rows are returned by queries, and RowDecoderMonadic cannot.

Note on thread safety: it is important to note the same thread that runs this must be the thread that consumes the returned Stream, and the returned Stream must be consumed completely (up to the last row or a postgres error) before you are able to run other queries.

query1 :: FromPgRow a => HPgConnection -> Query -> IO a Source #

Fetch exactly one row (not zero, not more than one) or throw an exception otherwise.

query1With :: RowDecoder a -> HPgConnection -> Query -> IO a Source #

Fetch exactly one row (not zero, not more than one) or throw an exception otherwise.

queryMay :: FromPgRow a => HPgConnection -> Query -> IO (Maybe a) Source #

Fetch one or zero rows (not more than one) or throw an exception otherwise.

queryMayWith :: RowDecoder a -> HPgConnection -> Query -> IO (Maybe a) Source #

Fetch one or zero rows (not more than one) or throw an exception otherwise.

execute :: HPgConnection -> Query -> IO Int64 Source #

Executes a SQL statement (that may or may not be row-returning) and returns the count of affected rows of the given query.

execute_ :: HPgConnection -> Query -> IO () Source #

Executes a SQL statement that may or may not be row-returning.

Useful re-exports

data HPgConnection Source #

Instances

Instances details
Eq HPgConnection Source # 
Instance details

Defined in Hpgsql.InternalTypes

data PostgresError Source #

An error coming from PostgreSQL. You can safely handle this and continue using the connection.

data IrrecoverableHpgsqlError Source #

If you receive this exception, don't run any further SQL statements or use it for anything. Just close the connection with closeForcefully and discard it.