content: {-# language DataKinds #-}
  {-# language FlexibleContexts #-}
  {-# language GADTs #-}
  {-# language LambdaCase #-}
  {-# language NamedFieldPuns #-}
  {-# language RankNTypes #-}
  {-# language ScopedTypeVariables #-}
  {-# language StandaloneKindSignatures #-}
  {-# language StrictData #-}
  {-# language TypeApplications #-}
  
  module Rel8.Statement.Returning
    ( Returning( NoReturning, Returning )
    , runReturning
    , ppReturning
    )
  where
  
  -- base
  import Data.Foldable ( toList )
  import Data.Kind ( Type )
  import Data.List.NonEmpty ( NonEmpty )
  import Prelude
  
  -- opaleye
  import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
  import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
  import qualified Opaleye.Internal.Sql as Opaleye
  import qualified Opaleye.Internal.Tag as Opaleye
  
  -- pretty
  import Text.PrettyPrint ( Doc, (<+>), text )
  
  -- rel8
  import Rel8.Expr (Expr)
  import Rel8.Query (Query)
  import Rel8.Schema.Name ( Selects )
  import Rel8.Schema.Table ( TableSchema(..) )
  import Rel8.Statement (Statement, statementNoReturning, statementReturning)
  import Rel8.Table (Table)
  import Rel8.Table.Opaleye ( castTable, exprs, view )
  
  -- transformers
  import Control.Monad.Trans.State.Strict (State)
  
  
  -- | 'Rel8.Insert', 'Rel8.Update' and 'Rel8.Delete' all support an optional
  -- @RETURNING@ clause.
  type Returning :: Type -> Type -> Type
  data Returning names a where
    -- | No @RETURNING@ clause
    NoReturning :: Returning names ()
  
    -- | 'Returning' allows you to project out of the affected rows, which can
    -- be useful if you want to log exactly which rows were deleted, or to view
    -- a generated id (for example, if using a column with an autoincrementing
    -- counter via 'Rel8.nextval').
    Returning :: (Selects names exprs, Table Expr a) => (exprs -> a) -> Returning names (Query a)
  
  
  projections :: ()
    => TableSchema names -> Returning names a -> Maybe (NonEmpty Opaleye.PrimExpr)
  projections TableSchema {columns} = \case
    NoReturning -> Nothing
    Returning f -> Just (exprs (castTable (f (view columns))))
  
  
  runReturning ::
    State Opaleye.Tag Doc ->
    Returning names a ->
    Statement a
  runReturning pp = \case
    NoReturning -> statementNoReturning pp
    Returning _ -> statementReturning pp
  
  
  ppReturning :: TableSchema names -> Returning names a -> Doc
  ppReturning schema returning = case projections schema returning of
    Nothing -> mempty
    Just columns ->
      text "RETURNING" <+> Opaleye.commaV Opaleye.ppSqlExpr (toList sqlExprs)
      where
        sqlExprs = Opaleye.sqlExpr <$> columns
  