1
1
module Database.Postgres
2
2
( Query (..)
3
- , Client ()
4
- , DB ()
5
- , ConnectionInfo ()
6
- , ConnectionString ()
7
- , mkConnectionString
3
+ , Client
4
+ , Pool
5
+ , DB
6
+ , ConnectionInfo
7
+ , ClientConfig
8
+ , PoolConfig
9
+ , ConnectionString
10
+ , connectionInfoFromConfig
11
+ , connectionInfoFromString
12
+ , defaultPoolConfig
8
13
, connect
9
- , disconnect
14
+ , release
10
15
, end
11
16
, execute , execute_
12
17
, query , query_
13
18
, queryValue , queryValue_
14
19
, queryOne , queryOne_
15
- , withConnection
16
20
, withClient
21
+ , mkPool
17
22
) where
18
23
19
24
import Prelude
20
25
21
26
import Control.Monad.Aff (Aff , bracket )
27
+ import Control.Monad.Aff.Compat (EffFnAff , fromEffFnAff )
22
28
import Control.Monad.Eff (kind Effect , Eff )
23
29
import Control.Monad.Eff.Class (liftEff )
24
30
import Control.Monad.Eff.Exception (error )
@@ -28,128 +34,149 @@ import Data.Array ((!!))
28
34
import Data.Either (Either , either )
29
35
import Data.Foreign (Foreign , MultipleErrors )
30
36
import Data.Foreign.Class (class Decode , decode )
31
- import Data.Function.Uncurried (Fn2 , runFn2 )
32
37
import Data.Maybe (Maybe (Just, Nothing), maybe )
33
38
import Data.Traversable (sequence )
34
39
import Database.Postgres.SqlValue (SqlValue )
40
+ import Unsafe.Coerce (unsafeCoerce )
35
41
36
42
newtype Query a = Query String
37
43
44
+ foreign import data Pool :: Type
45
+
38
46
foreign import data Client :: Type
39
47
40
48
foreign import data DB :: Effect
41
49
50
+ foreign import data ConnectionInfo :: Type
51
+
42
52
type ConnectionString = String
43
53
44
- type ConnectionInfo =
54
+ connectionInfoFromString :: ConnectionString -> ConnectionInfo
55
+ connectionInfoFromString s = unsafeCoerce { connectionString: s }
56
+
57
+ type ClientConfig =
45
58
{ host :: String
46
- , db :: String
59
+ , database :: String
47
60
, port :: Int
48
61
, user :: String
49
62
, password :: String
63
+ , ssl :: Boolean
50
64
}
51
65
52
- mkConnectionString :: ConnectionInfo -> ConnectionString
53
- mkConnectionString ci =
54
- " postgres://"
55
- <> ci.user <> " :"
56
- <> ci.password <> " @"
57
- <> ci.host <> " :"
58
- <> show ci.port <> " /"
59
- <> ci.db
66
+ type PoolConfig =
67
+ { connectionTimeoutMillis :: Int
68
+ , idleTimeoutMillis :: Int
69
+ , max :: Int
70
+ }
60
71
61
- -- | Makes a connection to the database.
62
- connect :: forall eff . ConnectionInfo -> Aff (db :: DB | eff ) Client
63
- connect = connect' <<< mkConnectionString
72
+ defaultPoolConfig :: PoolConfig
73
+ defaultPoolConfig =
74
+ { connectionTimeoutMillis: 0
75
+ , idleTimeoutMillis: 30000
76
+ , max: 10
77
+ }
78
+
79
+ connectionInfoFromConfig :: ClientConfig -> PoolConfig -> ConnectionInfo
80
+ connectionInfoFromConfig c p = unsafeCoerce
81
+ { host: c.host
82
+ , database: c.database
83
+ , port: c.port
84
+ , user: c.user
85
+ , password: c.password
86
+ , ssl: c.ssl
87
+ , connectionTimeoutMillis: p.connectionTimeoutMillis
88
+ , idleTimeoutMillis: p.idleTimeoutMillis
89
+ , max: p.max
90
+ }
91
+
92
+ -- | Makes a connection to the database via a Client.
93
+ connect :: forall eff . Pool -> Aff (db :: DB | eff ) Client
94
+ connect = fromEffFnAff <<< connect'
64
95
65
96
-- | Runs a query and returns nothing.
66
97
execute :: forall eff a . Query a -> Array SqlValue -> Client -> Aff (db :: DB | eff ) Unit
67
- execute (Query sql) params client = void $ runQuery sql params client
98
+ execute (Query sql) params client = void $ fromEffFnAff $ runQuery sql params client
68
99
69
100
-- | Runs a query and returns nothing
70
101
execute_ :: forall eff a . Query a -> Client -> Aff (db :: DB | eff ) Unit
71
- execute_ (Query sql) client = void $ runQuery_ sql client
102
+ execute_ (Query sql) client = void $ fromEffFnAff $ runQuery_ sql client
72
103
73
104
-- | Runs a query and returns all results.
74
105
query :: forall eff a
75
- . ( Decode a )
106
+ . Decode a
76
107
=> Query a -> Array SqlValue -> Client -> Aff (db :: DB | eff ) (Array a )
77
108
query (Query sql) params client = do
78
- rows <- runQuery sql params client
109
+ rows <- fromEffFnAff $ runQuery sql params client
79
110
either liftError pure (runExcept (sequence $ decode <$> rows))
80
111
81
112
-- | Just like `query` but does not make any param replacement
82
- query_ :: forall eff a . (Decode a ) => Query a -> Client -> Aff (db :: DB | eff ) (Array a )
113
+ query_ :: forall eff a
114
+ . Decode a
115
+ => Query a -> Client -> Aff (db :: DB | eff ) (Array a )
83
116
query_ (Query sql) client = do
84
- rows <- runQuery_ sql client
117
+ rows <- fromEffFnAff $ runQuery_ sql client
85
118
either liftError pure (runExcept (sequence $ decode <$> rows))
86
119
87
120
-- | Runs a query and returns the first row, if any
88
121
queryOne :: forall eff a
89
- . ( Decode a )
122
+ . Decode a
90
123
=> Query a -> Array SqlValue -> Client -> Aff (db :: DB | eff ) (Maybe a )
91
124
queryOne (Query sql) params client = do
92
- rows <- runQuery sql params client
125
+ rows <- fromEffFnAff $ runQuery sql params client
93
126
maybe (pure Nothing ) (either liftError (pure <<< Just )) (decodeFirst rows)
94
127
95
128
-- | Just like `queryOne` but does not make any param replacement
96
- queryOne_ :: forall eff a . (Decode a ) => Query a -> Client -> Aff (db :: DB | eff ) (Maybe a )
129
+ queryOne_ :: forall eff a
130
+ . Decode a
131
+ => Query a -> Client -> Aff (db :: DB | eff ) (Maybe a )
97
132
queryOne_ (Query sql) client = do
98
- rows <- runQuery_ sql client
133
+ rows <- fromEffFnAff $ runQuery_ sql client
99
134
maybe (pure Nothing ) (either liftError (pure <<< Just )) (decodeFirst rows)
100
135
101
136
-- | Runs a query and returns a single value, if any.
102
137
queryValue :: forall eff a
103
- . ( Decode a )
138
+ . Decode a
104
139
=> Query a -> Array SqlValue -> Client -> Aff (db :: DB | eff ) (Maybe a )
105
140
queryValue (Query sql) params client = do
106
- val <- runQueryValue sql params client
141
+ val <- fromEffFnAff $ runQueryValue sql params client
107
142
pure $ either (const Nothing ) Just (runExcept (decode val))
108
143
109
144
-- | Just like `queryValue` but does not make any param replacement
110
- queryValue_ :: forall eff a . (Decode a ) => Query a -> Client -> Aff (db :: DB | eff ) (Maybe a )
145
+ queryValue_ :: forall eff a
146
+ . Decode a
147
+ => Query a -> Client -> Aff (db :: DB | eff ) (Maybe a )
111
148
queryValue_ (Query sql) client = do
112
- val <- runQueryValue_ sql client
149
+ val <- fromEffFnAff $ runQueryValue_ sql client
113
150
either liftError (pure <<< Just ) $ runExcept (decode val)
114
151
115
152
-- | Connects to the database, calls the provided function with the client
116
153
-- | and returns the results.
117
- withConnection :: forall eff a
118
- . ConnectionInfo
119
- -> (Client -> Aff (db :: DB | eff ) a )
120
- -> Aff (db :: DB | eff ) a
121
- withConnection info p =
154
+ withClient :: forall eff a
155
+ . Pool -> (Client -> Aff (db :: DB | eff ) a ) -> Aff (db :: DB | eff ) a
156
+ withClient pool p =
122
157
bracket
123
- (connect info )
124
- (liftEff <<< end )
158
+ (connect pool )
159
+ (liftEff <<< release )
125
160
p
126
161
127
- -- | Takes a Client from the connection pool, runs the given function with
128
- -- | the client and returns the results.
129
- withClient :: forall eff a
130
- . ConnectionInfo
131
- -> (Client -> Aff (db :: DB | eff ) a )
132
- -> Aff (db :: DB | eff ) a
133
- withClient info p = runFn2 _withClient (mkConnectionString info) p
134
-
135
162
decodeFirst :: forall a . Decode a => Array Foreign -> Maybe (Either MultipleErrors a )
136
163
decodeFirst rows = runExcept <<< decode <$> (rows !! 0 )
137
164
138
165
liftError :: forall e a . MultipleErrors -> Aff e a
139
166
liftError errs = throwError $ error (show errs)
140
167
141
- foreign import connect' :: forall eff . String -> Aff (db :: DB | eff ) Client
168
+ foreign import mkPool :: forall eff . ConnectionInfo -> Eff (db :: DB | eff ) Pool
142
169
143
- foreign import _withClient :: forall eff a . Fn2 ConnectionString ( Client -> Aff (db :: DB | eff ) a ) ( Aff ( db :: DB | eff ) a )
170
+ foreign import connect' :: forall eff . Pool -> EffFnAff (db :: DB | eff ) Client
144
171
145
- foreign import runQuery_ :: forall eff . String -> Client -> Aff (db :: DB | eff ) (Array Foreign )
172
+ foreign import runQuery_ :: forall eff . String -> Client -> EffFnAff (db :: DB | eff ) (Array Foreign )
146
173
147
- foreign import runQuery :: forall eff . String -> Array SqlValue -> Client -> Aff (db :: DB | eff ) (Array Foreign )
174
+ foreign import runQuery :: forall eff . String -> Array SqlValue -> Client -> EffFnAff (db :: DB | eff ) (Array Foreign )
148
175
149
- foreign import runQueryValue_ :: forall eff . String -> Client -> Aff (db :: DB | eff ) Foreign
176
+ foreign import runQueryValue_ :: forall eff . String -> Client -> EffFnAff (db :: DB | eff ) Foreign
150
177
151
- foreign import runQueryValue :: forall eff . String -> Array SqlValue -> Client -> Aff (db :: DB | eff ) Foreign
178
+ foreign import runQueryValue :: forall eff . String -> Array SqlValue -> Client -> EffFnAff (db :: DB | eff ) Foreign
152
179
153
- foreign import end :: forall eff . Client -> Eff (db :: DB | eff ) Unit
180
+ foreign import release :: forall eff . Client -> Eff (db :: DB | eff ) Unit
154
181
155
- foreign import disconnect :: forall eff . Eff (db :: DB | eff ) Unit
182
+ foreign import end :: forall eff . Pool -> Eff (db :: DB | eff ) Unit
0 commit comments