mirror of
https://github.com/nikita-volkov/hasql.git
synced 2024-12-24 18:53:24 +03:00
Adds a basic README file
This commit is contained in:
parent
d34c06a4eb
commit
d38acbe88c
123
README.md
Normal file
123
README.md
Normal file
@ -0,0 +1,123 @@
|
||||
# Hasql [![Build Status](https://travis-ci.org/nikita-volkov/hasql.svg?branch=master)](https://travis-ci.org/nikita-volkov/hasql)
|
||||
|
||||
|
||||
Hasql provides a robust and concise yet powerful API for communication with arbitrary relational databases using SQL.
|
||||
|
||||
Currently the only backend available is for PostgreSQL ([which can yield great performance improvements](https://nikita-volkov.github.io/hasql-benchmarks/) over HDBC or postgresql-simple).
|
||||
|
||||
The code used here file is the [demo found in the repository](https://github.com/nikita-volkov/hasql/blob/master/demo/Main.hs)
|
||||
|
||||
## Openning a connection
|
||||
|
||||
For greater convenience the Hasql has a built-in connection pool. All interactions with the database backend are done within the context of such a pool.
|
||||
|
||||
So we have functions to create a pool and one to release all resources held by the pool:
|
||||
|
||||
```haskell
|
||||
H.acquirePool
|
||||
:: Hasql.Backend.Cx c =>
|
||||
Hasql.Backend.CxSettings c -> H.PoolSettings -> IO (H.Pool c)
|
||||
```
|
||||
|
||||
and
|
||||
|
||||
```haskell
|
||||
H.releasePool :: H.Pool c -> IO ()
|
||||
```
|
||||
|
||||
To create the pool we need to pass the connection settings (which are backend dependent) and the pool settings. The code sample below will open a connection to a PostgreSQL database.
|
||||
|
||||
```haskell
|
||||
{-# LANGUAGE QuasiQuotes, ScopedTypeVariables, OverloadedStrings #-}
|
||||
|
||||
-- Import the API from the "hasql" library
|
||||
import qualified Hasql as H
|
||||
|
||||
-- Import the backend API from the "hasql-postgres" library
|
||||
import qualified Hasql.Postgres as HP
|
||||
let postgresSettings = HP.ParamSettings "localhost" 5432 "postgres" "" "postgres"
|
||||
|
||||
-- Prepare the pool settings with a smart constructor,
|
||||
-- which checks the inputted values on correctness.
|
||||
-- Set the connection pool size to 6 and the timeout to 30 seconds.
|
||||
poolSettings <- maybe (fail "Improper session settings") return $
|
||||
H.poolSettings 6 30
|
||||
|
||||
-- Acquire the database connections pool.
|
||||
-- Gotta help the compiler with the type signature of the pool a bit.
|
||||
pool :: H.Pool HP.Postgres
|
||||
<- H.acquirePool postgresSettings poolSettings
|
||||
```
|
||||
|
||||
## Executing a statement
|
||||
|
||||
To execute statements we will use a ```Session```, which is just a wrapper for the ```ReaderT``` monad.
|
||||
This allow us to use the pool for all our session sub-computations.
|
||||
|
||||
So the ```session``` function, is a wrapper for the ```runReaderT```, and besides a parameter with the pool,
|
||||
we need to pass a function with the return type in ```H.Session``` monad. And as the return we get
|
||||
either a ```SessionError``` or the result of our function.
|
||||
|
||||
The function we will use to actually execute the transactions is the ```tx```
|
||||
which conveniently enough receives a transaction mode, the transactions we want to execute (along with their session context)
|
||||
and returns the type ```H.Session c m r```.
|
||||
|
||||
It is **important** to notice that running ```IO``` in ```Tx``` is prohibited.
|
||||
|
||||
let's take a look at the signatures proceding:
|
||||
|
||||
```haskell
|
||||
H.session
|
||||
:: H.Pool c -> H.Session c m a -> m (Either (H.SessionError c) a)
|
||||
|
||||
H.tx
|
||||
:: (Control.Monad.Trans.Control.MonadBaseControl IO m,
|
||||
Hasql.Backend.CxTx c) =>
|
||||
H.TxMode -> (forall s. H.Tx c s r) -> H.Session c m r
|
||||
```
|
||||
|
||||
The following code excerpt shows us how the demo code uses these functions to open a
|
||||
session and start a transaction to create a new table:
|
||||
|
||||
```haskell
|
||||
-- Provide a context for execution of transactions.
|
||||
-- 'Session' is merely a convenience wrapper around 'ReaderT'.
|
||||
H.session pool $ do
|
||||
|
||||
-- Execute a group of statements without any locking and ACID guarantees:
|
||||
H.tx Nothing $ do
|
||||
H.unitEx [H.stmt|DROP TABLE IF EXISTS a|]
|
||||
H.unitEx [H.stmt|CREATE TABLE a (id SERIAL NOT NULL, balance INT8, PRIMARY KEY (id))|]
|
||||
```
|
||||
|
||||
## Transactions (isolation levels and transaction modes)
|
||||
|
||||
You have probably noticed that the first parameter of ```tx``` belongs to the type ```TxMode```.
|
||||
This parameter deserves some consideration, for it will determine the behaviour of our transaction.
|
||||
Let's take a look at its type definition:
|
||||
|
||||
```haskell
|
||||
type TxMode = Maybe (TxIsolationLevel, TxWriteMode)
|
||||
|
||||
data TxIsolationLevel =
|
||||
RepeatableReads |
|
||||
Serializable |
|
||||
ReadCommitted |
|
||||
ReadUncommitted
|
||||
|
||||
type TxWriteMode = Maybe Bool
|
||||
```
|
||||
|
||||
So when the ```mode``` is ```Nothing```, no transaction is explicitly estabilished on the server.
|
||||
In PostgreSQL's case this means all commands be commited immediatly after execution
|
||||
and their isolation level will be *Read Committed*.
|
||||
|
||||
If we pass the tuple, the first element will be the transaction isolation level, you can read more about
|
||||
[transaction isolation levels on wikipedia](https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels).
|
||||
|
||||
The second element is the write mode, which will be interpreted as:
|
||||
|
||||
* ```Nothing``` indicates a "read" mode.
|
||||
* ```Just True``` indicates a "write" mode.
|
||||
* ```Just False``` indicates a "write" mode without committing (can be useful for testing purposes).
|
||||
|
Loading…
Reference in New Issue
Block a user