internal IEnumerable <TableSource> Commit(IList <TransactionObjectState> nameSpaceJournals) { var changedTablesList = new List <TableSource>(); // This is a transaction that will represent the view of the database // at the end of the commit ITransaction checkTransaction = null; bool entriesCommitted = false; try { // ---- Commit check stage ---- CheckConflicts(nameSpaceJournals); // Tests passed so go on to commit, // ---- Commit stage ---- var normalizedChangedTables = GetNormalizedChangedTables(); var normalizedDroppedTables = GetNormalizedDroppedTables(); // We now need to create a ITransaction object that we // use to send to the triggering mechanism. This // object represents a very specific view of the // transaction. This view contains the latest version of changed // tables in this transaction. It also contains any tables that have // been created by this transaction and does not contain any tables // that have been dropped. Any tables that have not been touched by // this transaction are shown in their current committed state. // To summarize - this view is the current view of the database plus // any modifications made by the transaction that is being committed. // How this works - All changed tables are merged with the current // committed table. All created tables are added into check_transaction // and all dropped tables are removed from check_transaction. If // there were no other changes to a table between the time the // transaction was created and now, the view of the table in the // transaction is used, otherwise the latest changes are merged. // Note that this view will be the view that the database will // ultimately become if this transaction successfully commits. Also, // you should appreciate that this view is NOT exactly the same as // the current trasaction view because any changes that have been // committed by concurrent transactions will be reflected in this view. // Create a new transaction of the database which will represent the // committed view if this commit is successful. checkTransaction = Composite.CreateTransaction(IsolationLevel.Serializable); // Overwrite this view with tables from this transaction that have // changed or have been added or dropped. // (Note that order here is important). First drop any tables from // this view. foreach (TableSource masterTable in normalizedDroppedTables) { // Drop this table in the current view checkTransaction.RemoveVisibleTable(masterTable); } // Now add any changed tables to the view. // Represents view of the changed tables var changedTableSource = FindChangedTables(checkTransaction, normalizedChangedTables); // The 'checkTransaction' now represents the view the database will be // if the commit succeeds. We Lock 'checkTransaction' so it is // Read-only (the view is immutable). checkTransaction.ReadOnly(true); CheckConstraintViolations(checkTransaction, normalizedChangedTables, changedTableSource); // Deferred trigger events. FireChangeEvents(normalizedChangedTables); // NOTE: This isn't as fail safe as it could be. We really need to // do the commit in two phases. The first writes updated indices to // the index files. The second updates the header pointer for the // respective table. Perhaps we can make the header update // procedure just one file Write. // Finally, at this point all constraint checks have passed and the // changes are ready to finally be committed as permanent changes // to the Composite. All that needs to be done is to commit our // IIndexSet indices for each changed table as final. // ISSUE: Should we separate the 'committing of indexes' changes and // 'committing of delete/add flags' to make the FS more robust? // It would be more robust if all indexes are committed in one go, // then all table flag data. // Set flag to indicate we have committed entries. entriesCommitted = true; // For each change to each table, foreach (CommitTableInfo tableInfo in normalizedChangedTables) { // Get the journal that details the change to the table. TableEventRegistry changeJournal = tableInfo.Journal; if (changeJournal != null) { // Get the master table with this table id. TableSource master = tableInfo.Master; // Commit the changes to the table. // We use 'this.commit_id' which is the current commit level we are // at. master.CommitTransactionChange(Composite.CurrentCommitId, changeJournal, tableInfo.IndexSet); // Add to 'changed_tables_list' changedTablesList.Add(master); } } // Only do this if we've created or dropped tables. if (CreatedTables.Any() || DroppedTables.Any()) { // Update the committed tables in the Composite state. // This will update and synchronize the headers in this Composite. Composite.CommitToTables(CreatedTables, DroppedTables); } // Update the namespace clash list if (ObjectsCreated.Any() || ObjectsDropped.Any()) { nameSpaceJournals.Add(new TransactionObjectState(CommitId, ObjectsCreated, ObjectsDropped)); } } finally { try { // If entries_committed == false it means we didn't get to a point // where any changed tables were committed. Attempt to rollback the // changes in this transaction if they haven't been committed yet. if (entriesCommitted == false) { // For each change to each table, foreach (TableEventRegistry changeJournal in ChangedTables) { // The table the changes were made to. int tableId = changeJournal.TableId; // Get the master table with this table id. TableSource master = Composite.GetTableSource(tableId); // Commit the rollback on the table. master.RollbackTransactionChange(changeJournal); } // TODO: Notify the system we're rolling back } } finally { try { // Dispose the 'checkTransaction' if (checkTransaction != null) { checkTransaction.Dispose(); Composite.CloseTransaction(checkTransaction); } // Always ensure a transaction close, even if we have an exception. // Notify the Composite that this transaction has closed. Composite.CloseTransaction(Transaction); } catch (Exception) { // TODO: notify the error } finally { Done = true; } } } return(changedTablesList.ToArray()); }