mirror of
https://github.com/agentm/project-m36.git
synced 2024-10-03 20:07:58 +03:00
move dataframe creation and sorting to server-side via Client.hs
add preliminary test
This commit is contained in:
parent
d4d1df8083
commit
e9f102334d
@ -77,6 +77,7 @@ Library
|
||||
ProjectM36.Tupleable,
|
||||
ProjectM36.DataConstructorDef,
|
||||
ProjectM36.DataTypes.Basic,
|
||||
ProjectM36.DataTypes.Sorting,
|
||||
ProjectM36.DataTypes.Day,
|
||||
ProjectM36.DataTypes.DateTime,
|
||||
ProjectM36.DataTypes.Either,
|
||||
@ -504,4 +505,12 @@ Executable handles
|
||||
if flag(profiler)
|
||||
GHC-Prof-Options: -fprof-auto -rtsopts -threaded -Wall
|
||||
|
||||
|
||||
Test-Suite test-dataframe
|
||||
Default-Language: Haskell2010
|
||||
Main-Is: test/DataFrame.hs
|
||||
type: exitcode-stdio-1.0
|
||||
Other-Modules: TutorialD.Interpreter, TutorialD.Interpreter.Base, TutorialD.Interpreter.DatabaseContextExpr, TutorialD.Interpreter.DatabaseContextIOOperator, TutorialD.Interpreter.Export.Base, TutorialD.Interpreter.Export.CSV, TutorialD.Interpreter.Import.Base, TutorialD.Interpreter.Import.BasicExamples, TutorialD.Interpreter.Import.CSV, TutorialD.Interpreter.Import.TutorialD, TutorialD.Interpreter.InformationOperator, TutorialD.Interpreter.RODatabaseContextOperator, TutorialD.Interpreter.RelationalExpr, TutorialD.Interpreter.SchemaOperator, TutorialD.Interpreter.TestBase, TutorialD.Interpreter.TransGraphRelationalOperator, TutorialD.Interpreter.TransactionGraphOperator, TutorialD.Interpreter.Types
|
||||
Build-Depends: base, HUnit, Cabal, containers, hashable, unordered-containers, mtl, vector, vector-binary-instances, time, hashable-time, bytestring, uuid, stm, deepseq, deepseq-generics, binary, parallel, cassava, attoparsec, gnuplot, directory, temporary, haskeline, megaparsec, text, base64-bytestring, data-interval, filepath, transformers, stm-containers, list-t, websockets, optparse-applicative, network, aeson, project-m36, random, MonadRandom, semigroups
|
||||
Default-Extensions: OverloadedStrings
|
||||
GHC-Options: -Wall -threaded
|
||||
Hs-Source-Dirs: ./src/bin, ., test/
|
||||
|
@ -1,2 +1,2 @@
|
||||
#!/bin/sh
|
||||
./.cabal-sandbox/bin/ghcid --command="cabal repl $1"
|
||||
~/.cabal/bin/ghcid --command="cabal new-repl $1"
|
||||
|
@ -13,6 +13,7 @@ module TutorialD.Interpreter.Base (
|
||||
import ProjectM36.Base
|
||||
import ProjectM36.AtomType
|
||||
import ProjectM36.Relation
|
||||
import ProjectM36.DataFrame
|
||||
|
||||
#if MIN_VERSION_megaparsec(6,0,0)
|
||||
import Text.Megaparsec.Char
|
||||
@ -59,8 +60,7 @@ displayOpResult (DisplayParseErrorResult mPromptLength err) = do
|
||||
pointyString len = T.justifyRight (len + fromIntegral errorIndent) '_' "^"
|
||||
maybe (pure ()) (TIO.putStrLn . pointyString) mPromptLength
|
||||
TIO.putStr ("ERR:" <> errString)
|
||||
displayOpResult (DisplayDataFrameResult dFrame) = do
|
||||
TIO.putStrLn (showRelation $ fromDataFrame dFrame)
|
||||
displayOpResult (DisplayDataFrameResult dFrame) = TIO.putStrLn (showDataFrame dFrame)
|
||||
|
||||
#if MIN_VERSION_megaparsec(6,0,0)
|
||||
type Parser = Parsec Void Text
|
||||
@ -126,6 +126,13 @@ integer = Lex.signed spaceConsumer Lex.decimal
|
||||
integer = Lex.signed spaceConsumer Lex.integer
|
||||
#endif
|
||||
|
||||
natural :: Parser Integer
|
||||
#if MIN_VERSION_megaparsec(6,0,0)
|
||||
natural = Lex.decimal <* spaceConsumer
|
||||
#else
|
||||
natural = Lex.integer <* spaceConsumer
|
||||
#endif
|
||||
|
||||
float :: Parser Double
|
||||
float = Lex.float
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
{-# LANGUAGE GADTs #-}
|
||||
module TutorialD.Interpreter.RODatabaseContextOperator where
|
||||
import ProjectM36.Base
|
||||
import ProjectM36.Attribute (attributeForName)
|
||||
import ProjectM36.DataFrame
|
||||
import ProjectM36.Relation (attributes)
|
||||
import qualified ProjectM36.DataFrame as DF
|
||||
import ProjectM36.Error
|
||||
import ProjectM36.InclusionDependency
|
||||
import qualified ProjectM36.Client as C
|
||||
@ -27,7 +25,7 @@ data RODatabaseContextOperator where
|
||||
ShowRelationVariables :: RODatabaseContextOperator
|
||||
ShowAtomFunctions :: RODatabaseContextOperator
|
||||
ShowDatabaseContextFunctions :: RODatabaseContextOperator
|
||||
ShowDataFrame :: RelationalExpr -> [AttributeOrderExpr] -> Maybe Integer -> Maybe Integer -> RODatabaseContextOperator
|
||||
ShowDataFrame :: DF.DataFrameExpr -> RODatabaseContextOperator
|
||||
Quit :: RODatabaseContextOperator
|
||||
deriving (Show)
|
||||
|
||||
@ -151,27 +149,11 @@ evalRODatabaseContextOp sessionId conn ShowDatabaseContextFunctions = do
|
||||
Left err -> pure $ DisplayErrorResult (T.pack (show err))
|
||||
Right rel -> evalRODatabaseContextOp sessionId conn (ShowRelation (ExistingRelation rel))
|
||||
|
||||
evalRODatabaseContextOp sessionId conn (ShowDataFrame expr attrOrdersExpr mbOffset mbLimit) = do
|
||||
res <- C.executeRelationalExpr sessionId conn expr
|
||||
case res of
|
||||
Left err -> pure $ DisplayErrorResult $ T.pack (show err)
|
||||
Right rel -> do
|
||||
let relAttrs = attributes rel
|
||||
attrName (AttributeOrderExpr name _) = name
|
||||
order (AttributeOrderExpr _ ord) = ord
|
||||
orders = map order attrOrdersExpr
|
||||
attributeForName' = flip attributeForName relAttrs
|
||||
attrNames = map attrName attrOrdersExpr
|
||||
verified = forM attrNames attributeForName'
|
||||
case verified of
|
||||
Left err -> pure $ DisplayErrorResult $ T.pack (show err)
|
||||
Right attrs -> do
|
||||
let attrOrders = map (\(attr',order')->AttributeOrder attr' order') (zip attrs orders)
|
||||
let dFrame = sortDataFrameBy attrOrders . toDataFrame $ rel
|
||||
let dFrame' = maybe dFrame (`drop'` dFrame) mbOffset
|
||||
let dFrame'' = maybe dFrame' (`take'` dFrame') mbLimit
|
||||
pure $ DisplayDataFrameResult $ dFrame''
|
||||
|
||||
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
|
||||
|
||||
@ -188,24 +170,24 @@ showDataFrameP = do
|
||||
attrOrdersExpr <- attrOrdersExprP
|
||||
mbOffset <- optional offsetP
|
||||
mbLimit <- optional limitP
|
||||
pure $ ShowDataFrame relExpr attrOrdersExpr mbOffset mbLimit
|
||||
pure $ ShowDataFrame (DF.DataFrameExpr relExpr attrOrdersExpr mbOffset mbLimit)
|
||||
|
||||
|
||||
offsetP :: Parser Integer
|
||||
offsetP = do
|
||||
reservedOp "offset"
|
||||
integer
|
||||
natural
|
||||
|
||||
limitP :: Parser Integer
|
||||
limitP = do
|
||||
reservedOp "limit"
|
||||
integer
|
||||
natural
|
||||
|
||||
attrOrdersExprP :: Parser [AttributeOrderExpr]
|
||||
attrOrdersExprP :: Parser [DF.AttributeOrderExpr]
|
||||
attrOrdersExprP = braces (sepBy attrOrderExprP comma)
|
||||
|
||||
attrOrderExprP :: Parser AttributeOrderExpr
|
||||
attrOrderExprP = AttributeOrderExpr <$> identifier <*> orderP
|
||||
attrOrderExprP :: Parser DF.AttributeOrderExpr
|
||||
attrOrderExprP = DF.AttributeOrderExpr <$> identifier <*> orderP
|
||||
|
||||
orderP :: Parser Order
|
||||
orderP = (try $ reservedOp "ASC" >> pure ASC) <|> (try $ reservedOp "DESC" >> pure DESC) <|> pure ASC
|
||||
orderP :: Parser DF.Order
|
||||
orderP = try (reservedOp "ascending" >> pure DF.AscendingOrder) <|> try (reservedOp "descending" >> pure DF.DescendingOrder) <|> pure DF.AscendingOrder
|
||||
|
@ -7,6 +7,7 @@ import qualified Data.Vector as V
|
||||
import qualified Data.Hashable as Hash
|
||||
import qualified Data.HashSet as HS
|
||||
import qualified Data.Map as M
|
||||
import Data.Either
|
||||
|
||||
arity :: Attributes -> Int
|
||||
arity = V.length
|
||||
@ -74,6 +75,9 @@ attributeForName attrName attrs = case V.find (\attr -> attributeName attr == at
|
||||
Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
|
||||
Just attr -> Right attr
|
||||
|
||||
isAttributeNameContained :: AttributeName -> Attributes -> Bool
|
||||
isAttributeNameContained nam attrs = isRight (attributeForName nam attrs)
|
||||
|
||||
--similar to attributesForNames, but returns error if some names are missing
|
||||
projectionAttributesForNames :: S.Set AttributeName -> Attributes -> Either RelationalError Attributes
|
||||
projectionAttributesForNames names attrsIn =
|
||||
|
@ -16,7 +16,8 @@ module ProjectM36.Client
|
||||
closeRemote_,
|
||||
executeRelationalExpr,
|
||||
executeDatabaseContextExpr,
|
||||
executeDatabaseContextIOExpr,
|
||||
executeDatabaseContextIOExpr,
|
||||
executeDataFrameExpr,
|
||||
executeGraphExpr,
|
||||
executeSchemaExpr,
|
||||
executeTransGraphRelationalExpr,
|
||||
@ -86,6 +87,10 @@ module ProjectM36.Client
|
||||
IncDepName,
|
||||
InclusionDependency(..),
|
||||
AttributeName,
|
||||
DF.DataFrame(..),
|
||||
DF.DataFrameExpr(..),
|
||||
DF.AttributeOrderExpr(..),
|
||||
DF.Order(..),
|
||||
RelationalError(..),
|
||||
RequestTimeoutException(..),
|
||||
RemoteProcessDiedException(..),
|
||||
@ -103,6 +108,7 @@ import ProjectM36.Atomable
|
||||
import ProjectM36.AtomFunction as AF
|
||||
import ProjectM36.StaticOptimizer
|
||||
import ProjectM36.Key
|
||||
import qualified ProjectM36.DataFrame as DF
|
||||
import ProjectM36.DatabaseContextFunction as DCF
|
||||
import qualified ProjectM36.IsomorphicSchema as Schema
|
||||
import Control.Monad.State
|
||||
@ -1137,3 +1143,29 @@ withTransaction sessionId conn io successFunc = bracketOnError (pure ()) (const
|
||||
Right _ -> pure (Right val)
|
||||
else -- no updates executed, so don't create a commit
|
||||
pure (Right val)
|
||||
|
||||
executeDataFrameExpr :: SessionId -> Connection -> DF.DataFrameExpr -> IO (Either RelationalError DF.DataFrame)
|
||||
executeDataFrameExpr sessionId conn@(InProcessConnection _) dfExpr = do
|
||||
eRel <- executeRelationalExpr sessionId conn (DF.convertExpr dfExpr)
|
||||
case eRel of
|
||||
Left err -> pure (Left err)
|
||||
Right rel -> do
|
||||
let relAttrs = R.attributes rel
|
||||
attrName (DF.AttributeOrderExpr name _) = name
|
||||
order (DF.AttributeOrderExpr _ ord) = ord
|
||||
orders = map order (DF.orderExprs dfExpr)
|
||||
attributeForName' = flip attributeForName relAttrs
|
||||
attrNames = map attrName (DF.orderExprs dfExpr)
|
||||
verified = forM attrNames attributeForName'
|
||||
case verified of
|
||||
Left err -> pure (Left err)
|
||||
Right attrs -> do
|
||||
let attrOrders = map (\(attr',order') -> DF.AttributeOrder (attributeName attr') order') (zip attrs orders)
|
||||
case DF.sortDataFrameBy attrOrders . DF.toDataFrame $ rel of
|
||||
Left err -> pure (Left err)
|
||||
Right dFrame -> do
|
||||
let dFrame' = maybe dFrame (`DF.drop'` dFrame) (DF.offset dfExpr)
|
||||
dFrame'' = maybe dFrame' (`DF.take'` dFrame') (DF.limit dfExpr)
|
||||
pure (Right dFrame'')
|
||||
executeDataFrameExpr sessionId conn@(RemoteProcessConnection _) dfExpr = remoteCall conn (ExecuteDataFrameExpr sessionId dfExpr)
|
||||
|
||||
|
108
src/lib/ProjectM36/DataFrame.hs
Normal file
108
src/lib/ProjectM36/DataFrame.hs
Normal file
@ -0,0 +1,108 @@
|
||||
{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
|
||||
{- A dataframe is a strongly-typed, ordered list of named tuples. A dataframe differs from a relation in that its tuples are ordered.-}
|
||||
module ProjectM36.DataFrame where
|
||||
import ProjectM36.Base
|
||||
import ProjectM36.Attribute as A
|
||||
import ProjectM36.Error
|
||||
import qualified ProjectM36.Relation as R
|
||||
import ProjectM36.Relation.Show.Term
|
||||
import ProjectM36.DataTypes.Sorting
|
||||
import ProjectM36.AtomType
|
||||
import qualified Data.Vector as V
|
||||
import GHC.Generics
|
||||
import qualified Data.List as L
|
||||
import qualified Data.Set as S
|
||||
import Data.Maybe
|
||||
import qualified Data.Text as T
|
||||
import Data.Binary
|
||||
|
||||
data AttributeOrderExpr = AttributeOrderExpr AttributeName Order deriving (Show, Generic, Binary)
|
||||
data AttributeOrder = AttributeOrder AttributeName Order deriving (Show, Generic, Binary)
|
||||
data Order = AscendingOrder | DescendingOrder deriving (Eq, Show, Generic, Binary)
|
||||
|
||||
data DataFrame = DataFrame Attributes [DataFrameTuple] deriving (Show, Generic, Binary)
|
||||
|
||||
data DataFrameTuple = DataFrameTuple Attributes (V.Vector Atom) deriving (Eq, Show, Generic, Binary)
|
||||
|
||||
attributes :: DataFrame -> Attributes
|
||||
attributes (DataFrame attrs _) = attrs
|
||||
|
||||
tuples :: DataFrame -> [DataFrameTuple]
|
||||
tuples (DataFrame _ tups) = tups
|
||||
|
||||
sortDataFrameBy :: [AttributeOrder] -> DataFrame -> Either RelationalError DataFrame
|
||||
sortDataFrameBy attrOrders frame = do
|
||||
attrs <- mapM (\(AttributeOrder nam _) -> A.attributeForName nam (attributes frame)) attrOrders
|
||||
mapM_ (\attr -> if not (isSortableAtomType (atomType attr)) then
|
||||
Left (AttributeNotSortableError attr)
|
||||
else
|
||||
pure ()) attrs
|
||||
pure $ DataFrame (attributes frame) (sortTuplesBy (compareTupleByAttributeOrders attrOrders) (tuples frame))
|
||||
|
||||
sortTuplesBy :: (DataFrameTuple -> DataFrameTuple -> Ordering) -> [DataFrameTuple] -> [DataFrameTuple]
|
||||
sortTuplesBy = L.sortBy
|
||||
|
||||
compareTupleByAttributeOrders :: [AttributeOrder] -> DataFrameTuple -> DataFrameTuple -> Ordering
|
||||
compareTupleByAttributeOrders attributeOrders tup1 tup2 =
|
||||
let compare' (AttributeOrder attr order) = if order == DescendingOrder
|
||||
then compareTupleByOneAttributeName attr tup2 tup1
|
||||
else compareTupleByOneAttributeName attr tup1 tup2
|
||||
res = map compare' attributeOrders in
|
||||
fromMaybe EQ (L.find (/= EQ) res)
|
||||
|
||||
compareTupleByOneAttributeName :: AttributeName -> DataFrameTuple -> DataFrameTuple -> Ordering
|
||||
compareTupleByOneAttributeName attr tuple1 tuple2 =
|
||||
let eAtom1 = atomForAttributeName attr tuple1
|
||||
eAtom2 = atomForAttributeName attr tuple2 in
|
||||
case eAtom1 of
|
||||
Left err -> error (show err)
|
||||
Right atom1 ->
|
||||
case eAtom2 of
|
||||
Left err -> error (show err)
|
||||
Right atom2 -> compareAtoms atom1 atom2
|
||||
|
||||
atomForAttributeName :: AttributeName -> DataFrameTuple -> Either RelationalError Atom
|
||||
atomForAttributeName attrName (DataFrameTuple tupAttrs tupVec) = case V.findIndex (\attr -> attributeName attr == attrName) tupAttrs of
|
||||
Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
|
||||
Just index -> case tupVec V.!? index of
|
||||
Nothing -> Left (NoSuchAttributeNamesError (S.singleton attrName))
|
||||
Just atom -> Right atom
|
||||
|
||||
take' :: Integer -> DataFrame -> DataFrame
|
||||
take' n (DataFrame attrs tuples') = DataFrame attrs (take (fromInteger n) tuples')
|
||||
|
||||
drop' :: Integer -> DataFrame -> DataFrame
|
||||
drop' n (DataFrame attrs tuples') = DataFrame attrs (drop (fromInteger n) tuples')
|
||||
|
||||
toDataFrame :: Relation -> DataFrame
|
||||
toDataFrame (Relation attrs (RelationTupleSet tuples')) = DataFrame attrs (map (\(RelationTuple tupAttrs tupVec) -> DataFrameTuple tupAttrs tupVec) tuples')
|
||||
|
||||
fromDataFrame :: DataFrame -> Either RelationalError Relation
|
||||
fromDataFrame (DataFrame attrs dftuples) = R.mkRelation attrs (RelationTupleSet tuples')
|
||||
where
|
||||
tuples' = map (\(DataFrameTuple attrs' tupVec) -> RelationTuple attrs' tupVec) dftuples
|
||||
|
||||
--terminal display
|
||||
dataFrameAsTable :: DataFrame -> Table
|
||||
dataFrameAsTable (DataFrame attrs tups) = (header, body)
|
||||
where
|
||||
oAttrNames = orderedAttributeNames attrs
|
||||
oAttrs = orderedAttributes attrs
|
||||
header = "DF" : map prettyAttribute oAttrs
|
||||
body = snd (L.foldl' tupleFolder (1 :: Int,[]) tups)
|
||||
tupleFolder (count, acc) tuple = (count + 1,
|
||||
acc ++ [T.pack (show count) : map (\attrName -> case atomForAttributeName attrName tuple of
|
||||
Left _ -> "?"
|
||||
Right atom -> showAtom 0 atom
|
||||
) oAttrNames])
|
||||
|
||||
showDataFrame :: DataFrame -> StringType
|
||||
showDataFrame = renderTable . dataFrameAsTable
|
||||
|
||||
-- | A Relation can be converted to a DataFrame for sorting, limits, and offsets.
|
||||
data DataFrameExpr = DataFrameExpr {
|
||||
convertExpr :: RelationalExpr,
|
||||
orderExprs :: [AttributeOrderExpr],
|
||||
offset :: Maybe Integer,
|
||||
limit :: Maybe Integer
|
||||
} deriving (Show, Binary, Generic)
|
30
src/lib/ProjectM36/DataTypes/Sorting.hs
Normal file
30
src/lib/ProjectM36/DataTypes/Sorting.hs
Normal file
@ -0,0 +1,30 @@
|
||||
module ProjectM36.DataTypes.Sorting where
|
||||
import ProjectM36.Base
|
||||
|
||||
compareAtoms :: Atom -> Atom -> Ordering
|
||||
compareAtoms (IntegerAtom i1) (IntegerAtom i2) = compare i1 i2
|
||||
compareAtoms (IntAtom i1) (IntAtom i2) = compare i1 i2
|
||||
compareAtoms (DoubleAtom d1) (DoubleAtom d2) = compare d1 d2
|
||||
compareAtoms (TextAtom t1) (TextAtom t2) = compare t1 t2
|
||||
compareAtoms (DayAtom d1) (DayAtom d2) = compare d1 d2
|
||||
compareAtoms (DateTimeAtom d1) (DateTimeAtom d2) = compare d1 d2
|
||||
compareAtoms (ByteStringAtom b1) (ByteStringAtom b2) = compare b1 b2
|
||||
compareAtoms (BoolAtom b1) (BoolAtom b2) = compare b1 b2
|
||||
compareAtoms (RelationAtom _) _ = EQ
|
||||
compareAtoms ConstructedAtom{} _ = EQ
|
||||
compareAtoms _ _ = EQ
|
||||
|
||||
isSortableAtomType :: AtomType -> Bool
|
||||
isSortableAtomType typ = case typ of
|
||||
IntAtomType -> True
|
||||
IntegerAtomType -> True
|
||||
DoubleAtomType -> True
|
||||
TextAtomType -> True
|
||||
DayAtomType -> True
|
||||
DateTimeAtomType -> True
|
||||
ByteStringAtomType -> False
|
||||
BoolAtomType -> True
|
||||
RelationAtomType _ -> False
|
||||
ConstructedAtomType _ _ -> False
|
||||
TypeVariableType _ -> False
|
||||
|
@ -62,6 +62,7 @@ data RelationalError = NoSuchAttributeNamesError (S.Set AttributeName)
|
||||
| AtomTypeNameInUseError AtomTypeName
|
||||
| IncompletelyDefinedAtomTypeWithConstructorError
|
||||
| AtomTypeNameNotInUseError AtomTypeName
|
||||
| AttributeNotSortableError Attribute
|
||||
| FunctionNameInUseError AtomFunctionName
|
||||
| FunctionNameNotInUseError AtomFunctionName
|
||||
| EmptyCommitError
|
||||
@ -141,4 +142,4 @@ data SchemaError = RelVarReferencesMissing (S.Set RelVarName) |
|
||||
-- errors returned from the distributed-process call handlers
|
||||
data ServerError = RequestTimeoutError |
|
||||
ProcessDiedError String
|
||||
deriving (Generic, Binary, Eq)
|
||||
deriving (Generic, Binary, Eq)
|
||||
|
@ -38,16 +38,6 @@ boxBB = "┴"
|
||||
boxC :: StringType
|
||||
boxC = "┼"
|
||||
|
||||
dboxH :: StringType
|
||||
dboxH = "═"
|
||||
dboxL :: StringType
|
||||
dboxL = "╞"
|
||||
dboxR :: StringType
|
||||
dboxR = "╡"
|
||||
|
||||
class TermSize a where
|
||||
termLength :: a -> Int
|
||||
|
||||
--represent a relation as a table similar to those drawn by Date
|
||||
type Cell = StringType
|
||||
type Table = ([Cell], [[Cell]]) --header, body
|
||||
|
@ -26,6 +26,7 @@ serverDefinition testBool ti = defaultProcess {
|
||||
apiHandlers = [
|
||||
handleCall (\conn (ExecuteHeadName sessionId) -> handleExecuteHeadName ti sessionId conn),
|
||||
handleCall (\conn (ExecuteRelationalExpr sessionId expr) -> handleExecuteRelationalExpr ti sessionId conn expr),
|
||||
handleCall (\conn (ExecuteDataFrameExpr sessionId expr) -> handleExecuteDataFrameExpr ti sessionId conn expr),
|
||||
handleCall (\conn (ExecuteDatabaseContextExpr sessionId expr) -> handleExecuteDatabaseContextExpr ti sessionId conn expr),
|
||||
handleCall (\conn (ExecuteDatabaseContextIOExpr sessionId expr) -> handleExecuteDatabaseContextIOExpr ti sessionId conn expr),
|
||||
handleCall (\conn (ExecuteGraphExpr sessionId expr) -> handleExecuteGraphExpr ti sessionId conn expr),
|
||||
|
@ -3,6 +3,7 @@ import ProjectM36.Base hiding (inclusionDependencies)
|
||||
import ProjectM36.IsomorphicSchema
|
||||
import ProjectM36.Client as C
|
||||
import ProjectM36.Error
|
||||
import ProjectM36.DataFrame
|
||||
import Control.Distributed.Process (Process, ProcessId)
|
||||
import Control.Distributed.Process.ManagedProcess (ProcessReply)
|
||||
import Control.Distributed.Process.ManagedProcess.Server (reply)
|
||||
@ -34,6 +35,11 @@ handleExecuteRelationalExpr :: Timeout -> SessionId -> Connection -> RelationalE
|
||||
handleExecuteRelationalExpr ti sessionId conn expr = do
|
||||
ret <- timeoutOrDie ti (executeRelationalExpr sessionId conn expr)
|
||||
reply ret conn
|
||||
|
||||
handleExecuteDataFrameExpr :: Timeout -> SessionId -> Connection -> DataFrameExpr -> Reply (Either RelationalError DataFrame)
|
||||
handleExecuteDataFrameExpr ti sessionId conn expr = do
|
||||
ret <- timeoutOrDie ti (executeDataFrameExpr sessionId conn expr)
|
||||
reply ret conn
|
||||
|
||||
handleExecuteDatabaseContextExpr :: Timeout -> SessionId -> Connection -> DatabaseContextExpr -> Reply (Either RelationalError ())
|
||||
handleExecuteDatabaseContextExpr ti sessionId conn dbexpr = do
|
||||
@ -164,4 +170,4 @@ handleRetrieveTypeConstructorMapping :: Timeout -> SessionId -> Connection -> Re
|
||||
handleRetrieveTypeConstructorMapping ti sessionId conn = do
|
||||
ret <- timeoutOrDie ti (C.typeConstructorMapping sessionId conn)
|
||||
reply ret conn
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ module ProjectM36.Server.RemoteCallTypes where
|
||||
import ProjectM36.Base
|
||||
import ProjectM36.IsomorphicSchema
|
||||
import ProjectM36.TransactionGraph
|
||||
import ProjectM36.DataFrame
|
||||
import ProjectM36.TransGraphRelationalExpression
|
||||
import ProjectM36.Session
|
||||
import GHC.Generics
|
||||
@ -18,6 +19,8 @@ data Logout = Logout
|
||||
deriving (Binary, Generic)
|
||||
data ExecuteRelationalExpr = ExecuteRelationalExpr SessionId RelationalExpr
|
||||
deriving (Binary, Generic)
|
||||
data ExecuteDataFrameExpr = ExecuteDataFrameExpr SessionId DataFrameExpr
|
||||
deriving (Binary, Generic)
|
||||
data ExecuteDatabaseContextExpr = ExecuteDatabaseContextExpr SessionId DatabaseContextExpr
|
||||
deriving (Binary, Generic)
|
||||
data ExecuteDatabaseContextIOExpr = ExecuteDatabaseContextIOExpr SessionId DatabaseContextIOExpr
|
||||
@ -65,4 +68,4 @@ data RetrieveSessionIsDirty = RetrieveSessionIsDirty SessionId
|
||||
data ExecuteAutoMergeToHead = ExecuteAutoMergeToHead SessionId MergeStrategy HeadName
|
||||
deriving (Binary, Generic)
|
||||
data RetrieveTypeConstructorMapping = RetrieveTypeConstructorMapping SessionId
|
||||
deriving (Binary, Generic)
|
||||
deriving (Binary, Generic)
|
||||
|
@ -188,7 +188,7 @@ verifyTuple :: Attributes -> RelationTuple -> Either RelationalError RelationTup
|
||||
verifyTuple attrs tuple = let attrsTypes = V.map atomType attrs
|
||||
tupleTypes = V.map atomTypeForAtom (tupleAtoms tuple) in
|
||||
if V.length attrs /= V.length tupleTypes then
|
||||
Left $ TupleAttributeCountMismatchError 0
|
||||
Left $ traceShow (attrs, tupleTypes) $ TupleAttributeCountMismatchError 0
|
||||
else do
|
||||
mapM_ (uncurry atomTypeVerify) (V.zip attrsTypes tupleTypes)
|
||||
Right tuple
|
||||
@ -207,4 +207,4 @@ reorderTuple attrs tupIn = if tupleAttributes tupIn == attrs then
|
||||
|
||||
--used in Generics derivation for ADTs without named attributes
|
||||
trimTuple :: Int -> RelationTuple -> RelationTuple
|
||||
trimTuple index (RelationTuple attrs vals) = RelationTuple (V.drop index attrs) (V.drop index vals)
|
||||
trimTuple index (RelationTuple attrs vals) = RelationTuple (V.drop index attrs) (V.drop index vals)
|
||||
|
34
test/DataFrame.hs
Normal file
34
test/DataFrame.hs
Normal file
@ -0,0 +1,34 @@
|
||||
import ProjectM36.Client
|
||||
import ProjectM36.DataFrame (atomForAttributeName, tuples)
|
||||
import TutorialD.Interpreter.TestBase
|
||||
|
||||
import Test.HUnit
|
||||
import System.Exit
|
||||
import qualified Data.Set as S
|
||||
|
||||
testList :: Test
|
||||
testList = TestList []
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
tcounts <- runTestTT testList
|
||||
if errors tcounts + failures tcounts > 0 then exitFailure else exitSuccess
|
||||
|
||||
testOrderBy :: Test
|
||||
testOrderBy = TestCase $ do
|
||||
(sessionId, dbconn) <- dateExamplesConnection emptyNotificationCallback
|
||||
Right df <- executeDataFrameExpr sessionId dbconn (
|
||||
DataFrameExpr {
|
||||
convertExpr = Project (AttributeNames (S.singleton "status")) (RelationVariable "s" ()),
|
||||
orderExprs = [AttributeOrderExpr "status" AscendingOrder],
|
||||
offset = Nothing,
|
||||
limit = Nothing
|
||||
})
|
||||
let vals = map (\tup -> case atomForAttributeName "status" tup of
|
||||
Left err -> error (show err)
|
||||
Right atom -> atom) (tuples df)
|
||||
assertEqual "sort order of s" [IntegerAtom 10, IntegerAtom 20, IntegerAtom 30] vals
|
||||
|
||||
--testOffset :: Test
|
||||
|
||||
--testLimit :: Test
|
@ -109,4 +109,4 @@ testDuplicateAttributes = TestCase $ do
|
||||
testExistingRelationType :: Test
|
||||
testExistingRelationType = TestCase $ do
|
||||
let typeResult = runReader (typeForRelationalExpr (ExistingRelation relationTrue)) (RelationalExprStateElems DBC.empty)
|
||||
assertEqual "ExistingRelation with tuples type" (Right relationFalse) typeResult
|
||||
assertEqual "ExistingRelation with tuples type" (Right relationFalse) typeResult
|
||||
|
@ -640,4 +640,4 @@ testAtomFunctionArgumentMismatch = TestCase $ do
|
||||
expectTutorialDErr sessionId dbconn (T.isPrefixOf err1) "x:=relation{tuple{a 5}} where ^gt(@a,1.5)"
|
||||
--wrong argument count
|
||||
let err2 = "FunctionArgumentCountMismatchError"
|
||||
expectTutorialDErr sessionId dbconn (T.isPrefixOf err2) "x:=relation{tuple{a 5}} where ^gt(@a,1,3)"
|
||||
expectTutorialDErr sessionId dbconn (T.isPrefixOf err2) "x:=relation{tuple{a 5}} where ^gt(@a,1,3)"
|
||||
|
Loading…
Reference in New Issue
Block a user