2016-12-24 12:34:27 +00:00
# purescript-postgresql-client
2019-11-10 14:27:24 +00:00
purescript-postgresql-client is a PostgreSQL client library for PureScript based on `node-postgres` .
2016-12-25 22:58:29 +00:00
2018-04-22 14:47:07 +00:00
## Install
2018-10-22 01:27:35 +00:00
To use this library, you need to add [`pg`][pg] and [`decimal.js`][decimal.js] as an npm dependency. You can also
find first of them on [https://github.com/brianc/node-postgres ](https://github.com/brianc/node-postgres ).
2017-06-08 12:05:06 +00:00
2018-10-21 01:24:58 +00:00
## Usage
2018-10-21 01:58:45 +00:00
This guide is a literate Purescript file which is extracted into testing module (using [`literate-purescript` ](https://github.com/Thimoteus/literate-purescript )) so it is a little verbose.
2018-10-21 01:24:58 +00:00
2018-10-21 09:39:25 +00:00
Let's start with imports.
```purescript
2018-12-06 19:14:08 +00:00
module Test.README where
2018-10-21 01:24:58 +00:00
import Prelude
2019-08-30 10:15:05 +00:00
import Control.Monad.Except.Trans (ExceptT, runExceptT)
2020-11-09 14:54:23 +00:00
import Database.PostgreSQL.PG (defaultConfiguration, PGError, command, execute, Pool, Connection, query, Query(Query))
2019-08-30 10:15:05 +00:00
import Database.PostgreSQL.PG as PG
2020-11-09 14:54:23 +00:00
import Database.PostgreSQL.Pool (new) as Pool
2018-10-21 01:24:58 +00:00
import Database.PostgreSQL.Row (Row0(Row0), Row3(Row3))
import Data.Decimal as Decimal
import Data.Maybe (Maybe(..))
import Data.Tuple.Nested ((/\))
2019-08-30 10:15:05 +00:00
import Effect.Aff (Aff)
2018-10-21 01:56:11 +00:00
import Effect.Class (liftEffect)
import Test.Assert (assert)
2018-10-21 09:39:25 +00:00
```
2018-12-06 19:30:38 +00:00
The whole API for interaction with PostgreSQL is performed asynchronously in `Aff`
2020-11-09 14:54:23 +00:00
(the only function which runs in plain `Effect` is `Pool.new` ). Core library
2019-05-07 18:16:59 +00:00
functions usually results in somthing like `Aff (Either PGError a)` which can be easily
2019-08-30 10:15:05 +00:00
wrapped by user into `ExceptT` or any other custom monad stack.
To be honest we provide alternatives to functions in the `Database.PostgreSQL.PG` module that work on any stack `m` with `MonadError PGError m` and `MonadAff m` .
The module contains two functions `withConnection` and `withTransaction` that require additional parameter - a transformation from a custom monad stack to `Aff (Either PGError a)` .
2019-05-07 18:16:59 +00:00
We are going to work with `PG` type in this tutorial but please don't consider it as the only option
if you encounter any troubles integrating it into your own app monad stack.
2018-12-06 19:30:38 +00:00
2019-08-30 10:15:05 +00:00
```purescript
type PG a = ExceptT PGError Aff a
withConnection :: forall a. Pool -> (Connection -> PG a) -> PG a
withConnection = PG.withConnection runExceptT
withTransaction :: forall a. Connection -> PG a -> PG a
withTransaction = PG.withTransaction runExceptT
```
2018-12-06 19:30:38 +00:00
2019-05-07 18:16:59 +00:00
We assume here that Postgres is running on a standard local port
2020-11-09 14:54:23 +00:00
with `ident` authentication so configuration can be nearly empty (`defaultConfiguration`).
2018-10-21 09:39:25 +00:00
It requires only database name which we pass to `newPool` function.
2019-05-07 18:16:59 +00:00
Additionally we pass `idleTimeoutMillis` value because this code
2018-12-09 20:50:13 +00:00
is run by our test suite and we want to exit after its execution quickly ;-)
2018-10-21 09:39:25 +00:00
2018-10-21 01:24:58 +00:00
2018-10-21 09:39:25 +00:00
```purescript
2018-12-06 19:14:08 +00:00
run ∷ PG Unit
2018-10-21 01:24:58 +00:00
run = do
2020-11-09 14:54:23 +00:00
pool < - liftEffect $ Pool . new
((defaultConfiguration "purspg") { idleTimeoutMillis = Just 1000 })
2018-10-21 01:24:58 +00:00
withConnection pool \conn -> do
2018-10-21 09:39:25 +00:00
```
We can now create our temporary table which we are going to query in this example.
`execute` ignores result value which is what we want in this case.
2018-10-21 09:41:25 +00:00
The last `Row0` value indicates that this `Query` doesn't take any additional parameters.
2018-10-21 09:39:25 +00:00
```purescript
2018-10-21 01:24:58 +00:00
execute conn (Query """
2018-10-21 01:56:11 +00:00
CREATE TEMPORARY TABLE fruits (
2018-10-21 01:24:58 +00:00
name text NOT NULL,
delicious boolean NOT NULL,
price NUMERIC(4,2) NOT NULL,
added TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (name)
);
""") Row0
2018-10-21 09:39:25 +00:00
```
2018-10-21 01:24:58 +00:00
2018-10-21 09:50:20 +00:00
There is `withTransaction` helper provided. You can wrap the whole
2018-10-21 15:58:51 +00:00
piece of interaction with database in it. It will rollback if any exception
is thrown during execution of a given `Aff` block. It excecutes `COMMIT`
in the other case.
We start our session with insert of some data. It is done by `execute`
function with `INSERT` statement.
Please notice that we are passing a tuple of the arguments to this query
using dedicated constructor. In this case `Row3` . This library provides types
from `Row0` to `Row19` and they are wrappers which provide instances for
automatic conversions from and to SQL values.
2019-05-07 18:16:59 +00:00
For details please investigate following classes `ToSQLRow` , `ToSQLValue` ,
2018-10-21 15:58:51 +00:00
`FromSQLRow` and `FromSQLValue` .
2018-10-21 09:50:20 +00:00
```purescript
withTransaction conn $ do
execute conn (Query """
INSERT INTO fruits (name, delicious, price)
VALUES ($1, $2, $3)
""") (Row3 "coconut" true (Decimal.fromString "8.30"))
2018-10-21 09:39:25 +00:00
```
2018-10-21 01:24:58 +00:00
2018-10-21 09:39:25 +00:00
We can also use nested tuples instead of `Row*` constructors. This can be a bit more
verbose but is not restricted to limited and constant number of arguments.
`/\` is just an alias for the `Tuple` constructor from `Data.Tuple.Nested` .
2018-10-21 01:24:58 +00:00
2018-10-21 09:39:25 +00:00
```purescript
2018-10-21 09:50:20 +00:00
execute conn (Query """
INSERT INTO fruits (name, delicious, price)
VALUES ($1, $2, $3)
""") ("lemon" /\ false /\ Decimal.fromString "3.30")
2018-10-21 09:39:25 +00:00
```
Of course `Row*` types and nested tuples can be also used when we are fetching
data from db.
`query` function processes db response and returns an `Array` of rows.
```purescript
2018-10-21 09:50:20 +00:00
names < - query conn ( Query " " "
SELECT name, delicious
FROM fruits
ORDER BY name ASC
""") Row0
liftEffect << < assert $ names = = [" coconut " / \ true , " lemon " / \ false ]
2018-10-21 09:39:25 +00:00
```
There is also a `command` function at our disposal.
Some postgres SQL expressions return a "command tag" which carries
2018-10-21 09:50:20 +00:00
a value with a number of rows which were affected by a given query.
2018-10-21 09:39:25 +00:00
For example we can have: `DELETE rows` , `UPDATE rows` , `INSERT oid rows` etc.
This function should return `rows` value associated with given response.
2018-10-21 01:24:58 +00:00
2018-10-21 09:39:25 +00:00
```purescript
2018-10-21 09:50:20 +00:00
deleted < - command conn ( Query " " " DELETE FROM fruits " " " ) Row0
liftEffect < < < assert $ deleted = = 2
2018-10-21 01:24:58 +00:00
```
2020-02-29 15:44:54 +00:00
## Generating SQL Queries
The `purspg` preprocessor has been replaced by `sqltopurs` , which is a code
generator instead of a preprocessor, and easier to use.
[sqltopurs]: https://github.com/rightfold/sqltopurs
[pg]: https://www.npmjs.com/package/pg
[decimal.js]: https://www.npmjs.com/package/decimal.js
## Hacking
### Testing
2018-10-21 01:32:06 +00:00
2020-11-09 14:54:23 +00:00
Test database is read from the environment or loaded from _.env_ file. You can find _.env-example_ in the repo with some simple testing db setup.
2018-10-21 01:24:58 +00:00
2020-02-29 15:44:54 +00:00
### Releasing
2018-10-21 01:24:58 +00:00
2020-02-29 15:48:02 +00:00
Till we are hosted on the github platform let's just use github releasing model for tagging new versions and `github-release-notes` to generate CHANGELOG.md from it:
2020-04-13 20:54:51 +00:00
`$ # This only requires repo access`
`$ export GREN_GITHUB_TOKEN=...`
`$ github-release-notes changelog --override`