hmssql
Safe HaskellNone
LanguageHaskell2010

Hmssql.Query.Internal

Description

What Hmssql.Query and Hmssql.BatchQuery share beyond the types of Hmssql.Query.Types: encoding a query into its sp_executesql call, reading a response step by step, decoding result sets from a reader, and packing statements into messages. Applications use Hmssql.Query, which re-exports readStep, drainResponse and paramNameProblem; nothing here is stable.

Synopsis

Documentation

executeSqlRpc :: Text -> [(Text, EncodedParam)] -> RpcRequest Source #

The sp_executesql call of a statement with its parameters named.

textParam :: Text -> RpcParam Source #

An nvarchar(max) parameter of sp_executesql's own: the statement, or the declaration.

declarationParam :: [(Text, EncodedParam)] -> RpcParam Source #

@params: the parameters' names and types, @p1 int,@p2 nvarchar(4000), written as UTF-16LE straight into one buffer.

encodeQuery :: TdsContext -> Query -> Either String (Text, [(Text, EncodedParam)]) Source #

A query's text and its parameters, encoded, checked and named, or why it can't be sent.

encodeQueryParams :: TdsContext -> [QueryParam] -> Either String [(Text, EncodedParam)] Source #

Parameters encoded, checked and named: the positional ones @pN by their position among the positional ones.

positionalName :: Int -> Text Source #

@p and the number.

reservedNameProblem :: Text -> Maybe String Source #

A named parameter can't be called @pN: that is how the positional parameters are named.

paramNameProblem :: Text -> Maybe String Source #

Why a parameter name can't be used, if it can't: it must be a T-SQL variable name, @ followed by one or more letters, combining marks, decimal digits, @, #, $ or _ (Unicode ones included, but only from the Basic Multilingual Plane: SQL Server rejects surrogates in identifiers). The query functions' parameter names are spliced into the sp_executesql declaration, where anything else (a space, a comma, a parenthesis, a comment) would change what is declared. callProcedure doesn't use this check: its parameter names are sent as data, and the server reports the ones it can't match. SQL Server itself accepts a few more names (a bare @, and ignores format characters such as U+200D and trailing spaces in RPC parameter names), and rejects some letters added in recent Unicode versions; it enforces its 128-character limit itself.

readStep :: ResponseReader -> RowRead r -> IO (ResponseStep r) Source #

The next thing in the response (nextStep), with the rows decoded by the given function: a batch of rows, a token other than a row or a nonfatal ERROR (withheld), or StepEnd, which throws the withheld error (or SevereError) instead if there was one. A response that ends with the connection closed after an error (by the server, as after a severity 20 error, or by a failed transport) throws that error; after close, the ConnectionClosed that every use of the connection gets. (The withheld error is the reader's, not a stream's: a stream run again from a step before the error would otherwise report an error it hadn't seen.)

A fatal ERROR is thrown by the common token reader immediately and leaves the connection broken.

Reading one statement of a batch query (respBatch): the statement's DONEPROC is returned and then StepEnd, for as long as the statement stays the current one; an ERROR token isn't withheld but ends the batch (scopeFail), and the response's end throws SevereError for a failed DONE but never a withheld error, which there is none of.

ignoreRows :: RowRead () Source #

Skip the rows without decoding them. A row before the first COLMETADATA is a ProtocolError, since there are no columns to skip it by, so the result-set loops need no state for that case.

drainResponse :: ResponseReader -> IO () Source #

Read the rest of the response and discard it. The withheld error, if any, is thrown at the end. Nothing to do once the end has been read.

yieldRows :: ResponseReader -> [r] -> Stream (Of r) IO () -> Stream (Of r) IO () Source #

Yield the rows of a batch, then continue. Before each row, the response must still be the current one (checkCurrent): the rows of a batch are read from the connection together, but a stream whose response was superseded by a later request (or canceled, or drained) throws ResultAbandoned at the next row it is asked for, not after the rows it already had.

type RowCompiler r = TdsContext -> [ColumnInfo] -> Either DecodeError (CompiledRow r) Source #

How to turn a result set's columns into a per-row decoding function.

compilerForM :: RowDecoderMonadic r -> RowCompiler r Source #

A RowDecoderMonadic runs per row (later fields may depend on earlier values). The completed decoder must consume every column; prefixes may be composed inside it. An empty result set cannot be checked this way.

sqlRowCompiler :: RowCompiler SqlRow Source #

How queryMulti and callProcedure decode: every column as a SqlValue, with the decoders compiled once per result set.

firstResultSet :: RowCompiler r -> ResponseReader -> IO (Maybe (RowRead r)) Source #

Skip to the first result set: its decoder, compiled, or Nothing if the response has none. On a compile failure the rest of the response is drained before the error is thrown.

endsResultSet :: Token -> Bool Source #

Whether a token ends the result set whose rows are being read.

foldFirstResultSet :: RowCompiler r -> ResponseReader -> a -> (a -> r -> IO a) -> IO a Source #

Fold over the rows of the first result set, a batch at a time, then drain the rest of the response. A row that doesn't decode drains the rest of the response before its DecodeError is thrown. Before each call of the step, the response must still be the current one (checkCurrent, as a queryS stream checks before each row): a step that abandons it (by making another request on the connection, or closing it) is the last to run, and the fold throws ResultAbandoned rather than run the steps for the rest of the batch it had.

foldBatches :: RowCompiler r -> ResponseReader -> a -> (a -> [r] -> IO a) -> IO a Source #

foldFirstResultSet a batch (the rows of one packet) at a time.

foldBatchesUsing :: (ResponseReader -> RowRead r -> IO (ResponseStep r)) -> RowCompiler r -> ResponseReader -> a -> (a -> [r] -> IO a) -> IO a Source #

readFirstResultSet :: RowCompiler r -> ResponseReader -> IO [r] Source #

The rows of the first result set, decoded, and the rest of the response drained.

newtype ReversedBatch r Source #

Constructors

ReversedBatch [r] 

streamFirstResultSet :: RowCompiler r -> ResponseReader -> IO () -> Stream (Of r) IO () Source #

Rows of the first result set, decoded, a batch (the rows of one packet) per effect; then the given action, which reads the rest of the response (drainResponse for a query of its own).

type StatementEncoder s a = TdsContext -> s -> a -> Either String (Text, RpcRequest, s) Source #

How to encode each item of a list as a statement: its text and its RPC, or why it can't be sent. The state is what one item passes on to the next.

queryStatement :: StatementEncoder () Query Source #

A Query as its sp_executesql call.

statementsRequest :: [(Text, RpcRequest)] -> IO Request Source #

One message of statements. It is named by its last statement's text (busyStatements): the statement a batchQueryS stream reads, which is what a message left unfinished is.

statementMessageBytes :: Int Source #

About how many bytes of statements runStatements sends per message.

takeStatements :: StatementEncoder s a -> TdsContext -> s -> Int -> [a] -> Either String ([(Text, RpcRequest)], Int, [a], s) Source #

The statements of the next message, encoded (with the text of each), how many there are, the items after them and the encoder's state; or why one can't be sent. i is the position of the first in the caller's list.

joinBatches :: [[r]] -> [r] Source #

The batches, newest first, as one list in order: one list cell per row, and none for the last batch, which is the list's tail as it is.

exactlyOne :: [r] -> IO r Source #

atMostOne :: [r] -> IO (Maybe r) Source #

readResultSets :: RowCompiler r -> ResponseReader -> IO [ResultSet r] Source #

Every result set the reader has left, each decoded by the compiler.

readResultSetsWith :: ResultSets a -> ResponseReader -> IO a Source #

Run the decoder over what the reader has left, then drain the rest.