beam icon indicating copy to clipboard operation
beam copied to clipboard

Error reporting is not sufficient

Open yaitskov opened this issue 3 years ago • 1 comments

Hi,

My app gets following exception up on basic select all from a single table:

ManyErrors [
  BeamRowReadError {brreColumn = Nothing, brreError = ColumnErrorInternal "Prelude.read: no parse"},
  BeamRowReadError {brreColumn = Just 17, brreError = ColumnTypeMismatch {ctmHaskellType = "Null", ctmSQLType = "TEXT", ctmMessage = "conversion failed: data is not null"}},
  BeamRowReadError {brreColumn = Just 0, brreError = ColumnTypeMismatch {ctmHaskellType = "Null", ctmSQLType = "TEXT", ctmMessage = "conversion failed: data is not null"}}]

I would like to see table name and a showed raw row which has problem values.

yaitskov avatar Sep 16 '22 21:09 yaitskov

I achieved an intermediate success with hard coding logger to show the raw row causing the issue:

-        translateErrors :: Maybe Int -> SomeException -> Maybe SomeException
-        translateErrors col (SomeException e) =
+        translateErrors :: Maybe Int -> [SQLData] -> SomeException -> Maybe SomeException
+        translateErrors col rawRow (SomeException e) =
           case cast e of
             Just (ConversionFailed { errSQLType     = typeString
                                    , errHaskellType = hsString
                                    , errMessage     = msg }) ->
-              Just (SomeException (BeamRowReadError col (ColumnTypeMismatch hsString typeString ("conversion failed: " ++ msg))))
+              Just (SomeException (BeamRowReadError col (ColumnTypeMismatch hsString typeString
+                                                          ("conversion failed: " ++ msg ++ " for row: " ++ show rawRow))))
             Just (UnexpectedNull {}) ->
               Just (SomeException (BeamRowReadError col ColumnUnexpectedNull))
             Just (Incompatible { errSQLType     = typeString
                                , errHaskellType = hsString
                                , errMessage     = msg }) ->
-              Just (SomeException (BeamRowReadError col (ColumnTypeMismatch hsString typeString ("incompatible: " ++ msg))))
+              Just (SomeException (BeamRowReadError col (ColumnTypeMismatch hsString typeString
+                                                          ("incompatible: " ++ msg ++ " for row: " ++ show rawRow))))
             Nothing -> Nothing
 
         finish = pure
@@ -230,7 +232,7 @@ instance FromBackendRow Sqlite a => FromRow (BeamSqliteRow a) where
             RP $ ReaderT $ \ro -> StateT $ \st@(col, _) ->
             case runStateT (runReaderT (unRP field) ro) st of
               Ok (x, st') -> runStateT (runReaderT (unRP (next x)) ro) st'
-              Errors errs -> Errors (mapMaybe (translateErrors (Just col)) errs)
+              Errors errs -> Errors (mapMaybe (translateErrors (Just col) (snd st)) errs)

Logging data might not be acceptable for production mode so I tried to make the feature plugable via runBeamSqliteDebug, but I don't see how to deliver another callback to translateErrors. I extended FromBackendRowF with extra field for the callback, but how to update existing instances?

newtype RawRowShow = RawRowShow (forall a. Show a => a -> Maybe String)

data FromBackendRowF be f where
  ParseOneField :: (BackendFromField be a, Typeable a) => RawRowShow -> (a -> f) -> FromBackendRowF be f

e.g.

parseOneField :: (BackendFromField be a, Typeable a) => FromBackendRowM be a
parseOneField = do
  x <- FromBackendRowM (liftF (ParseOneField id id))
  pure x

peekField :: (Typeable a, BackendFromField be a) => FromBackendRowM be (Maybe a)
peekField = fmap Just (FromBackendRowM (liftF (ParseOneField id id))) <|> pure Nothing

yaitskov avatar Sep 17 '22 01:09 yaitskov