{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Database.CQRS.TabularData.Optimisation
( Optimiser
, Optimisable(..)
) where
import qualified Control.Monad.Identity as Id
import Database.CQRS.TabularData
import Database.CQRS.TabularData.Internal
type Optimiser cols = [TabularDataAction cols] -> [TabularDataAction cols]
class Optimisable (cols :: Columns) where
optimiseActions :: Optimiser cols
optimiseActions = optimiseInsertBeforeDelete
optimiseInsertBeforeDelete :: Optimiser cols
instance Optimisable ('Flat cols) where
optimiseInsertBeforeDelete :: Optimiser ('Flat cols)
optimiseInsertBeforeDelete =
foldr
(\action actions ->
case action of
Insert tuple | isInsertBeforeDelete tuple actions -> actions
_ -> action: actions)
[]
where
isInsertBeforeDelete
:: Tuple Id.Identity ('Flat cols)
-> [TabularDataAction ('Flat cols)]
-> Bool
isInsertBeforeDelete tuple = \case
[] -> False
Delete conds : actions ->
tuple `matches` conds || isInsertBeforeDelete tuple actions
Update updates conds : actions
| tuple `matches` conds ->
let tuple' = update updates tuple
in isInsertBeforeDelete tuple' actions
| otherwise -> isInsertBeforeDelete tuple actions
Insert _ : actions -> isInsertBeforeDelete tuple actions
instance
( Eq (FlatTuple Id.Identity keyCols)
, MergeSplitTuple keyCols cols
)
=> Optimisable ('WithUniqueKey keyCols cols) where
optimiseInsertBeforeDelete
::
( Eq (FlatTuple Id.Identity keyCols)
, MergeSplitTuple keyCols cols
)
=> Optimiser ('WithUniqueKey keyCols cols)
optimiseInsertBeforeDelete =
foldr
(\action actions ->
case action of
Insert tuple | isInsertBeforeDelete tuple actions -> actions
_ -> action: actions)
[]
where
isInsertBeforeDelete
:: Tuple Id.Identity ('WithUniqueKey keyCols cols)
-> [TabularDataAction ('WithUniqueKey keyCols cols)]
-> Bool
isInsertBeforeDelete tuple = \case
[] -> False
Delete conds : actions ->
tuple `matches` conds || isInsertBeforeDelete tuple actions
Upsert tuple' : actions -> isUpsertBeforeDelete tuple tuple' actions
Update updates conds : actions
| tuple `matches` conds ->
let tuple' = update updates tuple
in isInsertBeforeDelete tuple' actions
| otherwise -> isInsertBeforeDelete tuple actions
Insert _ : actions -> isInsertBeforeDelete tuple actions
isUpsertBeforeDelete
:: Tuple Id.Identity ('WithUniqueKey keyCols cols)
-> Tuple Id.Identity ('WithUniqueKey keyCols cols)
-> [TabularDataAction ('WithUniqueKey keyCols cols)]
-> Bool
isUpsertBeforeDelete tuple tuple' actions =
let keyTuple, keyTuple' :: FlatTuple Id.Identity keyCols
_otherTuple, _otherTuple' :: FlatTuple Id.Identity cols
(keyTuple, _otherTuple) = splitTuple tuple
(keyTuple', _otherTuple') = splitTuple tuple'
in
isInsertBeforeDelete
(if keyTuple == keyTuple' then tuple' else tuple)
actions