hmssql
Safe HaskellNone
LanguageHaskell2010

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

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

fieldDecoder

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 singleField fieldDecoder. Tuples, Only 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

Instances details
FromSqlField Decimal Source #

Accepts decimal, numeric, money, smallmoney and integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField ByteString Source #

Accepts binary types and also the raw bytes of single-byte character types.

Instance details

Defined in Hmssql.Decoding

FromSqlField ByteString Source #

As a strict ByteString, with the whole value read into memory first. To read a large value as it arrives, use streamField.

Instance details

Defined in Hmssql.Decoding

FromSqlField Int16 Source #

bit and the integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField Int32 Source #

bit and the integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField Int64 Source #

bit and the integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField Int8 Source #

bit and the integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField Word8 Source #

bit and the integer types.

Instance details

Defined in Hmssql.Decoding

FromSqlField RawValue Source # 
Instance details

Defined in Hmssql.Decoding

FromSqlField SqlValue Source # 
Instance details

Defined in Hmssql.Decoding

FromSqlField DateTimeOffset Source # 
Instance details

Defined in Hmssql.Decoding

FromSqlField Money Source #

money and smallmoney, with all four decimal places.

Instance details

Defined in Hmssql.Decoding

FromSqlField Text Source #

Accepts Unicode types, and single-byte types in UTF-8 or a supported single-byte code page. The collation is checked when the result set's columns arrive, before any rows are decoded.

Instance details

Defined in Hmssql.Decoding

FromSqlField Text Source #

As Text, with the whole value read into memory first. To read a large value as it arrives, use streamTextField.

Instance details

Defined in Hmssql.Decoding

FromSqlField Day Source #

date.

Instance details

Defined in Hmssql.Decoding

FromSqlField UTCTime Source #

Zoneless types are taken to be UTC; datetimeoffset is converted to UTC.

Instance details

Defined in Hmssql.Decoding

FromSqlField LocalTime Source #

datetime, smalldatetime, datetime2 and date (at midnight).

Instance details

Defined in Hmssql.Decoding

FromSqlField TimeOfDay Source #

time at any scale.

Instance details

Defined in Hmssql.Decoding

FromSqlField ZonedTime Source # 
Instance details

Defined in Hmssql.Decoding

FromSqlField UUID Source #

uniqueidentifier.

Instance details

Defined in Hmssql.Decoding

FromSqlField Integer Source #

Integer types, and decimal / numeric with scale 0 (such as decimal(38,0), which Integer parameters beyond bigint are sent as).

Instance details

Defined in Hmssql.Decoding

FromSqlField String Source # 
Instance details

Defined in Hmssql.Decoding

FromSqlField Bool Source #

bit.

Instance details

Defined in Hmssql.Decoding

FromSqlField Double Source #

real and float.

Instance details

Defined in Hmssql.Decoding

FromSqlField Float Source #

real. A float value that takes eight bytes is a DecodeError, rather than being narrowed.

Instance details

Defined in Hmssql.Decoding

FromSqlField Int Source #

bit and the integer types. A value out of range is a DecodeError, as it is for the other fixed-size integers below.

Instance details

Defined in Hmssql.Decoding

FromSqlField a => FromSqlField (Maybe a) Source # 
Instance details

Defined in Hmssql.Decoding

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.

Instances

Instances details
Functor FieldDecoder Source # 
Instance details

Defined in Hmssql.Decoding

Methods

fmap :: (a -> b) -> FieldDecoder a -> FieldDecoder b #

(<$) :: a -> FieldDecoder b -> FieldDecoder a #

Semigroup (FieldDecoder a) Source # 
Instance details

Defined in Hmssql.Decoding

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.

newtype RawValue Source #

The bytes of any column's value as the server sends them, including the CLR types hmssql doesn't otherwise decode.

Constructors

RawValue ByteString 

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 DecodeError instead, return an Either String and wrap the decoder in refineField id.
  • The stream is valid only until the function returns.
  • A NULL is a DecodeError unless the decoder is wrapped in nullableField.
  • 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.

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

Instances details
FromSqlField a => FromSqlRow (Only a) Source # 
Instance details

Defined in Hmssql.Decoding

(FromSqlField a, FromSqlField b) => FromSqlRow (a, b) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b) Source #

(FromSqlField a, FromSqlField b, FromSqlField c) => FromSqlRow (a, b, c) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d) => FromSqlRow (a, b, c, d) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e) => FromSqlRow (a, b, c, d, e) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f) => FromSqlRow (a, b, c, d, e, f) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g) => FromSqlRow (a, b, c, d, e, f, g) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g, FromSqlField h) => FromSqlRow (a, b, c, d, e, f, g, h) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g, h) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g, FromSqlField h, FromSqlField i) => FromSqlRow (a, b, c, d, e, f, g, h, i) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g, h, i) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g, FromSqlField h, FromSqlField i, FromSqlField j) => FromSqlRow (a, b, c, d, e, f, g, h, i, j) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g, h, i, j) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g, FromSqlField h, FromSqlField i, FromSqlField j, FromSqlField k) => FromSqlRow (a, b, c, d, e, f, g, h, i, j, k) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g, h, i, j, k) Source #

(FromSqlField a, FromSqlField b, FromSqlField c, FromSqlField d, FromSqlField e, FromSqlField f, FromSqlField g, FromSqlField h, FromSqlField i, FromSqlField j, FromSqlField k, FromSqlField l) => FromSqlRow (a, b, c, d, e, f, g, h, i, j, k, l) Source # 
Instance details

Defined in Hmssql.Decoding

Methods

rowDecoder :: RowDecoder (a, b, c, d, e, f, g, h, i, j, k, l) Source #

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 

Instances

Instances details
Applicative RowDecoder Source # 
Instance details

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 # 
Instance details

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 inlinedField . nullableField, which gains nothing over singleField, 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 singleField . nullableField is its nullable form.

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.

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 streamWholeLimit bytes read whole and converted by the first three fields, as CellDecoder's are, and a longer one given as it arrives to the fourth, a recipe that follows StreamingCell's two rules below. Its pieces may be slices of a receive buffer, so it copies what it keeps. This is for a conversion that can work a piece at a time, such as writing text out as JSON, so that a long value is never held whole as it came, while a short one costs what a CellDecoder costs.

StreamingCell (StreamedValue -> Parser (Either ReadFailure a))

The way to read a value as it arrives. streamField and streamTextField build this; see Hmssql.Decoding. Writing one by hand has two rules:

  • The recipe must read its value to the end, even when it fails, because the next column's bytes come straight after it.
  • A value that can't be decoded must be a DecodeFailed. The rest of the row is then read past, the response is drained, and the DecodeError is thrown from the query, with the connection still usable. When consume gives Nothing, the response is being thrown away, and the result is ReadDiscarded.

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 fail does, only when the bytes can't be read at all, and the connection is then broken. A consumer's exception is neither: it ends the query at once, as described at Hmssql.Decoding.

Instances

Instances details
Functor CellDecoder Source # 
Instance details

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 CellDecoder's conversions, Borrowed bytes are a slice of a receive buffer, so a recipe that keeps them copies them (keepBytes).

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

Instances details
Show ReadFailure Source # 
Instance details

Defined in Hmssql.Tds.Parser

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.

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

  • rowPrepare :: Int -> Prepared (Parser a)

    The row's recipe, given the bound on allocating ahead of a value's bytes. A row that doesn't decode fails the recipe (failValue) once the row has been read, and the driver takes the failure (runParserAt); rowRecipe takes it out with the failure caught.

data Prepared a Source #

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.