예제 #1
0
        public void CommitTransaction(SystemTransaction transaction)
        {
            // If the transaction has had no changes, we return immediately
            if (!transaction.HasChanges)
                return;

            // The transaction we are working on through this commit process,
            SystemTransaction workingTransaction;

            // If the given transaction is of the same commit version as the current,
            // then the working transaction is the given transaction
            lock (commitSyncRoot) {
                // The list table ids we updated
                List<long> modifiedTables = new List<long>();

                // If the given transaction is of the same commit version, then the
                // working version is the given.  If there hasn't been a commit since
                // the given transaction was created then no transaction merge is
                // necessary.
                if (transaction.CommitVersion == currentCommitVersion) {
                    workingTransaction = transaction;

                    // We make sure we update the 'modified_tables' list with the set of
                    // unique tables that were changed
                    List<long> alteredTableIds = transaction.AlteredTables;
                    alteredTableIds.Sort();
                    int sz = alteredTableIds.Count;
                    long lastTableId = -1;
                    // This goes through the altered table list provided by the
                    // transaction and creates a unique set of tables that were
                    // altered.
                    for (int i = 0; i < sz; ++i) {
                        long table_id = alteredTableIds[i];
                        if (table_id != lastTableId) {
                            modifiedTables.Add(table_id);
                            lastTableId = table_id;
                        }
                    }

                }
                    // Otherwise we must create a new working transaction
                else {

                    // -----------------
                    // Transaction MERGE
                    // -----------------
                    // This condition occurs when a transaction has been committed after
                    // the given transaction was created, and we need to move any updates
                    // from the committing transaction to the latest update.

                    workingTransaction = new SystemTransaction(this, db.CreateTransaction(), currentCommitVersion, transaction.User.Verified());

                    // SystemTable merges,

                    // First of all replay the general database updates as described in
                    // the journal (table and index creation/drops etc).
                    TransactionJournal tranJournal = transaction.Journal;
                    IEnumerator<JournalEntry> i = tranJournal.GetEnumerator();

                    // The list of tables created in this transaction
                    List<long> tablesCreated = new List<long>();

                    while (i.MoveNext()) {
                        JournalEntry entry = i.Current;
                        JournalCommandCode command = entry.Code;
                        long ident = entry.TableId;
                        // Table commands,
                        if (command == JournalCommandCode.TableCreate) {
                            // Copy the created table from the original transaction to the
                            // working transaction.

                            // We check the table exists in the transaction.  If it doesn't
                            // it means the table was dropped.
                            if (transaction.TableExists(ident)) {
                                // Get the name of the object
                                TableName tname = transaction.GetObjectName(ident);
                                if (workingTransaction.TableExists(tname)) {
                                    // Create fails, the table name exists in the current
                                    // transaction
                                    throw new CommitException("CREATE TABLE '" + tname + "' failed: table with name exists in " +
                                                              "current transaction");
                                }
                                // Otherwise copy the table
                                workingTransaction.CopyTableFrom(transaction, ident);
                                tablesCreated.Add(ident);
                            }
                        }
                            // When the table is structurally changed (columns, added/removed,
                            // etc)
                        else if (command == JournalCommandCode.TableAlter) {
                            long tableId = ident;
                            // If this table was created by this transaction, then we don't
                            // need to worry, we have the most recent version of the table
                            // structure.

                            // Otherwise, the table was altered during the transaction, so
                            // we need to copy the most recent version if it hasn't changed.
                            if (!tablesCreated.Contains(tableId)) {
                                // Check it exists in the current
                                // If it doesn't it means it was dropped
                                if (transaction.TableExists(tableId)) {
                                    // Check the table exists
                                    if (!workingTransaction.TableExists(tableId)) {
                                        throw new CommitException("ALTER TABLE '" + transaction.GetObjectName(tableId) +
                                                                  "' failed: table does not exist in the " + "current transaction");
                                    }

                                    // Check the version of the table we are dropping is the same
                                    // version and exists.
                                    long ver1 = transaction.GetTableVersion(tableId);
                                    long ver2 = workingTransaction.GetTableVersion(tableId);
                                    // If the versions are different, some modification has happened
                                    // to the table so we generate an error
                                    if (ver1 != ver2)
                                        throw new CommitException("ALTER TABLE '" + transaction.GetObjectName(tableId) +
                                                                  "' failed: Table was modified by a concurrent transaction");

                                    // Okay, we can now copy the table.  We drop the existing table
                                    // and copy the new one over.
                                    workingTransaction.DropTable(tableId);
                                    workingTransaction.CopyTableFrom(transaction, tableId);
                                }
                            }

                        } else if (command == JournalCommandCode.TableDrop) {
                            long table_id = ident;
                            // Check the table exists
                            if (!workingTransaction.TableExists(table_id)) {
                                throw new CommitException("DROP TABLE '" + transaction.GetObjectName(table_id) +
                                                          "' failed: Table does not exist in the current transaction");
                            }
                            // Check the version of the table we are dropping is the same
                            // version and exists.
                            long ver1 = transaction.GetTableVersion(table_id);
                            long ver2 = workingTransaction.GetTableVersion(table_id);
                            // If the versions are different, some modification has happened
                            // to the table so we generate an error
                            if (ver1 != ver2) {
                                throw new CommitException("DROP TABLE '" + transaction.GetObjectName(table_id) +
                                                          "' failed: Table was modified by a concurrent transaction");
                            }
                            // Drop the table
                            workingTransaction.DropTable(table_id);
                        }

                            // Index commands,
                        else if (command == JournalCommandCode.IndexAdd) {
                            long indexId = ident;
                            // The name of the table of the index
                            TableName indexTname = transaction.GetIndexName(indexId).TableName;
                            // If the above returns null, it means the index no longer
                            // exists so we skip
                            if (indexTname != null) {
                                // If the table doesn't exist in the working transaction,
                                if (!workingTransaction.TableExists(indexTname))
                                    throw new CommitException("CREATE INDEX on '" + indexTname +
                                                              "' failed: table does not exist in the current transaction");

                                // Get the table id
                                long tableId = workingTransaction.GetTableId(indexTname);

                                // NOTE: This check ensures the index we copy from the transaction
                                //   we are committing is correct and up to date.  We should,
                                //   perhaps, rewrite this to rebuild the index if there were
                                //   concurrent modifications to the table.
                                // If the versions of the table we are creating the index on is
                                // different then we fail,
                                long ver1 = transaction.GetTableVersion(tableId);
                                long ver2 = workingTransaction.GetTableVersion(tableId);
                                // If the versions are different, some modification has happened
                                // to the table so we generate an error
                                if (ver1 != ver2) {
                                    throw new CommitException("CREATE INDEX on '" + indexTname + "' failed: Table was modified by " +
                                                              "a concurrent transaction");
                                }

                                // Copy the created index,
                                workingTransaction.CopyIndexFrom(transaction, ident);
                            }
                        } else if (command == JournalCommandCode.IndexDelete) {
                            long indexId = ident;
                            // Drop the index.  This fails if the index doesn't exist in the
                            // current version.  Note that this will succeed if there were
                            // modifications to the table by a concurrent transaction.
                            workingTransaction.DropIndex(indexId);
                        }
                            // Otherwise unrecognized
                        else {
                            throw new ApplicationException("Unknown journal entry: " + command);
                        }
                    }

                    // Now replay the table level operations,

                    // The list of all tables changed during the lifespan of the
                    // transaction,
                    List<long> alteredTableIds = transaction.AlteredTables;
                    alteredTableIds.Sort();

                    long lastTableId = -1;
                    int sz = alteredTableIds.Count;
                    // For each table id that was altered,
                    for (int n = 0; n < sz; ++n) {
                        long tableId = alteredTableIds[n];
                        if (tableId != lastTableId) {
                            // The SystemTable object
                            SystemTable currentTable = workingTransaction.GetTable(tableId);
                            // If the table no longer exists we ignore the journal and go to
                            // the next table.  We assume the table drop has not broken
                            // database integrity if we have reached this part.
                            if (currentTable != null) {
                                // Add this to the list of tables we updated
                                modifiedTables.Add(tableId);

                                // The table name
                                TableName tableName = currentTable.Name;
                                // The indexes on this table
                                SystemIndexSetDataSource[] indexes = workingTransaction.GetTableIndexes(tableName);

                                // The table from which we are merging in entries,
                                SystemTable toMerge = transaction.GetTable(tableId);
                                // Get the journal for this table id,
                                TransactionJournal journal = toMerge.Journal;
                                // Replay the operations in the journal,
                                IEnumerator<JournalEntry> ti = journal.GetEnumerator();
                                while (ti.MoveNext()) {
                                    JournalEntry entry = ti.Current;
                                    JournalCommandCode command = entry.Code;
                                    RowId ident = entry.RowId;
                                    if (command == JournalCommandCode.RowAdd) {
                                        currentTable.CopyRowIdFrom(toMerge, ident);
                                        // Update the indexes
                                        foreach (IIndexSetDataSource idx in indexes) {
                                            idx.Insert(ident);
                                        }
                                    } else if (command == JournalCommandCode.RowRemove) {
                                        // This will commit exception if the row no longer exists.
                                        currentTable.RemoveRowId(ident);
                                        // Update the indexes
                                        foreach (IIndexSetDataSource idx in indexes) {
                                            idx.Remove(ident);
                                        }
                                    } else if (command == JournalCommandCode.ColumnAdd) {
                                        // Can be ignored, this is handled by a structural change
                                        // entry in the transaction log, and copying the table over
                                        // from the lower version transaction.
                                    } else if (command == JournalCommandCode.ColumnRemove) {
                                        // Can be ignored, this is handled by a structural change
                                        // entry in the transaction log, and copying the table over
                                        // from the lower version transaction.
                                    }
                                }
                            }

                            lastTableId = tableId;
                        }
                    }

                    // We have now successfully replayed all the operations on the working
                    // transaction.  The following artifacts may now be in the database;
                    //   1) Unique constraints may be violated
                    //   2) Referential integrity may be violated
                    //   3) Check constraints need to be checked again if they contain
                    //        nested query functions.

                }

                // Go through the list of updated tables and increment the commit
                // version on each of them
                foreach (long modifiedTableId in modifiedTables) {
                    workingTransaction.IncrementTableVersion(modifiedTableId);
                }

                // Check any deferred constraints on the working_transaction

                // TODO: all the table/index merge functionality here...

                try {
                    // And commit the working transaction
                    db.CommitTransaction(workingTransaction.State);

                    // Increment the commit version number
                    ++currentCommitVersion;
                } catch (Exception e) {
                    throw new CommitException("The database state failed to commit the transaction changes: " + e.Message, e);
                }
            }
        }
예제 #2
0
        public void PerformSample(SystemTransaction transaction)
        {
            // Translate into tables and column names
            ITable tableSource = transaction.GetTable(var.TableName);
            // DOn't bother unless the table has 64 or more values
            if (tableSource.RowCount < (DivisionPointCount * 2)) {
                sampleCount = 0;
                totalSize = 0;
                return;
            }
            // The number of elements in total
            totalSize = tableSource.RowCount;
            // The actual number of samples,
            sampleCount = (int)System.Math.Min(tableSource.RowCount / 2, MaxSampleCount);

            String col_name = var.Name;
            int colId = tableSource.Columns.IndexOf(var.Name);
            // Work out the size
            long size = tableSource.RowCount;
            // The sample point difference
            double sampleDiff = (double)size / sampleCount;
            // The index of the tables used in sampling
            IIndex<RowId> sampleIndex = transaction.CreateTemporaryIndex<RowId>(sampleCount);
            // Create a RowIndexCollation for this
            SqlType type;
            type = tableSource.Columns[colId].Type;
            IndexCollation collation = new IndexCollation(type, col_name);
            // Create the collation object,
            CollationIndexResolver resolver = new CollationIndexResolver(tableSource, collation);

            // The row cursor
            IRowCursor rowCursor = tableSource.GetRowCursor();

            RowId[] sampleRowset = new RowId[sampleCount];

            // First read in the row_ids we are sampling,
            {
                // The current sample point
                double p = 0;
                // The number read,
                int samplesRead = 0;
                // Make a sorted sample index of the dataset
                while (samplesRead < sampleCount) {
                    long pos = ((long)p) - 1;
                    pos = System.Math.Min(pos, tableSource.RowCount - 2);
                    rowCursor.MoveTo(pos);
                    if (!rowCursor.MoveNext())
                        throw new SystemException();

                    RowId rowId = rowCursor.Current;
                    sampleRowset[samplesRead] = rowId;

                    // Should this be Math.random(sample_diff * 2) for random distribution
                    // of the samples?
                    p += sampleDiff;
                    ++samplesRead;
                }
            }

            // Now read the samples,
            {

                int samplePoint = 0;

                foreach (RowId rowId in sampleRowset) {
                    // Hint ahead the samples we are picking,
                    if ((samplePoint % 24) == 0) {
                        for (int i = samplePoint;
                             i < samplePoint + 24 && i < sampleRowset.Length;
                             ++i) {
                            tableSource.PrefetchValue(-1, sampleRowset[i]);
                        }
                    }

                    // Pick the sample and sort it,
                    SqlObject[] sample = new SqlObject[] { tableSource.GetValue(colId, rowId) };
                    sampleIndex.Insert(sample, rowId, resolver);

                    ++samplePoint;
                }
            }

            // Now extract the interesting sample points from the sorted set
            IIndexCursor<RowId> samplesCursor = sampleIndex.GetCursor();
            long sampleIndexSize = sampleIndex.Count;
            double divisionDiff = sampleIndexSize / (DivisionPointCount - 1);
            for (int i = 0; i < DivisionPointCount; ++i) {
                long samplePoint = (long)(divisionDiff * i);
                if (samplePoint >= sampleIndexSize) {
                    samplePoint = sampleIndexSize - 1;
                }

                samplesCursor.Position = samplePoint - 1;
                if (!samplesCursor.MoveNext())
                    throw new SystemException();

                RowId rowId = samplesCursor.Current;
                divisionPoints[i] = tableSource.GetValue(colId, rowId);
            }

            // Clear the temporary index
            sampleIndex.Clear();
        }
예제 #3
0
            public EmbeddedQueryContext(ITable backedTable, SystemTransaction transaction, object syncObject)
            {
                this.syncObject = syncObject;
                isClosed = false;
                this.backedTable = backedTable;

                // Determine the updatability of the result set

                notNotUpdatableReason = null;

                // If the result set is a mutable table data source object,
                if (backedTable is IMutableTable) {
                    updatableView = new UpdatableResultSetView(transaction, (IMutableTable) backedTable, null, backedTable.GetRowCursor());
                } else {
                    // Can we map this to a native table?
                    TableName nativeTableName = QueryProcessor.GetNativeTableName(backedTable);
                    // If we can,
                    if (nativeTableName != null) {
                        // The top table must be an operation table and must have all
                        // FETCHVAR operations,
                        if (backedTable is ExpressionTable) {
                            ExpressionTable expressionTable = (ExpressionTable)backedTable;
                            Expression[] projectionExps = expressionTable.Expressions;
                            foreach (Expression op in projectionExps) {
                                if (QueryProcessor.GetAsVariableRef(op) == null) {
                                    notNotUpdatableReason = "Not updatable, result set contains functional " +
                                                           "projections. Please simplify the select terms.";
                                    break;
                                }
                            }
                            // Otherwise, if it all looks ok, set the updatable table
                            if (notNotUpdatableReason == null) {
                                SystemTable nativeTableSource = transaction.GetTable(nativeTableName);
                                updatableView = new UpdatableResultSetView(transaction, nativeTableSource, projectionExps, backedTable.GetRowCursor());
                            }
                        } else {
                            notNotUpdatableReason = "This result set is not updatable.";
                        }
                    } else {
                        notNotUpdatableReason = "Not updatable, result set does not source " +
                                               "to a native table.";
                    }

                    // If we didn't create an updatable view, we create one with null values
                    // and use if for iterator caching only
                    if (updatableView == null) {
                        updatableView = new UpdatableResultSetView(null, null, null, backedTable.GetRowCursor());
                    }
                }
            }
예제 #4
0
        internal void CopyTableFrom(SystemTransaction transaction, long tableId)
        {
            // We get the table as a TSTableDataSource
            SystemTable tableSource = transaction.GetTable(tableId);
            // Check if an object with this name exists in this transaction,
            TableName tableName = tableSource.Name;
            if (TableExists(tableName))
                // It does exist, so generate an error
                throw new ApplicationException("Table copy failed, table '" + tableName + "' already exists");

            // Copy the table to this transaction
            tableSource.CopyTo(this);

            // Update the directory id,
            AddObject(tableId, tableName, user.Name);
        }