hmssql
Safe HaskellNone
LanguageHaskell2010

Hmssql.Encoding

Description

Turning Haskell values into parameters.

ToSqlField encodes one value as one parameter, with a fixed wire type per Haskell type (Hmssql.Types has the table); ToSqlRow encodes a value as a list of parameters: a tuple, a record with a derived instance, a list, Only, or NamedParams. A new ToSqlField instance is usually contramap over an existing encoder; one that must choose the wire type itself builds a FieldEncoder, or the caller sends a RawParam. A large value can be read as it is sent, rather than held in memory; see Hmssql.Encoding.

Synopsis

Field encoders

data FieldEncoder a Source #

How a value of a type becomes one parameter: its wire type and bytes, or why it can't be sent (what the query functions throw as EncodeError). The TdsContext is the connection's, for an encoding that depends on the negotiated protocol.

Constructors

FieldEncoder 

Fields

Instances

Instances details
Contravariant FieldEncoder Source #

An encoder of b from an encoder of a and a conversion: how most ToSqlField instances for a newtype or an enumeration are written.

import Data.Functor.Contravariant (contramap)

instance ToSqlField Status where
  fieldEncoder = contramap statusText fieldEncoder
Instance details

Defined in Hmssql.Encoding

Methods

contramap :: (a' -> a) -> FieldEncoder a -> FieldEncoder a' #

(>$) :: b -> FieldEncoder b -> FieldEncoder a #

class ToSqlField a where Source #

Types that can be sent as one parameter, through param or as a field of a ToSqlRow value. The usual Haskell types have instances, and Hmssql.Types has the table of which SQL type each is sent as. An instance of your own defines fieldEncoder, usually with contramap over a built-in type's encoder.

Instances

Instances details
ToSqlField Decimal Source #

decimal(p,s) with the value's own scale s and just enough precision p for its digits (so 0.001 is decimal(3,3), 1000000 decimal(7,0)). Values needing more than 38 digits, or a scale above 38, are rejected.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Decimal Source #

fieldParam :: Decimal -> FieldParams -> FieldParams

ToSqlField ByteString Source #

Any length of bytes. A value of up to 8000 bytes is declared varbinary(8000), and a longer one or a NULL (Nothing) is declared varbinary(max). Hmssql.Types says when the difference matters.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder ByteString Source #

fieldParam :: ByteString -> FieldParams -> FieldParams

ToSqlField ByteString Source #

Declared as a strict ByteString is, by the same rule. A value too long for varbinary(8000) is forced as it is sent, so it needn't all be in memory. A shorter one is forced before sending. Choosing the declaration forces the value as far as its 8001st byte or its end.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder ByteString Source #

fieldParam :: ByteString -> FieldParams -> FieldParams

ToSqlField Int16 Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Int16 Source #

fieldParam :: Int16 -> FieldParams -> FieldParams

ToSqlField Int32 Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Int32 Source #

fieldParam :: Int32 -> FieldParams -> FieldParams

ToSqlField Int64 Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Int64 Source #

fieldParam :: Int64 -> FieldParams -> FieldParams

ToSqlField Int8 Source #

smallint: tinyint is unsigned, so it can't hold negative values.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Int8 Source #

fieldParam :: Int8 -> FieldParams -> FieldParams

ToSqlField Word8 Source #

tinyint.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Word8 Source #

fieldParam :: Word8 -> FieldParams -> FieldParams

ToSqlField RawParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder RawParam Source #

fieldParam :: RawParam -> FieldParams -> FieldParams

ToSqlField SqlParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder SqlParam Source #

fieldParam :: SqlParam -> FieldParams -> FieldParams

ToSqlField StreamParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder StreamParam Source #

fieldParam :: StreamParam -> FieldParams -> FieldParams

ToSqlField SqlValue Source #

Each constructor is sent as the wire type FromSqlField SqlValue decodes it from, so SELECT @p1 returns the same SqlValue: SqlXml as xml (UTF-16 with a byte order mark, no schema collection), SqlText as nvarchar, and so on. SqlNull is an nvarchar(max) NULL, which the server won't convert to sql_variant (error 206).

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder SqlValue Source #

fieldParam :: SqlValue -> FieldParams -> FieldParams

ToSqlField DateTimeOffset Source #

datetimeoffset(7), rounded like LocalTime. The offset must be within ±14:00 and both the UTC and the local time within years 1 through 9999.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder DateTimeOffset Source #

fieldParam :: DateTimeOffset -> FieldParams -> FieldParams

ToSqlField Money Source #

money; values outside its range (±922,337,203,685,477.58) are rejected.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Money Source #

fieldParam :: Money -> FieldParams -> FieldParams

ToSqlField Text Source #

Any length of string. A value of up to 4000 UTF-16 code units is declared nvarchar(4000), and a longer one or a NULL (Nothing) is declared nvarchar(max). Hmssql.Types says when the difference matters.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Text Source #

fieldParam :: Text -> FieldParams -> FieldParams

ToSqlField Text Source #

Declared as Text is, by the same rule. A value too long for nvarchar(4000) is forced as it is sent, so it needn't all be in memory. A shorter one is forced before sending. Choosing the declaration forces the value as far as its 4001st code unit or its end.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Text Source #

fieldParam :: Text -> FieldParams -> FieldParams

ToSqlField Day Source #

date, years 1 through 9999.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Day Source #

fieldParam :: Day -> FieldParams -> FieldParams

ToSqlField UTCTime Source #

datetime2(7) holding the UTC clock time (zoneless).

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder UTCTime Source #

fieldParam :: UTCTime -> FieldParams -> FieldParams

ToSqlField LocalTime Source #

datetime2(7), rounded to 100 ns (carrying into the next day; a leap second counts as the following second). Years 1 through 9999.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder LocalTime Source #

fieldParam :: LocalTime -> FieldParams -> FieldParams

ToSqlField TimeOfDay Source #

time(7), rounded to 100 ns but never past 23:59:59.9999999 (there is no next day to carry into; a leap second also becomes 23:59:59.9999999).

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder TimeOfDay Source #

fieldParam :: TimeOfDay -> FieldParams -> FieldParams

ToSqlField ZonedTime Source #

As DateTimeOffset. The offset is checked before it is narrowed to the wire's 16-bit minute count.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder ZonedTime Source #

fieldParam :: ZonedTime -> FieldParams -> FieldParams

ToSqlField UUID Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder UUID Source #

fieldParam :: UUID -> FieldParams -> FieldParams

ToSqlField Integer Source #

bigint, or decimal(38,0) beyond its range; values of 39 or more digits are rejected.

Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Integer Source #

fieldParam :: Integer -> FieldParams -> FieldParams

ToSqlField String Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder String Source #

fieldParam :: String -> FieldParams -> FieldParams

ToSqlField Bool Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Bool Source #

fieldParam :: Bool -> FieldParams -> FieldParams

ToSqlField Double Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Double Source #

fieldParam :: Double -> FieldParams -> FieldParams

ToSqlField Float Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Float Source #

fieldParam :: Float -> FieldParams -> FieldParams

ToSqlField Int Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder Int Source #

fieldParam :: Int -> FieldParams -> FieldParams

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

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder (Maybe a) Source #

fieldParam :: Maybe a -> FieldParams -> FieldParams

newtype RawParam Source #

Send a value with an explicit wire type.

Constructors

RawParam (TypeInfo, RawBytes) 

Instances

Instances details
ToSqlField RawParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder RawParam Source #

fieldParam :: RawParam -> FieldParams -> FieldParams

data SqlParam Source #

Any value with a ToSqlField instance, so a parameter list can mix types.

Constructors

ToSqlField a => SqlParam a 

Instances

Instances details
ToSqlField SqlParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder SqlParam Source #

fieldParam :: SqlParam -> FieldParams -> FieldParams

type EncodedParam = (TypeInfo, ParamValue) Source #

A parameter's wire type and value.

Sending a large value

A parameter is normally encoded into memory before it is sent. binaryStream and textStream instead read the value as it is sent, so a value of any size can come from a file or a socket without being held in memory:

import qualified Data.ByteString as B
import Control.Monad (unless)
import Streaming (lift)
import qualified Streaming.Prelude as S
import System.IO (IOMode (..), withBinaryFile)

fileSource :: FilePath -> Source B.ByteString
fileSource path = Source $ \use -> withBinaryFile path ReadMode (use . chunks)
  where
    chunks h = do
      chunk <- lift (B.hGetSome h 65536)
      unless (B.null chunk) (S.yield chunk >> chunks h)

insertDocument :: Connection -> FilePath -> IO Int64
insertDocument conn path = execute conn $
  "INSERT INTO dbo.documents (payload) VALUES (" <> param (binaryStream (fileSource path)) <> ")"

A Source opens what it needs when sending starts and closes it when sending ends, so a Query built on this one can be sent again. sourceOf wraps a stream you already have, which can be sent only once. These rules come with it:

  • A source must not make a request on its own connection, which throws ReentrantSource. On a MARS connection it may read a stream that was opened earlier.
  • If a source throws, the request is abandoned, the exception reaches the caller, and the connection stays usable.
  • An output parameter can't be streamed.

A lazy ByteString or Text parameter is read as it is sent too, without any of this. To stream a large value in a result instead, see Hmssql.Decoding.

data ParamValue Source #

An in-memory value (Nothing is NULL), lazy bytes read as they are sent, or a source run as it is sent. A request holding either of the last two can be sent only once; a query builds a new request each time it is sent.

The constructors are public so that libraries can inspect and build values.

Instances

Instances details
Show ParamValue Source # 
Instance details

Defined in Hmssql.Tds.Parameter

newtype Source a Source #

Where a streamed parameter's bytes come from. It is a function of what consumes the stream, so a resource can be scoped to exactly the send, for example with withBinaryFile. A bare stream couldn't scope one, and a second send would run it from wherever it had got to.

Constructors

Source (forall r. (Stream (Of a) IO () -> IO r) -> IO r) 

sourceOf :: Stream (Of a) IO () -> Source a Source #

Use an existing stream. Holding on to the stream yourself keeps whatever it has produced alive, and a stream can be sent only once.

sourceIO :: IO (Stream (Of a) IO ()) -> Source a Source #

A stream made by this action when sending starts. For a resource to release after the send, use the Source constructor.

data StreamParam Source #

A parameter read as it is sent, so a large value needn't be in memory. Its type is a (max) type or xml; any other is an EncodeError before anything is sent.

Instances

Instances details
ToSqlField StreamParam Source # 
Instance details

Defined in Hmssql.Encoding

Methods

fieldEncoder :: FieldEncoder StreamParam Source #

fieldParam :: StreamParam -> FieldParams -> FieldParams

binaryStream :: Source ByteString -> StreamParam Source #

A varbinary(max) parameter, read from the source as it is sent.

textStream :: Source Text -> StreamParam Source #

An nvarchar(max) parameter, read from the source as it is sent.

streamAs :: TypeInfo -> Source ByteString -> StreamParam Source #

A parameter of the given (max) or xml type, read from the source as it is sent. The bytes are the caller's to encode for that type. For UTF-16, their total must be an even number of bytes.

lazyAs :: TypeInfo -> ByteString -> StreamParam Source #

streamAs for lazy bytes, forced as they are sent.

Row encoders

newtype RowEncoder a Source #

Produces each parameter with its name ("" means positional, @pN by position among the positional ones; otherwise the name with its leading @) and its encoding for a connection. The parameters, their number and their names never depend on the connection; only the encoded values may.

Constructors

RowEncoder 

Instances

Instances details
Contravariant RowEncoder Source #

An encoder of b from an encoder of a and a conversion.

Instance details

Defined in Hmssql.Encoding

Methods

contramap :: (a' -> a) -> RowEncoder a -> RowEncoder a' #

(>$) :: b -> RowEncoder b -> RowEncoder a #

class ToSqlRow a where Source #

Types that can be sent as a list of parameters: the @p1, @p2, ... of mkQuery, or a row of executeMany or bulkInsert. Derive it for a record whose fields are ToSqlField types, one parameter per field in order:

data NewCustomer = NewCustomer { newName :: Text, newBirthDate :: Maybe Day }
  deriving stock (Generic)
  deriving anyclass (ToSqlRow)

Tuples of up to ten fields, (), Only, a list of one ToSqlField type, a list of SqlParam to mix types, and NamedParams have instances.

Minimal complete definition

Nothing

Methods

rowEncoder :: RowEncoder a Source #

default rowEncoder :: (Generic a, GRowEncoder (Rep a)) => RowEncoder a Source #

Instances

Instances details
ToSqlRow NamedParams Source # 
Instance details

Defined in Hmssql.Encoding

ToSqlRow () Source # 
Instance details

Defined in Hmssql.Encoding

ToSqlField a => ToSqlRow (Only a) Source # 
Instance details

Defined in Hmssql.Encoding

ToSqlField a => ToSqlRow [a] Source #

Any number of parameters of the same type.

Instance details

Defined in Hmssql.Encoding

(ToSqlField a, ToSqlField b) => ToSqlRow (a, b) Source # 
Instance details

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b) Source #

(ToSqlField a, ToSqlField b, ToSqlField c) => ToSqlRow (a, b, c) Source # 
Instance details

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c) Source #

(ToSqlField a, ToSqlField b, ToSqlField c, ToSqlField d) => ToSqlRow (a, b, c, d) Source # 
Instance details

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d) Source #

(ToSqlField a, ToSqlField b, ToSqlField c, ToSqlField d, ToSqlField e) => ToSqlRow (a, b, c, d, e) Source # 
Instance details

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d, e) Source #

(ToSqlField a, ToSqlField b, ToSqlField c, ToSqlField d, ToSqlField e, ToSqlField f) => ToSqlRow (a, b, c, d, e, f) Source # 
Instance details

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d, e, f) Source #

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

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d, e, f, g) Source #

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

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d, e, f, g, h) Source #

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

Defined in Hmssql.Encoding

Methods

rowEncoder :: RowEncoder (a, b, c, d, e, f, g, h, i) Source #

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

Defined in Hmssql.Encoding

Methods

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

divideRow :: (a -> (b, c)) -> RowEncoder b -> RowEncoder c -> RowEncoder a Source #

Combine two encoders over a product: the parameters of the first, then those of the second. For a ToSqlRow written by hand, e.g. a record with a field that is sent as two parameters.

singleParam :: FieldEncoder a -> RowEncoder a Source #

A row of one positional parameter.

genericToSqlRow :: (Generic a, GRowEncoder (Rep a)) => RowEncoder a Source #

The encoder a derived ToSqlRow instance uses: one positional parameter per field of a record (or product constructor), in order.

Named parameters

type NamedParam = (Text, SqlParam) Source #

A parameter's name and its value.

newtype NamedParams Source #

Parameters that the SQL text refers to by name. The leading @ is optional. A name must be a T-SQL variable name, and not of the form @pN, which is reserved for the positional parameters; the query functions check both before sending anything. Bulk copy matches the names, with that one @ removed, against the table's columns (Hmssql.BulkCopy).

mkQuery "UPDATE dbo.customer SET email = @email WHERE id = @id"
  (NamedParams [("email", SqlParam newEmail), ("id", SqlParam customerId)])

Constructors

NamedParams [NamedParam] 

Instances

Instances details
ToSqlRow NamedParams Source # 
Instance details

Defined in Hmssql.Encoding