Пример #1
0
        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());
        }