opium

An opionated Haskell Postgres library.

Quick Start

We assume that our database contains this table:

CREATE TABLE person (
  name TEXT NOT NULL,
  age INT NOT NULL,
  score DOUBLE PRECISION NOT NULL,
  motto TEXT
)

We can use opium to decode query results into Haskell data types:

{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}

import GHC.Generics (Generic)
import Database.PostgreSQL.LibPQ (Connection)
import qualified Database.PostgreSQL.Opium as Opium

data User = User
  { name  :: String
  , age   :: Int
  , score :: Double
  } deriving (Eq, Generic, Show)

instance Opium.FromRow User where

getUsers :: Connection -> IO (Either Opium.Error [User])
getUsers = Opium.fetch_ "SELECT * FROM user"

The Opium.FromRow instance is implemented generically for all product types ("records"). It looks up the field name in the query result and decodes the column value using Opium.FromField.

The intended use case for this library is to enable us to write ad-hoc types for query results without having to manually write instances. For example, if we wanted to figure out how user age influences their score:

data ScoreByAge = ScoreByAge { t :: Double, m :: Double }
  deriving (Eq, Generic, Show)

instance Opium.FromRow ScoreByAge where
  
getScoreByAge :: Connection -> IO ScoreByAge
getScoreByAge conn = do
  let query = "SELECT regr_intercept(score, age) AS t, regr_slope(score, age) AS m FROM user"
  Right (Identity x) <- Opium.fetch_ query conn
  pure x

TO DO

  • Implement String and Text decoding
  • Implement Int decoding
  • Implement error reporting i.e. use Either OpiumError instead of Maybe
  • Implement Float and Double decoding
  • Clean up and document column table stuff
  • Decode LibPQ.Binary
  • Implement date -> Day decoding
  • Implement UTCTime
  • Implement ByteString decoding (bytea)
  • Test negative integer decoding, especially for Integer
  • Implement time intervals
  • and zoned time decoding
  • How about timezone? This could prove problematic when the server and application have different time zones
  • Implement fetch (fetch_ but with parameter passing)
  • Implement JSON decoding
  • Implement (anonymous) composite types
  • Catch UnicodeException when decoding text
    • This might not be necessary if Postgres guarantees us that having a textual OID on a field means that the field is encoded correctly.
  • Implement array decoding
  • Better docs and structure for FromRow module
  • Lexer for PostgreSQL that replaces $name by $1, $2, etc.
  • Tutorial
    • Rationale
    • FromRow
    • Custom FromField impls
  • Improve type errors when trying to instance a type that isn't a record (e.g. sum type)
  • Improve documentation for fromRow module
Description
An opionated Haskell Postgres library, WIP
Readme 256 KiB
Languages
Haskell 96.2%
Shell 2.2%
Nix 1.6%