orville-postgresql-1.1.0.0: src/Orville/PostgreSQL/Execution/Execute.hs
{- |
Copyright : Flipstone Technology Partners 2023
License : MIT
Stability : Stable
Low-level functions for executing 'RawSql.SqlExpression' values directly.
@since 1.0.0.0
-}
module Orville.PostgreSQL.Execution.Execute
( executeAndDecode
, executeAndReturnAffectedRows
, executeVoid
, executeAndDecodeIO
, executeAndReturnAffectedRowsIO
, executeVoidIO
, AffectedRowsDecodingError
)
where
import Control.Exception (Exception, throwIO)
import Control.Monad (void)
import Control.Monad.IO.Class (liftIO)
import qualified Database.PostgreSQL.LibPQ as LibPQ
import Orville.PostgreSQL.Execution.QueryType (QueryType)
import qualified Orville.PostgreSQL.Marshall.SqlMarshaller as SqlMarshaller
import Orville.PostgreSQL.Monad (MonadOrville, askOrvilleState, withConnection)
import Orville.PostgreSQL.OrvilleState (OrvilleState, orvilleErrorDetailLevel, orvilleSqlCommenterAttributes, orvilleSqlExecutionCallback)
import Orville.PostgreSQL.Raw.Connection (Connection)
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Orville.PostgreSQL.Raw.SqlCommenter as SqlCommenter
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue
{- | Executes a SQL query and decodes the result set using the provided
marshaller. Any SQL Execution callbacks that have been added to the
'OrvilleState' will be called.
If the query fails or if any row is unable to be decoded by the marshaller,
an exception will be raised.
@since 1.0.0.0
-}
executeAndDecode ::
(MonadOrville m, RawSql.SqlExpression sql) =>
QueryType ->
sql ->
SqlMarshaller.AnnotatedSqlMarshaller writeEntity readEntity ->
m [readEntity]
executeAndDecode queryType sql marshaller = do
orvilleState <- askOrvilleState
withConnection (liftIO . executeAndDecodeIO queryType sql marshaller orvilleState)
{- | Executes a SQL query and returns the number of rows affected by the query.
Any SQL Execution callbacks that have been added to the 'OrvilleState' will
be called.
This function can only be used for the execution of a SELECT, CREATE
TABLE AS, INSERT, UPDATE, DELETE, MOVE, FETCH, or COPY statement, or an
EXECUTE of a prepared query that contains an INSERT, UPDATE, or DELETE
statement. If the query is anything else, an 'AffectedRowsDecodingError'
wil be raised after the query is executed when the result is read.
If the query fails, an exception will be raised.
@since 1.0.0.0
-}
executeAndReturnAffectedRows ::
(MonadOrville m, RawSql.SqlExpression sql) =>
QueryType ->
sql ->
m Int
executeAndReturnAffectedRows queryType sql = do
orvilleState <- askOrvilleState
withConnection (liftIO . executeAndReturnAffectedRowsIO queryType sql orvilleState)
{- | Executes a SQL query and ignores the result. Any SQL Execution callbacks
that have been added to the 'OrvilleState' will be called.
If the query fails an exception will be raised.
@since 1.0.0.0
-}
executeVoid ::
(MonadOrville m, RawSql.SqlExpression sql) =>
QueryType ->
sql ->
m ()
executeVoid queryType sql = do
orvilleState <- askOrvilleState
withConnection (liftIO . executeVoidIO queryType sql orvilleState)
{- | Executes a SQL query and decodes the result set using the provided
marshaller. Any SQL Execution callbacks that have been added to the
'OrvilleState' will be called.
If the query fails or if any row is unable to be decoded by the marshaller,
an exception will be raised.
@since 1.0.0.0
-}
executeAndDecodeIO ::
RawSql.SqlExpression sql =>
QueryType ->
sql ->
SqlMarshaller.AnnotatedSqlMarshaller writeEntity readEntity ->
OrvilleState ->
Connection ->
IO [readEntity]
executeAndDecodeIO queryType sql marshaller orvilleState conn = do
libPqResult <- executeWithCallbacksIO queryType sql orvilleState conn
let
errorDetailLevel = orvilleErrorDetailLevel orvilleState
decodingResult <-
SqlMarshaller.marshallResultFromSql
errorDetailLevel
marshaller
libPqResult
case decodingResult of
Left err ->
throwIO err
Right entities ->
pure entities
{- | Executes a SQL query and returns the number of rows affected by the query.
Any SQL Execution callbacks that have been added to the 'OrvilleState' will
be called.
This function can only be used for the execution of a SELECT, CREATE
TABLE AS, INSERT, UPDATE, DELETE, MOVE, FETCH, or COPY statement, or an
EXECUTE of a prepared query that contains an INSERT, UPDATE, or DELETE
statement. If the query is anything else, an 'AffectedRowsDecodingError'
wil be raised after the query is executed when the result is read.
If the query fails, an exception will be raised.
@since 1.0.0.0
-}
executeAndReturnAffectedRowsIO ::
RawSql.SqlExpression sql =>
QueryType ->
sql ->
OrvilleState ->
Connection ->
IO Int
executeAndReturnAffectedRowsIO queryType sql orvilleState conn = do
libPqResult <- executeWithCallbacksIO queryType sql orvilleState conn
mbTupleCount <- LibPQ.cmdTuples libPqResult
case mbTupleCount of
Nothing ->
throwIO
. AffectedRowsDecodingError
$ "No affected row count was produced by the query"
Just bs ->
case SqlValue.toInt (SqlValue.fromRawBytes bs) of
Left err ->
throwIO . AffectedRowsDecodingError $ err
Right n ->
pure n
{- | Executes a SQL query and ignores the result. Any SQL Execution callbacks
that have been added to the 'OrvilleState' will be called.
If the query fails, an exception will be raised.
@since 1.0.0.0
-}
executeVoidIO ::
RawSql.SqlExpression sql =>
QueryType ->
sql ->
OrvilleState ->
Connection ->
IO ()
executeVoidIO queryType sql orvilleState =
void . executeWithCallbacksIO queryType sql orvilleState
executeWithCallbacksIO ::
RawSql.SqlExpression sql =>
QueryType ->
sql ->
OrvilleState ->
Connection ->
IO LibPQ.Result
executeWithCallbacksIO queryType sql orvilleState conn =
let
rawSql =
case orvilleSqlCommenterAttributes orvilleState of
Nothing ->
RawSql.toRawSql sql
Just sqlCommenterAttributes ->
SqlCommenter.addSqlCommenterAttributes sqlCommenterAttributes $ RawSql.toRawSql sql
in
orvilleSqlExecutionCallback
orvilleState
queryType
rawSql
(RawSql.execute conn rawSql)
{- | Thrown by 'executeAndReturnAffectedRows' and 'executeAndReturnAffectedRowsIO'
if the number of affected rows cannot be successfully read from the LibPQ
command result.
@since 1.0.0.0
-}
newtype AffectedRowsDecodingError
= AffectedRowsDecodingError String
deriving
( -- | @since 1.0.0.0
Show
)
-- | @since 1.0.0.0
instance Exception AffectedRowsDecodingError