public DbIndex GetIndex(string columnName) { CheckColumnNameValid(columnName); StringDictionary p = TableProperties; long columnId = p.GetValue(columnName + ".id", -1); if (columnId == -1) { throw new ApplicationException("Column '" + columnName + "' not found"); } if (!p.GetValue(columnName + ".index", false)) { throw new ApplicationException("Column '" + columnName + "' is not indexed"); } // Fetch the index object, IDataFile df = GetDataFile(GetIndexIdKey(columnId)); SortedIndex list = new SortedIndex(df); // And return it, IIndexedObjectComparer <string> comparer = GetIndexComparerFor(columnName, columnId); return(new DbIndex(this, currentVersion, comparer, columnId, list)); }
public void Update(DbRow row) { if (rowBufferId != 0) { throw new ApplicationException("State error: previous table operation not completed"); } // Check row is currently indexed, long rowid = row.RowId; IDataFile df = GetDataFile(rowIndexKey); SortedIndex rows = new SortedIndex(df); if (!rows.ContainsSortKey(rowid)) { throw new ApplicationException("Row being updated is not in the table"); } if (rowBuffer == null) { rowBuffer = new Dictionary <string, string>(); } rowBufferId = rowid; // Copy from the existing data in the row, string[] cols = ColumnNames; foreach (string col in cols) { string val = row.GetValue(col); if (val != null) { rowBuffer[col] = val; } } }
public Key RemoveItem(String name) { ++directoryVersion; StringDictionary pset = new StringDictionary(GetDataFile(propertySetKey)); long id = pset.GetValue <long>(name, -1); // Assert the item is stored, if (id == -1) { throw new ApplicationException("Item not found: " + name); } pset.SetValue(name, null); SortedIndex iset = new SortedIndex(GetDataFile(indexSetKey)); iset.Remove(name, id, collator); // Delete the associated datafile Key k = GetItemKey(id); IDataFile df = GetDataFile(k); df.Delete(); return(k); }
public DbRowCursor GetReverseCursor() { IDataFile df = GetDataFile(rowIndexKey); SortedIndex list = new SortedIndex(df); return(new DbRowCursor(this, currentVersion, new DbIndex.ReverseCursor(list.GetCursor()))); }
public Key AddItem(string name) { ++directoryVersion; StringDictionary pset = new StringDictionary(GetDataFile(propertySetKey)); // Assert the item isn't already stored, if (pset.GetValue <long>(name, -1) != -1) { throw new ApplicationException("Item already exists: " + name); } // Generate a unique identifier for the name, long id = GenerateId(); pset.SetValue(name, id); SortedIndex iset = new SortedIndex(GetDataFile(indexSetKey)); iset.Insert(name, id, collator); Key itemKey = GetItemKey(id); IDataFile df = GetDataFile(itemKey); try { BinaryWriter dout = new BinaryWriter(new DataFileStream(df), Encoding.Unicode); dout.Write(name); } catch (IOException e) { throw new ApplicationException(e.Message); } return(itemKey); }
public int Delete(IEnumerator <DbRow> rows) { int deleteCount = 0; while (rows.MoveNext()) { DbRow row = rows.Current; if (row == null) { continue; } long rowid = row.RowId; IDataFile df = GetDataFile(rowIndexKey); SortedIndex rowsIndex = new SortedIndex(df); if (rowsIndex.ContainsSortKey(rowid)) { // Remove the row from the main index, RemoveRowFromRowSet(rowid); // Remove the row from any indexes defined on the table, RemoveRowFromIndexSet(rowid); // Delete the row file IDataFile rowFile = GetDataFile(GetRowIdKey(rowid)); rowFile.Delete(); // Add this event to the transaction log, AddTransactionEvent("deleteRow", rowid); deleteCount++; } } ++currentVersion; return(deleteCount); }
private void RemoveRowFromRowSet(long rowid) { // Get the index object IDataFile df = GetDataFile(rowIndexKey); SortedIndex rows = new SortedIndex(df); // Remove the row in rowid value sorted order rows.RemoveSortKey(rowid); }
private void AddRowToRowSet(long rowid) { // Get the index object IDataFile df = GetDataFile(rowIndexKey); SortedIndex rows = new SortedIndex(df); // Add the row in rowid value sorted order rows.InsertSortKey(rowid); }
private void AddRowToIndexSet(long rowid) { // Get the set of columns that are indexed in this table, string[] indexedCols = IndexedColumns; foreach (String col in indexedCols) { // Resolve the column name to an id, turn it into a OrderedList64Bit, // and insert the row in the correct location in the index, long columnid = GetColumnId(col); IDataFile df = GetDataFile(GetIndexIdKey(columnid)); SortedIndex index = new SortedIndex(df); IIndexedObjectComparer <string> indexComparer = GetIndexComparerFor(col, columnid); index.Insert(GetValue(rowid, columnid), rowid, indexComparer); } }
public void CheckTableDataDelete(String table, long rowid) { // Is it in the modification set? if (tableDataChanged != null) { DbTable t; if (tableDataChanged.TryGetValue(table, out t)) { // Yes, so check if the given row in the modification set, SortedIndex deleteSet = t.Deletes; if (deleteSet.ContainsSortKey(rowid)) { // Yes, so generate a commit fault, throw new CommitFaultException(String.Format("Row in Table ''{0}'' was modified by a concurrent transaction", table)); } } } }
private void BuildIndex(long columnid, IIndexedObjectComparer <string> indexComparer) { // Get the index object IDataFile df = GetDataFile(GetIndexIdKey(columnid)); // Get the index and clear it, SortedIndex index = new SortedIndex(df); index.Clear(); // For each row in this table, foreach (DbRow row in this) { // Get the column value and the rowid string columnValue = row.GetValue(columnid); long rowid = row.RowId; // Add it into the index, index.Insert(columnValue, rowid, indexComparer); } // Done. }
public void BeginUpdate(long rowid) { if (rowBufferId != 0) { throw new ApplicationException("State error: previous table operation not completed"); } // Check row is currently indexed, IDataFile df = GetDataFile(rowIndexKey); SortedIndex rows = new SortedIndex(df); if (!rows.ContainsSortKey(rowid)) { throw new ApplicationException("Row being updated is not in the table"); } if (rowBuffer == null) { rowBuffer = new Dictionary <string, string>(); } rowBufferId = rowid; }
internal void PrepareForCommit() { // Write the transaction log for this table, IDataFile df = GetDataFile(AddLog); df.Delete(); SortedIndex addlist = new SortedIndex(df); foreach (long v in addRowList) { addlist.InsertSortKey(v); } df = GetDataFile(RemoveLog); df.Delete(); SortedIndex deletelist = new SortedIndex(df); foreach (long v in deleteRowList) { if (addlist.ContainsSortKey(v)) { addlist.RemoveSortKey(v); } else { deletelist.InsertSortKey(v); } } // Set the id gen key if (currentIdGen != -1) { StringDictionary p = TableProperties; p.SetValue("k", currentIdGen); } }
public void Delete(DbRow row) { long rowid = row.RowId; IDataFile df = GetDataFile(rowIndexKey); SortedIndex rows = new SortedIndex(df); if (!rows.ContainsSortKey(rowid)) { throw new ApplicationException("Row being deleted is not in the table"); } // Remove the row from the main index, RemoveRowFromRowSet(rowid); // Remove the row from any indexes defined on the table, RemoveRowFromIndexSet(rowid); // Delete the row file IDataFile rowFile = GetDataFile(GetRowIdKey(rowid)); rowFile.Delete(); // Add this event to the transaction log, AddTransactionEvent("deleteRow", rowid); ++currentVersion; }
public DataAddress Commit(IPathConnection connection, DataAddress rootNode) { // Turn the proposal into a proposed_transaction, ITransaction t = connection.CreateTransaction(rootNode); DbTransaction proposedTransaction = new DbTransaction(null, rootNode, t); try { // Fetch the base root from the proposed_transaction log, TextReader reader = proposedTransaction.GetLogReader(); string baseRootStr = reader.ReadLine(); // If 'base_root_str' is "no base root" then it means we are commiting // an introduced transaction that is not an iteration of previous // snapshots. if (baseRootStr.Equals("no base root")) { // In which case, we publish the proposed snapshot unconditionally // and return. connection.Publish(rootNode); return(rootNode); } DataAddress baseRoot = DataAddress.Parse(baseRootStr); // Find all the entries since this base DataAddress[] roots = connection.GetSnapshots(baseRoot); // If there are no roots, we can publish the proposed_transaction // unconditionally if (roots.Length == 0) { connection.Publish(rootNode); return(rootNode); } // Check historical log for clashes, and if none, replay the commands // in the log. // For each previous root, we build a structure that can answer the // following questions; // * Is file [name] created, deleted or changed in this root? // * Is table [name] created or deleted in this root? // * Is table [name] structurally changed in this root? // * Has row [rowid] been deleted in table [name] in this root? RootEvents[] rootEventSet = new RootEvents[roots.Length]; int i = 0; // TODO: RootEvents is pre-computed information which we could // store in a local cache for some speed improvements, so we don't // need to walk through through the same data multiple times. foreach (DataAddress root in roots) { RootEvents rootEvents = new RootEvents(); rootEventSet[i] = rootEvents; ++i; // Create a transaction object for this root ITransaction rootT = connection.CreateTransaction(root); DbTransaction rootTransaction = new DbTransaction(null, root, rootT); // Make a reader object for the log, TextReader rootReader = rootTransaction.GetLogReader(); // Read the base root from this transaction, String baseRootParent = rootReader.ReadLine(); // If 'bast_root_parent' is 'no base root' then it means a version // has been introduced that is not an iteration of previous // snapshots. In this case, it is not possible to merge updates // therefore we generate a commit fault. if (baseRootParent.Equals("no base root")) { throw new CommitFaultException("Transaction history contains introduced version."); } // Go through each log entry and determine if there's a clash, string rootLine = rootReader.ReadLine(); while (rootLine != null) { String mfile = rootLine.Substring(2); // This represents a file modification, bool unknownCommand = false; if (rootLine.StartsWith("F")) { rootEvents.SetFileChange(mfile); } // This is a table modification, else if (rootLine.StartsWith("T")) { char c = rootLine[1]; // If this is a table create or delete event, if (c == 'C' || c == 'D') { rootEvents.SetTableCreateOrDelete(mfile); } // This is a table structural change, else if (c == 'S') { rootEvents.SetTableStructuralChange(mfile); } // This is a table data change event, else if (c == 'M') { DbTable table = rootTransaction.GetTable(mfile); rootEvents.SetTableDataChange(mfile, table); } else { unknownCommand = true; } } else { unknownCommand = true; } if (unknownCommand) { throw new ApplicationException("Unknown transaction command: " + rootLine); } // Read the next log entry, rootLine = rootReader.ReadLine(); } } // Now we have a set of RootEvents objects that describe what // happens in each previous root. // Now replay the events in the proposal transaction in the latest // transaction. // A transaction representing the current state, DataAddress currentRoot = connection.GetSnapshot(); ITransaction currentT = connection.CreateTransaction(currentRoot); DbTransaction currentTransaction = new DbTransaction(null, currentRoot, currentT); String entry = reader.ReadLine(); while (entry != null) { String mfile = entry.Substring(2); // If it's a file entry, we need to check the file hasn't been // changed in any way in any roots if (entry.StartsWith("F")) { foreach (RootEvents events in rootEventSet) { events.CheckFileChange(mfile); } // All checks passed, so perform the operation currentTransaction.ReplayFileLogEntry(entry, proposedTransaction); } // If it's a table entry, else if (entry.StartsWith("T")) { // Check that a table with this name hasn't been created, deleted // or modified, foreach (RootEvents events in rootEventSet) { // This fails on any event on this table, except a data change // (insert or delete) events.CheckTableMetaChange(mfile); } // The type of operation, char c = entry[1]; // Is it a table structural change? if (c == 'S') { // A structural change can only happen if all the roots leave the // table untouched, foreach (RootEvents events in rootEventSet) { // This fails if it finds a delete event for this rowid events.CheckTableDataChange(mfile); } } // Is it a table modification command? else if (c == 'M') { // This is a table modification, we need to check the rowid // logs and look for possible clashes, // The delete set from the proposed transaction, DbTable proposedTable = proposedTransaction.GetTable(mfile); SortedIndex deleteSet = proposedTable.Deletes; IIndexCursor dsi = deleteSet.GetCursor(); while (dsi.MoveNext()) { long rowid = dsi.Current; foreach (RootEvents events in rootEventSet) { // This fails if it finds a delete event for this rowid events.CheckTableDataDelete(mfile, rowid); } } } // Go through each root, if the data in the table was changed // by any of the roots, we set 'has_data_changes' to true; bool hasDataChanges = false; foreach (RootEvents events in rootEventSet) { if (events.HasTableDataChanges(mfile)) { hasDataChanges = true; } } // Ok, checks passed, so reply all the data changes on the table currentTransaction.ReplayTableLogEntry(entry, proposedTransaction, hasDataChanges); } else { throw new ApplicationException("Unknown transaction command: " + entry); } // Read the next log entry, entry = reader.ReadLine(); } // Refresh the transaction log currentTransaction.RefreshTransactionLog(); // Flush and publish the change DataAddress finalRoot = connection.CommitTransaction(currentT); connection.Publish(finalRoot); // Done. return(finalRoot); } catch (IOException e) { throw new ApplicationException("IO Error: " + e.Message); } }
public ItemList(Directory directory, SortedIndex sortedIndex) { this.directory = directory; this.sortedIndex = sortedIndex; localDirVersion = directory.directoryVersion; }
internal void MergeFrom(DbTable from, bool structuralChange, bool historicDataChange) { // If structural_change is true, this can only happen if 'from' is the // immediate child of this table. // If 'historic_data_change' is false, this can only happen if 'from' is // the immediate child of this table. // Handle structural change, if (structuralChange || historicDataChange == false) { // Fetch all the indexes, string[] fromIndexes = from.IndexedColumns; List <long> fromIndexIds = new List <long>(fromIndexes.Length); foreach (String findex in fromIndexes) { fromIndexIds.Add(from.GetColumnId(findex)); } // Copy them into here, CopyFile(from.GetDataFile(from.rowIndexKey), GetDataFile(rowIndexKey)); foreach (long indexId in fromIndexIds) { // Copy all the indexes here, CopyFile(from.GetDataFile(from.GetIndexIdKey(indexId)), GetDataFile(GetIndexIdKey(indexId))); } // Move the column and index information into this table, CopyFile(from.propertiesFile, propertiesFile); // Copy the transaction logs CopyFile(from.GetDataFile(from.AddLog), GetDataFile(AddLog)); CopyFile(from.GetDataFile(from.RemoveLog), GetDataFile(RemoveLog)); // Replay the add and remove transaction events SortedIndex addEvents = new SortedIndex(from.GetDataFile(from.AddLog)); SortedIndex removeEvents = new SortedIndex(from.GetDataFile(from.RemoveLog)); // Adds foreach (long rowid in addEvents) { CopyFile(from.GetDataFile(from.GetRowIdKey(rowid)), GetDataFile(GetRowIdKey(rowid))); } // Removes foreach (long rowid in removeEvents) { // Delete the row data file, GetDataFile(GetRowIdKey(rowid)).Delete(); } } else { // If we are here, then we are merging a change that isn't a structural // change, and there are historical changes. Basically this means we // need to replay the add and remove events only, but more strictly, // Replay the add and remove transaction events SortedIndex addEvents = new SortedIndex(from.GetDataFile(from.AddLog)); SortedIndex removeEvents = new SortedIndex(from.GetDataFile(from.RemoveLog)); // Adds foreach (long fromRowid in addEvents) { // Generate a new id for the row, long toRowid = GenerateId(); // Copy record to the new id in this table, CopyFile(from.GetDataFile(from.GetRowIdKey(fromRowid)), GetDataFile(GetRowIdKey(toRowid))); // Update indexes, AddRowToRowSet(toRowid); AddRowToIndexSet(toRowid); // Add this event to the transaction log, AddTransactionEvent("insertRow", toRowid); } // Removes foreach (long fromRowid in removeEvents) { // Update indexes, RemoveRowFromRowSet(fromRowid); RemoveRowFromIndexSet(fromRowid); // Delete the row data file, GetDataFile(GetRowIdKey(fromRowid)).Delete(); // Add this event to the transaction log, AddTransactionEvent("deleteRow", fromRowid); } // Write out the transaction logs, PrepareForCommit(); } // Invalidate all the cached info, columnIdMap = null; cachedColumnList = null; cachedIndexList = null; ++currentVersion; }
public Cursor(SortedIndex index, long start, long end) { this.index = index; this.end = end; this.start = start; }