| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Hmssql.BatchQuery
Description
Several statements in one round trip, each with its own result.
A BatchQuery pairs each statement with what to do with its results,
and runBatchQuery sends them all in one request and reads the
results in order:
(updated, Only total, items) <- runBatchQuery conn $ (,,)
<$> batchExecute ("UPDATE item SET price = price * 1.1 WHERE category = " <> param cat)
<*> batchQuery1 ("SELECT SUM(price) FROM item WHERE category = " <> param cat)
<*> batchQuery ("SELECT id, name FROM item WHERE category = " <> param cat)
-- updated :: Int64, total :: Double, items :: [(Int32, Text)]Each statement is its own sp_executesql call, all of them in one RPC
message (as executeStatements sends them): the server compiles and runs
them in order, and the response carries each one's results between its
own DONEPROC markers, so a statement's consumer sees exactly its own
result sets and counts, whatever the statements before it returned. A
statement with no parameters is an sp_executesql call too, not a SQL
batch, so a SET option it sets or a #temp table it creates lasts
only until it returns; statements that must share such state go in one
Query (its text joined, as Query is a Monoid) to
queryResultSets instead. is
executeStatements conn qs with the
counting done in bulk, and runBatchQuery conn (traverse batchExecute qs)executeStatementsOutcomes the way to see which
statements failed rather than the first that did.
The consumers mirror Hmssql.Query: batchExecute for execute,
batchQuery for query (with batchQuery1, batchQueryMay,
batchQueryFold, batchQueryS and the With variants),
batchQueryMulti for queryMulti, batchQueryResultSets for
queryResultSets. A statement's results are read as its consumer asks
for them, and what it leaves unread is skipped before the next
statement's, so a BatchQuery holds no more memory than its results;
batchQueryS streams the last statement's rows after runBatchQuery
returns, as queryS does.
Errors
The server runs the statements in order, and __statements after a
failed one may have run__: an error that only ends its statement (a
constraint violation, say) lets the server go on to the next; an error
that aborts the batch (THROW, a conversion error, a deadlock, any
error under SET XACT_ABORT ON) ends the request there. Outside a
transaction each statement is committed as it completes. The first
error is thrown as SqlServerError with its statement's text, once the
whole response (and the messages after it, for a batch over about 1
MiB, as executeStatements explains) has been read, so the connection stays
usable. For all-or-nothing, run the batch inside
withTransaction and let the exception roll it
back.
A statement that can't be sent (a value that can't be encoded, a
placeholder with no parameter) throws EncodeError, naming its
position, before its message is sent; so does a batchQueryS that
isn't the last statement.
The shape is hpgsql's Hpgsql.Pipeline. It is called a batch because
that is what TDS has: several statements in one request, run in order,
each committed on its own, later ones running after an earlier one's
error.
Only the last statement may stream. batchQueryS gives a stream to read after
runBatchQuery returns, so a stream in the middle would leave the statements
after it as actions you would have to run later, in order, on one thread. With
only the last one streaming, there is no such order to get wrong.
Synopsis
- data BatchQuery a
- runBatchQuery :: Connection -> BatchQuery a -> IO a
- batchExecute :: Query -> BatchQuery Int64
- batchExecute_ :: Query -> BatchQuery ()
- batchQuery :: FromSqlRow r => Query -> BatchQuery [r]
- batchQueryWith :: RowDecoder r -> Query -> BatchQuery [r]
- batchQueryMWith :: RowDecoderMonadic r -> Query -> BatchQuery [r]
- batchQuery1 :: FromSqlRow r => Query -> BatchQuery r
- batchQuery1With :: RowDecoder r -> Query -> BatchQuery r
- batchQueryMay :: FromSqlRow r => Query -> BatchQuery (Maybe r)
- batchQueryMayWith :: RowDecoder r -> Query -> BatchQuery (Maybe r)
- batchQueryFold :: FromSqlRow r => Query -> a -> (a -> r -> IO a) -> BatchQuery a
- batchQueryFoldWith :: RowDecoder r -> Query -> a -> (a -> r -> IO a) -> BatchQuery a
- batchQueryS :: FromSqlRow r => Query -> BatchQuery (Stream (Of r) IO ())
- batchQuerySWith :: RowDecoder r -> Query -> BatchQuery (Stream (Of r) IO ())
- batchQuerySMWith :: RowDecoderMonadic r -> Query -> BatchQuery (Stream (Of r) IO ())
- batchQueryMulti :: Query -> BatchQuery [ResultSet SqlRow]
- batchQueryMultiWith :: RowDecoder r -> Query -> BatchQuery [ResultSet r]
- batchQueryMultiMWith :: RowDecoderMonadic r -> Query -> BatchQuery [ResultSet r]
- batchQueryResultSets :: ResultSets a -> Query -> BatchQuery a
Documentation
data BatchQuery a Source #
Several statements sent in one request, each paired with what to do with its results: see Hmssql.BatchQuery.
An Applicative, not a Monad: every statement is known before the
batch is sent, so a later statement can't depend on an earlier one's
rows (the ResultSets monad of one statement can, within it).
Instances
| Applicative BatchQuery Source # | |
Defined in Hmssql.BatchQuery Methods pure :: a -> BatchQuery a # (<*>) :: BatchQuery (a -> b) -> BatchQuery a -> BatchQuery b # liftA2 :: (a -> b -> c) -> BatchQuery a -> BatchQuery b -> BatchQuery c # (*>) :: BatchQuery a -> BatchQuery b -> BatchQuery b # (<*) :: BatchQuery a -> BatchQuery b -> BatchQuery a # | |
| Functor BatchQuery Source # | |
Defined in Hmssql.BatchQuery Methods fmap :: (a -> b) -> BatchQuery a -> BatchQuery b # (<$) :: a -> BatchQuery b -> BatchQuery a # | |
runBatchQuery :: Connection -> BatchQuery a -> IO a Source #
Run the batch: send the statements, read each one's results as its consumer asks, and read the response to its end. Nothing is sent for a batch of no statements. Statements that don't fit one message go in the next, sent once the previous response has been read; a statement's consumer runs when its results are reached.
Statements
batchExecute :: Query -> BatchQuery Int64 Source #
The statement's rows affected: what execute returns for it.
batchExecute_ :: Query -> BatchQuery () Source #
batchExecute without the count.
Queries
batchQuery :: FromSqlRow r => Query -> BatchQuery [r] Source #
The rows of the statement's first result set: what query returns
for it ([] for a statement with no result set).
batchQueryWith :: RowDecoder r -> Query -> BatchQuery [r] Source #
batchQuery with the given row decoder, as queryWith.
batchQueryMWith :: RowDecoderMonadic r -> Query -> BatchQuery [r] Source #
batchQuery with a RowDecoderMonadic, as queryMWith.
batchQuery1 :: FromSqlRow r => Query -> BatchQuery r Source #
Exactly one row, as query1.
batchQuery1With :: RowDecoder r -> Query -> BatchQuery r Source #
batchQuery1 with the given row decoder, as query1With.
batchQueryMay :: FromSqlRow r => Query -> BatchQuery (Maybe r) Source #
Zero or one row, as queryMay.
batchQueryMayWith :: RowDecoder r -> Query -> BatchQuery (Maybe r) Source #
batchQueryMay with the given row decoder, as queryMayWith.
Folding queries
batchQueryFold :: FromSqlRow r => Query -> a -> (a -> r -> IO a) -> BatchQuery a Source #
Fold over the rows of the statement's first result set as they
arrive, as queryFold: the step runs with the connection lock held,
while the batch is being read, so it can't use the connection.
batchQueryFoldWith :: RowDecoder r -> Query -> a -> (a -> r -> IO a) -> BatchQuery a Source #
batchQueryFold with the given row decoder, as queryFoldWith.
Streaming queries
batchQueryS :: FromSqlRow r => Query -> BatchQuery (Stream (Of r) IO ()) Source #
Stream the rows of the statement's first result set, as queryS:
they are read from the socket as the stream is consumed, after
runBatchQuery has returned. So the statement must be the last of
its batch (runBatchQuery throws EncodeError otherwise, before
sending anything), and its stream must be consumed before the next
request on the connection, as any queryS stream must (on a MARS
connection it stays open beside later requests instead, as a queryS
stream does). An error the statement reports is thrown by the stream;
one an earlier statement reports, by runBatchQuery.
batchQuerySWith :: RowDecoder r -> Query -> BatchQuery (Stream (Of r) IO ()) Source #
batchQueryS with the given row decoder, as querySWith.
batchQuerySMWith :: RowDecoderMonadic r -> Query -> BatchQuery (Stream (Of r) IO ()) Source #
batchQueryS with a RowDecoderMonadic, as querySMWith.
Multiple result sets
batchQueryMulti :: Query -> BatchQuery [ResultSet SqlRow] Source #
Every result set of the statement, as queryMulti.
batchQueryMultiWith :: RowDecoder r -> Query -> BatchQuery [ResultSet r] Source #
batchQueryMulti with the given row decoder, as queryMultiWith.
batchQueryMultiMWith :: RowDecoderMonadic r -> Query -> BatchQuery [ResultSet r] Source #
batchQueryMulti with a RowDecoderMonadic, as queryMultiMWith.
batchQueryResultSets :: ResultSets a -> Query -> BatchQuery a Source #
The statement's result sets decoded one at a time by a ResultSets
action, as queryResultSets: the result sets are the statement's own,
and asking for one past its last throws UnexpectedResult.