Skip to content

Commit d5fb949

Browse files
committed
Merge pull request #2 from anttih/query-params
Add functions that do query param replacement
2 parents 9ffbc8e + b57a0d1 commit d5fb949

File tree

4 files changed

+197
-34
lines changed

4 files changed

+197
-34
lines changed

MODULE.md

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,35 +42,67 @@ Makes a connection to the database
4242
#### `execute`
4343

4444
``` purescript
45-
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
45+
execute :: forall eff a. Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) Unit
46+
```
47+
48+
Runs a query and returns nothing
49+
50+
#### `execute_`
51+
52+
``` purescript
53+
execute_ :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
4654
```
4755

4856
Runs a query and returns nothing
4957

5058
#### `query`
5159

5260
``` purescript
53-
query :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
61+
query :: forall eff a p. (IsForeign a) => Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) [F a]
5462
```
5563

5664
Runs a query and returns all results
5765

66+
#### `query_`
67+
68+
``` purescript
69+
query_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
70+
```
71+
72+
Just like `query` but does not make any param replacement
73+
5874
#### `queryOne`
5975

6076
``` purescript
61-
queryOne :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
77+
queryOne :: forall eff a. (IsForeign a) => Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
6278
```
6379

6480
Runs a query and returns the first row, if any
6581

82+
#### `queryOne_`
83+
84+
``` purescript
85+
queryOne_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
86+
```
87+
88+
Just like `queryOne` but does not make any param replacement
89+
6690
#### `queryValue`
6791

6892
``` purescript
69-
queryValue :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
93+
queryValue :: forall eff a. (IsForeign a) => Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
7094
```
7195

7296
Runs a query and returns a single value, if any
7397

98+
#### `queryValue_`
99+
100+
``` purescript
101+
queryValue_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
102+
```
103+
104+
Just like `queryValue` but does not make any param replacement
105+
74106
#### `withConnection`
75107

76108
``` purescript
@@ -80,6 +112,42 @@ withConnection :: forall eff a. ConnectionInfo -> (Client -> Aff (db :: DB | eff
80112
Connects to the database, calls the provided function with the client
81113
and returns the results.
82114

115+
#### `SqlValue`
116+
117+
``` purescript
118+
data SqlValue :: *
119+
```
120+
121+
122+
#### `IsSqlValue`
123+
124+
``` purescript
125+
class IsSqlValue a where
126+
toSql :: a -> SqlValue
127+
```
128+
129+
130+
#### `isSqlValueString`
131+
132+
``` purescript
133+
instance isSqlValueString :: IsSqlValue String
134+
```
135+
136+
137+
#### `isSqlValueNumber`
138+
139+
``` purescript
140+
instance isSqlValueNumber :: IsSqlValue Number
141+
```
142+
143+
144+
#### `isSqlValueInt`
145+
146+
``` purescript
147+
instance isSqlValueInt :: IsSqlValue Int
148+
```
149+
150+
83151
#### `end`
84152

85153
``` purescript

bower.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"purescript-foreign": "*",
1717
"purescript-foldable-traversable": "*",
1818
"purescript-transformers": "*",
19-
"purescript-aff": "0.10.0"
19+
"purescript-aff": "0.10.0",
20+
"purescript-integers": "0.1.0"
2021
}
2122
}

src/Database/Postgres.purs

Lines changed: 102 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ module Database.Postgres
33
, Client()
44
, DB()
55
, ConnectionInfo()
6+
, SqlValue()
7+
, IsSqlValue
8+
, toSql
69
, connect
710
, end
8-
, execute
9-
, query
10-
, queryOne
11-
, queryValue
11+
, execute, execute_
12+
, query, query_
13+
, queryValue, queryValue_
14+
, queryOne, queryOne_
1215
, withConnection
1316
) where
1417

@@ -20,6 +23,7 @@ import Data.Array
2023
import Data.Foreign
2124
import Data.Foreign.Class
2225
import Data.Maybe
26+
import Data.Int
2327
import Control.Monad.Aff
2428
import Control.Monad.Eff.Class
2529
import Control.Monad.Eff.Exception(Error(), error)
@@ -51,25 +55,53 @@ connect ci = connect'
5155
<> ci.db
5256

5357
-- | Runs a query and returns nothing.
54-
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
55-
execute (Query sql) client = void $ runQuery sql client
58+
execute :: forall eff a. Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) Unit
59+
execute (Query sql) params client = void $ runQuery sql params client
60+
61+
-- | Runs a query and returns nothing
62+
execute_ :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
63+
execute_ (Query sql) client = void $ runQuery_ sql client
5664

5765
-- | Runs a query and returns all results.
58-
query :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
59-
query (Query sql) client = do
60-
rows <- runQuery sql client
66+
query :: forall eff a p
67+
. (IsForeign a)
68+
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) [F a]
69+
query (Query sql) params client = do
70+
rows <- runQuery sql params client
71+
pure $ read <$> rows
72+
73+
-- | Just like `query` but does not make any param replacement
74+
query_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
75+
query_ (Query sql) client = do
76+
rows <- runQuery_ sql client
6177
either liftError pure (sequence $ read <$> rows)
6278

6379
-- | Runs a query and returns the first row, if any
64-
queryOne :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
65-
queryOne (Query sql) client = do
66-
rows <- runQuery sql client
80+
queryOne :: forall eff a
81+
. (IsForeign a)
82+
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
83+
queryOne (Query sql) params client = do
84+
rows <- runQuery sql params client
85+
maybe (pure Nothing) (either liftError (pure <<< Just)) $ read <$> (rows !! 0)
86+
87+
-- | Just like `queryOne` but does not make any param replacement
88+
queryOne_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
89+
queryOne_ (Query sql) client = do
90+
rows <- runQuery_ sql client
6791
maybe (pure Nothing) (either liftError (pure <<< Just)) $ read <$> (rows !! 0)
6892

6993
-- | Runs a query and returns a single value, if any.
70-
queryValue :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
71-
queryValue (Query sql) client = do
72-
val <- runQueryValue sql client
94+
queryValue :: forall eff a
95+
. (IsForeign a)
96+
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
97+
queryValue (Query sql) params client = do
98+
val <- runQueryValue sql params client
99+
pure $ either (const Nothing) Just (read val)
100+
101+
-- | Just like `queryValue` but does not make any param replacement
102+
queryValue_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
103+
queryValue_ (Query sql) client = do
104+
val <- runQueryValue_ sql client
73105
either liftError (pure <<< Just) $ read val
74106

75107
-- | Connects to the database, calls the provided function with the client
@@ -91,6 +123,27 @@ finally a sequel = do
91123
sequel
92124
either throwError pure res
93125

126+
foreign import data SqlValue :: *
127+
128+
foreign import unsafeToSqlValue """
129+
function unsafeToSqlValue(x) {
130+
return x;
131+
}
132+
""" :: forall a. a -> SqlValue
133+
134+
class IsSqlValue a where
135+
toSql :: a -> SqlValue
136+
137+
instance isSqlValueString :: IsSqlValue String where
138+
toSql = unsafeToSqlValue
139+
140+
instance isSqlValueNumber :: IsSqlValue Number where
141+
toSql = unsafeToSqlValue
142+
143+
instance isSqlValueInt :: IsSqlValue Int where
144+
toSql = unsafeToSqlValue <<< toNumber
145+
146+
94147
foreign import connect' """
95148
function connect$prime(conString) {
96149
return function(success, error) {
@@ -108,8 +161,8 @@ foreign import connect' """
108161
}
109162
""" :: forall eff. String -> Aff (db :: DB | eff) Client
110163

111-
foreign import runQuery """
112-
function runQuery(queryStr) {
164+
foreign import runQuery_ """
165+
function runQuery_(queryStr) {
113166
return function(client) {
114167
return function(success, error) {
115168
client.query(queryStr, function(err, result) {
@@ -124,8 +177,23 @@ foreign import runQuery """
124177
}
125178
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) [Foreign]
126179

127-
foreign import runQueryValue """
128-
function runQueryValue(queryStr) {
180+
foreign import runQuery """
181+
function runQuery(queryStr) {
182+
return function(params) {
183+
return function(client) {
184+
return function(success, error) {
185+
client.query(queryStr, params, function(err, result) {
186+
if (err) return error(err);
187+
success(result.rows);
188+
})
189+
};
190+
};
191+
}
192+
}
193+
""" :: forall eff. String -> [SqlValue] -> Client -> Aff (db :: DB | eff) [Foreign]
194+
195+
foreign import runQueryValue_ """
196+
function runQueryValue_(queryStr) {
129197
return function(client) {
130198
return function(success, error) {
131199
client.query(queryStr, function(err, result) {
@@ -137,6 +205,21 @@ foreign import runQueryValue """
137205
}
138206
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) Foreign
139207

208+
foreign import runQueryValue """
209+
function runQueryValue(queryStr) {
210+
return function(params) {
211+
return function(client) {
212+
return function(success, error) {
213+
client.query(queryStr, params, function(err, result) {
214+
if (err) return error(err);
215+
success(result.rows.length > 0 ? result.rows[0][result.fields[0].name] : undefined);
216+
})
217+
};
218+
};
219+
}
220+
}
221+
""" :: forall eff. String -> [SqlValue] -> Client -> Aff (db :: DB | eff) Foreign
222+
140223
foreign import end """
141224
function end(client) {
142225
return function() {

test/Main.purs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@ import Data.Foreign.Class
1515
import Data.Foreign.Index
1616
import Control.Monad.Aff
1717

18-
1918
main = runAff (trace <<< show) (const $ trace "All ok") $ do
2019
exampleUsingWithConnection
2120
exampleLowLevel
2221

2322
res <- attempt exampleError
2423
liftEff $ either (const $ trace "got an error, like we should") (const $ trace "FAIL") res
2524

25+
exampleQueries
26+
2627
data Artist = Artist
2728
{ name :: String
2829
, year :: Number
@@ -38,26 +39,36 @@ connectionInfo =
3839

3940
exampleUsingWithConnection :: forall eff. Aff (trace :: Trace, db :: DB | eff) Unit
4041
exampleUsingWithConnection = withConnection connectionInfo $ \c -> do
41-
execute (Query "delete from artist") c
42-
execute (Query "insert into artist values ('Led Zeppelin', 1968)") c
43-
execute (Query "insert into artist values ('Deep Purple', 1968)") c
44-
year <- queryValue (Query "insert into artist values ('Fairport Convention', 1967) returning year" :: Query Number) c
42+
execute_ (Query "delete from artist") c
43+
execute_ (Query "insert into artist values ('Led Zeppelin', 1968)") c
44+
execute_ (Query "insert into artist values ('Deep Purple', 1968)") c
45+
year <- queryValue_ (Query "insert into artist values ('Fairport Convention', 1967) returning year" :: Query Number) c
4546
liftEff $ print (show year)
46-
artists <- query (Query "select * from artist" :: Query Artist) c
47+
artists <- query_ (Query "select * from artist" :: Query Artist) c
4748
liftEff $ printRows artists
4849

4950
exampleLowLevel :: forall eff. Aff (trace :: Trace, db :: DB | eff) Unit
5051
exampleLowLevel = do
5152
client <- connect connectionInfo
52-
artists <- query (Query "select * from artist order by name desc" :: Query Artist) client
53+
artists <- query_ (Query "select * from artist order by name desc" :: Query Artist) client
5354
liftEff $ printRows artists
5455
liftEff $ end client
5556

5657
exampleError :: forall eff. Aff (db :: DB | eff) (Maybe Artist)
5758
exampleError = withConnection connectionInfo $ \c -> do
58-
execute (Query "delete from artist") c
59-
execute (Query "insert into artist values ('Led Zeppelin', 1968)") c
60-
queryOne (Query "select year from artist") c
59+
execute_ (Query "delete from artist") c
60+
execute_ (Query "insert into artist values ('Led Zeppelin', 1968)") c
61+
queryOne_ (Query "select year from artist") c
62+
63+
exampleQueries :: forall eff. Aff (trace :: Trace, db :: DB | eff) Unit
64+
exampleQueries = withConnection connectionInfo $ \c -> do
65+
liftEff $ trace "Example queries with params:"
66+
execute_ (Query "delete from artist") c
67+
execute_ (Query "insert into artist values ('Led Zeppelin', 1968)") c
68+
execute_ (Query "insert into artist values ('Deep Purple', 1968)") c
69+
execute_ (Query "insert into artist values ('Toto', 1977)") c
70+
artists <- query (Query "select * from artist where name = $1" :: Query Artist) [toSql "Toto"] c
71+
liftEff $ printRows artists
6172

6273
printRows :: forall a eff. (Show a) => [a] -> Eff (trace :: Trace | eff) Unit
6374
printRows rows = trace $ "result:\n" <> foldMap stringify rows

0 commit comments

Comments
 (0)