{-# LANGUAGE GADTs #-}

{- |
Copyright : Flipstone Technology Partners 2023
License   : MIT
Stability : Stable

Functions for working with executable @DELETE@ statements. The 'Delete' type is
a value that can be passed around and executed later. The 'Delete' is directly
associated with the presence of a returning clause and how to decode any rows
returned by that clause. This means it can be safely executed via
'executeDelete' or 'executeDeleteReturnEntities' as appropriate. It is a lower-level
API than the entity delete functions in
"Orville.PostgreSQL.Execution.EntityOperations", but not as primitive as
"Orville.PostgreSQL.Expr.Delete".

@since 1.0.0.0
-}
module Orville.PostgreSQL.Execution.Delete
  ( Delete
  , deleteFromDeleteExpr
  , executeDelete
  , executeDeleteReturnEntities
  , deleteFromTableReturning
  , deleteFromTable
  , rawDeleteExpr
  )
where

import qualified Orville.PostgreSQL.Execution.Execute as Execute
import qualified Orville.PostgreSQL.Execution.QueryType as QueryType
import Orville.PostgreSQL.Execution.ReturningOption (NoReturningClause, ReturningClause, ReturningOption (WithReturning, WithoutReturning))
import qualified Orville.PostgreSQL.Expr as Expr
import Orville.PostgreSQL.Marshall.SqlMarshaller (AnnotatedSqlMarshaller)
import qualified Orville.PostgreSQL.Monad as Monad
import Orville.PostgreSQL.Schema (TableDefinition, mkTableReturningClause, tableMarshaller, tableName)

{- |
Represents a @DELETE@ statement that can be executed against a database. A
'Delete' has a 'Orville.PostgreSQL.SqlMarshaller' bound to it that, when the
delete returns data from the database, will be used to decode the database
result set when it is executed.

@since 1.0.0.0
-}
data Delete readEntity returningClause where
  Delete ::
    Expr.DeleteExpr ->
    Delete readEntity NoReturningClause
  DeleteReturning :: AnnotatedSqlMarshaller writeEntity readEntity -> Expr.DeleteExpr -> Delete readEntity ReturningClause

{- |
  Extracts the query that will be run when the delete is executed. Normally you
  don't want to extract the query and run it yourself, but this function is
  useful to view the query for debugging or query explanation.

@since 1.0.0.0
-}
deleteFromDeleteExpr :: Delete readEntity returningClause -> Expr.DeleteExpr
deleteFromDeleteExpr :: forall readEntity returningClause.
Delete readEntity returningClause -> DeleteExpr
deleteFromDeleteExpr (Delete DeleteExpr
expr) = DeleteExpr
expr
deleteFromDeleteExpr (DeleteReturning AnnotatedSqlMarshaller writeEntity readEntity
_ DeleteExpr
expr) = DeleteExpr
expr

{- |
  Executes the database query for the 'Delete' and returns the number of
  rows affected by the query.

@since 1.0.0.0
-}
executeDelete ::
  Monad.MonadOrville m =>
  Delete readEntity NoReturningClause ->
  m Int
executeDelete :: forall (m :: * -> *) readEntity.
MonadOrville m =>
Delete readEntity NoReturningClause -> m Int
executeDelete (Delete DeleteExpr
expr) =
  QueryType -> DeleteExpr -> m Int
forall (m :: * -> *) sql.
(MonadOrville m, SqlExpression sql) =>
QueryType -> sql -> m Int
Execute.executeAndReturnAffectedRows QueryType
QueryType.DeleteQuery DeleteExpr
expr

{- |
Executes the database query for the 'Delete' and uses its
'Orville.PostgreSQL.SqlMarshaller' to decode the rows (that were just deleted)
as returned via a RETURNING clause.

@since 1.0.0.0
-}
executeDeleteReturnEntities ::
  Monad.MonadOrville m =>
  Delete readEntity ReturningClause ->
  m [readEntity]
executeDeleteReturnEntities :: forall (m :: * -> *) readEntity.
MonadOrville m =>
Delete readEntity ReturningClause -> m [readEntity]
executeDeleteReturnEntities (DeleteReturning AnnotatedSqlMarshaller writeEntity readEntity
marshaller DeleteExpr
expr) =
  QueryType
-> DeleteExpr
-> AnnotatedSqlMarshaller writeEntity readEntity
-> m [readEntity]
forall (m :: * -> *) sql writeEntity readEntity.
(MonadOrville m, SqlExpression sql) =>
QueryType
-> sql
-> AnnotatedSqlMarshaller writeEntity readEntity
-> m [readEntity]
Execute.executeAndDecode QueryType
QueryType.DeleteQuery DeleteExpr
expr AnnotatedSqlMarshaller writeEntity readEntity
marshaller

{- |
  Builds a 'Delete' that will delete all of the writable columns described in the
  'TableDefinition' without returning the data as seen by the database.

@since 1.0.0.0
-}
deleteFromTable ::
  TableDefinition key writeEntity readEntity ->
  Maybe Expr.BooleanExpr ->
  Delete readEntity NoReturningClause
deleteFromTable :: forall key writeEntity readEntity.
TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr -> Delete readEntity NoReturningClause
deleteFromTable =
  ReturningOption NoReturningClause
-> TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr
-> Delete readEntity NoReturningClause
forall returningClause key writeEntity readEntity.
ReturningOption returningClause
-> TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr
-> Delete readEntity returningClause
deleteTable ReturningOption NoReturningClause
WithoutReturning

{- |
  Builds a 'Delete' that will delete all of the writable columns described in the
  'TableDefinition' and return the data as seen by the database. This is useful for getting
  database-managed columns such as auto-incrementing identifiers and sequences.

@since 1.0.0.0
-}
deleteFromTableReturning ::
  TableDefinition key writeEntity readEntity ->
  Maybe Expr.BooleanExpr ->
  Delete readEntity ReturningClause
deleteFromTableReturning :: forall key writeEntity readEntity.
TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr -> Delete readEntity ReturningClause
deleteFromTableReturning =
  ReturningOption ReturningClause
-> TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr
-> Delete readEntity ReturningClause
forall returningClause key writeEntity readEntity.
ReturningOption returningClause
-> TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr
-> Delete readEntity returningClause
deleteTable ReturningOption ReturningClause
WithReturning

-- an internal helper function for creating a delete with a given `ReturningOption`
deleteTable ::
  ReturningOption returningClause ->
  TableDefinition key writeEntity readEntity ->
  Maybe Expr.BooleanExpr ->
  Delete readEntity returningClause
deleteTable :: forall returningClause key writeEntity readEntity.
ReturningOption returningClause
-> TableDefinition key writeEntity readEntity
-> Maybe BooleanExpr
-> Delete readEntity returningClause
deleteTable ReturningOption returningClause
returningOption TableDefinition key writeEntity readEntity
tableDef Maybe BooleanExpr
whereCondition =
  let
    deleteExpr :: DeleteExpr
deleteExpr =
      Qualified TableName
-> Maybe WhereClause -> Maybe ReturningExpr -> DeleteExpr
Expr.deleteExpr
        (TableDefinition key writeEntity readEntity -> Qualified TableName
forall key writeEntity readEntity.
TableDefinition key writeEntity readEntity -> Qualified TableName
tableName TableDefinition key writeEntity readEntity
tableDef)
        ((BooleanExpr -> WhereClause)
-> Maybe BooleanExpr -> Maybe WhereClause
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap BooleanExpr -> WhereClause
Expr.whereClause Maybe BooleanExpr
whereCondition)
        (ReturningOption returningClause
-> TableDefinition key writeEntity readEntity
-> Maybe ReturningExpr
forall returningClause key writeEntity readEntty.
ReturningOption returningClause
-> TableDefinition key writeEntity readEntty -> Maybe ReturningExpr
mkTableReturningClause ReturningOption returningClause
returningOption TableDefinition key writeEntity readEntity
tableDef)
  in
    ReturningOption returningClause
-> AnnotatedSqlMarshaller writeEntity readEntity
-> DeleteExpr
-> Delete readEntity returningClause
forall returningClause writeEntity readEntity.
ReturningOption returningClause
-> AnnotatedSqlMarshaller writeEntity readEntity
-> DeleteExpr
-> Delete readEntity returningClause
rawDeleteExpr ReturningOption returningClause
returningOption (TableDefinition key writeEntity readEntity
-> AnnotatedSqlMarshaller writeEntity readEntity
forall key writeEntity readEntity.
TableDefinition key writeEntity readEntity
-> AnnotatedSqlMarshaller writeEntity readEntity
tableMarshaller TableDefinition key writeEntity readEntity
tableDef) DeleteExpr
deleteExpr

{- |
  Builds a 'Delete' that will execute the specified query and use the given
  'Orville.PostgreSQL.SqlMarshaller' to decode it. It is up to the caller to
  ensure that the given 'Expr.DeleteExpr' makes sense and returns a result that
  the 'Orville.PostgreSQL.SqlMarshaller' can decode.

  This is the lowest level of escape hatch available for 'Delete'. The caller can build any query
  that Orville supports using the expression-building functions, or use @RawSql.fromRawSql@ to build
  a raw 'Expr.DeleteExpr'. It is expected that the 'ReturningOption' given matches the
  'Expr.DeleteExpr'. This level of interface does not provide an automatic enforcement of the
  expectation, however failure is likely if that is not met.

@since 1.0.0.0
-}
rawDeleteExpr :: ReturningOption returningClause -> AnnotatedSqlMarshaller writeEntity readEntity -> Expr.DeleteExpr -> Delete readEntity returningClause
rawDeleteExpr :: forall returningClause writeEntity readEntity.
ReturningOption returningClause
-> AnnotatedSqlMarshaller writeEntity readEntity
-> DeleteExpr
-> Delete readEntity returningClause
rawDeleteExpr ReturningOption returningClause
WithReturning AnnotatedSqlMarshaller writeEntity readEntity
marshaller = AnnotatedSqlMarshaller writeEntity readEntity
-> DeleteExpr -> Delete readEntity ReturningClause
forall writeEntity readEntity.
AnnotatedSqlMarshaller writeEntity readEntity
-> DeleteExpr -> Delete readEntity ReturningClause
DeleteReturning AnnotatedSqlMarshaller writeEntity readEntity
marshaller
rawDeleteExpr ReturningOption returningClause
WithoutReturning AnnotatedSqlMarshaller writeEntity readEntity
_ = DeleteExpr -> Delete readEntity returningClause
DeleteExpr -> Delete readEntity NoReturningClause
forall readEntity.
DeleteExpr -> Delete readEntity NoReturningClause
Delete