module Database.Sql.Util.Lineage.Table where
import Database.Sql.Type
import Database.Sql.Util.Tables
import qualified Data.Set as S
import Data.Set (Set)
import qualified Data.Map as M
import Data.Map (Map)
import qualified Data.Foldable as F
import Data.Functor.Identity
type TableLineage = Map FQTN (Set FQTN)
class HasTableLineage q where
getTableLineage :: q -> TableLineage
instance HasTableLineage (Statement d ResolvedNames a) where
getTableLineage stmt = tableLineage stmt
mkFQTN :: FQTableName a -> FullyQualifiedTableName
mkFQTN (QTableName _ (Identity (QSchemaName _ (Identity (DatabaseName _ database)) schema _)) name) = FullyQualifiedTableName database schema name
emptyLineage :: FullyQualifiedTableName -> TableLineage
emptyLineage fqtn = M.singleton fqtn S.empty
squashTableLineage :: TableLineage -> TableLineage -> TableLineage
squashTableLineage old new =
let new' = M.map (foldMap (\ s -> maybe (S.singleton s) id $ M.lookup s old)) new
in M.union new' old
tableLineage :: Statement d ResolvedNames a -> TableLineage
tableLineage (QueryStmt _) = M.empty
tableLineage (InsertStmt Insert{insertTable = RTableName tableName _, ..}) = case insertValues of
InsertExprValues _ _ -> filterByInsertBehavior soloAncestor
InsertDefaultValues _ -> filterByInsertBehavior soloAncestor
InsertDataFromFile _ _ -> filterByInsertBehavior soloAncestor
InsertSelectValues query ->
let sources = S.insert fqtn $ getTables query
ancestry = M.singleton fqtn sources
in filterByInsertBehavior ancestry
where
fqtn = mkFQTN tableName
soloAncestor = M.singleton fqtn $ S.singleton fqtn
filterByInsertBehavior :: TableLineage -> TableLineage
filterByInsertBehavior ancestry = case insertBehavior of
InsertOverwrite _ -> M.adjust (S.delete fqtn) fqtn ancestry
InsertAppend _ -> ancestry
InsertOverwritePartition _ _ -> ancestry
InsertAppendPartition _ _ -> ancestry
tableLineage (UpdateStmt Update{..}) =
let RTableName table _ = updateTable
fqtn = mkFQTN table
sources = S.insert fqtn $ S.unions [ getTables updateFrom
, getTables updateSetExprs
, getTables updateWhere
]
in M.singleton fqtn sources
tableLineage (DeleteStmt (Delete _ (RTableName table _) maybeExpr)) = case maybeExpr of
Nothing -> emptyLineage fqtn
Just expr ->
let sources = S.insert fqtn $ getTables expr
in M.singleton fqtn sources
where fqtn = mkFQTN table
tableLineage (TruncateStmt (Truncate _ (RTableName table _))) =
M.singleton (mkFQTN table) S.empty
tableLineage (CreateTableStmt CreateTable{createTableName = RCreateTableName tableName _, ..}) = case createTableDefinition of
TableColumns _ _ -> emptyLineage fqtn
TableLike _ _ -> emptyLineage fqtn
TableAs _ _ query -> M.singleton fqtn $ getTables query
TableNoColumnInfo _ -> emptyLineage fqtn
where
fqtn = mkFQTN tableName
tableLineage (DropTableStmt DropTable{dropTableNames = tables}) =
F.foldl' (\acc v ->
case v of
RDropExistingTableName tableName _ -> M.insert (mkFQTN tableName) S.empty acc
RDropMissingTableName _ -> acc
) M.empty tables
tableLineage (AlterTableStmt (AlterTableRenameTable _ (RTableName from _) (RTableName to _))) =
let a = mkFQTN from
d = mkFQTN to
in M.fromList [(d, S.singleton a), (a, S.empty)]
tableLineage (AlterTableStmt (AlterTableRenameColumn _ _ _ _)) = M.empty
tableLineage (AlterTableStmt (AlterTableAddColumns _ _ _)) = M.empty
tableLineage (CreateViewStmt _) = M.empty
tableLineage (DropViewStmt _) = M.empty
tableLineage (CreateSchemaStmt _) = M.empty
tableLineage (GrantStmt _) = M.empty
tableLineage (RevokeStmt _) = M.empty
tableLineage (BeginStmt _) = M.empty
tableLineage (CommitStmt _) = M.empty
tableLineage (RollbackStmt _) = M.empty
tableLineage (ExplainStmt _ _) = M.empty
tableLineage (EmptyStmt _) = M.empty