packages feed

dsh-sql-0.2.0.0: src/Database/DSH/Backend/Sql/Opt/Properties/ICols.hs

{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE TemplateHaskell     #-}

-- | Infer the input columns required in TableAlgebra plans.
module Database.DSH.Backend.Sql.Opt.Properties.ICols where

import qualified Data.Set.Monad                           as S

import           Database.Algebra.Table.Lang

import           Database.DSH.Backend.Sql.Opt.Properties.Auxiliary

inferIColsBinOp :: S.Set Attr  -- ^ columns that are required from us
                -> S.Set Attr  -- ^ Columns required from the left child
                -> S.Set Attr  -- ^ Output of the left child
                -> S.Set Attr  -- ^ Columns required from the right child
                -> S.Set Attr  -- ^ Output of the left child
                -> BinOp       -- ^ The operator
                -> (S.Set Attr, S.Set Attr)
inferIColsBinOp ownICols leftICols leftCols rightICols rightCols op =
    case op of
         -- Require columns from the originating side.
         Cross _ -> ( leftICols ∪ (ownICols ∩ leftCols)
                    , rightICols ∪ (ownICols ∩ rightCols) )

         -- Require columns from the originating side, in addition to the join
         -- columns.
         EqJoin (leftJoinCol, rightJoinCol) ->
             ( leftICols ∪ (ownICols ∩ leftCols) ∪ (S.singleton leftJoinCol)
             , rightICols ∪ (ownICols ∩rightCols) ∪ (S.singleton rightJoinCol) )
         ThetaJoin cs ->
             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs
                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs

                 leftICols' = leftICols ∪ (ownICols ∩ leftCols) ∪ leftExprCols
                 rightICols' = rightICols ∪ (ownICols ∩ rightCols) ∪ rightExprCols
             in (leftICols', rightICols')

         -- FIXME recheck
         LeftOuterJoin cs ->
             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs
                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs

                 leftICols' = leftICols ∪ (ownICols ∩ leftCols) ∪ leftExprCols
                 rightICols' = rightICols ∪ (ownICols ∩ rightCols) ∪ rightExprCols
             in (leftICols', rightICols')

         -- From the left, we require all columns required by us, in addition to
         -- the left join columns.
         SemiJoin cs ->
             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs
                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs

                 leftICols' = leftICols ∪ ownICols ∪ leftExprCols
                 rightICols' = rightICols ∪ rightExprCols
             in (leftICols', rightICols')
         AntiJoin cs ->
             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs
                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs

                 leftICols' = leftICols ∪ ownICols ∪ leftExprCols
                 rightICols' = rightICols ∪ rightExprCols
             in (leftICols', rightICols')

         -- The schemata of both union inputs must be kept in sync. No
         -- ICols-based (i.e. colummn-pruning) rewrites can be
         -- performed unless there is a guarantee that they happen in
         -- both branches.
         DisjUnion _  -> (leftCols, rightCols)

         Difference _ -> (leftICols ∪ leftCols, rightICols ∪ leftCols)

inferIColsUnOp :: S.Set Attr -> S.Set Attr -> UnOp -> S.Set Attr
inferIColsUnOp ownICols childICols op =
    case op of
        WinFun ((resCol, fun), partExprs, sortInf, _) ->
            (S.delete resCol ownICols)
            ∪ (winFunInput fun)
            ∪ (S.unions $ map (exprCols . fst) sortInf)
            ∪ (S.unions $ map exprCols partExprs)
            ∪ childICols
        -- Require the sorting columns, if the rownum output is required.
        RowNum (resCol, sortInf, groupExprs) ->
            (S.delete resCol ownICols)
            ∪ (S.unions $ map (exprCols . fst) sortInf)
            ∪ (S.unions $ map exprCols groupExprs)
            ∪ childICols

        RowRank (resCol, sortInf)   ->
            (S.delete resCol ownICols)
            ∪ (S.unions $ map (exprCols . fst) sortInf)
            ∪ childICols
        Rank (resCol, sortInf)      ->
            (S.delete resCol ownICols)
            ∪ (S.unions $ map (exprCols . fst) sortInf)
            ∪ childICols

        -- For projections we require input columns of expressions, but only for
        -- those output columns which are actually required from downstream.
        Project projs         -> S.foldr (∪) childICols $ S.fromList $ map (exprCols . snd) projs

        -- Require all columns for the select columns, in addition to columns
        -- required downstream
        Select e              -> childICols ∪ ownICols ∪ exprCols e
        Distinct _            -> childICols ∪ ownICols

        Aggr (acols, pexprs)  -> (S.foldr (∪) childICols $ S.fromList $ map (aggrInput . fst) acols)
                                 ∪
                                 (S.foldr (∪) S.empty $ S.fromList $ map (exprCols . snd) pexprs)

        Serialize cs          ->
            let (ref, key, ord, items) = cs
            in childICols ∪
               (S.unions (map (\(RefCol _ e) -> exprCols e) ref
                          ++ map (\(KeyCol _ e) -> exprCols e) key
                          ++ map (\(OrdCol _ e) -> exprCols e) ord
                          ++ map (\(PayloadCol _ e) -> exprCols e) items))