opium/README.md

72 lines
2.3 KiB
Markdown

# opium
> An opionated Haskell Postgres library.
## Quick Start
We assume that our database contains this table:
```sql
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:
```haskell
{-# 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 [Users])
getUsers conn = Opium.fetch_ conn "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:
```haskell
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 [x] <- Opium.fetch_ conn query
pure x
```
## TO DO
- [x] Implement `String` and `Text` decoding
- [x] Implement `Int` decoding
- [x] Implement error reporting i.e. use `Either OpiumError` instead of `Maybe`
- [x] Implement `Float` and `Double` decoding
- [x] Clean up and document column table stuff
- [ ] Implement `fetch` (`fetch_` but with parameter passing)
- [ ] Implement `UTCTime` and zoned time decoding
- [ ] Implement JSON decoding
- [ ] Implement `ByteString` decoding (`bytea`)
- Can we make the fromField instance choose whether it wants binary or text?
- [ ] Implement (anonymous) composite types
- It seems that in order to decode these, we'd need to use binary mode. In order to avoid writing everything twice it would be wise to move the whole `FromField` machinery to decoding from binary first