@@ -7,16 +7,19 @@ import ProjectM36.Relation
7import ProjectM36.TupleSet
7import ProjectM36.TupleSet
8import ProjectM36.Tuple
8import ProjectM36.Tuple
9import ProjectM36.RelationalExpression
9import ProjectM36.RelationalExpression
10+import ProjectM36.TransactionGraph.Merge
11+import qualified ProjectM36.DisconnectedTransaction as Discon
10import qualified Data.Vector as V
13import qualified Data.Vector as V
11import qualified ProjectM36.Attribute as A
14import qualified ProjectM36.Attribute as A
12import qualified Data.UUID as U
15import qualified Data.UUID as U
13import qualified Data.Set as S
16import qualified Data.Set as S
14import qualified Data.Map as M
17import qualified Data.Map as M
15import Control.Monad
18import Control.Monad
19+import Data.Time.Clock
16import qualified Data.Text as T
20import qualified Data.Text as T
17import GHC.Generics
21import GHC.Generics
18import Data.Binary
22import Data.Binary
19−import ProjectM36.TransactionGraph.Merge
20import Data.Either (lefts, rights, isRight)
23import Data.Either (lefts, rights, isRight)
21import Data.Monoid
24import Data.Monoid
22import Control.Arrow
25import Control.Arrow
@@ -35,13 +38,15 @@ data TransactionIdLookup = TransactionIdLookup TransactionId |
36-- | Used for git-style head backtracking such as topic~3^2.
39-- | Used for git-style head backtracking such as topic~3^2.
37data TransactionIdHeadBacktrack = TransactionIdHeadParentBacktrack Int | -- ^ git equivalent of ~: walk back n parents, arbitrarily choosing a parent when a choice must be made
40data TransactionIdHeadBacktrack = TransactionIdHeadParentBacktrack Int | -- ^ git equivalent of ~: walk back n parents, arbitrarily choosing a parent when a choice must be made
38− TransactionIdHeadBranchBacktrack Int -- ^ git equivalent of ^: walk back one parent level to the nth arbitrarily-chosen parent
41+ TransactionIdHeadBranchBacktrack Int | -- ^ git equivalent of ^: walk back one parent level to the nth arbitrarily-chosen parent
42+ TransactionStampHeadBacktrack UTCTime -- ^ git equivalent of 'git-rev-list -n 1 --before X' find the first transaction which was created before the timestamp
39 deriving (Show, Eq, Binary, Generic)
43 deriving (Show, Eq, Binary, Generic)
42-- | Operators which manipulate a transaction graph and which transaction the current 'Session' is based upon.
46-- | Operators which manipulate a transaction graph and which transaction the current 'Session' is based upon.
43data TransactionGraphOperator = JumpToHead HeadName |
47data TransactionGraphOperator = JumpToHead HeadName |
44 JumpToTransaction TransactionId |
48 JumpToTransaction TransactionId |
49+ WalkBackToTime UTCTime |
45 Branch HeadName |
50 Branch HeadName |
46 DeleteBranch HeadName |
51 DeleteBranch HeadName |
47 MergeTransactions MergeStrategy HeadName HeadName |
52 MergeTransactions MergeStrategy HeadName HeadName |
@@ -56,12 +61,12 @@ isCommit _ = False
56data ROTransactionGraphOperator = ShowGraph
61data ROTransactionGraphOperator = ShowGraph
57 deriving Show
62 deriving Show
59−bootstrapTransactionGraph :: TransactionId -> DatabaseContext -> TransactionGraph
64+bootstrapTransactionGraph :: UTCTime -> TransactionId -> DatabaseContext -> TransactionGraph
60−bootstrapTransactionGraph freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
65+bootstrapTransactionGraph stamp freshId context = TransactionGraph bootstrapHeads bootstrapTransactions
62 bootstrapHeads = M.singleton "master" freshTransaction
67 bootstrapHeads = M.singleton "master" freshTransaction
63 newSchemas = Schemas context M.empty
68 newSchemas = Schemas context M.empty
64− freshTransaction = Transaction freshId (TransactionInfo U.nil S.empty) newSchemas
69+ freshTransaction = Transaction freshId (TransactionInfo U.nil S.empty stamp) newSchemas
65 bootstrapTransactions = S.singleton freshTransaction
70 bootstrapTransactions = S.singleton freshTransaction
67emptyTransactionGraph :: TransactionGraph
72emptyTransactionGraph :: TransactionGraph
@@ -98,33 +103,33 @@ transactionsForIds idSet graph = do
98 return (S.fromList transList)
103 return (S.fromList transList)
100isRootTransaction :: Transaction -> TransactionGraph -> Bool
105isRootTransaction :: Transaction -> TransactionGraph -> Bool
101−isRootTransaction (Transaction _ (TransactionInfo pId _) _) _ = U.null pId
106+isRootTransaction (Transaction _ (TransactionInfo pId _ _) _) _ = U.null pId
102isRootTransaction (Transaction _ MergeTransactionInfo{} _) _ = False
107isRootTransaction (Transaction _ MergeTransactionInfo{} _) _ = False
104-- the first transaction has no parent - all other do have parents- merges have two parents
109-- the first transaction has no parent - all other do have parents- merges have two parents
105parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
110parentTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
106−parentTransactions (Transaction _ (TransactionInfo pId _) _) graph = do
111+parentTransactions (Transaction _ (TransactionInfo pId _ _) _) graph = do
107 trans <- transactionForId pId graph
112 trans <- transactionForId pId graph
108 return (S.singleton trans)
113 return (S.singleton trans)
110−parentTransactions (Transaction _ (MergeTransactionInfo pId1 pId2 _) _ ) graph = transactionsForIds (S.fromList [pId1, pId2]) graph
115+parentTransactions (Transaction _ (MergeTransactionInfo pId1 pId2 _ _) _ ) graph = transactionsForIds (S.fromList [pId1, pId2]) graph
113childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
118childTransactions :: Transaction -> TransactionGraph -> Either RelationalError (S.Set Transaction)
114−childTransactions (Transaction _ (TransactionInfo _ children) _) = transactionsForIds children
119+childTransactions (Transaction _ (TransactionInfo _ children _) _) = transactionsForIds children
115−childTransactions (Transaction _ (MergeTransactionInfo _ _ children) _) = transactionsForIds children
120+childTransactions (Transaction _ (MergeTransactionInfo _ _ children _) _) = transactionsForIds children
117-- create a new commit and add it to the heads
122-- create a new commit and add it to the heads
118-- 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)
123-- 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)
119−addBranch :: TransactionId -> HeadName -> TransactionId -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
124+addBranch :: UTCTime -> TransactionId -> HeadName -> TransactionId -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
120−addBranch newId newBranchName branchPointId graph = do
125+addBranch stamp newId newBranchName branchPointId graph = do
121 parentTrans <- transactionForId branchPointId graph
126 parentTrans <- transactionForId branchPointId graph
122− let newTrans = Transaction newId (TransactionInfo branchPointId S.empty) (schemas parentTrans)
127+ let newTrans = Transaction newId (TransactionInfo branchPointId S.empty stamp) (schemas parentTrans)
123 addTransactionToGraph newBranchName newTrans graph
128 addTransactionToGraph newBranchName newTrans graph
125--adds a disconnected transaction to a transaction graph at some head
130--adds a disconnected transaction to a transaction graph at some head
126−addDisconnectedTransaction :: TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
131+addDisconnectedTransaction :: UTCTime -> TransactionId -> HeadName -> DisconnectedTransaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
127−addDisconnectedTransaction newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName (Transaction newId (TransactionInfo parentId S.empty) schemas')
132+addDisconnectedTransaction stamp newId headName (DisconnectedTransaction parentId schemas' _) = addTransactionToGraph headName (Transaction newId (TransactionInfo parentId S.empty stamp) schemas')
130addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
135addTransactionToGraph :: HeadName -> Transaction -> TransactionGraph -> Either RelationalError (Transaction, TransactionGraph)
@@ -201,20 +206,30 @@ walkChildTransactions seenTransSet graph trans =
202-- returns the new "current" transaction, updated graph, and tutorial d result
207-- returns the new "current" transaction, updated graph, and tutorial d result
203-- the current transaction is not part of the transaction graph until it is committed
208-- the current transaction is not part of the transaction graph until it is committed
204−evalGraphOp :: TransactionId -> DisconnectedTransaction -> TransactionGraph -> TransactionGraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
209+evalGraphOp :: UTCTime -> TransactionId -> DisconnectedTransaction -> TransactionGraph -> TransactionGraphOperator -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
206−evalGraphOp _ _ graph (JumpToTransaction jumpId) = case transactionForId jumpId graph of
211+evalGraphOp _ _ _ graph (JumpToTransaction jumpId) = case transactionForId jumpId graph of
207 Left err -> Left err
212 Left err -> Left err
208 Right parentTrans -> Right (newTrans, graph)
213 Right parentTrans -> Right (newTrans, graph)
210 newTrans = DisconnectedTransaction jumpId (schemas parentTrans) False
215 newTrans = DisconnectedTransaction jumpId (schemas parentTrans) False
212-- switch from one head to another
217-- switch from one head to another
213−evalGraphOp _ _ graph (JumpToHead headName) =
218+evalGraphOp _ _ _ graph (JumpToHead headName) =
214 case transactionForHead headName graph of
219 case transactionForHead headName graph of
215 Just newHeadTransaction -> let disconnectedTrans = DisconnectedTransaction (transactionId newHeadTransaction) (schemas newHeadTransaction) False in
220 Just newHeadTransaction -> let disconnectedTrans = DisconnectedTransaction (transactionId newHeadTransaction) (schemas newHeadTransaction) False in
216 Right (disconnectedTrans, graph)
221 Right (disconnectedTrans, graph)
217 Nothing -> Left $ NoSuchHeadNameError headName
222 Nothing -> Left $ NoSuchHeadNameError headName
224+evalGraphOp _ _ discon graph (WalkBackToTime backTime) = do
225+ let startTransId = Discon.parentId discon
226+ jumpDest <- backtrackGraph graph startTransId (TransactionStampHeadBacktrack backTime)
227+ case transactionForId jumpDest graph of
228+ Left err -> Left err
230+ let disconnectedTrans = DisconnectedTransaction (transactionId trans) (schemas trans) False
231+ Right (disconnectedTrans, graph)
218-- add new head pointing to branchPoint
233-- add new head pointing to branchPoint
219-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
234-- repoint the disconnected transaction to the new branch commit (with a potentially different disconnected context)
220-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
235-- affects transactiongraph and the disconnectedtransaction is recreated based off the branch
@@ -230,15 +245,15 @@ evalGraphOp newId discon@(DisconnectedTransaction parentId disconContext) graph
231-- create a new commit and add it to the heads
246-- create a new commit and add it to the heads
232-- 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)
247-- 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)
233−evalGraphOp newId (DisconnectedTransaction parentId schemas' _) graph (Branch newBranchName) = do
248+evalGraphOp stamp newId (DisconnectedTransaction parentId schemas' _) graph (Branch newBranchName) = do
234 let newDiscon = DisconnectedTransaction newId schemas' False
249 let newDiscon = DisconnectedTransaction newId schemas' False
235− case addBranch newId newBranchName parentId graph of
250+ case addBranch stamp newId newBranchName parentId graph of
236 Left err -> Left err
251 Left err -> Left err
237 Right (_, newGraph) -> Right (newDiscon, newGraph)
252 Right (_, newGraph) -> Right (newDiscon, newGraph)
239-- add the disconnected transaction to the graph
254-- add the disconnected transaction to the graph
240-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
255-- affects graph and disconnectedtransaction- the new disconnectedtransaction's parent is the freshly committed transaction
241−evalGraphOp newTransId discon@(DisconnectedTransaction parentId schemas' _) graph Commit = case transactionForId parentId graph of
256+evalGraphOp stamp newTransId discon@(DisconnectedTransaction parentId schemas' _) graph Commit = case transactionForId parentId graph of
242 Left err -> Left err
257 Left err -> Left err
243 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
258 Right parentTransaction -> case headNameForTransaction parentTransaction graph of
244 Nothing -> Left $ TransactionIsNotAHeadError parentId
259 Nothing -> Left $ TransactionIsNotAHeadError parentId
@@ -247,18 +262,18 @@ evalGraphOp newTransId discon@(DisconnectedTransaction parentId schemas' _) grap
247 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
262 Right (_, updatedGraph) -> Right (newDisconnectedTrans, updatedGraph)
249 newDisconnectedTrans = DisconnectedTransaction newTransId schemas' False
264 newDisconnectedTrans = DisconnectedTransaction newTransId schemas' False
250− maybeUpdatedGraph = addDisconnectedTransaction newTransId headName discon graph
265+ maybeUpdatedGraph = addDisconnectedTransaction stamp newTransId headName discon graph
252-- refresh the disconnected transaction, return the same graph
267-- refresh the disconnected transaction, return the same graph
253−evalGraphOp _ (DisconnectedTransaction parentId _ _) graph Rollback = case transactionForId parentId graph of
268+evalGraphOp _ _ (DisconnectedTransaction parentId _ _) graph Rollback = case transactionForId parentId graph of
254 Left err -> Left err
269 Left err -> Left err
255 Right parentTransaction -> Right (newDiscon, graph)
270 Right parentTransaction -> Right (newDiscon, graph)
257 newDiscon = DisconnectedTransaction parentId (schemas parentTransaction) False
272 newDiscon = DisconnectedTransaction parentId (schemas parentTransaction) False
259−evalGraphOp newId (DisconnectedTransaction parentId _ _) graph (MergeTransactions mergeStrategy headNameA headNameB) = mergeTransactions newId parentId mergeStrategy (headNameA, headNameB) graph
274+evalGraphOp stamp newId (DisconnectedTransaction parentId _ _) graph (MergeTransactions mergeStrategy headNameA headNameB) = mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) graph
261−evalGraphOp _ discon graph@(TransactionGraph graphHeads transSet) (DeleteBranch branchName) = case transactionForHead branchName graph of
276+evalGraphOp _ _ discon graph@(TransactionGraph graphHeads transSet) (DeleteBranch branchName) = case transactionForHead branchName graph of
262 Nothing -> Left (NoSuchHeadNameError branchName)
277 Nothing -> Left (NoSuchHeadNameError branchName)
263 Just _ -> Right (discon, TransactionGraph (M.delete branchName graphHeads) transSet)
278 Just _ -> Right (discon, TransactionGraph (M.delete branchName graphHeads) transSet)
@@ -269,6 +284,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
269 mkRelationFromList attrs tupleMatrix
284 mkRelationFromList attrs tupleMatrix
271 attrs = A.attributesFromList [Attribute "id" TextAtomType,
286 attrs = A.attributesFromList [Attribute "id" TextAtomType,
287+ Attribute "stamp" DateTimeAtomType,
272 Attribute "parents" (RelationAtomType parentAttributes),
288 Attribute "parents" (RelationAtomType parentAttributes),
273 Attribute "current" BoolAtomType,
289 Attribute "current" BoolAtomType,
274 Attribute "head" TextAtomType
290 Attribute "head" TextAtomType
@@ -277,6 +293,7 @@ graphAsRelation (DisconnectedTransaction parentId _ _) graph@(TransactionGraph _
277 tupleGenerator transaction = case transactionParentsRelation transaction graph of
293 tupleGenerator transaction = case transactionParentsRelation transaction graph of
278 Left err -> Left err
294 Left err -> Left err
279 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
295 Right parentTransRel -> Right [TextAtom $ T.pack $ show (transactionId transaction),
296+ DateTimeAtom (transactionTimestamp transaction),
280 RelationAtom parentTransRel,
297 RelationAtom parentTransRel,
281 BoolAtom $ parentId == transactionId transaction,
298 BoolAtom $ parentId == transactionId transaction,
282 TextAtom $ fromMaybe "" (headNameForTransaction transaction graph)
299 TextAtom $ fromMaybe "" (headNameForTransaction transaction graph)
@@ -303,16 +320,16 @@ evalROGraphOp discon graph ShowGraph = do
305-- | Execute the merge strategy against the transactions, returning a new transaction which can be then added to the transaction graph
322-- | Execute the merge strategy against the transactions, returning a new transaction which can be then added to the transaction graph
306−createMergeTransaction :: TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
323+createMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
307−createMergeTransaction newId (SelectedBranchMergeStrategy selectedBranch) graph t2@(trans1, trans2) = do
324+createMergeTransaction stamp newId (SelectedBranchMergeStrategy selectedBranch) graph t2@(trans1, trans2) = do
308 selectedTrans <- validateHeadName selectedBranch graph t2
325 selectedTrans <- validateHeadName selectedBranch graph t2
309− pure $ Transaction newId (MergeTransactionInfo (transactionId trans1) (transactionId trans2) S.empty) (schemas selectedTrans)
326+ pure $ Transaction newId (MergeTransactionInfo (transactionId trans1) (transactionId trans2) S.empty stamp) (schemas selectedTrans)
311-- merge functions, relvars, individually
328-- merge functions, relvars, individually
312−createMergeTransaction newId strat@UnionMergeStrategy graph t2 = createUnionMergeTransaction newId strat graph t2
329+createMergeTransaction stamp newId strat@UnionMergeStrategy graph t2 = createUnionMergeTransaction stamp newId strat graph t2
314-- merge function, relvars, but, on error, just take the component from the preferred branch
331-- merge function, relvars, but, on error, just take the component from the preferred branch
315−createMergeTransaction newId strat@(UnionPreferMergeStrategy _) graph t2 = createUnionMergeTransaction newId strat graph t2
332+createMergeTransaction stamp newId strat@(UnionPreferMergeStrategy _) graph t2 = createUnionMergeTransaction stamp newId strat graph t2
317-- | Returns the correct Transaction for the branch name in the graph and ensures that it is one of the two transaction arguments in the tuple.
334-- | Returns the correct Transaction for the branch name in the graph and ensures that it is one of the two transaction arguments in the tuple.
318validateHeadName :: HeadName -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
335validateHeadName :: HeadName -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
@@ -352,8 +369,8 @@ subGraphOfFirstCommonAncestor origGraph resultHeads currentTrans goalTrans trave
352 else -- we found a path
369 else -- we found a path
353 Right (TransactionGraph resultHeads (S.unions (traverseSet : pathsFound)))
370 Right (TransactionGraph resultHeads (S.unions (traverseSet : pathsFound)))
355− oneParent (Transaction _ (TransactionInfo parentId _) _) = transactionForId parentId origGraph
372+ oneParent (Transaction _ (TransactionInfo parentId _ _) _) = transactionForId parentId origGraph
356− oneParent (Transaction _ (MergeTransactionInfo parentId _ _) _) = transactionForId parentId origGraph
373+ oneParent (Transaction _ (MergeTransactionInfo parentId _ _ _) _) = transactionForId parentId origGraph
358-- | Search from a past graph point to all following heads for a specific transaction. If found, return the transaction path, otherwise a RelationalError.
375-- | Search from a past graph point to all following heads for a specific transaction. If found, return the transaction path, otherwise a RelationalError.
359pathToTransaction :: TransactionGraph -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError (S.Set Transaction)
376pathToTransaction :: TransactionGraph -> Transaction -> Transaction -> S.Set Transaction -> Either RelationalError (S.Set Transaction)
@@ -376,8 +393,8 @@ pathToTransaction graph currentTransaction targetTransaction accumTransSet = do
376 else --we have some paths!
393 else --we have some paths!
377 Right (S.unions paths)
394 Right (S.unions paths)
379−mergeTransactions :: TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
396+mergeTransactions :: UTCTime -> TransactionId -> TransactionId -> MergeStrategy -> (HeadName, HeadName) -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
380−mergeTransactions newId parentId mergeStrategy (headNameA, headNameB) graph = do
397+mergeTransactions stamp newId parentId mergeStrategy (headNameA, headNameB) graph = do
381 let transactionForHeadErr name = case transactionForHead name graph of
398 let transactionForHeadErr name = case transactionForHead name graph of
382 Nothing -> Left (NoSuchHeadNameError name)
399 Nothing -> Left (NoSuchHeadNameError name)
383 Just t -> Right t
400 Just t -> Right t
@@ -387,7 +404,7 @@ mergeTransactions newId parentId mergeStrategy (headNameA, headNameB) graph = do
387 let subHeads = M.filterWithKey (\k _ -> elem k [headNameA, headNameB]) (transactionHeadsForGraph graph)
404 let subHeads = M.filterWithKey (\k _ -> elem k [headNameA, headNameB]) (transactionHeadsForGraph graph)
388 subGraph <- subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
405 subGraph <- subGraphOfFirstCommonAncestor graph subHeads transA transB S.empty
389 subGraph' <- filterSubGraph subGraph subHeads
406 subGraph' <- filterSubGraph subGraph subHeads
390− case createMergeTransaction newId mergeStrategy subGraph' (transA, transB) of
407+ case createMergeTransaction stamp newId mergeStrategy subGraph' (transA, transB) of
391 Left err -> Left (MergeTransactionError err)
408 Left err -> Left (MergeTransactionError err)
392 Right mergedTrans -> case checkConstraints (concreteDatabaseContext mergedTrans) of
409 Right mergedTrans -> case checkConstraints (concreteDatabaseContext mergedTrans) of
393 Just err -> Left err
410 Just err -> Left err
@@ -423,8 +440,8 @@ filterSubGraph graph heads = Right $ TransactionGraph newHeads newTransSet
423 newHeads = M.map (filterTransaction validIds) heads
440 newHeads = M.map (filterTransaction validIds) heads
425--helper function for commonalities in union merge
442--helper function for commonalities in union merge
426−createUnionMergeTransaction :: TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
443+createUnionMergeTransaction :: UTCTime -> TransactionId -> MergeStrategy -> TransactionGraph -> (Transaction, Transaction) -> Either MergeError Transaction
427−createUnionMergeTransaction newId strategy graph (t1,t2) = do
444+createUnionMergeTransaction stamp newId strategy graph (t1,t2) = do
428 let contextA = concreteDatabaseContext t1
445 let contextA = concreteDatabaseContext t1
429 contextB = concreteDatabaseContext t2
446 contextB = concreteDatabaseContext t2
@@ -452,7 +469,7 @@ createUnionMergeTransaction newId strategy graph (t1,t2) = do
452 typeConstructorMapping = types
469 typeConstructorMapping = types
454 newSchemas = Schemas newContext (subschemas t1)
471 newSchemas = Schemas newContext (subschemas t1)
455− pure (Transaction newId (MergeTransactionInfo (transactionId t1) (transactionId t2) S.empty) newSchemas)
472+ pure (Transaction newId (MergeTransactionInfo (transactionId t1) (transactionId t2) S.empty stamp) newSchemas)
457lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
474lookupTransaction :: TransactionGraph -> TransactionIdLookup -> Either RelationalError Transaction
458lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
475lookupTransaction graph (TransactionIdLookup tid) = transactionForId tid graph
@@ -488,23 +505,34 @@ backtrackGraph graph currentTid (TransactionIdHeadBranchBacktrack steps) = do
488 Left (ParentCountTraversalError (S.size parents) steps)
505 Left (ParentCountTraversalError (S.size parents) steps)
490 pure (S.elemAt (steps - 1) parents)
507 pure (S.elemAt (steps - 1) parents)
509+backtrackGraph graph currentTid btrack@(TransactionStampHeadBacktrack stamp) = do
510+ trans <- transactionForId currentTid graph
511+ let parents = transactionParentIds trans
512+ if transactionTimestamp trans < stamp then
514+ else if S.null parents then
515+ Left RootTransactionTraversalError
517+ let arbitraryParent = head (S.toList parents) in
518+ backtrackGraph graph arbitraryParent btrack
492-- | Create a temporary branch for commit, merge the result to head, delete the temporary branch. This is useful to atomically commit a transaction, avoiding a TransactionIsNotHeadError but trading it for a potential MergeError.
520-- | Create a temporary branch for commit, merge the result to head, delete the temporary branch. This is useful to atomically commit a transaction, avoiding a TransactionIsNotHeadError but trading it for a potential MergeError.
493--this is not a GraphOp because it combines multiple graph operations
521--this is not a GraphOp because it combines multiple graph operations
494−autoMergeToHead :: (TransactionId, TransactionId, TransactionId) -> DisconnectedTransaction -> HeadName -> MergeStrategy -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
522+autoMergeToHead :: UTCTime -> (TransactionId, TransactionId, TransactionId) -> DisconnectedTransaction -> HeadName -> MergeStrategy -> TransactionGraph -> Either RelationalError (DisconnectedTransaction, TransactionGraph)
495−autoMergeToHead (tempBranchTransId, tempCommitTransId, mergeTransId) discon mergeToHeadName strat graph = do
523+autoMergeToHead stamp (tempBranchTransId, tempCommitTransId, mergeTransId) discon mergeToHeadName strat graph = do
496 let tempBranchName = "mergebranch_" <> U.toText tempBranchTransId
524 let tempBranchName = "mergebranch_" <> U.toText tempBranchTransId
497 --create the temp branch
525 --create the temp branch
498− (discon', graph') <- evalGraphOp tempBranchTransId discon graph (Branch tempBranchName)
526+ (discon', graph') <- evalGraphOp stamp tempBranchTransId discon graph (Branch tempBranchName)
500 --commit to the new branch- possible future optimization: don't require fsync for this- create a temp commit type
528 --commit to the new branch- possible future optimization: don't require fsync for this- create a temp commit type
501− (discon'', graph'') <- evalGraphOp tempCommitTransId discon' graph' Commit
529+ (discon'', graph'') <- evalGraphOp stamp tempCommitTransId discon' graph' Commit
503 --create the merge
531 --create the merge
504− (discon''', graph''') <- evalGraphOp mergeTransId discon'' graph'' (MergeTransactions strat tempBranchName mergeToHeadName)
532+ (discon''', graph''') <- evalGraphOp stamp mergeTransId discon'' graph'' (MergeTransactions strat tempBranchName mergeToHeadName)
506 --delete the temp branch
534 --delete the temp branch
507− (discon'''', graph'''') <- evalGraphOp tempBranchTransId discon''' graph''' (DeleteBranch tempBranchName)
535+ (discon'''', graph'''') <- evalGraphOp stamp tempBranchTransId discon''' graph''' (DeleteBranch tempBranchName)
509 let rel = runReader (evalRelationalExpr (RelationVariable "s" ())) (mkRelationalExprState $ D.concreteDatabaseContext discon'''')
537 let rel = runReader (evalRelationalExpr (RelationVariable "s" ())) (mkRelationalExprState $ D.concreteDatabaseContext discon'''')
510 traceShowM rel
538 traceShowM rel