@@ -7,6 +7,7 @@ import RelationTupleSet
7import RelationalError
7import RelationalError
8import RelationExpr
8import RelationExpr
9import RelationTerm
9import RelationTerm
10+import RelationalTransaction
10import Text.Parsec
11import Text.Parsec
11import Text.Parsec.String
12import Text.Parsec.String
12import Text.Parsec.Expr
13import Text.Parsec.Expr
@@ -21,14 +22,16 @@ import Control.Monad.State
21import System.Console.Haskeline
22import System.Console.Haskeline
22import System.IO
23import System.IO
23import System.Directory (getHomeDirectory)
24import System.Directory (getHomeDirectory)
25+import qualified Data.UUID as U
26+import Data.UUID.V4 (nextRandom)
25lexer :: Token.TokenParser ()
28lexer :: Token.TokenParser ()
26lexer = Token.makeTokenParser tutD
29lexer = Token.makeTokenParser tutD
27 where tutD = emptyDef {
30 where tutD = emptyDef {
28 Token.reservedOpNames = ["join", "where", "union", "group", "ungroup"],
31 Token.reservedOpNames = ["join", "where", "union", "group", "ungroup"],
29 Token.reservedNames = [],
32 Token.reservedNames = [],
30− Token.identStart = letter <|> char '_',
33+ Token.identStart = letter <|> char '_' <|> char '#',
31− Token.identLetter = alphaNum <|> char '_'}
34+ Token.identLetter = alphaNum <|> char '_' <|> char '#'}
33parens = Token.parens lexer
36parens = Token.parens lexer
34reservedOp = Token.reservedOp lexer
37reservedOp = Token.reservedOp lexer
@@ -38,6 +41,13 @@ identifier = Token.identifier lexer
38comma = Token.comma lexer
41comma = Token.comma lexer
39semi = Token.semi lexer
42semi = Token.semi lexer
44+uuidP :: Parser U.UUID
46+ uuidStr <- many (alphaNum <|> char '-')
47+ case U.fromString uuidStr of
48+ Nothing -> fail "Invalid uuid string"
49+ Just uuid -> return uuid
41--used in projection
51--used in projection
42attributeList :: Parser [AttributeName]
52attributeList :: Parser [AttributeName]
43attributeList = sepBy identifier comma
53attributeList = sepBy identifier comma
@@ -235,41 +245,89 @@ parseString str = case parse multipleDatabaseExpr "" str of
235 Left err -> Left $ ParseError (show err)
245 Left err -> Left $ ParseError (show err)
236 Right r -> Right r
246 Right r -> Right r
238−data TutorialDOperator where
248+--operators which only rely on database context reading
249+data ContextOperator where
239− ShowRelation :: RelationalExpr -> TutorialDOperator
250+ ShowRelation :: RelationalExpr -> ContextOperator
240− ShowRelationType :: RelationalExpr -> TutorialDOperator
251+ ShowRelationType :: RelationalExpr -> ContextOperator
241− ShowConstraint :: String -> TutorialDOperator
252+ ShowConstraint :: String -> ContextOperator
242− Quit :: TutorialDOperator
253+ Quit :: ContextOperator
256+--operators which manipulate a transaction graph
257+data GraphOperator where
258+ JumpToHead :: String -> GraphOperator
259+ JumpToTransaction :: U.UUID -> GraphOperator
260+ Branch :: HeadName -> GraphOperator
261+ Commit :: GraphOperator
262+ Rollback :: GraphOperator
243 deriving (Show)
263 deriving (Show)
245−typeP :: Parser TutorialDOperator
265+typeP :: Parser ContextOperator
246typeP = do
266typeP = do
247− reservedOp ":t"
267+ reservedOp ":type"
248 expr <- relExpr
268 expr <- relExpr
249 return $ ShowRelationType expr
269 return $ ShowRelationType expr
251−showRelP :: Parser TutorialDOperator
271+showRelP :: Parser ContextOperator
252showRelP = do
272showRelP = do
253− reservedOp ":s"
273+ reservedOp ":show"
254 expr <- relExpr
274 expr <- relExpr
255 return $ ShowRelation expr
275 return $ ShowRelation expr
257−quitP :: Parser TutorialDOperator
277+quitP :: Parser ContextOperator
258quitP = do
278quitP = do
259− reservedOp ":q"
279+ reservedOp ":quit"
260 return Quit
280 return Quit
262−showConstraintsP :: Parser TutorialDOperator
282+showConstraintsP :: Parser ContextOperator
263showConstraintsP = do
283showConstraintsP = do
264− reservedOp ":c"
284+ reservedOp ":constraints"
265 constraintName <- option "" identifier
285 constraintName <- option "" identifier
266 return $ ShowConstraint constraintName
286 return $ ShowConstraint constraintName
268−interpreterOps :: Parser TutorialDOperator
288+jumpToHeadP :: Parser GraphOperator
269−interpreterOps = typeP
289+jumpToHeadP = do
290+ reservedOp ":jumphead"
292+ return $ JumpToHead head
294+jumpToTransactionP :: Parser GraphOperator
295+jumpToTransactionP = do
298+ return $ JumpToTransaction uuid
300+branchTransactionP :: Parser GraphOperator
301+branchTransactionP = do
302+ reservedOp ":branch"
303+ branchName <- identifier
304+ return $ Branch branchName
306+commitTransactionP :: Parser GraphOperator
307+commitTransactionP = do
308+ reservedOp ":commit"
311+rollbackTransactionP :: Parser GraphOperator
312+rollbackTransactionP = do
313+ reservedOp ":rollback"
316+transactionGraphOps = do
318+ <|> jumpToTransactionP
319+ <|> branchTransactionP
320+ <|> commitTransactionP
321+ <|> rollbackTransactionP
323+contextOps :: Parser ContextOperator
270− <|> showRelP
325+ <|> showRelP
271− <|> showConstraintsP
326+ <|> showConstraintsP
272− <|> quitP
327+ <|> quitP
329+interpreterOps :: Parser (Either ContextOperator GraphOperator)
330+interpreterOps = liftM Left contextOps <|> liftM Right transactionGraphOps
274showRelationAttributes :: Relation -> String
332showRelationAttributes :: Relation -> String
275showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
333showRelationAttributes rel = "{" ++ concat (L.intersperse ", " $ map showAttribute attrs) ++ "}"
@@ -285,17 +343,17 @@ data TutorialDOperatorResult = QuitResult |
285 DisplayErrorResult String |
343 DisplayErrorResult String |
286 NoActionResult
344 NoActionResult
288−evalTutorialDOp :: DatabaseContext -> TutorialDOperator -> TutorialDOperatorResult
346+evalContextOp :: DatabaseContext -> ContextOperator -> TutorialDOperatorResult
289−evalTutorialDOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
347+evalContextOp context (ShowRelationType expr) = case runState (typeForRelationalExpr expr) context of
290 (Right rel, _) -> DisplayResult $ showRelationAttributes rel
348 (Right rel, _) -> DisplayResult $ showRelationAttributes rel
291 (Left err, _) -> DisplayErrorResult $ show err
349 (Left err, _) -> DisplayErrorResult $ show err
293−evalTutorialDOp context (ShowRelation expr) = do
351+evalContextOp context (ShowRelation expr) = do
294 case runState (evalRelationalExpr expr) context of
352 case runState (evalRelationalExpr expr) context of
295 (Left err, _) -> DisplayErrorResult $ show err
353 (Left err, _) -> DisplayErrorResult $ show err
296 (Right rel, _) -> DisplayResult $ showRelation rel
354 (Right rel, _) -> DisplayResult $ showRelation rel
298−evalTutorialDOp context (ShowConstraint name) = do
356+evalContextOp context (ShowConstraint name) = do
299 DisplayResult $ show filteredDeps
357 DisplayResult $ show filteredDeps
301 deps = inclusionDependencies context
359 deps = inclusionDependencies context
@@ -303,51 +361,114 @@ evalTutorialDOp context (ShowConstraint name) = do
303 "" -> deps
361 "" -> deps
304 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
362 name -> HS.filter (\(InclusionDependency n _ _) -> n == name) deps
364+--I need to pass the transaction graph around instead of the singular context :/
306−evalTutorialDOp context (Quit) = QuitResult
365+evalContextOp context (Quit) = QuitResult
308−example1 = "relA {a,b, c}"
367+-- returns the new "current" transaction, updated graph, and tutorial d result
309−example2 = "relA join relB"
368+-- the current transaction is not part of the transaction graph until it is committed
310−example3 = "relA join relB {x,y,z}"
369+evalGraphOp :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> GraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
311−example4 = "(relA) {x,y,z}"
370+
312−example5 = "relA union relB"
371+--affects only disconncted transaction
313−example6 = "rv1 := true"
372+evalGraphOp newUUID trans graph (JumpToTransaction jumpUUID) = case transactionForUUID jumpUUID graph of
314−example7 = "rv1 := relA union relB"
373+ Nothing -> Left $ NoSuchTransactionError jumpUUID
315−example8 = "relA := true; relB := false"
374+ Just parentTrans -> Right (newTrans, graph)
316−example9 = "relA := relation { SNO CHAR }"
375+ where
376+ newTrans = DisconnectedTransaction jumpUUID (transactionContext parentTrans)
378+-- switch from one head to another
379+-- affects only disconnectedtransaction
380+evalGraphOp newUUID currentTransaction graph (JumpToHead headName) =
381+ case transactionForHead headName graph of
382+ Just newHeadTransaction -> let disconnectedTrans = newDisconnectedTransaction (transactionUUID newHeadTransaction) (transactionContext newHeadTransaction) in
383+ Right (disconnectedTrans, graph)
384+ Nothing -> Left $ NoSuchHeadNameError headName
385+-- add new head pointing to branchPoint
386+-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
387+-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
389+evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
390+ Nothing -> (discon, graph, DisplayErrorResult "Failed to find parent transaction.")
391+ Just parentTrans -> case addBranch newBranchName parentTrans graph of
392+ Nothing -> (discon, graph, DisplayErrorResult "Failed to add branch.")
393+ Just newGraph -> (newDiscon, newGraph, DisplayResult "Branched.")
395+ newDiscon = DisconnectedTransaction (transactionUUID parentTrans) disconContext
398+-- create a new commit and add it to the heads
399+-- technically, the new head could be added to an existing commit, but by adding a new commit, the new head is unambiguously linked to a new commit (with a context indentical to its parent)
400+evalGraphOp newUUID discon@(DisconnectedTransaction parentUUID disconContext) graph (Branch newBranchName) = case transactionForUUID parentUUID graph of
401+ Nothing -> Left $ NoSuchTransactionError parentUUID
402+ Just parentTrans -> case addBranch newUUID newBranchName parentTrans graph of
403+ Nothing -> Left $ NoSuchTransactionError parentUUID -- improve error
404+ Just (newTrans, newGraph) -> Right (newDiscon, newGraph)
406+ newDiscon = DisconnectedTransaction newUUID disconContext
408+-- add the disconnected transaction to the graph
409+-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
410+evalGraphOp newTransUUID discon@(DisconnectedTransaction parentUUID context) graph Commit = case transactionForUUID parentUUID graph of
411+ Nothing -> Left $ NoSuchTransactionError parentUUID
412+ Just parentTransaction -> case headNameForTransaction parentTransaction graph of
413+ Nothing -> Left $ TransactionIsNotAHead parentUUID
414+ Just headName -> case maybeUpdatedGraph of
415+ Nothing -> Left $ TransactionIsNotAHead parentUUID -- add better error
416+ Just (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
418+ newDisconnectedTrans = newDisconnectedTransaction newTransUUID context
419+ --parentTransaction = transactionForUUID parentUUID graph
420+ maybeUpdatedGraph = addDisconnectedTransaction newTransUUID headName discon graph
318interpret :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
422interpret :: DatabaseContext -> String -> (Maybe RelationalError, DatabaseContext)
319interpret context tutdstring = case parseString tutdstring of
423interpret context tutdstring = case parseString tutdstring of
320 Left err -> (Just err, context)
424 Left err -> (Just err, context)
321 Right parsed -> runState (evalContextExpr parsed) context
425 Right parsed -> runState (evalContextExpr parsed) context
323-- for interpreter-specific operations
427-- for interpreter-specific operations
324−interpretOps :: DatabaseContext -> String -> TutorialDOperatorResult
428+interpretOps :: U.UUID -> DisconnectedTransaction -> TransactionGraph -> String -> (DisconnectedTransaction, TransactionGraph, TutorialDOperatorResult)
325−interpretOps context instring = case parse interpreterOps "" instring of
429+interpretOps newUUID trans@(DisconnectedTransaction parentUUID context) transGraph instring = case parse interpreterOps "" instring of
326− Left err -> NoActionResult
430+ Left err -> (trans, transGraph, NoActionResult)
327− Right ops -> evalTutorialDOp context ops
431+ Right ops -> case ops of
432+ Left contextOp -> (trans, transGraph, (evalContextOp context contextOp))
433+ Right graphOp -> case evalGraphOp newUUID trans transGraph graphOp of
434+ Left err -> (trans, transGraph, DisplayErrorResult $ show err)
435+ Right (newDiscon, newGraph) -> (newDiscon, newGraph, DisplayResult "Done.")
329−reprLoop :: DatabaseContext -> IO ()
437+promptText :: DisconnectedTransaction -> TransactionGraph -> String
330−reprLoop context = do
438+promptText (DisconnectedTransaction parentUUID _) graph = "TutorialD (" ++ transInfo ++ "): "
440+ transInfo = case transactionForUUID parentUUID graph of
441+ Nothing -> "unknown"
442+ Just parentTrans -> case headNameForTransaction parentTrans graph of
443+ Nothing -> show $ transactionUUID parentTrans
444+ Just headName -> headName
446+reprLoop :: DisconnectedTransaction -> TransactionGraph -> IO ()
447+reprLoop currentTransaction@(DisconnectedTransaction currentParentUUID currentContext) graph = do
331 homeDirectory <- getHomeDirectory
448 homeDirectory <- getHomeDirectory
332− let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
449+ let settings = defaultSettings {historyFile = Just (homeDirectory ++ "/.tutd_history")}
333− maybeLine <- runInputT settings $ do
451+ maybeLine <- runInputT settings $ getInputLine (promptText currentTransaction graph)
334− getInputLine "TutorialD: "
452+
453+ let roloop = reprLoop currentTransaction graph
335 case maybeLine of
454 case maybeLine of
336 Nothing -> return ()
455 Nothing -> return ()
337 Just line -> do
456 Just line -> do
457+ newUUID <- nextRandom
338− case interpretOps context line of
458+ case interpretOps newUUID currentTransaction graph line of
339− QuitResult -> return ()
459+ (_, _, QuitResult) -> return ()
340− DisplayErrorResult err -> hPutStrLn stderr ("ERR: " ++ err) >> reprLoop context
460+ (_, _, DisplayErrorResult err) -> hPutStrLn stderr ("ERR: " ++ err) >> roloop
341− DisplayResult out -> do
461+ (updatedTrans, updatedGraph, DisplayResult out) -> do
342 putStrLn out
462 putStrLn out
343− reprLoop context
463+ reprLoop updatedTrans updatedGraph
344− NoActionResult -> do
464+ (_, _, NoActionResult) -> do
345− let (value, contextup) = interpret context line
465+ let (value, contextup) = interpret currentContext line
466+ let updatedTransaction = DisconnectedTransaction currentParentUUID contextup
346− case value of
467+ case value of
347− Nothing -> reprLoop contextup
468+ Nothing -> reprLoop updatedTransaction graph
348− (Just err) -> do
469+ Just err -> do
349− hPutStrLn stderr ("ERR:" ++ show err)
470+ hPutStrLn stderr ("ERR:" ++ show err)
350− reprLoop context
471+ reprLoop currentTransaction graph
352restrictionPredicateP :: Parser RestrictionPredicateExpr
473restrictionPredicateP :: Parser RestrictionPredicateExpr
353restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
474restrictionPredicateP = buildExpressionParser predicateOperators predicateTerm
@@ -382,4 +503,8 @@ stringAtomP = liftA StringAtom (string "\"" *> many quotedChar <* string "\"")
382intAtomP = do
503intAtomP = do
383 intstr <- many digit
504 intstr <- many digit
384 return $ IntAtom (read intstr)
505 return $ IntAtom (read intstr)
508+displayTransactionGraph :: TransactionGraph -> String
509+displayTransactionGraph (TransactionGraph heads transSet) = L.intercalate "\n" $ S.foldr (\(Transaction tUUID pUUID _ ) acc -> acc ++ [show tUUID ++ " " ++ show pUUID]) [] transSet