algebra-sql-0.1.0.0: src/Database/Algebra/SQL/Tile/Flatten.hs
-- | When the tiles have been transformed, all children of 'TileNode' are
-- references and every tree contains just a single node. This is where this
-- module is used, it removes the redundant type structure.
-- It also provides the possibilty to change the transformation behaviour, but
-- the tile merging is much worse, since we lost a lot of useful information.
--
-- This can be used as a finishing step for materialization or as an
-- intermediate step, since it provides an interface for 'String' and a
-- generic interface which takes a materializer and a substituter.
module Database.Algebra.SQL.Tile.Flatten
( flattenTransformResultWith
, flattenTransformResult
, FlatTile
) where
import qualified Data.IntMap.Lazy as IntMap
( empty
, insert
, lookup
)
import qualified Data.MultiSet as MultiSet
( MultiSet
, empty
, insert
, union
, singleton
)
import qualified Data.DList as DL
( toList
)
import Data.Maybe (fromMaybe)
import qualified Database.Algebra.SQL.Query as Q
import Database.Algebra.SQL.Query.Substitution
import Database.Algebra.SQL.Query.Util
( emptySelectStmt
, mkPCol
)
import Database.Algebra.SQL.Termination
import Database.Algebra.SQL.Tile
-- TODO error used in lookup
-- | A flat tile containing:
--
-- * the body of the tile
--
-- * a multiset of a type which identifies a referenceable flat tile
--
type FlatTile a = (Q.SelectStmt, MultiSet.MultiSet a)
-- | Flatten a transform result using SQL identifiers directly.
flattenTransformResult :: ([TileTree], DependencyList)
-> ([FlatTile String], [(String, FlatTile String)])
flattenTransformResult =
flattenTransformResultWith m s
where -- Materializes a table reference as string (a SQL identifier).
m tableRef = 't' : show tableRef
s = Q.FETableReference
-- | Flatten the transformed result and apply the given functions where
-- possible.
-- The materializer should produce the desired representation, and the
-- substituter should produce expressions which can be substituted as
-- 'Q.FromExpr'.
flattenTransformResultWith :: Ord a
=> (ExternalReference -> a) -- ^ The materializer.
-> (a -> Q.FromExpr) -- ^ The substituter.
-> ([TileTree], DependencyList)
-> ([FlatTile a], [(a, FlatTile a)])
flattenTransformResultWith materializer substituter (tiles, deps) =
( map (flattenTileTreeWith materializer substituter) tiles
, map f $ DL.toList deps
)
where f (extRef, tile) =
( materializer extRef
, flattenTileTreeWith materializer substituter tile
)
flattenTileTreeWith :: Ord a
=> (ExternalReference -> a)
-> (a -> Q.FromExpr)
-> TileTree
-> FlatTile a
flattenTileTreeWith materializer substituter (TileNode fs body children) =
flattenTileNodeWith materializer substituter fs body children
-- This case should never happen, but is provided for completeness.
flattenTileTreeWith materializer substituter (ReferenceLeaf tableId s) =
( emptySelectStmt
{ Q.selectClause = map f s
, Q.fromClause =
[Q.FPAlias (substituter alias) aliasName $ Just s]
}
, MultiSet.singleton alias
)
where f col = Q.SCAlias (Q.EEBase $ mkPCol aliasName col) col
aliasName = "tmpAlias"
alias = materializer tableId
flattenTileNodeWith :: Ord a
=> (ExternalReference -> a)
-> (a -> Q.FromExpr)
-> FeatureSet
-> Q.SelectStmt
-> TileChildren
-> FlatTile a
flattenTileNodeWith materializer substituter _ body children =
-- Merge the replacements into the body.
( replaceReferencesSelectStmt lookupFun body
, externalReferences
)
where lookupFun v =
fromMaybe (error "missing reference while replacing")
$ IntMap.lookup v bodySubstitutes
(bodySubstitutes, externalReferences) =
foldr f (IntMap.empty, MultiSet.empty) children
f (ref, tile) (substitutes, refs) = case tile of
-- There is probably no better way to merge here, because we have no
-- information about where the references are located. (TODO lookup
-- is possible, but expensive)
TileNode m b c ->
let (b', externalRefs) =
flattenTileNodeWith materializer substituter m b c
in
( IntMap.insert ref (Q.FESubQuery $ Q.VQSelect b') substitutes
, MultiSet.union externalRefs refs
)
-- Most of the time this case should be taken.
ReferenceLeaf tableId _ ->
( IntMap.insert ref (substituter alias) substitutes
, MultiSet.insert alias refs
)
where alias = materializer tableId