Skip to content

Add functions that do query param replacement #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 20, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 72 additions & 4 deletions MODULE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,35 +42,67 @@ Makes a connection to the database
#### `execute`

``` purescript
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
execute :: forall eff a. Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) Unit
```

Runs a query and returns nothing

#### `execute_`

``` purescript
execute_ :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
```

Runs a query and returns nothing

#### `query`

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

Runs a query and returns all results

#### `query_`

``` purescript
query_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
```

Just like `query` but does not make any param replacement

#### `queryOne`

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

Runs a query and returns the first row, if any

#### `queryOne_`

``` purescript
queryOne_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
```

Just like `queryOne` but does not make any param replacement

#### `queryValue`

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

Runs a query and returns a single value, if any

#### `queryValue_`

``` purescript
queryValue_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
```

Just like `queryValue` but does not make any param replacement

#### `withConnection`

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

#### `SqlValue`

``` purescript
data SqlValue :: *
```


#### `IsSqlValue`

``` purescript
class IsSqlValue a where
toSql :: a -> SqlValue
```


#### `isSqlValueString`

``` purescript
instance isSqlValueString :: IsSqlValue String
```


#### `isSqlValueNumber`

``` purescript
instance isSqlValueNumber :: IsSqlValue Number
```


#### `isSqlValueInt`

``` purescript
instance isSqlValueInt :: IsSqlValue Int
```


#### `end`

``` purescript
Expand Down
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"purescript-foreign": "*",
"purescript-foldable-traversable": "*",
"purescript-transformers": "*",
"purescript-aff": "0.10.0"
"purescript-aff": "0.10.0",
"purescript-integers": "0.1.0"
}
}
121 changes: 102 additions & 19 deletions src/Database/Postgres.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module Database.Postgres
, Client()
, DB()
, ConnectionInfo()
, SqlValue()
, IsSqlValue
, toSql
, connect
, end
, execute
, query
, queryOne
, queryValue
, execute, execute_
, query, query_
, queryValue, queryValue_
, queryOne, queryOne_
, withConnection
) where

Expand All @@ -20,6 +23,7 @@ import Data.Array
import Data.Foreign
import Data.Foreign.Class
import Data.Maybe
import Data.Int
import Control.Monad.Aff
import Control.Monad.Eff.Class
import Control.Monad.Eff.Exception(Error(), error)
Expand Down Expand Up @@ -51,25 +55,53 @@ connect ci = connect'
<> ci.db

-- | Runs a query and returns nothing.
execute :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
execute (Query sql) client = void $ runQuery sql client
execute :: forall eff a. Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) Unit
execute (Query sql) params client = void $ runQuery sql params client

-- | Runs a query and returns nothing
execute_ :: forall eff a. Query a -> Client -> Aff (db :: DB | eff) Unit
execute_ (Query sql) client = void $ runQuery_ sql client

-- | Runs a query and returns all results.
query :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
query (Query sql) client = do
rows <- runQuery sql client
query :: forall eff a p
. (IsForeign a)
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) [F a]
query (Query sql) params client = do
rows <- runQuery sql params client
pure $ read <$> rows

-- | Just like `query` but does not make any param replacement
query_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) [a]
query_ (Query sql) client = do
rows <- runQuery_ sql client
either liftError pure (sequence $ read <$> rows)

-- | Runs a query and returns the first row, if any
queryOne :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
queryOne (Query sql) client = do
rows <- runQuery sql client
queryOne :: forall eff a
. (IsForeign a)
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
queryOne (Query sql) params client = do
rows <- runQuery sql params client
maybe (pure Nothing) (either liftError (pure <<< Just)) $ read <$> (rows !! 0)

-- | Just like `queryOne` but does not make any param replacement
queryOne_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
queryOne_ (Query sql) client = do
rows <- runQuery_ sql client
maybe (pure Nothing) (either liftError (pure <<< Just)) $ read <$> (rows !! 0)

-- | Runs a query and returns a single value, if any.
queryValue :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
queryValue (Query sql) client = do
val <- runQueryValue sql client
queryValue :: forall eff a
. (IsForeign a)
=> Query a -> [SqlValue] -> Client -> Aff (db :: DB | eff) (Maybe a)
queryValue (Query sql) params client = do
val <- runQueryValue sql params client
pure $ either (const Nothing) Just (read val)

-- | Just like `queryValue` but does not make any param replacement
queryValue_ :: forall eff a. (IsForeign a) => Query a -> Client -> Aff (db :: DB | eff) (Maybe a)
queryValue_ (Query sql) client = do
val <- runQueryValue_ sql client
either liftError (pure <<< Just) $ read val

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

foreign import data SqlValue :: *

foreign import unsafeToSqlValue """
function unsafeToSqlValue(x) {
return x;
}
""" :: forall a. a -> SqlValue

class IsSqlValue a where
toSql :: a -> SqlValue

instance isSqlValueString :: IsSqlValue String where
toSql = unsafeToSqlValue

instance isSqlValueNumber :: IsSqlValue Number where
toSql = unsafeToSqlValue

instance isSqlValueInt :: IsSqlValue Int where
toSql = unsafeToSqlValue <<< toNumber


foreign import connect' """
function connect$prime(conString) {
return function(success, error) {
Expand All @@ -108,8 +161,8 @@ foreign import connect' """
}
""" :: forall eff. String -> Aff (db :: DB | eff) Client

foreign import runQuery """
function runQuery(queryStr) {
foreign import runQuery_ """
function runQuery_(queryStr) {
return function(client) {
return function(success, error) {
client.query(queryStr, function(err, result) {
Expand All @@ -124,8 +177,23 @@ foreign import runQuery """
}
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) [Foreign]

foreign import runQueryValue """
function runQueryValue(queryStr) {
foreign import runQuery """
function runQuery(queryStr) {
return function(params) {
return function(client) {
return function(success, error) {
client.query(queryStr, params, function(err, result) {
if (err) return error(err);
success(result.rows);
})
};
};
}
}
""" :: forall eff. String -> [SqlValue] -> Client -> Aff (db :: DB | eff) [Foreign]

foreign import runQueryValue_ """
function runQueryValue_(queryStr) {
return function(client) {
return function(success, error) {
client.query(queryStr, function(err, result) {
Expand All @@ -137,6 +205,21 @@ foreign import runQueryValue """
}
""" :: forall eff. String -> Client -> Aff (db :: DB | eff) Foreign

foreign import runQueryValue """
function runQueryValue(queryStr) {
return function(params) {
return function(client) {
return function(success, error) {
client.query(queryStr, params, function(err, result) {
if (err) return error(err);
success(result.rows.length > 0 ? result.rows[0][result.fields[0].name] : undefined);
})
};
};
}
}
""" :: forall eff. String -> [SqlValue] -> Client -> Aff (db :: DB | eff) Foreign

foreign import end """
function end(client) {
return function() {
Expand Down
31 changes: 21 additions & 10 deletions test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import Data.Foreign.Class
import Data.Foreign.Index
import Control.Monad.Aff


main = runAff (trace <<< show) (const $ trace "All ok") $ do
exampleUsingWithConnection
exampleLowLevel

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

exampleQueries

data Artist = Artist
{ name :: String
, year :: Number
Expand All @@ -38,26 +39,36 @@ connectionInfo =

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

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

exampleError :: forall eff. Aff (db :: DB | eff) (Maybe Artist)
exampleError = withConnection connectionInfo $ \c -> do
execute (Query "delete from artist") c
execute (Query "insert into artist values ('Led Zeppelin', 1968)") c
queryOne (Query "select year from artist") c
execute_ (Query "delete from artist") c
execute_ (Query "insert into artist values ('Led Zeppelin', 1968)") c
queryOne_ (Query "select year from artist") c

exampleQueries :: forall eff. Aff (trace :: Trace, db :: DB | eff) Unit
exampleQueries = withConnection connectionInfo $ \c -> do
liftEff $ trace "Example queries with params:"
execute_ (Query "delete from artist") c
execute_ (Query "insert into artist values ('Led Zeppelin', 1968)") c
execute_ (Query "insert into artist values ('Deep Purple', 1968)") c
execute_ (Query "insert into artist values ('Toto', 1977)") c
artists <- query (Query "select * from artist where name = $1" :: Query Artist) [toSql "Toto"] c
liftEff $ printRows artists

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