This can get critical if applications depend on this error message: for example they expect that a Constraint Violation of a Primary key is thrown, but they get that a Not Null Constraint Violation. Note that the main error (in this case Constraint Violation) is not prone to this race condition, but the additional details are.
import Database.SQLite.Simple
createPeopleTableSQL :: Query
createPeopleTableSQL =
"CREATE TABLE People (name varchar(50) NOT NULL UNIQUE \
\ ,id int NOT NULL UNIQUE \
\ );"
testErrors :: IO ()
testErrors = do
conn <- open ":memory:"
execute_ conn createPeopleTableSQL
_ <- toMsg $ execute conn "INSERT INTO People (name, id) VALUES (?, ?)" ( ("Ann", 0) :: (Text, Int) )
forkIO $ thread0 conn
forkIO $ thread1 conn
threadDelay 100000
thread0 :: Connection -> IO ()
thread0 conn = do
forM_ [1..10000] $ \n -> do
result1 <- toMsg $ execute conn "INSERT INTO People (name, id) VALUES (?, ?)" ( ("Ann", n) :: (Text, Int) )
unless (result1 == "SQLite3 returned ErrorConstraint while attempting to perform step: UNIQUE constraint failed: People.name")
(error $ ("thread 0 trial " <> show n <> " got: " :: String) <> result1)
thread1 :: Connection -> IO ()
thread1 conn = do
forM_ [1..10000] $ \n -> do
_ <- toMsg $ execute conn "INSERT INTO People (name, id) VALUES (?, ?)" ( (show n, 0) :: (Text, Int) )
return ()
toMsg :: IO () -> IO String
toMsg action = do
res <- try action
case res of
Left (e :: SomeException) -> return $ displayException e
Right _ -> return $ "no error"
I understand this is not exactly a bug of this library, but a strange behaviour of SQlite. SQlite suggests the use of sqlite3_mutex_enter(), if we want to have reliably the correct message (sqlite-direct does not have bindings for this one) or to use the extended error codes. Are there any plans to add any of those?