Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add persistence exception handling section #7

Merged
merged 7 commits into from
Apr 15, 2016
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ For non-Yesod code, see also [Snippets](https://github.com/yesodweb/yesod-cookbo
* [Example MySQL Connection code](https://github.com/yesodweb/yesod-cookbook/blob/master/cookbook/Example-MySQL-Connection-code.md)
* [Activate foreign key checking in Sqlite](https://github.com/yesodweb/yesod-cookbook/blob/master/cookbook/Activate-foreign-key-checking-in-Sqlite.md)
* [Adding Seed data to Scaffolded Site](https://github.com/yesodweb/yesod-cookbook/blob/master/cookbook/Adding-Seed-Data-to-Scaffolded-Site.md)
* [Handling exception in persistence](https://github.com/yesodweb/yesod-cookbook/blob/master/cookbook/Handling-Persistence-Exception.md)

## State

Expand Down
82 changes: 82 additions & 0 deletions cookbook/Handling-Persistence-Exception.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Handling exception in persistence

Say, you have a code like this which throws exception:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps "throws an exception"?


``` haskell
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
age Int
UniqueAge age
deriving Show
|]

getUser :: MonadIO m => ReaderT SqlBackend m [Entity User]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor recommendation: getUsers instead of getUser may be a bit easier to parse for some.

getUser = selectList [] []

insertJane :: MonadIO m => ReaderT SqlBackend m ()
insertJane = insert_ $ User 40

sqliteTest :: IO ()
sqliteTest = runSqlite ":memory:" $ do
runMigration migrateAll

insertJane
insertJane

users <- getUser
liftIO $ print (users :: [Entity User])
```

The above code will throw exception when executed because the uniqueness constraint is being violated:

```
$ ./sqlite-code

Migrating: CREATE TABLE "user"("id" INTEGER PRIMARY KEY,"age" INTEGER NOT NULL,CONSTRAINT "unique_age" UNIQUE ("age"))
persistent-try-bugs: SQLite3 returned ErrorConstraint while attempting to perform step.
```

You can use the [exceptions](https://hackage.haskell.org/package/exceptions) package to handle exceptions. Have the appropriate imports:

```
import Control.Exception (SomeException)
import Control.Monad.Catch
```

And the rest of the code is like this:

``` haskell
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
age Int
UniqueAge age
deriving Show
|]

getUser :: MonadIO m => ReaderT SqlBackend m [Entity User]
getUser = selectList [] []

insertJane :: (MonadCatch m, MonadIO m) => ReaderT SqlBackend m ()
insertJane = (insert_ (User 40)) `catch` (\(SomeException e) -> return ())

sqliteTest :: IO ()
sqliteTest = runSqlite ":memory:" $ do
runMigration migrateAll

insertJane
insertJane

users <- getUser
liftIO $ print (users :: [Entity User])
```

And this time, it will execute without crashing:

```
$ ./sqlite-code

Migrating: CREATE TABLE "user"("id" INTEGER PRIMARY KEY,"age" INTEGER NOT NULL,CONSTRAINT "unique_age" UNIQUE ("age"))
[Entity {entityKey = UserKey {unUserKey = SqlBackendKey {unSqlBackendKey = 1}}, entityVal = User {userAge = 40}}]
```