2{-# language LambdaCase #-}
3{-# language NamedFieldPuns #-}
4{-# language RankNTypes #-}
5{-# language ScopedTypeVariables #-}
6{-# language StandaloneKindSignatures #-}
7{-# language StrictData #-}
8{-# language TypeApplications #-}
10module Rel8.Statement.Returning
11 ( Returning( NumberOfRowsAffected, Projection )
18import Control.Applicative ( liftA2 )
19import Data.Foldable ( toList )
20import Data.Int ( Int64 )
21import Data.Kind ( Type )
22import Data.List.NonEmpty ( NonEmpty )
26import qualified Hasql.Decoders as Hasql
29import qualified Opaleye.Internal.HaskellDB.PrimQuery as Opaleye
30import qualified Opaleye.Internal.HaskellDB.Sql.Print as Opaleye
31import qualified Opaleye.Internal.Sql as Opaleye
34import Text.PrettyPrint ( Doc, (<+>), text )
37import Rel8.Schema.Name ( Selects )
38import Rel8.Schema.Table ( TableSchema(..) )
39import Rel8.Table.Opaleye ( castTable, exprs, view )
40import Rel8.Table.Serialize ( Serializable, parse )
43import Data.Functor.Apply ( Apply, (<.>) )
46-- | 'Rel8.Insert', 'Rel8.Update' and 'Rel8.Delete' all support returning either
47-- the number of rows affected, or the actual rows modified.
48type Returning :: Type -> Type -> Type
49data Returning names a where
50 Pure :: a -> Returning names a
51 Ap :: Returning names (a -> b) -> Returning names a -> Returning names b
53 -- | Return the number of rows affected.
54 NumberOfRowsAffected :: Returning names Int64
56 -- | 'Projection' allows you to project out of the affected rows, which can
57 -- be useful if you want to log exactly which rows were deleted, or to view
58 -- a generated id (for example, if using a column with an autoincrementing
59 -- counter via 'Rel8.nextval').
60 Projection :: (Selects names exprs, Serializable returning a)
61 => (exprs -> returning)
62 -> Returning names [a]
65instance Functor (Returning names) where
68 Ap g a -> Ap (fmap (f .) g) a
72instance Apply (Returning names) where
76instance Applicative (Returning names) where
82 => TableSchema names -> Returning names a -> Maybe (NonEmpty Opaleye.PrimExpr)
83projections schema@TableSchema {columns} = \case
85 Ap f a -> projections schema f <> projections schema a
86 NumberOfRowsAffected -> Nothing
87 Projection f -> Just (exprs (castTable (f (view columns))))
91 => ((Int64 -> a) -> r)
92 -> (forall x. Hasql.Row x -> ([x] -> a) -> r)
95runReturning rowCount rowList = \case
96 Pure a -> rowCount (const a)
101 (\withCount' -> rowCount (withCount <*> withCount'))
102 (\decoder -> rowList decoder . liftA2 withCount length64)
104 (\decoder withRows ->
106 (\withCount -> rowList decoder $ withRows <*> withCount . length64)
107 (\decoder' withRows' ->
108 rowList (liftA2 (,) decoder decoder') $
109 withRows <$> fmap fst <*> withRows' . fmap snd)
112 NumberOfRowsAffected -> rowCount id
113 Projection (_ :: exprs -> returning) -> rowList decoder' id
115 decoder' = parse @returning
117 length64 :: Foldable f => f x -> Int64
118 length64 = fromIntegral . length
121decodeReturning :: Returning names a -> Hasql.Result a
122decodeReturning = runReturning
123 (<$> Hasql.rowsAffected)
124 (\decoder withRows -> withRows <$> Hasql.rowList decoder)
127ppReturning :: TableSchema names -> Returning names a -> Doc
128ppReturning schema returning = case projections schema returning of
131 text "RETURNING" <+> Opaleye.commaV Opaleye.ppSqlExpr (toList sqlExprs)
133 sqlExprs = Opaleye.sqlExpr <$> columns
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list