fix: Execute deferred constraint triggers when using Prefer: tx=rollback
Resolves #2020
This commit is contained in:
committed by
Wolfgang Walther
parent
bbc07d3f40
commit
1cb00d3c62
@@ -9,6 +9,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- #2020, Execute deferred constraint triggers when using `Prefer: tx=rollback` - @wolfgangwalther
|
||||
|
||||
## [9.0.0] - 2021-11-25
|
||||
|
||||
### Added
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Module : PostgREST.Middleware
|
||||
Description : Sets CORS policy. Also the PostgreSQL GUCs, role, search_path and pre-request function.
|
||||
-}
|
||||
{-# LANGUAGE BlockArguments #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
module PostgREST.Middleware
|
||||
( runPgLocals
|
||||
@@ -170,8 +171,9 @@ optionalRollback
|
||||
-> ExceptT Error SQL.Transaction Wai.Response
|
||||
optionalRollback AppConfig{..} ApiRequest{..} transaction = do
|
||||
resp <- catchError transaction $ return . errorResponseFor
|
||||
when (shouldRollback || (configDbTxRollbackAll && not shouldCommit))
|
||||
(lift SQL.condemn)
|
||||
when (shouldRollback || (configDbTxRollbackAll && not shouldCommit)) $ lift do
|
||||
SQL.sql "SET CONSTRAINTS ALL IMMEDIATE"
|
||||
SQL.condemn
|
||||
return $ Wai.mapResponseHeaders preferenceApplied resp
|
||||
where
|
||||
shouldCommit =
|
||||
|
||||
@@ -70,6 +70,35 @@ shouldRespondToReads reqHeaders respHeaders = do
|
||||
[json|[{"id":1}]|]
|
||||
{ matchHeaders = respHeaders }
|
||||
|
||||
shouldRaiseExceptions reqHeaders respHeaders = do
|
||||
it "raises immediate constraints" $ do
|
||||
request methodPost "/rpc/raise_constraint"
|
||||
reqHeaders
|
||||
""
|
||||
`shouldRespondWith`
|
||||
[json|{
|
||||
"hint":null,
|
||||
"details":"Key (col)=(1) already exists.",
|
||||
"code":"23505",
|
||||
"message":"duplicate key value violates unique constraint \"deferrable_unique_constraint_col_key\""
|
||||
}|]
|
||||
{ matchStatus = 409
|
||||
, matchHeaders = respHeaders }
|
||||
|
||||
it "raises deferred constraints" $ do
|
||||
request methodPost "/rpc/raise_constraint"
|
||||
reqHeaders
|
||||
[json|{"deferred": true}|]
|
||||
`shouldRespondWith`
|
||||
[json|{
|
||||
"hint":null,
|
||||
"details":"Key (col)=(1) already exists.",
|
||||
"code":"23505",
|
||||
"message":"duplicate key value violates unique constraint \"deferrable_unique_constraint_col_key\""
|
||||
}|]
|
||||
{ matchStatus = 409
|
||||
, matchHeaders = respHeaders }
|
||||
|
||||
shouldPersistMutations reqHeaders respHeaders = do
|
||||
it "does persist post" $ do
|
||||
request methodPost "/items"
|
||||
@@ -178,28 +207,38 @@ allowed = describe "tx-allow-override = true" $ do
|
||||
describe "without Prefer tx" $ do
|
||||
preferDefault `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferDefault `shouldNotPersistMutations` withoutPreferenceApplied
|
||||
preferDefault `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=commit" $ do
|
||||
preferCommit `shouldRespondToReads` withPreferenceCommitApplied
|
||||
preferCommit `shouldPersistMutations` withPreferenceCommitApplied
|
||||
-- Exceptions are always without preference applied,
|
||||
-- because they return before the end of the transaction.
|
||||
preferCommit `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=rollback" $ do
|
||||
preferRollback `shouldRespondToReads` withPreferenceRollbackApplied
|
||||
preferRollback `shouldNotPersistMutations` withPreferenceRollbackApplied
|
||||
-- Exceptions are always without preference applied,
|
||||
-- because they return before the end of the transaction.
|
||||
preferRollback `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
disallowed :: SpecWith ((), Application)
|
||||
disallowed = describe "tx-rollback-all = false, tx-allow-override = false" $ do
|
||||
describe "without Prefer tx" $ do
|
||||
preferDefault `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferDefault `shouldPersistMutations` withoutPreferenceApplied
|
||||
preferDefault `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=commit" $ do
|
||||
preferCommit `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferCommit `shouldPersistMutations` withoutPreferenceApplied
|
||||
preferCommit `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=rollback" $ do
|
||||
preferRollback `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferRollback `shouldPersistMutations` withoutPreferenceApplied
|
||||
preferRollback `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
|
||||
forced :: SpecWith ((), Application)
|
||||
@@ -207,12 +246,15 @@ forced = describe "tx-rollback-all = true, tx-allow-override = false" $ do
|
||||
describe "without Prefer tx" $ do
|
||||
preferDefault `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferDefault `shouldNotPersistMutations` withoutPreferenceApplied
|
||||
preferDefault `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=commit" $ do
|
||||
preferCommit `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferCommit `shouldNotPersistMutations` withoutPreferenceApplied
|
||||
preferCommit `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
describe "Prefer tx=rollback" $ do
|
||||
preferRollback `shouldRespondToReads` withoutPreferenceApplied
|
||||
preferRollback `shouldNotPersistMutations` withoutPreferenceApplied
|
||||
preferRollback `shouldRaiseExceptions` withoutPreferenceApplied
|
||||
|
||||
|
||||
Vendored
+1
@@ -25,6 +25,7 @@ GRANT ALL ON TABLE
|
||||
, complex_items
|
||||
, compound_pk
|
||||
, compound_pk_view
|
||||
, deferrable_unique_constraint
|
||||
, empty_table
|
||||
, has_count_column
|
||||
, has_fk
|
||||
|
||||
Vendored
+14
@@ -2448,3 +2448,17 @@ CREATE TABLE chores (
|
||||
, name text
|
||||
, done bool
|
||||
);
|
||||
|
||||
CREATE TABLE deferrable_unique_constraint (
|
||||
col INT UNIQUE DEFERRABLE INITIALLY IMMEDIATE
|
||||
);
|
||||
|
||||
CREATE FUNCTION raise_constraint(deferred BOOL DEFAULT FALSE) RETURNS void
|
||||
LANGUAGE plpgsql AS $$
|
||||
BEGIN
|
||||
IF deferred THEN
|
||||
SET CONSTRAINTS ALL DEFERRED;
|
||||
END IF;
|
||||
|
||||
INSERT INTO deferrable_unique_constraint VALUES (1), (1);
|
||||
END$$;
|
||||
|
||||
Reference in New Issue
Block a user