| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Hmssql.Decoding
Description
Turning the columns of a result set into Haskell values.
FromSqlField decodes one column, with instances for the usual types
(Hmssql.Types has the table of which SQL types each accepts);
FromSqlRow decodes a row, derived for a record or given for a tuple.
A new field decoder is usually refineField over an existing one (an
enumeration from a varchar, a JSON column), or fieldDecoderFor from
the value's bytes.
A FieldDecoder checks its column's ColumnInfo once per result set,
rejecting an incompatible SQL type before any row is read. It then reads
each value whole, or gives it to a consumer as it arrives (streamField).
RowDecoder composes field decoders applicatively and knows how many
columns it consumes, so a whole result set is type-checked before the
first row is decoded. RowDecoderMonadic is a monadic variant for shapes
only known at runtime.
Synopsis
- class FromSqlField a where
- newtype FieldDecoder a = FieldDecoder (TdsContext -> ColumnInfo -> Either String (CellDecoder a))
- fieldDecoderFor :: String -> (TypeInfo -> Bool) -> (TdsContext -> TypeInfo -> ByteString -> Either String a) -> FieldDecoder a
- fieldDecoderForTypes :: String -> (SqlDataType -> Bool) -> (TdsContext -> TypeInfo -> ByteString -> Either String a) -> FieldDecoder a
- refineField :: (a -> Either String b) -> FieldDecoder a -> FieldDecoder b
- nullableField :: FieldDecoder a -> FieldDecoder (Maybe a)
- newtype RawValue = RawValue ByteString
- dayTimeFieldDecoder :: String -> (Day -> Int64 -> a) -> FieldDecoder a
- dateTimeOffsetFieldDecoder :: String -> (Day -> Int64 -> Int16 -> a) -> FieldDecoder a
- timeFieldDecoder :: String -> (Int64 -> a) -> FieldDecoder a
- decimalFieldDecoder :: String -> (Bool -> Word64 -> Word64 -> Word8 -> a) -> FieldDecoder a
- data ValueStream = ValueStream {
- valueColumn :: !ColumnInfo
- valueLength :: !(Maybe Int64)
- valueChunks :: Stream (Of ByteString) IO ()
- streamField :: (ValueStream -> IO a) -> FieldDecoder a
- streamTextField :: (ColumnInfo -> Stream (Of Text) IO () -> IO a) -> FieldDecoder a
- class FromSqlRow a where
- rowDecoder :: RowDecoder a
- data RowDecoder a = RowDecoder {
- compileRowAt :: TdsContext -> ResultSetInfo -> Int -> Either DecodeError (RowPart a)
- numExpectedColumns :: !Int
- singleField :: FieldDecoder a -> RowDecoder a
- inlinedField :: FieldDecoder a -> RowDecoder a
- inlinedNullableField :: FieldDecoder a -> RowDecoder (Maybe a)
- byColumn :: (ColumnInfo -> RowDecoder a) -> RowDecoder a
- genericFromSqlRow :: (Generic a, GRowDecoder (Rep a)) => RowDecoder a
- newtype RowDecoderMonadic a = RowDecoderMonadic {
- runRowDecoderMonadic :: TdsContext -> ResultSetInfo -> Int -> ByteString -> Int -> Parser (Either ReadFailure a, Int)
- singleFieldM :: FieldDecoder a -> RowDecoderMonadic a
- remainingColumns :: RowDecoderMonadic [ColumnInfo]
- toMonadicRowDecoder :: RowDecoder a -> RowDecoderMonadic a
- sqlRowDecoder :: RowDecoderMonadic SqlRow
- sqlValueDecoder :: FieldDecoder SqlValue
- sqlRowDecoderFor :: [ColumnInfo] -> RowDecoder SqlRow
- compileField :: FieldDecoder a -> TdsContext -> ColumnInfo -> Either String (CellDecoder a)
- data CellDecoder a
- = CellDecoder (Either String a) (ValueReader a) (ValueReader a)
- | FoldingCell (Either String a) (ValueReader a) (ValueReader a) (Stream (Of ByteString) Parser () -> Parser (Either ReadFailure a))
- | StreamingCell (StreamedValue -> Parser (Either ReadFailure a))
- data StreamedValue
- = StreamedNull
- | StreamedWhole !ValueBytes
- | StreamedBytes !(Maybe Int64) (Stream (Of ByteString) Parser ())
- streamWholeLimit :: Int
- data ReadFailure
- decodeCell :: CellDecoder a -> RawBytes -> Either String a
- fieldDecoderCompiled :: (TdsContext -> ColumnInfo -> Either String (CellDecoder a)) -> FieldDecoder a
- readCellRecipe :: ColumnInfo -> CellDecoder a -> Int -> Cell -> Parser a
- compileRowDecoder :: RowDecoder a -> TdsContext -> [ColumnInfo] -> Either DecodeError (CompiledRow a)
- compileRowDecoderFor :: RowDecoder a -> TdsContext -> ResultSetInfo -> Either DecodeError (CompiledRow a)
- compileMonadicRowDecoder :: RowDecoderMonadic a -> TdsContext -> [ColumnInfo] -> CompiledRow a
- skipRowDecoder :: [ColumnInfo] -> CompiledRow ()
- data CompiledRow a = CompiledRow {
- rowPrepare :: Int -> Prepared (Parser a)
- data Prepared a = Prepared a
- rowRecipe :: CompiledRow a -> Int -> Parser (Either ReadFailure a)
- decodeColumnChunks :: forall (m :: Type -> Type) r. Monad m => TypeInfo -> Either String (Stream (Of ByteString) m r -> Stream (Of Text) m r)
- decodeColumnText :: TypeInfo -> ByteString -> Either String Text
Field decoders
class FromSqlField a where Source #
Types that decode from one column. The usual Haskell types have
instances, and Hmssql.Types has the table of which SQL types each
accepts. Maybe accepts NULL; without it, a NULL is a DecodeError.
An instance of your own defines fieldDecoder, usually with
refineField over a built-in type's decoder.
Minimal complete definition
Methods
fieldDecoder :: FieldDecoder a Source #
The decoder of one column of this type.
fieldRowDecoder :: RowDecoder a Source #
A row decoder of one column of this type, the same as . Tuples, singleField
fieldDecoderOnly and derived FromSqlRow instances read their
columns with it, and the built-in types' are faster than singleField. An
instance doesn't define it.
Instances
newtype FieldDecoder a Source #
How to decode one column. Given the connection's context and the column, it
returns a CellDecoder for the column's values, or why it can't decode them.
The check runs when a result set's columns arrive, before its first row, so a type mismatch is reported even for an empty result.
Constructors
| FieldDecoder (TdsContext -> ColumnInfo -> Either String (CellDecoder a)) |
Instances
| Functor FieldDecoder Source # | |
Defined in Hmssql.Decoding Methods fmap :: (a -> b) -> FieldDecoder a -> FieldDecoder b # (<$) :: a -> FieldDecoder b -> FieldDecoder a # | |
| Semigroup (FieldDecoder a) Source # | |
Defined in Hmssql.Decoding Methods (<>) :: FieldDecoder a -> FieldDecoder a -> FieldDecoder a # sconcat :: NonEmpty (FieldDecoder a) -> FieldDecoder a # stimes :: Integral b => b -> FieldDecoder a -> FieldDecoder a # | |
fieldDecoderFor :: String -> (TypeInfo -> Bool) -> (TdsContext -> TypeInfo -> ByteString -> Either String a) -> FieldDecoder a Source #
A decoder from a value's bytes, given a name for error messages, the wire
types it accepts and the conversion. A NULL is a DecodeError unless the
decoder is wrapped in nullableField. The bytes may be part of a receive
buffer, so a conversion should copy any it keeps, as CellDecoder explains.
fieldDecoderForTypes :: String -> (SqlDataType -> Bool) -> (TdsContext -> TypeInfo -> ByteString -> Either String a) -> FieldDecoder a Source #
fieldDecoderFor, accepting by SQL data type rather than by wire type. The
conversion still receives the column's TypeInfo.
refineField :: (a -> Either String b) -> FieldDecoder a -> FieldDecoder b Source #
Convert a decoded value further. A Left becomes a DecodeError naming the
column. The inner decoder's type check and NULL handling still apply.
instance FromSqlField Status where fieldDecoder = refineField parseStatus fieldDecoder
nullableField :: FieldDecoder a -> FieldDecoder (Maybe a) Source #
Decode NULL as Nothing, keeping the inner decoder's type check. A streaming
consumer isn't called for a NULL.
The bytes of any column's value as the server sends them, including the CLR types hmssql doesn't otherwise decode.
Constructors
| RawValue ByteString |
Instances
| Show RawValue Source # | |
| Eq RawValue Source # | |
| FromSqlField RawValue Source # | |
Defined in Hmssql.Decoding Methods fieldDecoder :: FieldDecoder RawValue Source # fieldRowDecoder :: RowDecoder RawValue Source # nullableRowDecoder :: RowDecoder (Maybe RawValue) rowDecoderWith :: Nulls RawValue b -> RowDecoder b | |
Field decoders from a value's parts
The date and time types decode to the time package's types, and decimal
to Decimal, which are built from Integers. For code that writes a value
straight out, as JSON or text, or keeps it in a type of its own, these decoders
give your function the value's parts instead: a date's Day, its picoseconds
since midnight and its offset in minutes, or a decimal's sign, magnitude and
scale. A value is checked as the built-in decoders check it, and a NULL
is a DecodeError unless the decoder is wrapped in nullableField.
-- the minutes since midnight of a datetime2 or datetime column minuteOfDay :: FieldDecoder Int minuteOfDay = dayTimeFieldDecoder "minute of day" $ \_ ps -> fromIntegral (ps `quot` 60_000_000_000_000)
dayTimeFieldDecoder :: String -> (Day -> Int64 -> a) -> FieldDecoder a Source #
A decoder of the types LocalTime accepts (date, datetime,
smalldatetime and datetime2), given a name for error messages and a
function of each value's day and picoseconds since midnight, which are 0 for
a date. A datetime's picoseconds are rounded down, as LocalTime's are,
and a day count or a time count out of range is a DecodeError, as it is for
LocalTime.
dateTimeOffsetFieldDecoder :: String -> (Day -> Int64 -> Int16 -> a) -> FieldDecoder a Source #
A decoder of datetimeoffset, given a name for error messages and a
function of each value's day and picoseconds since midnight, both in UTC, and
its offset in minutes, as DateTimeOffset holds them. The value is checked as
DateTimeOffset is: the offset must lie within ±14:00, and the local date
must be in range.
timeFieldDecoder :: String -> (Int64 -> a) -> FieldDecoder a Source #
A decoder of time at any scale, given a name for error messages and a
function of each value's picoseconds since midnight.
decimalFieldDecoder :: String -> (Bool -> Word64 -> Word64 -> Word8 -> a) -> FieldDecoder a Source #
A decoder of the types Decimal accepts (decimal, numeric, money,
smallmoney and the integer types), given a name for error messages and a
function of each value's parts: whether it is negative, the high and low 64 bits
of its magnitude, and its scale. A decimal is checked as Decimal checks it,
a money value has scale 4 and an integer scale 0, and a zero is never negative.
The high bits are 0 unless the value needs more than 64, which only a decimal
of precision 20 or more can.
-- a value's whole part, when its magnitude fits 64 bits wholePart :: FieldDecoder (Maybe Integer) wholePart = decimalFieldDecoder "whole part" $ \negative hi lo scale -> if hi /= 0 then Nothing else Just ((if negative then negate else id) (toInteger (lo `quot` 10 ^ scale)))
Streaming a large value
A decoder normally reads a whole value into memory. streamField instead
gives each value to your function as it arrives, so a column of any size can go
to a file or a socket without being held in memory:
import qualified Data.ByteString as B
import qualified Streaming.Prelude as S
import System.IO (IOMode (..), withBinaryFile)
saveDocument :: Connection -> Int -> FilePath -> IO ()
saveDocument conn docId path =
query1With (singleField (streamField toFile)) conn
("SELECT payload FROM dbo.documents WHERE id = " <> param docId)
where
toFile value = withBinaryFile path WriteMode $ \h ->
S.mapM_ (B.hPut h) (valueChunks value)streamTextField does the same for character data, with the chunks decoded to
Text. Either one composes with other columns like any field decoder:
(,) <$> singleField (fieldDecoder @Int) <*> singleField (streamField toFile)
Your function runs once per row, on the thread reading the rows, and these rules come with it:
- A value of at most
streamWholeLimit(64 KiB) is read whole first, and your function gets it as one chunk; only a larger one arrives in pieces. Given a value whole, your function may run before rows read ahead of it reach you. - Bytes it doesn't read are skipped, so it can stop early.
- An exception it throws ends the query: the statement is canceled, and the
exception reaches the caller as it was thrown, with the connection still
usable. To report a bad value as a
DecodeErrorinstead, return anEither Stringand wrap the decoder in.refineFieldid - The stream is valid only until the function returns.
- A NULL is a
DecodeErrorunless the decoder is wrapped innullableField. - It must not make a request on its own connection. On a MARS connection it may make requests beside the stream.
- The connection is locked while it runs, and the statement may be holding locks on the server, so slow work there holds up other work.
When one column's value decides what to do with a later one, such as a name for the file, use cells instead (Hmssql.Query). Whole rows can be streamed with Hmssql.Query, and large parameters can be sent as they are read with Hmssql.Encoding.
data ValueStream Source #
A non-NULL value's bytes as they arrive, with its column and its total length if the server sent it. A chunk is safe to keep. The stream is valid only inside its consumer, and bytes the consumer doesn't read are skipped.
Constructors
| ValueStream | |
Fields
| |
streamField :: (ValueStream -> IO a) -> FieldDecoder a Source #
A decoder that gives any non-NULL value to a consumer as it arrives, so a large
value needn't be in memory. A NULL is a DecodeError unless the decoder is
wrapped in nullableField. The consumer runs on the thread reading the rows.
Reading this stream twice or after the consumer returns throws
ResultAbandoned. On a plain connection, the consumer
must not use the connection; on MARS it may use other responses.
streamTextField :: (ColumnInfo -> Stream (Of Text) IO () -> IO a) -> FieldDecoder a Source #
streamField for character and XML columns, as Text chunks. A column whose
encoding can't be decoded is rejected before any row is read.
Row decoders
class FromSqlRow a where Source #
Types that decode from a whole row, one column per field in order.
Tuples of up to twelve fields and Only have instances. Derive one for
a record whose fields are FromSqlField types, one field per column of
the SELECT, in order:
data Customer = Customer { customerId :: Int, name :: Text, birthDate :: Maybe Day }
deriving stock (Show, Generic)
deriving anyclass (FromSqlRow)The column types are checked against the fields before the first row is
decoded, and a mismatch is a DecodeError naming the column. For a row
decoder written by hand, see singleField.
Minimal complete definition
Nothing
Methods
rowDecoder :: RowDecoder a Source #
default rowDecoder :: (Generic a, GRowDecoder (Rep a)) => RowDecoder a Source #
Instances
data RowDecoder a Source #
A decoder of a whole row, with a fixed column count, checked against a result
set's columns before its first row. Compose it with its Applicative instance.
It has no Monad instance; use RowDecoderMonadic when later columns depend on
earlier values.
Constructors
| RowDecoder | |
Fields
| |
Instances
| Applicative RowDecoder Source # | |
Defined in Hmssql.Decoding Methods pure :: a -> RowDecoder a # (<*>) :: RowDecoder (a -> b) -> RowDecoder a -> RowDecoder b # liftA2 :: (a -> b -> c) -> RowDecoder a -> RowDecoder b -> RowDecoder c # (*>) :: RowDecoder a -> RowDecoder b -> RowDecoder b # (<*) :: RowDecoder a -> RowDecoder b -> RowDecoder a # | |
| Functor RowDecoder Source # | |
Defined in Hmssql.Decoding Methods fmap :: (a -> b) -> RowDecoder a -> RowDecoder b # (<$) :: a -> RowDecoder b -> RowDecoder a # | |
singleField :: FieldDecoder a -> RowDecoder a Source #
A row decoder of one column, with this field decoder. For a type's own decoder,
fieldRowDecoder is the same.
inlinedField :: FieldDecoder a -> RowDecoder a Source #
singleField, compiled as a built-in type's fieldRowDecoder is. The
reading of a value that lies in the packet behind a fixed width or a one- or
two-byte length is copied around the decoder's conversion, which the copy then
calls as a known function on the value's place in the buffer, rather than as an
unknown function on a ByteString made for it. The result decodes exactly
what singleField decodes: the column's type is checked once per result set, a
NULL is a DecodeError unless the decoder allows it, and a value cut by a
packet, like every value of a max, legacy LOB or sql_variant column, is read
through singleField's own path.
It pays for a decoder of your own whose conversion is small and runs for many
rows, such as one made by fieldDecoderFor. A large conversion, such as a
date's, is cheaper left a call through singleField, as the built-in date and
time decoders leave theirs, because inlined it allocates more than the call
saves.
Each use is a copy of the cell's reading, a few kilobytes of object code where
singleField is one call, so bind the result once at the top level with
NOINLINE, which keeps GHC from copying the binding into its uses. The
conversion inlines into the copy when its definition is visible there, which an
INLINE definition or one in the same module is.
idField :: RowDecoder Builder
idField = inlinedField (fieldDecoderFor "id" isIdType (\_ _ bytes -> Right (idDigits bytes)))
{-# NOINLINE idField #-}inlinedNullableField :: FieldDecoder a -> RowDecoder (Maybe a) Source #
inlinedField of a nullable column, with the NULL and the Just handled in
the copy as well. Use it rather than , which
gains nothing over inlinedField . nullableFieldsingleField, because nullableField is out of line and the
copy can't see the conversion through it. singleField needs no such
counterpart, since its conversion is a call either way, so
is its nullable form.singleField . nullableField
byColumn :: (ColumnInfo -> RowDecoder a) -> RowDecoder a Source #
A row decoder of one column, chosen by the function from the column's
ColumnInfo when a result set's columns arrive, before its first row; the
decoder chosen reads every value of the column, and must read one column. This
is for a program that builds its queries and their decoders at runtime and
decodes a column by what the server says it is, such as a long column through a
FoldingCell and a short one through its type's fieldRowDecoder. The choice
costs nothing per row, where RowDecoderMonadic makes it for every row and
reads each column through singleField.
-- a long text a piece at a time, any other whole textOrFold :: RowDecoder Summary textOrFold = byColumn $ \col -> if columnIsLong col then singleField folded else summarize <$> fieldRowDecoder @Text
genericFromSqlRow :: (Generic a, GRowDecoder (Rep a)) => RowDecoder a Source #
The decoder a derived FromSqlRow instance uses: one column per field of a
record or product constructor, in order.
Monadic row decoders
newtype RowDecoderMonadic a Source #
A row decoder that runs per row, so later columns can depend on earlier values
or on the columns themselves (remainingColumns). Each row must read all the
columns. Its checks run per row, so an empty result set isn't checked at all.
Constructors
| RowDecoderMonadic | |
Fields
| |
Instances
| Applicative RowDecoderMonadic Source # | |
Defined in Hmssql.Decoding Methods pure :: a -> RowDecoderMonadic a # (<*>) :: RowDecoderMonadic (a -> b) -> RowDecoderMonadic a -> RowDecoderMonadic b # liftA2 :: (a -> b -> c) -> RowDecoderMonadic a -> RowDecoderMonadic b -> RowDecoderMonadic c # (*>) :: RowDecoderMonadic a -> RowDecoderMonadic b -> RowDecoderMonadic b # (<*) :: RowDecoderMonadic a -> RowDecoderMonadic b -> RowDecoderMonadic a # | |
| Functor RowDecoderMonadic Source # | |
Defined in Hmssql.Decoding Methods fmap :: (a -> b) -> RowDecoderMonadic a -> RowDecoderMonadic b # (<$) :: a -> RowDecoderMonadic b -> RowDecoderMonadic a # | |
| Monad RowDecoderMonadic Source # | |
Defined in Hmssql.Decoding Methods (>>=) :: RowDecoderMonadic a -> (a -> RowDecoderMonadic b) -> RowDecoderMonadic b # (>>) :: RowDecoderMonadic a -> RowDecoderMonadic b -> RowDecoderMonadic b # return :: a -> RowDecoderMonadic a # | |
singleFieldM :: FieldDecoder a -> RowDecoderMonadic a Source #
Decode the next column with this field decoder.
remainingColumns :: RowDecoderMonadic [ColumnInfo] Source #
The columns not yet read.
toMonadicRowDecoder :: RowDecoder a -> RowDecoderMonadic a Source #
Read the next columns with a RowDecoder.
sqlRowDecoder :: RowDecoderMonadic SqlRow Source #
Decode the remaining columns as SqlValues, keeping their ColumnInfos.
Values known only at runtime
sqlValueDecoder :: FieldDecoder SqlValue Source #
Any column as a SqlValue (the FromSqlField instance), with the
reader chosen by the column's type.
sqlRowDecoderFor :: [ColumnInfo] -> RowDecoder SqlRow Source #
A decoder of every column as a SqlValue. A column with no such conversion
fails on the first row rather than at the check, so an empty result set still
gives its columns.
Running decoders
What a field decoder compiles to, and how a row decoder is run: for code that reads a response itself (the low-level part of Hmssql.Query) or decodes values it already holds. Nothing here is needed to use the query functions.
compileField :: FieldDecoder a -> TdsContext -> ColumnInfo -> Either String (CellDecoder a) Source #
Run a FieldDecoder against a column: the CellDecoder for its values, or
why it can't decode them.
data CellDecoder a Source #
How to decode a column's values, once the column has been checked. There
are two ways, because nearly every value is small and a few are too large to
hold in memory; the ordinary way is much the cheaper, since it runs no IO
and needs no stream or exception handler per value.
Constructors
| CellDecoder (Either String a) (ValueReader a) (ValueReader a) | The ordinary way: the whole value is read, and then converted by a pure function. The fields are the result for a NULL, which stays lazy until a NULL is decoded, and two conversions. The first takes bytes borrowed from a receive buffer, and the second takes bytes in a buffer of their own. Nothing ever overwrites borrowed bytes, so a conversion that keeps them still gets correct data. The danger is memory: they hold the whole receive buffer of up to 32 KiB, so keeping small values from many rows could keep far more memory than those values need. So the first conversion should copy any bytes it keeps. The second can keep them as they are, and a conversion that keeps none, such as an integer's, can use the same function for both. |
| FoldingCell (Either String a) (ValueReader a) (ValueReader a) (Stream (Of ByteString) Parser () -> Parser (Either ReadFailure a)) | A value of any length, converted with no consumer: one of at most
|
| StreamingCell (StreamedValue -> Parser (Either ReadFailure a)) | The way to read a value as it arrives.
A failure is returned because a thrown one would end the recipe, and with it the
reading of the row. So a recipe throws, as |
Instances
| Functor CellDecoder Source # | |
Defined in Hmssql.Decoding Methods fmap :: (a -> b) -> CellDecoder a -> CellDecoder b # (<$) :: a -> CellDecoder b -> CellDecoder a # | |
data StreamedValue Source #
A value as a StreamingCell receives it: NULL, the whole value, or its total
length if known and a stream of its bytes. A value of at most streamWholeLimit
bytes is read whole first, since giving it to the consumer as it arrives costs
several times what reading it does; a larger one is streamed.
Constructors
| StreamedNull | |
| StreamedWhole !ValueBytes | The value, read whole. As for |
| StreamedBytes !(Maybe Int64) (Stream (Of ByteString) Parser ()) |
streamWholeLimit :: Int Source #
The largest value a StreamingCell is given whole, 64 KiB. Up to this size a
value read whole and handed over costs about what fieldDecoder does, where
streaming it cost seven times as much for a value of 100 bytes; past it, the
stream's cost per value is small beside the value's own bytes, and what is held
at once stays bounded.
data ReadFailure Source #
Why a value or a row was read past without a result.
Constructors
| DecodeFailed !DecodeError | The value was read but can't be decoded, or the row doesn't have the columns its decoder expects. |
| ReadDiscarded | The response is being thrown away, so the value wasn't assembled and its consumer didn't run. Nothing reads this result. |
Instances
| Show ReadFailure Source # | |
Defined in Hmssql.Tds.Parser Methods showsPrec :: Int -> ReadFailure -> ShowS # show :: ReadFailure -> String # showList :: [ReadFailure] -> ShowS # | |
decodeCell :: CellDecoder a -> RawBytes -> Either String a Source #
Decode a value already in memory (Nothing is NULL). A StreamingCell gives
a Left, because it reads its value as the value arrives.
fieldDecoderCompiled :: (TdsContext -> ColumnInfo -> Either String (CellDecoder a)) -> FieldDecoder a Source #
A decoder from a function that checks the column and chooses how to decode
its values. For a decoder fieldDecoderFor can't express.
readCellRecipe :: ColumnInfo -> CellDecoder a -> Int -> Cell -> Parser a Source #
Read and decode one value, given the column, its decoder, the column's
connectValueAllocAhead and the value's header. A value that
doesn't decode fails the recipe (failValue) once its bytes have been read.
compileRowDecoder :: RowDecoder a -> TdsContext -> [ColumnInfo] -> Either DecodeError (CompiledRow a) Source #
Check a row decoder against a result set's columns, before any row is read. A
wrong column count or an incompatible type is a DecodeError.
compileRowDecoderFor :: RowDecoder a -> TdsContext -> ResultSetInfo -> Either DecodeError (CompiledRow a) Source #
compileRowDecoder for columns already prepared with compileResultSet.
compileMonadicRowDecoder :: RowDecoderMonadic a -> TdsContext -> [ColumnInfo] -> CompiledRow a Source #
Prepare a RowDecoderMonadic to read rows. A failed conversion or a wrong
column count is reported once the rest of the row has been read past, so the
following rows can still be read.
skipRowDecoder :: [ColumnInfo] -> CompiledRow () Source #
Read past each row of a result set without decoding it or keeping its values in memory.
data CompiledRow a Source #
A row decoder compiled against a result set's columns.
Constructors
| CompiledRow | |
Fields
| |
A value built once and then used many times, such as a row's recipe, which is prepared once for a result set and then read for each row.
Constructors
| Prepared a |
rowRecipe :: CompiledRow a -> Int -> Parser (Either ReadFailure a) Source #
The row's recipe, given the bound on allocating ahead of a value's bytes. A
Left is a failure of this row alone, with the row fully read: the driver stops the
batch there and a DecodeFailed is thrown once the response is drained. An
exception thrown by the recipe means the bytes can't be read, and it breaks the
connection. Prepare it once per result set and use it for each row.
decodeColumnChunks :: forall (m :: Type -> Type) r. Monad m => TypeInfo -> Either String (Stream (Of ByteString) m r -> Stream (Of Text) m r) Source #
Decode a stream of a character column's bytes to Text chunks, or why the
column's encoding can't be decoded.
decodeColumnText :: TypeInfo -> ByteString -> Either String Text Source #
Decode a character value; see columnTextDecoder.