psql-0.0.0: test/PostgreSQL/Result/RowSpec.hs
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
module PostgreSQL.Result.RowSpec (spec) where
import Test.Hspec (Spec, describe, it, shouldBe)
import Control.Monad (when)
import qualified Control.Monad.Except as Except
import qualified Control.Monad.Reader as Reader
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Massiv.Array as Massiv
import Data.Traversable (for)
import qualified Data.Vector as Vector
import Database.PostgreSQL.LibPQ (Format (Text), Oid, invalidOid)
import qualified PostgreSQL.Result.Cell as Cell
import qualified PostgreSQL.Result.Column as Column
import qualified PostgreSQL.Result.Row as Row
import PostgreSQL.Types (ColumnNum, ProcessorError (..), ProcessorErrors, Value (..))
type Matrix = Massiv.Array Massiv.B Massiv.Ix2
runMatrixRow
:: Vector.Vector (Oid, Format)
-> HashMap.HashMap ByteString ColumnNum
-> Matrix Value
-> Row.Row a
-> Either ProcessorErrors [a]
runMatrixRow columns names datas row = Except.runExcept $ do
let
Massiv.Sz2 numRows' numCols' = Massiv.size datas
numRows = fromIntegral numRows'
numCols = min (fromIntegral numCols') $ fromIntegral $ Vector.length columns
runner <- Row.runRow row $ \req -> do
col <-
case Row.columnRequest_position req of
Row.FixedColumn col -> pure col
Row.NamedColumn name ->
case HashMap.lookup name names of
Just col -> pure col
Nothing -> Except.throwError [MissingNamedColumn name]
when (col >= numCols) $ Except.throwError [NotEnoughColumns col numCols]
let (oid, format) = columns Vector.! fromIntegral col
cell <-
Except.liftEither $ first (fmap (ColumnParserError col oid format)) $
Column.parseColumn (Row.columnRequest_parser req) oid format
pure $ Reader.ReaderT $ \row -> do
let value = Massiv.index' datas $ Massiv.Ix2 (fromIntegral row) $ fromIntegral col
Except.liftEither $ first (fmap (CellParserError col oid format row value)) $
Cell.parseCell cell value
for [0 .. numRows - 1] (Reader.runReaderT runner)
spec :: Spec
spec =
describe "Row" $ do
it "works as an Applicative" $
runMatrixRow [] [] [[], []] (pure ())
`shouldBe` Right [(), ()]
it "works on single columns" $
runMatrixRow
[(invalidOid, Text)]
[]
[["Hello"], ["World"]]
(Row.columnWith Column.raw)
`shouldBe` Right ["Hello", "World"]
it "works on multiple columns" $
runMatrixRow
[(invalidOid, Text), (invalidOid, Text)]
[]
[["1", "Hello"], ["2", "World"]]
(replicate <$> Row.column <*> Row.columnWith Column.raw)
`shouldBe` Right [["Hello"], ["World", "World"]]
it "resolves named columns" $
runMatrixRow
[(invalidOid, Text), (invalidOid, Text)]
[("b", 1)]
[["1", "Hello"], ["2", "World"]]
(Row.namedColumnWith "b" Column.raw)
`shouldBe` Right ["Hello", "World"]
it "works with fixed columns" $
runMatrixRow
[(invalidOid, Text), (invalidOid, Text)]
[]
[["1", "Hello"], ["2", "World"]]
(Row.fixedColumnWith 1 Column.raw)
`shouldBe` Right ["Hello", "World"]
it "fails on too few columns" $
runMatrixRow
[(invalidOid, Text)]
[]
[["Hello"]]
(Row.fixedColumnWith 1 Column.raw)
`shouldBe` Left [NotEnoughColumns 1 1]
it "fails on unknown named column" $
runMatrixRow
[(invalidOid, Text)]
[]
[["Hello"]]
(Row.namedColumnWith "unknown" Column.raw)
`shouldBe` Left [MissingNamedColumn "unknown"]