packages feed

project-m36-1.1.0: src/bin/TutorialD/Interpreter/RODatabaseContextOperator.hs

{-# LANGUAGE GADTs #-}
module TutorialD.Interpreter.RODatabaseContextOperator where
import ProjectM36.Base
import ProjectM36.Relation
import ProjectM36.Interpreter
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 (when)
import qualified Data.Text as T
import ProjectM36.Relation.Show.Gnuplot
import ProjectM36.HashSecurely
import qualified Data.Map as M
import Data.Maybe
import Data.Functor

--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
  GetDDLHash :: RODatabaseContextOperator
  ShowDDL :: RODatabaseContextOperator
  ShowRegisteredQueries :: 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 "" identifierP
  
plotRelExprP :: Parser RODatabaseContextOperator  
plotRelExprP = do
  colonOp ":plotexpr"
  PlotRelation <$> relExprP

roDatabaseContextOperatorP :: Parser RODatabaseContextOperator
roDatabaseContextOperatorP = typeP
             <|> showRelP
             <|> showRelationVariables
             <|> plotRelExprP
             <|> showConstraintsP
             <|> showPlanP
             <|> showTypesP
             <|> showAtomFunctionsP
             <|> showDatabaseContextFunctionsP
             <|> showDataFrameP
             <|> ddlHashP
             <|> showDDLP
             <|> showRegisteredQueriesP
             <|> quitP

--logically, these read-only operations could happen purely, but not if a remote call is required
evalRODatabaseContextOp :: C.SessionId -> C.Connection -> RODatabaseContextOperator -> IO ConsoleResult
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 sessionId conn GetDDLHash = do
  eHash <- C.getDDLHash sessionId conn
  case eHash of
    Left err -> pure (DisplayErrorResult (T.pack (show err)))
    Right h -> do
      let eRel = mkRelationFromList (C.attributesFromList [Attribute "ddlHash" ByteStringAtomType]) [[ByteStringAtom (_unSecureHash h)]]
      case eRel of
        Left err -> pure (DisplayErrorResult (T.pack (show err)))
        Right rel -> 
          evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel))

evalRODatabaseContextOp sessionId conn ShowDDL = do
  eDDL <- C.ddlAsRelation sessionId conn
  case eDDL of
    Left err -> pure (DisplayErrorResult (T.pack (show err)))
    Right ddl ->
      evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation ddl))

evalRODatabaseContextOp sessionId conn ShowRegisteredQueries = do
  eRv <- C.registeredQueriesAsRelation sessionId conn
  case eRv of
    Left err -> pure (DisplayErrorResult (T.pack (show err)))
    Right rv ->
      evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rv))      
 
evalRODatabaseContextOp _ _ Quit = pure QuitResult

interpretRODatabaseContextOp :: C.SessionId -> C.Connection -> T.Text -> IO ConsoleResult
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"
  ShowDataFrame <$> dataFrameP

dataFrameP :: Parser DF.DataFrameExpr
dataFrameP = do
  relExpr <- parens relExprP
  attrOrdersExpr <- try attrOrdersExprP <|> pure []
  mbLimit <- optional limitP
  mbOffset <- optional offsetP
  pure $ 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 = reserved "orderby" *> braces (sepBy attrOrderExprP comma)

attrOrderExprP :: Parser DF.AttributeOrderExpr
attrOrderExprP = DF.AttributeOrderExpr <$> identifierP <*> 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
                                 ]
    
ddlHashP :: Parser RODatabaseContextOperator
ddlHashP = colonOp ":ddlhash" $> GetDDLHash

showDDLP :: Parser RODatabaseContextOperator
showDDLP = colonOp ":showddl" $> ShowDDL

showRegisteredQueriesP :: Parser RODatabaseContextOperator
showRegisteredQueriesP = colonOp ":showregisteredqueries" $> ShowRegisteredQueries