{-# LANGUAGE GADTs #-} module TutorialD.Interpreter.RODatabaseContextOperator where import ProjectM36.Base import ProjectM36.Relation import qualified ProjectM36.DataFrame as DF import ProjectM36.Error import ProjectM36.Tuple import ProjectM36.InclusionDependency import qualified ProjectM36.Client as C import TutorialD.Interpreter.Base import TutorialD.Interpreter.RelationalExpr import TutorialD.Interpreter.DatabaseContextExpr import TutorialD.Printer import Control.Monad.State import qualified Data.Text as T import ProjectM36.Relation.Show.Gnuplot import qualified Data.Map as M import Data.Maybe --operators which only rely on database context reading data RODatabaseContextOperator where ShowRelation :: RelationalExpr -> RODatabaseContextOperator PlotRelation :: RelationalExpr -> RODatabaseContextOperator ShowRelationType :: RelationalExpr -> RODatabaseContextOperator ShowConstraints :: StringType -> RODatabaseContextOperator ShowPlan :: DatabaseContextExpr -> RODatabaseContextOperator ShowTypes :: RODatabaseContextOperator ShowRelationVariables :: RODatabaseContextOperator ShowAtomFunctions :: RODatabaseContextOperator ShowDatabaseContextFunctions :: RODatabaseContextOperator ShowDataFrame :: DF.DataFrameExpr -> RODatabaseContextOperator Quit :: RODatabaseContextOperator deriving (Show) typeP :: Parser RODatabaseContextOperator typeP = do colonOp ":type" ShowRelationType <$> relExprP showRelP :: Parser RODatabaseContextOperator showRelP = do colonOp ":showexpr" ShowRelation <$> relExprP showPlanP :: Parser RODatabaseContextOperator showPlanP = do colonOp ":showplan" ShowPlan <$> databaseContextExprP showTypesP :: Parser RODatabaseContextOperator showTypesP = colonOp ":showtypes" >> pure ShowTypes showRelationVariables :: Parser RODatabaseContextOperator showRelationVariables = colonOp ":showrelvars" >> pure ShowRelationVariables showAtomFunctionsP :: Parser RODatabaseContextOperator showAtomFunctionsP = colonOp ":showatomfunctions" >> pure ShowAtomFunctions showDatabaseContextFunctionsP :: Parser RODatabaseContextOperator showDatabaseContextFunctionsP = colonOp ":showdatabasecontextfunctions" >> pure ShowDatabaseContextFunctions quitP :: Parser RODatabaseContextOperator quitP = do colonOp ":quit" return Quit showConstraintsP :: Parser RODatabaseContextOperator showConstraintsP = do colonOp ":constraints" ShowConstraints <$> option "" identifier plotRelExprP :: Parser RODatabaseContextOperator plotRelExprP = do colonOp ":plotexpr" PlotRelation <$> relExprP roDatabaseContextOperatorP :: Parser RODatabaseContextOperator roDatabaseContextOperatorP = typeP <|> showRelP <|> showRelationVariables <|> plotRelExprP <|> showConstraintsP <|> showPlanP <|> showTypesP <|> showAtomFunctionsP <|> showDatabaseContextFunctionsP <|> showDataFrameP <|> quitP --logically, these read-only operations could happen purely, but not if a remote call is required evalRODatabaseContextOp :: C.SessionId -> C.Connection -> RODatabaseContextOperator -> IO TutorialDOperatorResult evalRODatabaseContextOp sessionId conn (ShowRelationType expr) = do res <- C.typeForRelationalExpr sessionId conn expr case res of Left err -> pure $ DisplayErrorResult $ T.pack (show err) Right rel -> pure $ DisplayRelationResult rel evalRODatabaseContextOp sessionId conn (ShowRelation expr) = do res <- C.executeRelationalExpr sessionId conn expr case res of Left err -> pure $ DisplayErrorResult $ T.pack (show err) Right rel -> pure $ DisplayRelationResult rel evalRODatabaseContextOp sessionId conn (PlotRelation expr) = do res <- C.executeRelationalExpr sessionId conn expr pure $ case res of Left err -> DisplayErrorResult $ T.pack (show err) Right rel -> DisplayIOResult $ do err <- plotRelation rel when (isJust err) $ print err evalRODatabaseContextOp sessionId conn (ShowConstraints name) = do eIncDeps <- C.inclusionDependencies sessionId conn let val = case eIncDeps of Left err -> Left err Right incDeps -> case name of "" -> inclusionDependenciesAsRelation incDeps >>= renderRelExprsInIncDeps depName -> case M.lookup depName incDeps of Nothing -> Left (InclusionDependencyNameNotInUseError depName) Just dep -> inclusionDependenciesAsRelation (M.singleton depName dep) >>= renderRelExprsInIncDeps pure $ case val of Left err -> DisplayErrorResult (T.pack (show err)) Right rel -> DisplayRelationResult rel evalRODatabaseContextOp sessionId conn (ShowPlan dbExpr) = do plan <- C.planForDatabaseContextExpr sessionId conn dbExpr pure $ case plan of Left err -> DisplayErrorResult (T.pack (show err)) Right optDbExpr -> DisplayResult $ T.pack (show optDbExpr) evalRODatabaseContextOp sessionId conn ShowTypes = do eRel <- C.atomTypesAsRelation sessionId conn case eRel of Left err -> pure $ DisplayErrorResult (T.pack (show err)) Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel)) evalRODatabaseContextOp sessionId conn ShowRelationVariables = do eRel <- C.relationVariablesAsRelation sessionId conn case eRel of Left err -> pure $ DisplayErrorResult (T.pack (show err)) Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel)) evalRODatabaseContextOp sessionId conn ShowAtomFunctions = do eRel <- C.atomFunctionsAsRelation sessionId conn case eRel of Left err -> pure $ DisplayErrorResult (T.pack (show err)) Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel)) evalRODatabaseContextOp sessionId conn ShowDatabaseContextFunctions = do eRel <- C.databaseContextFunctionsAsRelation sessionId conn case eRel of Left err -> pure $ DisplayErrorResult (T.pack (show err)) Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel)) evalRODatabaseContextOp sessionId conn (ShowDataFrame dfExpr) = do eDataFrame <- C.executeDataFrameExpr sessionId conn dfExpr case eDataFrame of Left err -> pure (DisplayErrorResult (T.pack (show err))) Right dframe -> pure (DisplayDataFrameResult dframe) evalRODatabaseContextOp _ _ Quit = pure QuitResult interpretRODatabaseContextOp :: C.SessionId -> C.Connection -> T.Text -> IO TutorialDOperatorResult interpretRODatabaseContextOp sessionId conn tutdstring = case parse roDatabaseContextOperatorP "" tutdstring of Left err -> pure $ DisplayErrorResult (T.pack (show err)) Right parsed -> evalRODatabaseContextOp sessionId conn parsed showDataFrameP :: Parser RODatabaseContextOperator showDataFrameP = do colonOp ":showdataframe" relExpr <- relExprP reservedOp "orderby" attrOrdersExpr <- attrOrdersExprP mbOffset <- optional offsetP mbLimit <- optional limitP pure $ ShowDataFrame (DF.DataFrameExpr relExpr attrOrdersExpr mbOffset mbLimit) offsetP :: Parser Integer offsetP = do reservedOp "offset" natural limitP :: Parser Integer limitP = do reservedOp "limit" natural attrOrdersExprP :: Parser [DF.AttributeOrderExpr] attrOrdersExprP = braces (sepBy attrOrderExprP comma) attrOrderExprP :: Parser DF.AttributeOrderExpr attrOrderExprP = DF.AttributeOrderExpr <$> identifier <*> orderP orderP :: Parser DF.Order orderP = try (reservedOp "ascending" >> pure DF.AscendingOrder) <|> try (reservedOp "descending" >> pure DF.DescendingOrder) <|> pure DF.AscendingOrder -- render RelationalExprAtoms as TutorialD renderRelExprsInIncDeps :: Relation -> Either RelationalError Relation renderRelExprsInIncDeps = relMogrify tupMapper attrs where tupMapper tup = pure $ mkRelationTupleFromMap (M.map mapper (tupleToMap tup)) mapper (RelationalExprAtom expr) = TextAtom (T.pack (show (prettyRelationalExpr expr))) mapper atom = atom attrs = C.attributesFromList [Attribute "name" TextAtomType, Attribute "sub" TextAtomType, Attribute "super" TextAtomType ]