-- | -- An intermediate array representation -- and utilities for its (de)composition. module PostgreSQLBinary.Array where import PostgreSQLBinary.Prelude hiding (Data) -- | -- A representation of a data serializable to the PostgreSQL array binary format. -- -- Consists of a list of dimensions, a list of encoded elements, -- a flag specifying, whether it contains any nulls, and an oid. type Data = ([Dimension], [Value], Bool, Word32) -- | -- A width and a lower bound. -- -- Currently the lower bound is only allowed to have a value of @1@. type Dimension = (Word32, Word32) -- | -- An encoded value. 'Nothing' if it represents a @NULL@. type Value = Maybe ByteString -- | -- Access a value of data, if the data represents a single value. asSingleton :: Data -> Maybe Value asSingleton (dimensions, elements, nulls, oid) = if null dimensions then case elements of [x] -> return x l -> $bug $ "Unexpected amount of elements: " <> show l else mzero -- | -- Construct from a non-empty list, -- taking the shared parameters from the first element. fromListUnsafe :: [Data] -> Data fromListUnsafe list = case list of (dimensions, values, nulls, oid) : tail -> ((fromIntegral $ length list, 1) : dimensions, values <> foldMap valuesOf tail, nulls, oid) where valuesOf (_, x, _, _) = x _ -> error "Empty list" fromSingleton :: Value -> Bool -> Word32 -> Data fromSingleton value nullable oid = ([], [value], nullable, oid) -- | -- Get a list of elements. elements :: Data -> [Data] elements (dimensions, values, nulls, oid) = do subvalues <- slice values return (subdimensions, subvalues, nulls, oid) where ((width, lowerBound), subdimensions) = case dimensions of h : t -> (h, t) _ -> ((0, 1), []) chunkSize = if null subdimensions then 1 else product $ map dimensionWidth $ subdimensions where dimensionWidth (x, _) = fromIntegral x slice = foldr (\f g l -> case f l of (a, b) -> a : g b) (const mzero) $ replicate (fromIntegral width) (splitAt chunkSize)