packages feed

project-m36-1.2.0: src/lib/ProjectM36/ReferencedTransactionIds.hs

{-# LANGUAGE FlexibleInstances #-}
module ProjectM36.ReferencedTransactionIds where
import ProjectM36.Base
import ProjectM36.ValueMarker
import ProjectM36.Error
import ProjectM36.DatabaseContext.Types
import ProjectM36.Transaction.Types
import ProjectM36.TransactionGraph.Types
import ProjectM36.RelationalExpression
import qualified Data.Map as M
import qualified Data.Set as S
import Control.Monad (foldM)

type TransactionIds = S.Set TransactionId

-- return all transactionIds referenced recursively- can be used to create subgraph of transaction dependencies
class ReferencedTransactionIds a where
  referencedTransactionIds :: a -> TransactionIds

instance ReferencedTransactionIds a => ReferencedTransactionIds (RelationalExprBase a) where
  referencedTransactionIds x = case x of
    MakeRelationFromExprs (Just attrExprs) tupleExprs ->
      S.unions (referencedTransactionIds tupleExprs : map referencedTransactionIds attrExprs)
    MakeRelationFromExprs Nothing tupleExprs ->
      referencedTransactionIds tupleExprs
    MakeStaticRelation{} -> S.empty
    ExistingRelation{} -> S.empty
    RelationVariable _ marker -> referencedTransactionIds marker
    RelationValuedAttribute _ -> S.empty
    Project attrNames expr -> S.union (referencedTransactionIds attrNames) (referencedTransactionIds expr)
    Union exprA exprB -> S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
    Join exprA exprB -> S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
    Rename _ expr -> referencedTransactionIds expr
    Difference exprA exprB -> S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
    Group attrNames _ expr -> S.union (referencedTransactionIds attrNames) (referencedTransactionIds expr)
    Ungroup _ expr -> referencedTransactionIds expr
    Restrict pred' expr -> S.union (referencedTransactionIds pred') (referencedTransactionIds expr)
    Equals exprA exprB -> S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
    NotEquals exprA exprB -> S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
    Extend extendTupleExpr expr -> S.union (referencedTransactionIds extendTupleExpr) (referencedTransactionIds expr)
    With assocs expr -> S.unions (referencedTransactionIds expr : map tAssocs assocs)
      where
        tAssocs (withNameExpr, rExpr) = S.union (referencedTransactionIds withNameExpr) (referencedTransactionIds rExpr)

instance ReferencedTransactionIds a => ReferencedTransactionIds (AttributeExprBase a) where
  referencedTransactionIds NakedAttributeExpr{} = S.empty
  referencedTransactionIds (AttributeAndTypeNameExpr _ _ marker) = referencedTransactionIds marker

instance ReferencedTransactionIds a => ReferencedTransactionIds (TupleExprBase a) where
  referencedTransactionIds (TupleExpr tMap) =
    S.unions (referencedTransactionIds <$> M.elems tMap)

instance ReferencedTransactionIds a => ReferencedTransactionIds (TupleExprsBase a) where
  referencedTransactionIds (TupleExprs marker tupleExprs) =
    S.unions (referencedTransactionIds marker : (referencedTransactionIds <$> tupleExprs))

instance ReferencedTransactionIds GraphRefTransactionMarker where
  referencedTransactionIds (TransactionMarker tid) = S.singleton tid
  referencedTransactionIds UncommittedContextMarker = S.empty -- we have other methods to determine if there is an uncommitted transaction marker in the expr

instance ReferencedTransactionIds a => ReferencedTransactionIds (AttributeNamesBase a) where
  referencedTransactionIds names =
    case names of
      AttributeNames{} -> S.empty
      InvertedAttributeNames{} -> S.empty
      UnionAttributeNames exprA exprB ->
        S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
      IntersectAttributeNames exprA exprB ->
        S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
      RelationalExprAttributeNames rExpr ->
        referencedTransactionIds rExpr

instance ReferencedTransactionIds a => ReferencedTransactionIds (RestrictionPredicateExprBase a) where
  referencedTransactionIds expr =
    case expr of
      TruePredicate -> mempty
      AndPredicate exprA exprB ->
        S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
      OrPredicate exprA exprB ->
        S.union (referencedTransactionIds exprA) (referencedTransactionIds exprB)
      NotPredicate exprA ->
        referencedTransactionIds exprA
      RelationalExprPredicate rExpr ->
        referencedTransactionIds rExpr
      AtomExprPredicate aExpr ->
        referencedTransactionIds aExpr
      AttributeEqualityPredicate _ aExpr ->
        referencedTransactionIds aExpr

instance ReferencedTransactionIds a => ReferencedTransactionIds (ExtendTupleExprBase a) where
  referencedTransactionIds (AttributeExtendTupleExpr _ aExpr) =
    referencedTransactionIds aExpr

instance ReferencedTransactionIds a => ReferencedTransactionIds (WithNameExprBase a) where
  referencedTransactionIds (WithNameExpr _ marker) = referencedTransactionIds marker

instance ReferencedTransactionIds a => ReferencedTransactionIds (AtomExprBase a) where
  referencedTransactionIds expr =
    case expr of
      AttributeAtomExpr{} -> mempty
      NakedAtomExpr{} -> mempty
      SubrelationAttributeAtomExpr{} -> mempty
      FunctionAtomExpr _ args marker ->
        S.unions (referencedTransactionIds marker : (referencedTransactionIds <$> args))
      RelationAtomExpr rExpr ->
        referencedTransactionIds rExpr
      ConstructedAtomExpr _ args marker ->
        S.unions (referencedTransactionIds marker : (referencedTransactionIds <$> args))
      IfThenAtomExpr ifE thenE elseE ->
        S.unions [referencedTransactionIds ifE,
                  referencedTransactionIds thenE,
                  referencedTransactionIds elseE]

-- only the relvars can reference other transactions
instance ReferencedTransactionIds DatabaseContext where
  referencedTransactionIds dbc =
    S.unions [
    --referencedTransactionIds (inclusionDependencies dbc),
    referencedTransactionIds (relationVariables dbc)
    --referencedTransactionIds (atomFunctions dbc),
    --referencedTransactionIds (dbcFunctions dbc),
    --referencedTransactionIds (notifications dbc),
    --referencedTransactionIds (typeConstructorMapping dbc),
    --referencedTransactionIds (registeredQueries dbc)
    ]

instance ReferencedTransactionIds a => ReferencedTransactionIds (ValueMarker a) where
  referencedTransactionIds (ValueMarker a) = referencedTransactionIds a
  referencedTransactionIds (NotChangedSinceMarker tid) = S.singleton tid

instance ReferencedTransactionIds RelationVariables where
  referencedTransactionIds relVars =
    S.unions (referencedTransactionIds <$> M.elems relVars)

-- | Recurse relvars references and transaction parents to extract a subset of relevant transactions.
-- probably could do some trimming of transactions that are not referenced by relvars, but that is rare, so probably of not much benefit
-- should be trim merge parents that don't contribute to the relvars? maybe
referencedTransactionIdsForTransaction :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
referencedTransactionIdsForTransaction trans graph
  | parentIds' == rootParent = pure (S.singleton trans)
  | otherwise = 
    foldM folder (S.singleton trans) parentIds'
  where
    parentIds' = parents (transactionInfo trans)
    folder acc transId' = do
      trans' <- transactionForId transId' graph
      transSet <- referencedTransactionIdsForTransaction trans' graph
      pure (S.union acc transSet)