dsh-sql-0.2.0.0: src/Database/DSH/Backend/Sql/Opt/Properties/Nullable.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MonadComprehensions #-}
module Database.DSH.Backend.Sql.Opt.Properties.Nullable
( inferNullableNullOp
, inferNullableUnOp
, inferNullableBinOp
) where
import qualified Data.Set.Monad as S
import Database.Algebra.Table.Lang
import Database.DSH.Backend.Sql.Opt.Properties.Types
import Database.DSH.Backend.Sql.Opt.Properties.Auxiliary
nullableExpr :: S.Set Attr -> Expr -> Bool
nullableExpr ns e =
case e of
BinAppE Coalesce e1 e2 -> (nullableExpr ns e1 && nullableExpr ns e2)
||
(not (nullableExpr ns e1) && nullableExpr ns e2)
BinAppE _ e1 e2 -> nullableExpr ns e1 || nullableExpr ns e2
UnAppE _ e1 -> nullableExpr ns e1
ColE c -> c `S.member` ns
IfE e1 e2 e3 -> any (nullableExpr ns) [e1, e2, e3]
ConstE _ -> False
nullableAggr :: S.Set Attr -> AggrType -> Bool
nullableAggr ns a =
case a of
CountStar -> False
Count _ -> False
Avg e -> nullableExpr ns e
Max e -> nullableExpr ns e
Min e -> nullableExpr ns e
Sum e -> nullableExpr ns e
All e -> nullableExpr ns e
Any e -> nullableExpr ns e
inferNullableNullOp :: NullOp -> S.Set Attr
inferNullableNullOp op =
case op of
LitTable _ -> S.empty
TableRef _ -> S.empty
inferNullableUnOp :: S.Set Attr -> UnOp -> S.Set Attr
inferNullableUnOp ns op =
case op of
Serialize _ -> ns
Select _ -> ns
Distinct _ -> ns
Project ps -> ls [ a | (a, e) <- ps, nullableExpr ns e ]
RowNum (c, _, _) -> S.delete c ns
RowRank (c, _) -> S.delete c ns
Rank (c, _) -> S.delete c ns
-- Non-grouped aggregate functions might return NULL if their
-- input is empty (except for COUNT)
Aggr (as, []) -> ns ∪ (ls [ c | (a, c) <- as, nullableAggr ns a ])
-- For grouped aggregates:
-- 1. The grouping columns might be NULL if they were nullable in the input.
--
-- 2. Aggregate output (except for COUNT) is nullable if the
-- input expression is nullable
Aggr (as, gs) -> (ls [ c | (c, e) <- gs, nullableExpr ns e ])
∪
(ls [ c | (a, c) <- as, nullableAggr ns a ])
-- FIXME under what circumstances does the window aggregate
-- output get NULL? This is the safe variant that considers
-- the output always nullable.
WinFun a -> let ((c, _), _, _, _) = a in S.insert c ns
inferNullableBinOp :: BottomUpProps -> BottomUpProps -> BinOp -> S.Set Attr
inferNullableBinOp ps1 ps2 op =
case op of
Cross _ -> pNullable ps1 ∪ pNullable ps2
-- FIXME for joins we could be more precise: any column that
-- shows up in the join predicate can be considered non-null
-- in the join result (tuples in which the predicate evaluates
-- to NULL will not be in the result).
EqJoin _ -> (pNullable ps1) ∪ (pNullable ps2)
ThetaJoin _ -> pNullable ps1 ∪ pNullable ps2
LeftOuterJoin _ -> pNullable ps1 ∪ [ c | (c, _) <- pCols ps2 ]
SemiJoin _ -> pNullable ps1
AntiJoin _ -> pNullable ps1
DisjUnion _ -> pNullable ps1 ∪ pNullable ps2
Difference _ -> pNullable ps1