Add unit tests and reimplement FromField

This commit is contained in:
2023-09-03 04:02:07 +02:00
parent f55e7fd06f
commit 8c8740e4b8
10 changed files with 293 additions and 34 deletions
+29 -13
View File
@@ -5,12 +5,19 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module Database.PostgreSQL.Opium where
module Database.PostgreSQL.Opium
( FromField (..)
, FromRow (..)
, fetch_
)
where
import Control.Monad.Trans.Maybe (MaybeT (..))
import Data.ByteString (ByteString)
import Data.Proxy (Proxy (Proxy))
import Database.PostgreSQL.LibPQ
(Result
( Connection
, Result
, Row
)
import GHC.Generics (C, D, Generic, K1 (..), M1 (..), Meta (..), Rec0, Rep, S, to, (:*:) (..))
@@ -21,6 +28,18 @@ import qualified Data.Text as Text
import qualified Data.Text.Encoding as Encoding
import qualified Database.PostgreSQL.LibPQ as LibPQ
import Database.PostgreSQL.Opium.FromField (FromField (..))
fetch_ :: FromRow a => Connection -> ByteString -> IO (Maybe [a])
fetch_ conn query = runMaybeT $ do
result <- MaybeT $ LibPQ.execParams conn query [] LibPQ.Text
MaybeT $ fetchResult result
fetchResult :: FromRow a => Result -> IO (Maybe [a])
fetchResult result = do
nRows <- LibPQ.ntuples result
runMaybeT $ mapM (MaybeT . flip fromRow result) [0..nRows - 1]
class FromRow a where
fromRow :: Row -> Result -> IO (Maybe a)
default fromRow :: (Generic a, FromRow' (Rep a)) => Row -> Result -> IO (Maybe a)
@@ -48,16 +67,13 @@ instance (KnownSymbol nameSym, FromField t) => FromRow' (M1 S ('MetaSel ('Just n
Nothing -> pure Nothing
Just column -> do
mbField <- LibPQ.getvalue result row column
printf "%s: %s" (show name) (show mbField)
pure $ M1 . K1 <$> fromField mbField
ty <- LibPQ.ftype result column
case fromField ty . Encoding.decodeUtf8 =<< mbField of
Nothing -> do
format <- LibPQ.fformat result column
printf "field %s: %s (oid: %s, format: %s)\n" (show name) (show mbField) (show ty) (show format)
pure Nothing
Just value ->
pure $ Just $ M1 $ K1 value
where
name = Encoding.encodeUtf8 $ Text.pack $ symbolVal (Proxy :: Proxy nameSym)
class FromField a where
fromField :: Maybe ByteString -> Maybe a
instance FromField String where
fromField = fmap (Text.unpack . Encoding.decodeUtf8)
instance FromField Int where
fromField = fmap (read . Text.unpack . Encoding.decodeUtf8)
@@ -0,0 +1,52 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleInstances #-}
module Database.PostgreSQL.Opium.FromField (FromField (..)) where
import Data.Attoparsec.Text
( Parser
, decimal
, parseOnly
, signed
)
import Data.Text (Text)
import Database.PostgreSQL.LibPQ (Oid)
import qualified Data.Text as Text
import qualified Database.PostgreSQL.Opium.Oid as Oid
(\/) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
p \/ q = \x -> p x || q x
eitherToMaybe :: Either b a -> Maybe a
eitherToMaybe = \case
Left _ -> Nothing
Right x -> Just x
fromParser
:: (Oid -> Bool)
-> Parser a
-> Oid
-> Text
-> Maybe a
fromParser validOid parser oid value
| validOid oid = eitherToMaybe $ parseOnly parser value
| otherwise = Nothing
class FromField a where
fromField :: Oid -> Text -> Maybe a
instance FromField Int where
fromField = fromParser
(Oid.smallint \/ Oid.integer \/ Oid.bigint)
(signed decimal)
instance FromField Text where
fromField oid text =
if Oid.text oid || Oid.character oid || Oid.characterVarying oid then
Just text
else
Nothing
instance FromField String where
fromField oid text = Text.unpack <$> fromField oid text
+31
View File
@@ -0,0 +1,31 @@
module Database.PostgreSQL.Opium.Oid where
import Database.PostgreSQL.LibPQ (Oid (..))
eq :: Eq a => a -> a -> Bool
eq = (==)
-- integer types
-- | 16-bit integer
smallint :: Oid -> Bool
smallint = eq $ Oid 21
-- | 32-bit integer
integer :: Oid -> Bool
integer = eq $ Oid 23
-- | 64-bit integer
bigint :: Oid -> Bool
bigint = eq $ Oid 20
-- string types
text :: Oid -> Bool
text = eq $ Oid 25
character :: Oid -> Bool
character = eq $ Oid 1042
characterVarying :: Oid -> Bool
characterVarying = eq $ Oid 1043