Use more descriptive error messages for invalid row structures

This commit is contained in:
rightfold 2017-04-18 14:33:43 +02:00
parent e58c71e26c
commit 552e59ad92
No known key found for this signature in database
GPG Key ID: 199D0373AC917A8F

View File

@ -25,6 +25,7 @@ import Control.Monad.Eff.Exception (error)
import Control.Monad.Error.Class (catchError, throwError) import Control.Monad.Error.Class (catchError, throwError)
import Control.Monad.Except (runExcept) import Control.Monad.Except (runExcept)
import Data.Array (head, uncons) import Data.Array (head, uncons)
import Data.Bifunctor (lmap)
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.DateTime.Instant (Instant) import Data.DateTime.Instant (Instant)
import Data.Either (Either(..)) import Data.Either (Either(..))
@ -69,7 +70,7 @@ class ToSQLRow a where
-- | Convert things from SQL rows. -- | Convert things from SQL rows.
class FromSQLRow a where class FromSQLRow a where
fromSQLRow :: Array Foreign -> Maybe a fromSQLRow :: Array Foreign -> Either String a
-- | Convert things to SQL values. -- | Convert things to SQL values.
class ToSQLValue a where class ToSQLValue a where
@ -77,7 +78,7 @@ class ToSQLValue a where
-- | Convert things from SQL values. -- | Convert things from SQL values.
class FromSQLValue a where class FromSQLValue a where
fromSQLValue :: Foreign -> Maybe a fromSQLValue :: Foreign -> Either String a
instance toSQLRowUnit :: ToSQLRow Unit where instance toSQLRowUnit :: ToSQLRow Unit where
toSQLRow _ = [] toSQLRow _ = []
@ -86,56 +87,57 @@ instance toSQLRowTuple :: (ToSQLValue a, ToSQLRow b) => ToSQLRow (Tuple a b) whe
toSQLRow (a /\ b) = [toSQLValue a] <> toSQLRow b toSQLRow (a /\ b) = [toSQLValue a] <> toSQLRow b
instance fromSQLRowUnit :: FromSQLRow Unit where instance fromSQLRowUnit :: FromSQLRow Unit where
fromSQLRow [] = Just unit fromSQLRow [] = pure unit
fromSQLRow _ = Nothing fromSQLRow _ = throwError "FromSQLRow: row has too many columns"
instance fromSQLRowTuple :: (FromSQLValue a, FromSQLRow b) => FromSQLRow (Tuple a b) where instance fromSQLRowTuple :: (FromSQLValue a, FromSQLRow b) => FromSQLRow (Tuple a b) where
fromSQLRow = uncons >=> case _ of fromSQLRow = uncons >>> case _ of
{head, tail} -> (/\) <$> fromSQLValue head <*> fromSQLRow tail Just {head, tail} -> (/\) <$> fromSQLValue head <*> fromSQLRow tail
Nothing -> throwError "FromSQLRow: row has too few columns"
instance toSQLValueBoolean :: ToSQLValue Boolean where instance toSQLValueBoolean :: ToSQLValue Boolean where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueBoolean :: FromSQLValue Boolean where instance fromSQLValueBoolean :: FromSQLValue Boolean where
fromSQLValue = fromRight <<< runExcept <<< readBoolean fromSQLValue = lmap show <<< runExcept <<< readBoolean
instance toSQLValueChar :: ToSQLValue Char where instance toSQLValueChar :: ToSQLValue Char where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueChar :: FromSQLValue Char where instance fromSQLValueChar :: FromSQLValue Char where
fromSQLValue = fromRight <<< runExcept <<< readChar fromSQLValue = lmap show <<< runExcept <<< readChar
instance toSQLValueInt :: ToSQLValue Int where instance toSQLValueInt :: ToSQLValue Int where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueInt :: FromSQLValue Int where instance fromSQLValueInt :: FromSQLValue Int where
fromSQLValue = fromRight <<< runExcept <<< readInt fromSQLValue = lmap show <<< runExcept <<< readInt
instance toSQLValueNumber :: ToSQLValue Number where instance toSQLValueNumber :: ToSQLValue Number where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueNumber :: FromSQLValue Number where instance fromSQLValueNumber :: FromSQLValue Number where
fromSQLValue = fromRight <<< runExcept <<< readNumber fromSQLValue = lmap show <<< runExcept <<< readNumber
instance toSQLValueString :: ToSQLValue String where instance toSQLValueString :: ToSQLValue String where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueString :: FromSQLValue String where instance fromSQLValueString :: FromSQLValue String where
fromSQLValue = fromRight <<< runExcept <<< readString fromSQLValue = lmap show <<< runExcept <<< readString
instance fromSQLValueArray :: (FromSQLValue a) => FromSQLValue (Array a) where instance fromSQLValueArray :: (FromSQLValue a) => FromSQLValue (Array a) where
fromSQLValue = traverse fromSQLValue <=< fromRight <<< runExcept <<< readArray fromSQLValue = traverse fromSQLValue <=< lmap show <<< runExcept <<< readArray
instance fromSQLValueList :: (FromSQLValue a) => FromSQLValue (List a) where instance fromSQLValueList :: (FromSQLValue a) => FromSQLValue (List a) where
fromSQLValue = map List.fromFoldable <<< traverse fromSQLValue <=< fromRight <<< runExcept <<< readArray fromSQLValue = map List.fromFoldable <<< traverse fromSQLValue <=< lmap show <<< runExcept <<< readArray
instance toSQLValueByteString :: ToSQLValue ByteString where instance toSQLValueByteString :: ToSQLValue ByteString where
toSQLValue = toForeign toSQLValue = toForeign
instance fromSQLValueByteString :: FromSQLValue ByteString where instance fromSQLValueByteString :: FromSQLValue ByteString where
fromSQLValue x fromSQLValue x
| unsafeIsBuffer x = Just $ unsafeFromForeign x | unsafeIsBuffer x = pure $ unsafeFromForeign x
| otherwise = Nothing | otherwise = throwError "FromSQLValue ByteString: not a buffer"
instance toSQLValueInstant :: ToSQLValue Instant where instance toSQLValueInstant :: ToSQLValue Instant where
toSQLValue = instantToString toSQLValue = instantToString
@ -145,7 +147,7 @@ instance toSQLValueMaybe :: (ToSQLValue a) => ToSQLValue (Maybe a) where
toSQLValue (Just x) = toSQLValue x toSQLValue (Just x) = toSQLValue x
instance fromSQLValueMaybe :: (FromSQLValue a) => FromSQLValue (Maybe a) where instance fromSQLValueMaybe :: (FromSQLValue a) => FromSQLValue (Maybe a) where
fromSQLValue x | isNull x = Just Nothing fromSQLValue x | isNull x = pure Nothing
| otherwise = Just <$> fromSQLValue x | otherwise = Just <$> fromSQLValue x
foreign import instantToString :: Instant -> Foreign foreign import instantToString :: Instant -> Foreign
@ -202,8 +204,8 @@ query
query conn (Query sql) values = query conn (Query sql) values =
_query conn sql (toSQLRow values) _query conn sql (toSQLRow values)
>>= traverse (fromSQLRow >>> case _ of >>= traverse (fromSQLRow >>> case _ of
Just row -> pure row Right row -> pure row
Nothing -> throwError (error "incompatible row structure")) Left msg -> throwError (error msg))
scalar scalar
:: i o eff :: i o eff