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); } } }
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(); }
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()); } } }
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); }