Add quick start section to readme
This commit is contained in:
parent
d54cf6ea7f
commit
423f28f0a2
54
README.md
54
README.md
@ -2,14 +2,66 @@
|
||||
|
||||
> 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)
|
||||
|
||||
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?
|
||||
- [x] Clean up and document column table stuff
|
||||
|
@ -1,5 +1,6 @@
|
||||
{-# LANGUAGE DeriveGeneric #-}
|
||||
{-# LANGUAGE DuplicateRecordFields #-}
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
@ -38,6 +39,13 @@ data ManyFields = ManyFields
|
||||
|
||||
instance Opium.FromRow ManyFields where
|
||||
|
||||
data ScoreByAge = ScoreByAge
|
||||
{ m :: Double
|
||||
, t :: Double
|
||||
} deriving (Eq, Generic, Show)
|
||||
|
||||
instance Opium.FromRow ScoreByAge where
|
||||
|
||||
isLeft :: Either a b -> Bool
|
||||
isLeft (Left _) = True
|
||||
isLeft _ = False
|
||||
@ -125,3 +133,7 @@ spec = do
|
||||
it "Fails for the wrong column type" $ \conn -> do
|
||||
rows <- Opium.fetch_ @Person conn "SELECT 'quby' AS name, 'indeterminate' AS age"
|
||||
rows `shouldBe` Left (Opium.ErrorInvalidOid "age" $ LibPQ.Oid 25)
|
||||
|
||||
it "Works for the readme regression example" $ \conn -> do
|
||||
rows <- Opium.fetch_ @ScoreByAge conn "SELECT regr_intercept(score, age) AS t, regr_slope(score, age) AS m FROM person"
|
||||
rows `shouldSatisfy` \case { (Right [ScoreByAge _ _]) -> True; _ -> False }
|
||||
|
@ -23,8 +23,8 @@ setupConnection = do
|
||||
conn <- LibPQ.connectdb $ Encoding.encodeUtf8 $ Text.pack dsn
|
||||
_ <- LibPQ.setClientEncoding conn "UTF8"
|
||||
|
||||
_ <- LibPQ.exec conn "CREATE TABLE person (name TEXT NOT NULL, age INT NOT NULL, motto TEXT)"
|
||||
_ <- LibPQ.exec conn "INSERT INTO person VALUES ('paul', 25), ('albus', 103)"
|
||||
_ <- LibPQ.exec conn "CREATE TABLE person (name TEXT NOT NULL, age INT NOT NULL, score DOUBLE PRECISION NOT NULL, motto TEXT)"
|
||||
_ <- LibPQ.exec conn "INSERT INTO person VALUES ('paul', 25, 30), ('albus', 103, 50.42)"
|
||||
|
||||
pure conn
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user