Repository: hasura/graphql-engine
File: server/src-lib/Hasura/SQL/Value.hs (Haskell)

Code before:
pgColValueToInt _                 = Nothing

withConstructorFn :: PGScalarType -> S.SQLExp -> S.SQLExp
withConstructorFn ty v
  | isGeoType ty = S.SEFnApp "ST_GeomFromGeoJSON" [v] Nothing
  | ty == PGRaster = S.SEFnApp "ST_RastFromHexWKB" [v] Nothing
  | otherwise = v

parsePGValue :: PGScalarType -> Value -> AT.Parser PGScalarValue
parsePGValue ty val = case (ty, val) of
  (_          , Null)     -> pure $ PGNull ty
  (PGUnknown _, String t) -> pure $ PGValUnknown t
  (PGRaster   , _)        -> parseTyped -- strictly parse raster value
  (_          , String t) -> parseTyped <|> pure (PGValUnknown t)
  (_          , _)        -> parseTyped
  where
    parseBoundedInt :: forall i. (Integral i, Bounded i) => Value -> AT.Parser i
    parseBoundedInt val' = do
      parsedM <-
        withScientific
          ("Integer expected for input type: " ++ show ty)
          (pure . toBoundedInteger)
          val'
      case parsedM of
        Just parsed -> return parsed
        Nothing     -> fail $ "Integer does not fit.  Maybe it is a float,"
                       ++ " or is there integer overflow?"
    parseBoundedFloat :: forall a. (RealFloat a) => Value -> AT.Parser a
    parseBoundedFloat val' = do
      parsedM <-
        withScientific
          ("Float expected for input type: " ++ show ty)
          (pure . toBoundedRealFloat)
          val'
      case parsedM of
        Left _       -> fail $ "Float does not fit; is there an overflow?"
        Right parsed -> return parsed
    parseTyped = case ty of
      PGSmallInt -> PGValSmallInt <$> parseBoundedInt val
      PGInteger -> PGValInteger <$> parseBoundedInt val
      PGBigInt -> PGValBigInt <$> parseBoundedInt val
      PGSerial -> PGValInteger <$> parseBoundedInt val
      PGBigSerial -> PGValBigInt <$> parseBoundedInt val
      PGFloat -> PGValFloat <$> parseBoundedFloat val
      PGDouble -> PGValDouble <$> parseBoundedFloat val
      PGNumeric -> PGValNumeric <$> parseJSON val
      PGBoolean -> PGValBoolean <$> parseJSON val
      PGChar -> PGValChar <$> parseJSON val
      PGVarchar -> PGValVarchar <$> parseJSON val
      PGText -> PGValText <$> parseJSON val
      PGCitext -> PGValCitext <$> parseJSON val

Review comment (suggestion):
It would be more helpful to have a message which includes the provided value and the type, maybe something like this?
```
the value '....' exceeds the bounds of type 'int'
```

Code after:

pgColValueToInt :: PGScalarValue -> Maybe Int
pgColValueToInt (PGValInteger i)  = Just $ fromIntegral i
pgColValueToInt (PGValSmallInt i) = Just $ fromIntegral i
pgColValueToInt (PGValBigInt i)   = Just $ fromIntegral i
pgColValueToInt _                 = Nothing

withConstructorFn :: PGScalarType -> S.SQLExp -> S.SQLExp
withConstructorFn ty v
  | isGeoType ty = S.SEFnApp "ST_GeomFromGeoJSON" [v] Nothing
  | ty == PGRaster = S.SEFnApp "ST_RastFromHexWKB" [v] Nothing
  | otherwise = v

parsePGValue :: PGScalarType -> Value -> AT.Parser PGScalarValue
parsePGValue ty val = case (ty, val) of
  (_          , Null)     -> pure $ PGNull ty
  (PGUnknown _, String t) -> pure $ PGValUnknown t
  (PGRaster   , _)        -> parseTyped -- strictly parse raster value
  (_          , String t) -> parseTyped <|> pure (PGValUnknown t)
  (_          , _)        -> parseTyped
  where
    parseBoundedInt :: forall i. (Integral i, Bounded i) => Value -> AT.Parser i
    parseBoundedInt val' =
      withScientific
        ("Integer expected for input type: " ++ show ty)
        go
        val'
      where
        go num = case toBoundedInteger num of
          Just parsed -> return parsed
          Nothing     -> fail $ "The value " ++ show num ++ " lies outside the "
                         ++ "bounds or is not an integer.  Maybe it is a "
                         ++ "float, or is there integer overflow?"
    parseBoundedFloat :: forall a. (RealFloat a) => Value -> AT.Parser a
    parseBoundedFloat val' =
      withScientific
        ("Float expected for input type: " ++ show ty)
        go
        val'
      where
        go num = case toBoundedRealFloat num of
          Left _       -> fail $ "The value " ++ show num ++ " lies outside the "
                          ++ "bounds.  Is it overflowing the float bounds?"
          Right parsed -> return parsed
    parseTyped = case ty of
      PGSmallInt -> PGValSmallInt <$> parseBoundedInt val
      PGInteger -> PGValInteger <$> parseBoundedInt val
      PGBigInt -> PGValBigInt <$> parseBoundedInt val
      PGSerial -> PGValInteger <$> parseBoundedInt val
      PGBigSerial -> PGValBigInt <$> parseBoundedInt val
      PGFloat -> PGValFloat <$> parseBoundedFloat val