internal void AddVisibleTable(ITableSource source, IRowIndexSet indexSet)
        {
            var tableName = source.TableInfo.TableName;

            visibleTables[tableName] = source;
            tableIndices[tableName]  = indexSet;
        }
        public void UpdateVisibleTable(ITableSource table, IRowIndexSet indexSet)
        {
            // TODO: verify if the transaction is read-only

            RemoveVisibleTable(table);
            AddVisibleTable(table, indexSet);
        }
        internal void CommitTransactionChange(long commitId, ITableEventRegistry change, IRowIndexSet indexSet)
        {
            lock (this) {
                // ASSERT: Can't do this if source is Read only.
                if (IsReadOnly)
                {
                    throw new InvalidOperationException("Can't commit transaction journal, table is Read only.");
                }

                // CHECK!
                // TODO: change.CommitId = commitId;

                try {
                    // Add this registry to the multi version table indices log
                    Registries.AddRegistry(change);

                    // Write the modified index set to the index store
                    // (Updates the index file)
                    CommitIndexSet(indexSet);

                    // Update the state of the committed added data to the file system.
                    // (Updates data to the allocation file)
                    //
                    // ISSUE: This can add up to a lot of changes to the allocation file and
                    //   the runtime could potentially be terminated in the middle of
                    //   the update.  If an interruption happens the allocation information
                    //   may be incorrectly flagged.  The type of corruption this would
                    //   result in would be;
                    //   + From an 'update' the updated record may disappear.
                    //   + From a 'delete' the deleted record may not delete.
                    //   + From an 'insert' the inserted record may not insert.
                    //
                    // Note, the possibility of this type of corruption occuring has been
                    // minimized as best as possible given the current architecture.
                    // Also note that is not possible for a table file to become corrupted
                    // beyond recovery from this issue.

                    foreach (var entry in change)
                    {
                        if (entry is TableRowEvent)
                        {
                            var rowEvent = (TableRowEvent)entry;
                            var rowIndex = rowEvent.RowNumber;

                            if (rowEvent.EventType == TableRowEventType.Add)
                            {
                                // Record commit added
                                var oldType = WriteRecordState(rowIndex, RecordState.CommittedAdded);

                                // Check the record was in an uncommitted state before we changed
                                // it.
                                if (oldType != RecordState.Uncommitted)
                                {
                                    WriteRecordState(rowIndex, oldType);
                                    throw new InvalidOperationException($"Record {rowIndex} of table {TableName} was not in an uncommitted state!");
                                }
                            }
                            else if (rowEvent.EventType == TableRowEventType.Remove)
                            {
                                // Record commit removed
                                var oldType = WriteRecordState(rowIndex, RecordState.CommittedRemoved);

                                // Check the record was in an added state before we removed it.
                                if (oldType != RecordState.CommittedAdded)
                                {
                                    WriteRecordState(rowIndex, oldType);
                                    throw new InvalidOperationException($"Record {rowIndex} of table {TableName} was not in an added state!");
                                }

                                // Notify collector that this row has been marked as deleted.
                                GC.DeleteRow(rowIndex);
                            }
                        }
                    }
                } catch (IOException e) {
                    throw new InvalidOperationException("IO Error: " + e.Message, e);
                }
            }
        }
 private void CommitIndexSet(IRowIndexSet indexSet)
 {
     indexSetStore.CommitIndexSet(indexSet);
     indexSet.Dispose();
 }
 internal bool TryGetRowIndexSet(ObjectName tableName, out IRowIndexSet indexSet)
 => tableIndices.TryGetValue(tableName, out indexSet);
        public void CommitIndexSet(IRowIndexSet indexSet)
        {
            var removedBlocks = new List <IndexBlock>();

            lock (this) {
                var sIndexSet = (SnapshotIndexSet)indexSet;

                try {
                    try {
                        Store.Lock();

                        // For each Index in the index set,
                        foreach (Index index in sIndexSet)
                        {
                            int indexNum = index.IndexNumber;

                            // The IndexBlock we are changing
                            var curIndexBlock = indexBlocks[indexNum];

                            // Get all the blocks in the list
                            var blocks = index.AllBlocks.ToList();

                            // Make up a new block list for this index set.
                            long blockP;

                            using (var area = Store.CreateArea(16 + (blocks.Count * 28))) {
                                blockP = area.Id;
                                area.Write(1);                                 // version
                                area.Write(0);                                 // reserved
                                area.Write((long)blocks.Count);                // block count

                                foreach (var block in blocks)
                                {
                                    var mappedBlock = (IMappedBlock)block;

                                    long bottomInt = 0;
                                    long topInt    = 0;
                                    var  blockSize = mappedBlock.Count;

                                    if (blockSize > 0)
                                    {
                                        bottomInt = mappedBlock.Bottom;
                                        topInt    = mappedBlock.Top;
                                    }

                                    long blockPointer = mappedBlock.BlockPointer;

                                    // Is the block new or was it changed?
                                    if (blockPointer == -1 || mappedBlock.HasChanged)
                                    {
                                        // If this isn't -1 then write this sector on the list of
                                        // sectors to delete during GC.
                                        if (blockPointer != -1)
                                        {
                                            curIndexBlock.AddDeletedArea(blockPointer);
                                        }

                                        // This is a new block or a block that's been changed
                                        // Write the block to the file system
                                        blockPointer = mappedBlock.Flush();
                                    }

                                    area.Write(bottomInt);
                                    area.Write(topInt);
                                    area.Write(blockPointer);
                                    area.Write((int)(blockSize | ((mappedBlock.CompactType) << 24)));
                                }

                                // Finish initializing the area
                                area.Flush();
                            }

                            // Add the deleted blocks
                            var deletedBlocks = index.DeletedBlocks.ToArray();

                            for (int i = 0; i < deletedBlocks.Length; ++i)
                            {
                                long delBlockP = deletedBlocks[i].BlockPointer;
                                if (delBlockP != -1)
                                {
                                    curIndexBlock.AddDeletedArea(delBlockP);
                                }
                            }

                            // Mark the current block as deleted
                            curIndexBlock.MarkAsDeleted();

                            // Now create a new IndexBlock object
                            var newIndexBlock = new IndexBlock(this, indexNum, curIndexBlock.BlockSize, blockP);
                            newIndexBlock.Parent = curIndexBlock;

                            // Add reference to the new one
                            newIndexBlock.AddReference();

                            // Update the index_blocks list
                            indexBlocks[indexNum] = newIndexBlock;

                            // We remove this later.
                            removedBlocks.Add(curIndexBlock);
                        }

                        // Commit the new index header (index_blocks)
                        CommitIndexHeader();
                    } finally {
                        Store.Unlock();
                    }

                    // Commit finished.
                } catch (IOException e) {
                    throw new InvalidOperationException("Error while committing index changed", e);
                }
            }

            // Remove all the references for the changed blocks,
            foreach (var block in removedBlocks)
            {
                block.RemoveReference();
            }
        }