private ITable[] FindChangedTables(ITransaction checkTransaction, CommitTableInfo[] normalizedChangedTables) { var changedTableSource = new ITable[normalizedChangedTables.Length]; // Set up the above arrays for (int i = 0; i < normalizedChangedTables.Length; ++i) { // Get the information for this changed table CommitTableInfo tableInfo = normalizedChangedTables[i]; // Get the master table that changed from the normalized list. TableSource master = tableInfo.Master; // Did this table change since the transaction started? var allTableChanges = tableInfo.ChangesSinceCommit; if (allTableChanges == null || allTableChanges.Length == 0) { // No changes so we can pick the correct IIndexSet from the current // transaction. // Get the state of the changed tables from the Transaction var mtable = Transaction.GetMutableTable(master.TableInfo.TableName); // Get the current index set of the changed table tableInfo.IndexSet = Transaction.GetIndexSetForTable(master); // Flush all index changes in the table mtable.FlushIndexes(); // Set the 'check_transaction' object with the latest version of the // table. checkTransaction.State.UpdateVisibleTable(tableInfo.Master, tableInfo.IndexSet); } else { // There were changes so we need to merge the changes with the // current view of the table. // It's not immediately obvious how this merge update works, but // basically what happens is we WriteByte the table journal with all the // changes into a new IMutableTableDataSource of the current // committed state, and then we flush all the changes into the // index and then update the 'check_transaction' with this change. // Create the IMutableTableDataSource with the changes from this // journal. var mtable = master.GetMutableTable(checkTransaction, tableInfo.Journal); // Get the current index set of the changed table tableInfo.IndexSet = checkTransaction.GetIndexSetForTable(master); // Flush all index changes in the table mtable.FlushIndexes(); // Dispose the table mtable.Dispose(); } // And now refresh the 'changedTableSource' entry changedTableSource[i] = checkTransaction.GetTable(master.TableInfo.TableName); } return(changedTableSource); }
public ITableSource CreateTableSource(TableInfo tableInfo, bool temporary) { lock (commitLock) { try { int tableId = NextTableId(); // Create the object. var storeSystem = StoreSystem; if (temporary) { storeSystem = tempStoreSystem; } var source = new TableSource(this, storeSystem, tableId, tableInfo.TableName.FullName); source.Create(tableInfo); tableSources.Add(tableId, source); if (!temporary) { MarkUncommitted(tableId); StateStore.Flush(); } // And return it. return(source); } catch (IOException e) { throw new InvalidOperationException($"Unable to create source for table '{tableInfo.TableName}'.", e); } } }
public TableSourceGC(TableSource tableSource) { this.tableSource = tableSource; deletedRows = new BlockIndex <int>(); // lastSuccess = DateTime.UtcNow; // lastTry = null; }
public TableSourceGC(TableSource tableSource) { this.tableSource = tableSource; deletedRows = new SortedCollection<SqlObject, long>(); // lastSuccess = DateTime.UtcNow; // lastTry = null; }
internal void UpdateVisibleTable(TableSource table, IIndexSet indexSet) { if (Transaction.ReadOnly()) { throw new Exception("Transaction is Read-only."); } RemoveVisibleTable(table); AddVisibleTable(table, indexSet); }
private CommitTableInfo[] GetNormalizedChangedTables() { // Create a normalized list of TableSource of all tables that // were either changed (and not dropped), and created (and not dropped). // This list represents all tables that are either new or changed in // this transaction. var normalizedChangedTables = new List <CommitTableInfo>(8); // Add all tables that were changed and not dropped in this transaction. normalizedChangedTables.AddRange( ChangedTables.Select(tableJournal => new { tableJournal, tableId = tableJournal.TableId }) .Where(t => !DroppedTables.Contains(t.tableId)) .Select(t => new { t, source = TableSystem.GetTableSource(t.tableId) }) .Select(t => new CommitTableInfo { Master = t.source, Journal = t.t.tableJournal, ChangesSinceCommit = t.source.FindChangesSinceCommit(Id).ToArray() })); // Add all tables that were created and not dropped in this transaction. foreach (var tableId in CreatedTables) { // If this table is not dropped in this transaction then this is a // new table in this transaction. if (!DroppedTables.Contains(tableId)) { TableSource masterTable = TableSystem.GetTableSource(tableId); if (!CommitTableListContains(normalizedChangedTables, masterTable)) { // This is for entries that are created but modified (no journal). var tableInfo = new CommitTableInfo { Master = masterTable }; normalizedChangedTables.Add(tableInfo); } } } return(normalizedChangedTables.ToArray()); }
private TableSource[] GetNormalizedDroppedTables() { // Create a normalized list of TableSource of all tables that // were dropped (and not created) in this transaction. This list // represents tables that will be dropped if the transaction // successfully commits. var normalizedDroppedTables = new List <TableSource>(8); foreach (var tableId in DroppedTables) { // Was this dropped table also created? If it was created in this // transaction then we don't care about it. if (!CreatedTables.Contains(tableId)) { TableSource masterTable = TableSystem.GetTableSource(tableId); normalizedDroppedTables.Add(masterTable); } } return(normalizedDroppedTables.ToArray()); }
public VersionedTableIndexList(TableSource tableSource) { TableSource = tableSource; eventRegistries = new List <TableEventRegistry>(); }
private static bool CommitTableListContains(IEnumerable <CommitTableInfo> list, TableSource master) { return(list.Any(info => info.Master.Equals(master))); }
public IEnumerable <TableSource> Execute(IList <ObjectCommitState> objectStates) { 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(objectStates); // 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 = TableSystem.Database.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.State.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. var 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(TableSystem.Database.OpenTransactions.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. TableSystem.CommitToTables(CreatedTables, DroppedTables); } // Update the namespace clash list if (ObjectsCreated.Any() || ObjectsDropped.Any()) { objectStates.Add(new ObjectCommitState(Id, 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 (ITableEventRegistry changeJournal in ChangedTables) { // The table the changes were made to. int tableId = changeJournal.TableId; // Get the master table with this table id. TableSource master = TableSystem.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(); TableSystem.CloseTransaction(checkTransaction); } // Always ensure a transaction close, even if we have an exception. // Notify the Composite that this transaction has closed. TableSystem.CloseTransaction(Transaction); } catch (Exception) { // TODO: notify the error } finally { Done = true; } } } return(changedTablesList.ToArray()); }
private void CheckConflicts(IEnumerable <ObjectCommitState> namespaceJournals) { AssertNoDirtySelect(); // Check there isn't a namespace clash with database objects. // We need to create a list of all create and drop activity in the // Composite from when the transaction started. var allDroppedObs = new List <ObjectName>(); var allCreatedObs = new List <ObjectName>(); foreach (var nsJournal in namespaceJournals) { if (nsJournal.CommitId >= Id) { allDroppedObs.AddRange(nsJournal.DroppedObjects); allCreatedObs.AddRange(nsJournal.CreatedObjects); } } // The list of all dropped objects since this transaction // began. bool conflict5 = false; ObjectName conflictName = null; string conflictDesc = ""; foreach (ObjectName droppedOb in allDroppedObs) { if (ObjectsDropped.Contains(droppedOb)) { conflict5 = true; conflictName = droppedOb; conflictDesc = "dropped"; } } // The list of all created objects since this transaction // began. foreach (ObjectName createdOb in allCreatedObs) { if (ObjectsCreated.Contains(createdOb)) { conflict5 = true; conflictName = createdOb; conflictDesc = "created"; } } if (conflict5) { // Namespace conflict... throw new ObjectDuplicatedConflictException(conflictName, conflictDesc); } // For each journal, foreach (var changeJournal in ChangedTables) { // The table the change was made to. int tableId = changeJournal.TableId; // Get the master table with this table id. TableSource master = TableSystem.GetTableSource(tableId); // True if the state contains a committed resource with the given name bool committedResource = TableSystem.ContainsVisibleResource(tableId); // Check this table is still in the committed tables list. if (!CreatedTables.Contains(tableId) && !committedResource) { // This table is no longer a committed table, so rollback throw new NonCommittedConflictException(master.TableInfo.TableName); } // Since this journal was created, check to see if any changes to the // tables have been committed since. // This will return all journals on the table with the same commit_id // or greater. var journalsSince = master.FindChangesSinceCommit(Id); // For each journal, determine if there's any clashes. foreach (var tableJournal in journalsSince) { // This will thrown an exception if a commit classes. if (changeJournal.TestCommitClash(tableJournal, out var conflict)) { throw new RowRemoveConflictException(master.TableName, conflict.RowId); } } } // Look at the transaction journal, if a table is dropped that has // journal entries since the last commit then we have an exception // case. foreach (int tableId in DroppedTables) { // Get the master table with this table id. TableSource master = TableSystem.GetTableSource(tableId); // Any journal entries made to this dropped table? if (master.FindChangesSinceCommit(Id).Any()) { // Oops, yes, rollback! throw new DroppedModifiedObjectConflictException(master.TableInfo.TableName); } } }