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

How to write a decoder that fails unless exactly one row was updated? #94

Open
mgsloan opened this issue Jul 5, 2018 · 3 comments
Open

Comments

@mgsloan
Copy link

mgsloan commented Jul 5, 2018

I'm using an older arrowized version of hasql. How would you write a decoder that fails if rowsAffected does not yield 1? It does not seem to be possible because not enough stuff is exported for users to create their own exceptions.

For now I am doing the check and throw outside of the transaction, and that works ok, but it is not how I'd prefer to decompose concerns.

@nikita-volkov
Copy link
Owner

Can you please elaborate on your case? I'm struggling to understand why encoding this failure with logic and then handling the result in your session isn't sufficient. E.g., like this:

someQuery :: Query SomeParams Bool
someQuery = Query.statement someSql someEncoder decoder True
  where
    decoder = fmap (== 1) Decoders.rowsAffected

someSession :: Session SomeSessionResult
someSession = do
  valid <- Session.query someQueryParams someQuery
  if valid
    then continueAsIfItsValid
    else handleFailure

@mgsloan
Copy link
Author

mgsloan commented Jul 5, 2018

Hey, thanks for taking the time to consider this.

Indeed, that works. I'm working on a codebase that has a datatype of Transaction fields which implement different operations. The datatype encapsulates how to perform a particular operation at a particular database version. So, we have data DbApi = DbApi { insertFoo :: Foo -> Transaction () } and dbV1 :: DbApi, dbV2 :: DbApi etc. Perhaps that is just the wrong way to slice the abstraction, but that is what I'm working with.

I then have some insert queries that rely on UUIDs and uniqueness constraints to ensure no collisions. Even so, I'd like to check rowsAffected as a sanity check. It seems ugly to need to bubble this all the way out to the invoker of DbApi, particularly considering that other decoders do have failure cases.

@nikita-volkov
Copy link
Owner

particularly considering that other decoders do have failure cases.

Those failure cases are sort of forced upon us by the Postgres side. It doesn't mean that we should extend in that direction.

Even so, I'd like to check rowsAffected as a sanity check. It seems ugly to need to bubble this all the way out to the invoker of DbApi

Sanity check is in part what you're doing in your insertFoo function. You're in a language which is valued for its type system. So why not use that power and encode your intention in types? E.g.:

insertFoo :: Foo -> ExceptT ValidationError Transaction ()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants