2016-01-01 20:57:54 +05:30
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
{-# LANGUAGE RecordWildCards #-}
|
|
|
|
{-# LANGUAGE CPP #-}
|
2016-02-03 16:00:39 +05:30
|
|
|
{-# LANGUAGE GADTs #-}
|
|
|
|
{-# LANGUAGE PatternSynonyms #-}
|
|
|
|
|
2016-02-05 16:17:57 +05:30
|
|
|
module Ringo.Validator (validateEnv) where
|
2015-12-16 02:05:36 +05:30
|
|
|
|
2015-12-28 19:28:35 +05:30
|
|
|
import qualified Data.Map as Map
|
|
|
|
import qualified Data.Text as Text
|
|
|
|
|
2015-12-18 02:37:17 +05:30
|
|
|
#if MIN_VERSION_base(4,8,0)
|
|
|
|
#else
|
|
|
|
import Control.Applicative ((<$>))
|
|
|
|
#endif
|
|
|
|
|
2016-02-05 16:17:57 +05:30
|
|
|
import Control.Monad.Reader (Reader, ask, runReader)
|
2016-06-23 12:29:18 +05:30
|
|
|
import Data.Function (on)
|
2015-12-30 12:21:41 +05:30
|
|
|
import Data.Maybe (isJust, fromJust)
|
2016-06-23 12:29:18 +05:30
|
|
|
import Data.List (nub, group, groupBy, sort)
|
2015-12-16 02:05:36 +05:30
|
|
|
|
2015-12-30 12:21:41 +05:30
|
|
|
import Ringo.Extractor.Internal
|
2015-12-16 02:05:36 +05:30
|
|
|
import Ringo.Types
|
2016-02-05 16:17:57 +05:30
|
|
|
import Ringo.Types.Internal
|
|
|
|
import Ringo.Utils
|
|
|
|
|
|
|
|
data RawEnv = RawEnv ![Table] ![Fact] !Settings !TypeDefaults deriving (Show)
|
2015-12-16 02:05:36 +05:30
|
|
|
|
|
|
|
checkTableForCol :: Table -> ColumnName -> [ValidationError]
|
|
|
|
checkTableForCol tab colName =
|
|
|
|
[ MissingColumn (tableName tab) colName |
|
|
|
|
not . any ((colName ==) . columnName) . tableColumns $ tab ]
|
|
|
|
|
2016-02-05 16:17:57 +05:30
|
|
|
validateTable :: Table -> Reader RawEnv [ValidationError]
|
2015-12-16 02:05:36 +05:30
|
|
|
validateTable table = do
|
2016-02-05 16:17:57 +05:30
|
|
|
RawEnv tables _ _ _ <- ask
|
2015-12-29 16:24:40 +05:30
|
|
|
return . concatMap (checkConstraint tables) . tableConstraints $ table
|
2015-12-16 02:05:36 +05:30
|
|
|
where
|
|
|
|
checkConstraint _ (PrimaryKey colName) = checkTableForCol table colName
|
|
|
|
checkConstraint _ (UniqueKey columnNames) = checkTableForColRefs table columnNames
|
|
|
|
checkConstraint tables (ForeignKey oTableName columnNames) =
|
|
|
|
case findTable oTableName tables of
|
|
|
|
Just oTable -> checkTableForColRefs table (map fst columnNames)
|
|
|
|
++ checkTableForColRefs oTable (map snd columnNames)
|
|
|
|
Nothing -> [ MissingTable oTableName ]
|
|
|
|
|
|
|
|
checkTableForColRefs tab = concatMap (checkTableForCol tab)
|
|
|
|
|
2016-02-05 16:17:57 +05:30
|
|
|
validateFact :: Fact -> Reader RawEnv [ValidationError]
|
2015-12-16 02:05:36 +05:30
|
|
|
validateFact Fact {..} = do
|
2016-02-05 16:17:57 +05:30
|
|
|
RawEnv tables _ _ typeDefaults <- ask
|
|
|
|
let defaults = Map.keys typeDefaults
|
2015-12-16 02:05:36 +05:30
|
|
|
case findTable factTableName tables of
|
|
|
|
Nothing -> return [ MissingTable factTableName ]
|
|
|
|
Just table -> do
|
2015-12-30 12:21:41 +05:30
|
|
|
tableVs <- validateTable table
|
|
|
|
parentVs <- concat <$> mapM checkFactParents factParentNames
|
2015-12-29 16:24:40 +05:30
|
|
|
let colVs = concatMap (checkColumn tables table) factColumns
|
|
|
|
timeVs = [ MissingTimeColumn factTableName
|
2016-06-22 16:52:04 +05:30
|
|
|
| null ([ cName | FactColumn cName DimTime <- factColumns ] :: [ColumnName]) ]
|
2016-02-03 16:00:39 +05:30
|
|
|
notNullVs = [ MissingNotNullConstraint factTableName cName
|
2016-06-22 16:52:04 +05:30
|
|
|
| FactColumn cName DimTime <- factColumns
|
|
|
|
, let col = findColumn cName (tableColumns table)
|
2015-12-29 16:24:40 +05:30
|
|
|
, isJust col
|
|
|
|
, columnNullable (fromJust col) == Null ]
|
|
|
|
typeDefaultVs =
|
|
|
|
[ MissingTypeDefault cType
|
2016-06-22 16:52:04 +05:30
|
|
|
| cName <- [ c | FactColumn c DimVal {..} <- factColumns ]
|
|
|
|
++ [ c | FactColumn c NoDimId <- factColumns ]
|
|
|
|
++ [ c | FactColumn c TenantId <- factColumns ]
|
|
|
|
++ [ c | FactColumn c DimId {..} <- factColumns ]
|
2015-12-29 16:24:40 +05:30
|
|
|
, let col = findColumn cName (tableColumns table)
|
|
|
|
, isJust col
|
|
|
|
, let cType = columnType $ fromJust col
|
2015-12-29 18:22:01 +05:30
|
|
|
, not . any (`Text.isPrefixOf` cType) $ defaults ]
|
2015-12-29 16:24:40 +05:30
|
|
|
|
|
|
|
return $ tableVs ++ parentVs ++ colVs ++ timeVs ++ notNullVs ++ typeDefaultVs
|
2015-12-16 02:05:36 +05:30
|
|
|
where
|
|
|
|
checkFactParents fName = do
|
2016-02-05 16:17:57 +05:30
|
|
|
RawEnv _ facts _ _ <- ask
|
2015-12-16 02:05:36 +05:30
|
|
|
case findFact fName facts of
|
|
|
|
Nothing -> return [ MissingFact fName ]
|
|
|
|
Just pFact -> validateFact pFact
|
|
|
|
|
2015-12-17 20:02:13 +05:30
|
|
|
checkColumn tables table factCol =
|
2015-12-30 12:21:41 +05:30
|
|
|
maybe [] (checkTableForCol table) (factSourceColumnName factCol)
|
2015-12-17 20:02:13 +05:30
|
|
|
++ checkColumnTable tables factCol
|
|
|
|
|
2016-02-03 16:00:39 +05:30
|
|
|
checkColumnTable :: [Table] -> FactColumn -> [ValidationError]
|
|
|
|
checkColumnTable tables FactColumn {..} = case factColType of
|
|
|
|
DimId {factColTargetTable = tName} -> maybe [ MissingTable tName ] (const []) $ findTable tName tables
|
|
|
|
_ -> []
|
2016-02-05 16:17:57 +05:30
|
|
|
|
|
|
|
validateEnv :: [Table] -> [Fact] -> Settings -> TypeDefaults -> Either [ValidationError] Env
|
2016-06-23 12:29:18 +05:30
|
|
|
validateEnv tables facts settings@Settings {..} typeDefaults =
|
2016-02-05 16:17:57 +05:30
|
|
|
flip runReader (RawEnv tables facts settings typeDefaults) $ do
|
|
|
|
tableVs <- concat <$> mapM validateTable tables
|
|
|
|
factVs <- concat <$> mapM validateFact facts
|
|
|
|
let dupTableVs = [ DuplicateTable table | table <- findDups . map tableName $ tables ]
|
|
|
|
let dupFactVs = [ DuplicateFact fact | fact <- findDups . map factName $ facts ]
|
|
|
|
let dupColVs = [ DuplicateColumn tableName col
|
|
|
|
| Table{..} <- tables
|
|
|
|
, col <- findDups . map columnName $ tableColumns ]
|
2016-06-23 12:29:18 +05:30
|
|
|
let dupDimVs = facts
|
|
|
|
>>- concatMap (dimColumnMappings settingDimPrefix)
|
|
|
|
>>> sort
|
|
|
|
>>> groupBy ((==) `on` fst)
|
|
|
|
>>> filter (map snd >>> nub >>> length >>> (/= 1))
|
|
|
|
>>> map (head >>> fst >>> DuplicateDimension)
|
|
|
|
vs = nub $ tableVs ++ factVs ++ dupTableVs ++ dupFactVs ++ dupColVs ++ dupDimVs
|
2016-02-05 16:17:57 +05:30
|
|
|
if null vs
|
|
|
|
then return . Right $ Env tables facts settings typeDefaults
|
|
|
|
else return . Left $ vs
|
|
|
|
where
|
|
|
|
findDups =
|
|
|
|
sort >>> group >>> map (head &&& length) >>> filter (snd >>> (> 1)) >>> map fst
|