Merge branch '0.4'

* 0.4:
  Release
  Fix the bug
  Reproduce the bug in a test
  Switch to the "hspec" testing framework
  Begin a changes log (#18)
  Update Demo
This commit is contained in:
Nikita Volkov 2014-12-18 02:33:12 +03:00
commit ac620b05f4
7 changed files with 155 additions and 70 deletions

2
CHANGELOG.md Normal file
View File

@ -0,0 +1,2 @@
# 0.4.1
* Fix the transaction conflicts bug

View File

@ -11,12 +11,12 @@ import Data.Functor.Identity
import qualified Hasql as H
-- Import the backend settings from the "hasql-postgres" library
import qualified Hasql.Postgres as H
import qualified Hasql.Postgres as HP
main = do
let postgresSettings = H.Postgres "localhost" 5432 "postgres" "" "postgres"
let postgresSettings = HP.ParamSettings "localhost" 5432 "postgres" "" "postgres"
-- Prepare session settings with a smart constructor,
-- which checks the inputted values on correctness.

View File

@ -1,7 +1,7 @@
name:
hasql
version:
0.4.0
0.4.1
synopsis:
A minimalistic general high level API for relational databases
description:
@ -66,6 +66,8 @@ build-type:
Simple
cabal-version:
>=1.10
extra-source-files:
CHANGELOG.md
source-repository head
@ -118,11 +120,11 @@ library
base >= 4.5 && < 4.8
test-suite tests
test-suite hspec
type:
exitcode-stdio-1.0
hs-source-dirs:
tests
hspec
main-is:
Main.hs
ghc-options:
@ -134,19 +136,23 @@ test-suite tests
default-language:
Haskell2010
build-depends:
--
hasql,
hasql-backend == 0.2.*,
base-prelude == 0.1.*,
HTF == 0.12.*,
base >= 4.5 && < 4.8
--
hspec == 2.1.*,
--
mtl-prelude < 3,
base-prelude,
base
test-suite postgres-tests
type:
test-suite hspec-postgres
type:
exitcode-stdio-1.0
hs-source-dirs:
postgres-tests
main-is:
hs-source-dirs:
hspec-postgres
main-is:
Main.hs
ghc-options:
-threaded
@ -157,12 +163,19 @@ test-suite postgres-tests
default-language:
Haskell2010
build-depends:
--
hasql,
hasql-postgres == 0.7.*,
hasql-postgres == 0.8.*,
--
slave-thread == 0.1.*,
--
hspec == 2.1.*,
--
text,
--
mtl-prelude < 3,
base-prelude == 0.1.*,
HTF == 0.12.*,
base >= 4.5 && < 4.8
base-prelude,
base
-- Well, it's not a benchmark actually,
@ -184,7 +197,7 @@ benchmark demo
Haskell2010
build-depends:
hasql,
hasql-postgres == 0.7.*,
hasql-postgres == 0.8.*,
transformers >= 0.3 && < 0.5,
base >= 4.5 && < 4.8

104
hspec-postgres/Main.hs Normal file
View File

@ -0,0 +1,104 @@
import BasePrelude
import MTLPrelude
import Test.Hspec
import Data.Text (Text)
import qualified Hasql as H
import qualified Hasql.Postgres as HP
import qualified SlaveThread
main =
hspec $ do
context "Bug" $ do
context "Unhandled transaction conflict" $ do
it "should not be" $ do
session $ H.tx Nothing $ do
H.unit [H.q|DROP TABLE IF EXISTS artist|]
H.unit [H.q|DROP TABLE IF EXISTS artist_union|]
H.unit $
[H.q|
CREATE TABLE "artist_union" (
"id" BIGSERIAL,
PRIMARY KEY ("id")
)
|]
H.unit $
[H.q|
CREATE TABLE "artist" (
"id" BIGSERIAL,
"artist_union_id" INT8 NOT NULL,
"names" TEXT[] NOT NULL,
PRIMARY KEY ("id"),
FOREIGN KEY ("artist_union_id") REFERENCES "artist_union" ("id") ON DELETE CASCADE
)
|]
(signal, block) <- newBatchGate 6
let
insertArtistUnion :: H.Tx HP.Postgres s Int64
insertArtistUnion =
fmap (runIdentity . fromJust) $ H.single $
[H.q|
INSERT INTO artist_union DEFAULT VALUES RETURNING id
|]
insertArtist :: Int64 -> [Text] -> H.Tx HP.Postgres s Int64
insertArtist unionID artistNames =
fmap (runIdentity . fromJust) $ H.single $
[H.q|
INSERT INTO artist
(artist_union_id,
names)
VALUES (?, ?)
RETURNING id
|]
unionID
artistNames
process =
SlaveThread.fork $ do
session $ replicateM_ 100 $ do
H.tx (Just (H.Serializable, True)) $ do
unionID <- insertArtistUnion
insertArtist unionID ["a", "b", "c"]
signal
replicateM_ 6 process
block
context "RowParser" $ do
it "should fail on incorrect arity" $ do
flip shouldThrow (\case H.UnparsableRow _ -> True; _ -> False) $
session $ do
H.tx Nothing $ do
H.unit [H.q|DROP TABLE IF EXISTS data|]
H.unit [H.q|CREATE TABLE data (
field1 DECIMAL NOT NULL,
field2 BIGINT NOT NULL,
PRIMARY KEY (field1)
)|]
H.unit [H.q|INSERT INTO data (field1, field2) VALUES (0, 0)|]
mrow :: Maybe (Double, Int64, String) <-
H.tx Nothing $
H.single $ [H.q|SELECT * FROM data|]
return ()
-- * Helpers
-------------------------
session :: (forall s. H.Session HP.Postgres s IO r) -> IO r
session =
H.session backendSettings poolSettings
where
backendSettings = HP.ParamSettings "localhost" 5432 "postgres" "" "postgres"
poolSettings = fromJust $ H.sessionSettings 6 30
newBatchGate :: Int -> IO (IO (), IO ())
newBatchGate amount =
do
counter <- atomically $ newTVar amount
return $
let signal = atomically $ readTVar counter >>= writeTVar counter . pred
block = atomically $ readTVar counter >>= \x -> when (x > 0) retry
in (signal, block)

View File

@ -1,11 +1,8 @@
{-# OPTIONS_GHC -F -pgmF htfpp #-}
import BasePrelude
import Test.Framework
import Test.Hspec
import qualified Hasql as H
import qualified Hasql.Backend as HB
main =
htfMain $ htf_thisModulesTests
data X
instance HB.Backend X where
@ -16,7 +13,10 @@ instance HB.Mapping X Char where
renderValue = StatementArgument . show
test_quasiQuoterGeneratesAProperStatement =
assertEqual
(" SELECT ? ", [HB.renderValue 'a'], True)
([H.q| SELECT ? |] 'a' :: HB.Statement X)
main =
hspec $ do
context "Quasi quoter" $ do
it "generates a proper statement" $ do
(flip shouldBe)
(" SELECT ? ", [HB.renderValue 'a'], True)
([H.q| SELECT ? |] 'a' :: HB.Statement X)

View File

@ -250,16 +250,16 @@ tx m t =
Backend b =>
Backend.TransactionMode -> Backend.Connection b -> IO r -> IO r
inTransaction mode c io =
do
Backend.beginTransaction mode c
try io >>= \case
Left Backend.TransactionConflict -> do
Backend.finishTransaction False c
inTransaction mode c io
Left e -> throwIO e
Right r -> do
Backend.finishTransaction True c
return r
let
io' =
Backend.beginTransaction mode c *> io <* Backend.finishTransaction True c
in
try io' >>= \case
Left Backend.TransactionConflict -> do
Backend.finishTransaction False c
inTransaction mode c io
Left e -> throwIO e
Right r -> return r
-- * Results Stream

View File

@ -1,34 +0,0 @@
{-# OPTIONS_GHC -F -pgmF htfpp #-}
import BasePrelude
import MTLPrelude
import Test.Framework
import qualified Hasql as H
import qualified Hasql.Postgres as HP
main =
htfMain $ htf_thisModulesTests
test_wrongRowParserArity =
flip assertThrowsIO (\case H.UnparsableRow _ -> True; _ -> False) $
session $ do
H.tx Nothing $ do
H.unit [H.q|DROP TABLE IF EXISTS data|]
H.unit [H.q|CREATE TABLE data (
field1 DECIMAL NOT NULL,
field2 BIGINT NOT NULL,
PRIMARY KEY (field1)
)|]
H.unit [H.q|INSERT INTO data (field1, field2) VALUES (0, 0)|]
mrow :: Maybe (Double, Int64, String) <-
H.tx Nothing $
H.single $ [H.q|SELECT * FROM data|]
return ()
session :: (forall s. H.Session HP.Postgres s IO r) -> IO r
session =
H.session backendSettings poolSettings
where
backendSettings = HP.Postgres "localhost" 5432 "postgres" "" "postgres"
poolSettings = fromJust $ H.sessionSettings 6 30