-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDBTypes.hs
71 lines (55 loc) · 3.1 KB
/
DBTypes.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
module DBTypes where
import Data.List (intercalate)
import Data.Monoid ((<>), mconcat)
import Data.Proxy (Proxy(Proxy))
import qualified Hasql.Decoders as Decode
import qualified Hasql.Encoders as Encode
import Hasql.Session (Session, statement)
import Hasql.Statement (Statement(Statement))
import qualified UnambiguiousStrings as US
class DBTuple t where
columns :: Proxy t -> [String]
encoder :: Proxy t -> Encode.Params t
decoder :: Proxy t -> Decode.Row t
class DBTuple row => Table row where
table :: Proxy row -> String
class (DBTuple key, DBTuple row, Table row) => KeyedTable key row where {} -- It seems very likely that there should be a functional dependancy key -> row
class (DBTuple row, Table row) => WritableTable row where {}
buildSession sqlParts encoder' decoder' param =
statement param $ Statement (US.strictEncode $ US.packSText $ mconcat sqlParts) encoder' decoder' False
names :: DBTuple t => Proxy t -> String
names p = intercalate ", " $ columns p
placeholders :: DBTuple t => Proxy t -> String
placeholders p = let placeholder (i, _) = "$" <> (show i)
in intercalate ", " $ fmap placeholder $ zip [1..] $ columns p
whereClauses :: DBTuple t => Proxy t -> String
whereClauses p = let placeholder (i, name) = name <> " = $" <> (show i)
in intercalate " AND " $ fmap placeholder $ zip [1..] $ columns p
getAllRows :: (DBTuple row, Table row) => Session [row]
getAllRows = let getAllRows' template = buildSession
["SELECT ", names template,
" FROM ", table template]
Encode.unit
(Decode.rowList $ decoder template)
()
in getAllRows' Proxy
getRow :: (DBTuple key, DBTuple row, Table row, KeyedTable key row) => key -> Session (Maybe row)
getRow = let getRow' keyTemplate rowTemplate k = buildSession
["SELECT ", names rowTemplate,
" FROM ", table rowTemplate,
" WHERE ", whereClauses keyTemplate]
(encoder keyTemplate)
(Decode.rowMaybe $ decoder rowTemplate)
k
in getRow' Proxy Proxy
addRow :: (DBTuple row, Table row) => row -> Session ()
addRow = let addRow' template newRow = buildSession
["INSERT INTO ", table template,
" (", names template, ")",
" VALUES (", placeholders template, ")"]
(encoder template)
Decode.unit
newRow
in addRow' Proxy