{-|
This module contains helper functions for constructing algebraic plans.
-}
module Database.Ferry.Algebra.Data.Create where

import Database.Ferry.Algebra.Data.Algebra
import Database.Ferry.Algebra.Data.GraphBuilder

-- * Value constructors

-- | Create an algebraic int value
int :: Integer -> AVal
int = VInt

-- | Create an algebraic string value
string :: String -> AVal
string = VStr

-- | Create an algebraic boolean value
bool :: Bool -> AVal
bool = VBool

-- | Create an algebraic double value
double :: Double -> AVal
double = VDouble

-- | Create an algebraic decimal value
dec :: Float -> AVal
dec = VDec

-- | Create an algebraic nat value
nat :: Integer -> AVal
nat = VNat

-- | Types of algebraic values
intT, stringT, boolT, decT, doubleT, natT, surT :: ATy
intT = AInt
stringT = AStr
boolT = ABool
natT = ANat
surT = ASur

-- * Graph construction combinators for table algebra

-- | Construct an empty table node with
emptyTable :: SchemaInfos -> GraphM AlgNode
emptyTable = insertNode . EmptyTable

-- | Construct a database table node
-- The first argument is the \emph{qualified} name of the database
-- table. The second describes the columns in alphabetical order.
-- The third argument describes the database keys (one table key can
-- span over multiple columns).
dbTable :: String -> Columns -> KeyInfos -> GraphM AlgNode
dbTable n cs ks = insertNode $TableRef (n, attr, ks) where attr = map (\c -> case c of (NCol n' [Col i t]) -> (n', "item" ++ show i, t) _ -> error "Not a named column") cs -- | Construct a table with one value litTable :: AVal -> String -> ATy -> GraphM AlgNode litTable v s t = insertNode$ LitTable [[v]] [(s, t)]

-- | Attach a column 'ResAttrName' of type ATy' with value
-- AVal' in all rows to table AlgNode'
attach :: ResAttrName -> ATy -> AVal -> AlgNode -> GraphM AlgNode
attach n t v c = insertNode $Attach (n, (t, v)) c -- | Cast column AttrName' to type ATy' and give it the name -- ResAttrName' afterwards. cast :: AttrName -> ResAttrName -> ATy -> AlgNode -> GraphM AlgNode cast n r t c = insertNode$ Cast (r, n, t) c

-- | Join two plans where the columns n1 of table 1 and columns n2 of table
--  2 are equal.
eqJoin :: String -> String -> AlgNode -> AlgNode -> GraphM AlgNode
eqJoin n1 n2 c1 c2 = insertNode $EqJoin (n1, n2) c1 c2 -- | The same as eqJoin but with multiple columns. eqTJoin :: [(String, String)] -> ProjInf -> AlgNode -> AlgNode -> GraphM AlgNode eqTJoin eqs projI q1 q2 = let (a, b) = head eqs in foldr filterEqs (eqJoin a b q1 q2)$ tail eqs
where resCol = "item99999002"
filterEqs :: (String, String) -> GraphM AlgNode -> GraphM AlgNode
filterEqs (l, r) res = proj projI =<< select resCol =<< oper "==" resCol l r =<< res

-- | Assign a number to each row in column 'ResAttrName' incrementing
-- sorted by SortInf'. The numbering is not dense!
rank :: ResAttrName -> SortInf -> AlgNode -> GraphM AlgNode
rank res sort c1 = insertNode $Rank (res, sort) c1 -- | Compute the difference between two plans. difference :: AlgNode -> AlgNode -> GraphM AlgNode difference q1 q2 = insertNode$ Difference q1 q2

-- | Same as rank but provides a dense numbering.
rowrank :: ResAttrName -> SortInf -> AlgNode -> GraphM AlgNode
rowrank res sort c1 = insertNode $RowRank (res, sort) c1 -- | Get's the nth element(s) of a (partitioned) table. posSelect :: Int -> SortInf -> Maybe AttrName -> AlgNode -> GraphM AlgNode posSelect n sort part c1 = insertNode$ PosSel (n, sort, part) c1

-- | Select rows where the column SelAttrName' contains True.
select :: SelAttrName -> AlgNode -> GraphM AlgNode
select sel c1 = insertNode $Sel sel c1 -- | Remove duplicate rows distinct :: AlgNode -> GraphM AlgNode distinct c1 = insertNode$ Distinct c1

-- | Make cross product from two plans
cross :: AlgNode -> AlgNode -> GraphM AlgNode
cross c1 c2 = insertNode $Cross c1 c2 -- | Negate the boolen value in column n and store it in column r notC :: AttrName -> AttrName -> AlgNode -> GraphM AlgNode notC r n c1 = insertNode$ FunBoolNot (r, n) c1

-- | Union between two plans
union :: AlgNode -> AlgNode -> GraphM AlgNode
union c1 c2 = insertNode $DisjUnion c1 c2 -- | Project/rename certain column out of a plan proj :: ProjInf -> AlgNode -> GraphM AlgNode proj cols c = insertNode$ Proj cols c

-- | Apply aggregate functions to a plan
aggr :: [(AggrType, ResAttrName, Maybe AttrName)] -> Maybe PartAttrName -> AlgNode -> GraphM AlgNode
aggr aggrs part c1 = insertNode $Aggr (aggrs, part) c1 -- | Similar to rowrank but this will assign a \emph{unique} number to every row -- (even if two rows are equal) rownum :: AttrName -> [AttrName] -> Maybe AttrName -> AlgNode -> GraphM AlgNode rownum res sort part c1 = insertNode$ RowNum (res, zip sort $repeat Asc, part) c1 -- | Same as rownum but columns can be assigned an ordering direction rownum' :: AttrName -> [(AttrName, SortDir)] -> Maybe AttrName -> AlgNode -> GraphM AlgNode rownum' res sort part c1 = insertNode$ RowNum (res, sort, part) c1

-- | Apply an operator to the element in LeftAttrName' and RightAttrName',
-- store the result in ResAttrName'
oper :: String -> ResAttrName -> LeftAttrName -> RightAttrName -> AlgNode -> GraphM AlgNode
oper o r la ra c = insertNode \$ FunBinOp (o, r, la, ra) c

-- | Shorthand for the initial loop condition used by Ferry.
initLoop :: Algebra
initLoop =  LitTable [[(nat 1)]] [("iter", natT)]
`