public void OnParentDelete(RowId parentId, HashSet <ChildRow> deleteList)
            {
                TField parentValue = this.primaryKey.Value(parentId, this.childTable.table.SnapStore.Version);

                foreach (RowId childId in this.childTable.Select <TField>(this.childColumn, parentValue))
                {
                    switch (this.action)
                    {
                    case ForeignKeyAction.Restrict:
                        throw new ForeignKeyViolationException(this.Name);

                    case ForeignKeyAction.Cascade:
                        if (deleteList.Add(new ChildRow(this.childTable, childId)) && this.childTable.table.PrimaryKey != null)
                        {
                            foreach (IForeignKey fk in this.childTable.table.Children)
                            {
                                fk.OnParentDelete(childId, deleteList);
                            }
                        }
                        break;

                    case ForeignKeyAction.SetDefault:
                        this.childTable.SetField <TField>(childId, this.childColumn, this.childColumn.DefaultValue);
                        break;
                    }
                }
            }
            public void Validate()
            {
                int version = this.childTable.StoreSnapshot.Version;

                if (this.childTable.table.WasChangedIn(version))
                {
                    using (IEnumerator <SnapTableChange <TRecord> > enumerator = this.childTable.table.GetChanges(version)) {
                        while (enumerator.MoveNext())
                        {
                            if (enumerator.Current.Action != SnapTableAction.Delete)
                            {
                                TField value = enumerator.Current.GetNewField <TField>(this.childColumn);
                                if (!this.allowsDefault || this.childColumn.Compare(value, this.childColumn.DefaultValue) != 0)
                                {
                                    RowId parentId = this.primaryKey.FindUnique(value, version);
                                    if (parentId == RowId.Empty)
                                    {
                                        throw new ForeignKeyViolationException(this.Name);
                                    }
                                }
                            }
                        }
                    }
                }
            }
Beispiel #3
0
        public void Insert(TField value, RowId rowId)
        {
            Key key = new Key()
            {
                field = value,
                rowId = rowId
            };
            RowId rootId = this.Root(this.table.SnapStore.Version);

            if (this.table.GetLatestField <int>(rootId, this.countField) < this.keyFields.Length)
            {
                this.InsertNoneFull(rootId, key);
            }
            else
            {
                Debug.Assert(this.table.GetLatestField <int>(rootId, this.countField) == this.keyFields.Length);
                Node node = new Node()
                {
                    IsLeaf = false,
                    C0     = rootId
                };
                rootId = this.table.Insert(ref node);
                this.SetRoot(rootId);
                this.SplitChild(rootId, 0);
                this.InsertNoneFull(rootId, key);
            }
                        #if ValidateTree
            this.Validate();
                        #endif
        }
 /// <summary>
 /// Updates field of the row
 /// </summary>
 /// <typeparam name="TField">Type of the field</typeparam>
 /// <param name="rowId">row id</param>
 /// <param name="field">Field to update</param>
 /// <param name="value">New value to assign</param>
 /// <returns></returns>
 public bool SetField <TField>(RowId rowId, IField <TRecord, TField> field, TField value)
 {
     this.ValidateModification();
     this.table.ValidateField(field);
     if (field.Compare(this.table.GetLatestField <TField>(rowId, field), value) != 0)
     {
         List <IIndex <TRecord> > list = this.table.Indexes[field.Order];
         if (list != null)
         {
             foreach (IIndex <TRecord> index in list)
             {
                 // no need to check for timestamp as only one field updated
                 index.Delete(rowId);
             }
         }
         bool updated = this.table.SetField <TField>(rowId, field, value);
         Debug.Assert(updated);
         if (list != null)
         {
             foreach (IIndex <TRecord> index in list)
             {
                 // no need to check for timestamp as only one field updated
                 index.Insert(rowId);
             }
         }
         return(true);
     }
     return(false);
 }
 /// <summary>
 /// Returns action made to the row during the transaction
 /// </summary>
 /// <param name="change"></param>
 /// <returns></returns>
 public SnapTableAction Action(int change)
 {
     if (change < this.InsertCount())
     {
         Debug.Assert(this.forRollback || !this.table.IsDeleted(this.RowId(change), this.newVersion.Version, false),
                      "This row is invalid and should not be enumerated as a change"
                      );
         return(SnapTableAction.Insert);
     }
     else
     {
         RowId rowId      = this.RowId(change);
         bool  oldDeleted = this.table.IsDeleted(rowId, this.oldVersion.Version, false);
         bool  newDeleted = this.table.IsDeleted(rowId, this.newVersion.Version, false);
         if (oldDeleted != newDeleted)
         {
             return(oldDeleted ? SnapTableAction.Insert : SnapTableAction.Delete);
         }
         else
         {
             Debug.Assert(!oldDeleted || this.forRollback, "Row must not be deleted in order to be modified");
             return(SnapTableAction.Update);
         }
     }
 }
Beispiel #6
0
        private void Validate(RowId rowId)
        {
            int  version = this.table.SnapStore.Version;
            Node node;

            this.table.GetData(rowId, version, out node);
            Debug.Assert(0 < node.Count && node.Count <= this.keyFields.Length || node.Count == 0 && node.IsLeaf && this.Root(version) == rowId);
            Key key = node.K0;

            for (int i = 1; i < node.Count; i++)
            {
                Key k2 = this.keyFields[i].GetValue(ref node);
                Debug.Assert(this.keyFields[i].Compare(key, k2) < 0);
                if (!node.IsLeaf)
                {
                    this.Validate(this.childFields[i].GetValue(ref node), key, k2);
                }
                key = k2;
            }
            if (!node.IsLeaf)
            {
                this.ValidateFirst(this.childFields[0].GetValue(ref node), node.K0);
                this.ValidateLast(this.childFields[node.Count].GetValue(ref node), this.keyFields[node.Count - 1].GetValue(ref node));
            }
        }
 /// <summary>
 /// Pushes row to log. Row can be logged only once in transaction, so only initial state (as it was at the beginning of transaction) will be saved
 /// </summary>
 /// <param name="row">Reference to the data to be logged</param>
 /// <param name="rowId">Id of this data. This should be the Id of row provided in the first param.</param>
 private void PushToLog(ref Row row, RowId rowId)
 {
     // ref to row should be in table at rowId. It is impossible to check this in C#, so just check if bits are equal
     Debug.Assert(
         0 == TableSnapshot <TRecord> .Compare(
             this.Fields, ref row.Data, ref this.table.ItemAddress(rowId.Value).Page[this.table.ItemAddress(rowId.Value).Index].Data
             ),
         "ref to row should be in table at rowId"
         );
     ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(this.snap.Count - 1);
     if (snapAddress.Page[snapAddress.Index].Version < this.SnapStore.Version)
     {
         // first change in this transaction so the row is older
         Debug.Assert(rowId.Value < snapAddress.Page[snapAddress.Index].TableSize, "Row should be already in the table");
         this.snap.PrepareAdd();
         this.log.PrepareAdd();
         Snap point = new Snap()
         {
             Version   = this.SnapStore.Version,
             TableSize = this.table.Count,
             LogSize   = this.log.Count + 1
         };
         RuntimeHelpers.PrepareConstrainedRegions();
         try {} finally {
             int index = this.log.FixedAllocate();
             ValueList <Log> .Address logAddress = this.log.ItemAddress(index);
             logAddress.Page[logAddress.Index].Data        = row.Data;
             logAddress.Page[logAddress.Index].RowId       = rowId;
             logAddress.Page[logAddress.Index].RawLogIndex = row.RawLogIndex;
             row.LogIndex = index;
             this.snap.FixedAdd(ref point);
         }
     }
     else
     {
         Debug.Assert(snapAddress.Page[snapAddress.Index].Version == this.SnapStore.Version, "Impossible state: this should be the current transaction");
         Debug.Assert(snapAddress.Page[snapAddress.Index].TableSize == this.table.Count, "Impossible state: wrong table size");
         Debug.Assert(snapAddress.Page[snapAddress.Index].LogSize == this.log.Count, "Impossible state: wrong log size");
         // some changes were made in this transaction. Check if the row was already modified in this transaction.
         // get size of log in the previous transaction
         ValueList <Snap> .Address oldSnapAddress = this.snap.ItemAddress(this.snap.Count - 2);
         if (rowId.Value < oldSnapAddress.Page[oldSnapAddress.Index].TableSize && row.LogIndex < oldSnapAddress.Page[oldSnapAddress.Index].LogSize)
         {
             // this is the first time the row is updated in this transaction
             Debug.Assert(snapAddress.Page[snapAddress.Index].LogSize == this.log.Count, "Invalid state: wrong log size");
             this.log.PrepareAdd();
             RuntimeHelpers.PrepareConstrainedRegions();
             try {} finally {
                 int index = this.log.FixedAllocate();
                 ValueList <Log> .Address logAddress = this.log.ItemAddress(index);
                 logAddress.Page[logAddress.Index].Data        = row.Data;
                 logAddress.Page[logAddress.Index].RowId       = rowId;
                 logAddress.Page[logAddress.Index].RawLogIndex = row.RawLogIndex;
                 row.LogIndex = index;
                 snapAddress.Page[snapAddress.Index].LogSize = this.log.Count;
             }
         }
     }
 }
Beispiel #8
0
            public IEnumerable <RowId> Find(TField value, int version)
            {
                RowId rowId = this.FindUnique(value, version);

                if (!rowId.IsEmpty)
                {
                    yield return(rowId);
                }
            }
            private RowId NewRow(int change)
            {
                RowId rowId = this.RowId(change);

                if (this.table.IsDeleted(rowId, this.newVersion.Version, false))
                {
                    throw new InvalidOperationException(Properties.Resources.ErrorWrongNewData);
                }
                return(rowId);
            }
Beispiel #10
0
        private void ValidateFirst(RowId rowId, Key max)
        {
            int  version = this.table.SnapStore.Version;
            Node node;

            this.table.GetData(rowId, version, out node);
            Debug.Assert(this.MinDegree <= node.Count && node.Count <= this.keyFields.Length);
            Debug.Assert(this.keyFields[0].Compare(this.keyFields[node.Count - 1].GetValue(ref node), max) < 0);
            this.Validate(rowId);
        }
        /// <summary>
        /// Deletes row.
        /// </summary>
        /// <param name="rowId"></param>
        public void Delete(RowId rowId)
        {
            Debug.Assert(0 <= rowId.Value && rowId.Value < this.table.Count, "broken rowId");
            this.ValidateModification();
            ValueList <Row> .Address address = this.table.ItemAddress(rowId.Value);
            SnapTable <TRecord> .ValidateModification(ref address);

            this.PushToLog(ref address.Page[address.Index], rowId);
            // if the row was inserted in this transaction, then after deletion it will be invalid. so just ignore it in all future operations
            address.Page[address.Index].IsDeleted = true;
        }
 /// <summary>
 /// Undelete previously deleted row.
 /// </summary>
 /// <param name="rowId"></param>
 public void UnDelete(RowId rowId)
 {
     Debug.Assert(0 <= rowId.Value && rowId.Value < this.table.Count, "broken rowId");
     this.ValidateModification();
     ValueList <Row> .Address address = this.table.ItemAddress(rowId.Value);
     if (!address.Page[address.Index].IsDeleted)
     {
         throw new InvalidOperationException(Properties.Resources.ErrorUndeleteRow);
     }
     this.PushToLog(ref address.Page[address.Index], rowId);
     address.Page[address.Index].IsDeleted = false;
 }
        /// <summary>
        /// Gets record in the specified version
        /// </summary>
        /// <param name="rowId"></param>
        /// <param name="version"></param>
        /// <param name="data"></param>
        public void GetData(RowId rowId, int version, out TRecord data)
        {
            if (version != 0)
            {
                this.ValidateVersion(version);
            }
            int pointIndex = this.snap.Count - 1;

            ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(pointIndex);
            while (version < snapAddress.Page[snapAddress.Index].Version)
            {
                snapAddress = this.snap.ItemAddress(--pointIndex);
            }
            if (!(0 <= rowId.Value && rowId.Value < snapAddress.Page[snapAddress.Index].TableSize))
            {
                throw new ArgumentOutOfRangeException(nameof(rowId));
            }
            // cache log size here. the original can only grow in time
            int logSize = snapAddress.Page[snapAddress.Index].LogSize;

            ValueList <Row> .Address rowAddress = this.table.ItemAddress(rowId.Value);
            if (rowAddress.Page[rowAddress.Index].LogIndex < logSize)
            {
                // so the latest version of data was requested.
                data = rowAddress.Page[rowAddress.Index].Data;
                bool isDeleted = rowAddress.Page[rowAddress.Index].IsDeleted;
                LockFreeSync.ReadBarrier();
                // check if the row is still of the latest version
                if (rowAddress.Page[rowAddress.Index].LogIndex < logSize)
                {
                    // if it is still latest return.
                    if (isDeleted)
                    {
                        data = default;
                        throw new ArgumentOutOfRangeException(nameof(rowId));
                    }
                    return;
                }
            }
            // older version of the row requested. The data is in the log. Log is immutable so easy to read.
            ValueList <Log> .Address logAddress = this.log.ItemAddress(rowAddress.Page[rowAddress.Index].LogIndex);
            while (logSize <= logAddress.Page[logAddress.Index].LogIndex)
            {
                Debug.Assert(0 < logAddress.Page[logAddress.Index].LogIndex, "Log entry at 0 does not contain any real data and used as a stub");
                logAddress = this.log.ItemAddress(logAddress.Page[logAddress.Index].LogIndex);
            }
            if (logAddress.Page[logAddress.Index].IsDeleted)
            {
                data = default;
                throw new ArgumentOutOfRangeException(nameof(rowId));
            }
            data = logAddress.Page[logAddress.Index].Data;
        }
Beispiel #14
0
        private string BuildDebuggingVisualization(int version)
        {
            System.Text.StringBuilder text = new System.Text.StringBuilder();
            RowId root  = this.Root(version);
            int   level = 0;

            while (this.BuildDebuggingVisualization(text, version, level, root))
            {
                text.AppendLine();
                level++;
            }
            return(text.ToString());
        }
Beispiel #15
0
        private Key Maximum(RowId rowId, int version)
        {
            Node node;

            for (;;)
            {
                this.table.GetData(rowId, version, out node);
                if (node.IsLeaf)
                {
                    return(this.keyFields[node.Count - 1].GetValue(ref node));
                }
                rowId = this.childFields[node.Count].GetValue(ref node);
            }
        }
Beispiel #16
0
        private Key Minimum(RowId rowId, int version)
        {
            Node node;

            for (;;)
            {
                this.table.GetData(rowId, version, out node);
                if (node.IsLeaf)
                {
                    return(node.K0);
                }
                rowId = node.C0;
            }
        }
        /// <summary>
        /// Sets entire structure.
        /// </summary>
        /// <param name="rowId"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SetData(RowId rowId, ref TRecord data)
        {
            Debug.Assert(0 <= rowId.Value && rowId.Value < this.table.Count, "broken rowId");
            this.ValidateModification();
            ValueList <Row> .Address address = this.table.ItemAddress(rowId.Value);
            SnapTable <TRecord> .ValidateModification(ref address);

            if (TableSnapshot <TRecord> .Compare(this.Fields, ref address.Page[address.Index].Data, ref data) != 0)
            {
                this.PushToLog(ref address.Page[address.Index], rowId);
                address.Page[address.Index].Data = data;
                Debug.Assert(TableSnapshot <TRecord> .Compare(this.Fields, ref address.Page[address.Index].Data, ref data) == 0, "Assignment or comparison failed");
                return(true);
            }
            return(false);
        }
Beispiel #18
0
        public bool Remove(TField value, RowId rowId)
        {
            Key key = new Key()
            {
                field = value,
                rowId = rowId
            };
            RowId rootId  = this.Root(this.table.SnapStore.Version);
            bool  deleted = this.Delete(rootId, key);

                        #if ValidateTree
            this.Validate();
                        #endif

            return(deleted);
        }
Beispiel #19
0
        private void Rehash(int newSize)
        {
            int version = this.SnapStore.Version;
            int oldSize = this.Size(version);

            Debug.Assert(oldSize <= newSize);
            int count = this.Count(version);
            List <KeyValuePair <TField, RowId> > list = new List <KeyValuePair <TField, RowId> >(count);
            Bucket empty = new Bucket();

            for (int i = 0; i < oldSize; i++)
            {
                Bucket bucket;
                this.table.GetLatestData(new RowId(i), out bucket);
                if (!bucket.IsFree)
                {
                    list.Add(new KeyValuePair <TField, RowId>(bucket.Value, bucket.RowId));
                }
                if (bucket.IsDirty)
                {
                    this.table.SetData(new RowId(i), ref empty);
                }
            }
            int currentSize = Math.Min(newSize, this.table.LatestCount());

            for (int i = oldSize; i < currentSize; i++)
            {
                RowId rowId = new RowId(i);
                if (this.table.IsLatestDeleted(rowId))
                {
                    this.table.UnDelete(rowId);
                }
                this.table.SetData(rowId, ref empty);
            }
            for (int i = currentSize; i < newSize; i++)
            {
                this.table.Insert(ref empty);
            }
            this.SetSize(newSize);
            this.SetCount(0);
            this.SetOccupancy(0);
            foreach (KeyValuePair <TField, RowId> pair in list)
            {
                this.Insert(pair.Key, pair.Value);
            }
            Debug.Assert(count == this.Count(version), "Count been changed by rehashing");
        }
            public SnapTableAction Action(RowId rowId)
            {
                SnapTableAction action = this.enumerator.Action(rowId);

                switch (action)
                {
                case SnapTableAction.Insert: return(SnapTableAction.Delete);

                case SnapTableAction.Delete: return(SnapTableAction.Insert);

                case SnapTableAction.Update: return(SnapTableAction.Update);

                default:
                    Debug.Fail("Unknown action");
                    throw new InvalidOperationException();
                }
            }
        /// <summary>
        /// Updates field of the row
        /// </summary>
        /// <typeparam name="TField"></typeparam>
        /// <param name="rowId"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SetField <TField>(RowId rowId, IField <TRecord, TField> field, TField value)
        {
            Debug.Assert(0 <= rowId.Value && rowId.Value < this.table.Count, "broken rowId");
            this.ValidateModification();
            // It is only possible to set value via basic fields defined on table.
            this.ValidateField(field);
            ValueList <Row> .Address address = this.table.ItemAddress(rowId.Value);
            SnapTable <TRecord> .ValidateModification(ref address);

            if (field.Compare(field.GetValue(ref address.Page[address.Index].Data), value) != 0)
            {
                this.PushToLog(ref address.Page[address.Index], rowId);
                field.SetValue(ref address.Page[address.Index].Data, value);
                Debug.Assert(field.Compare(field.GetValue(ref address.Page[address.Index].Data), value) == 0, "Assignment or comparison failed");
                return(true);
            }
            return(false);
        }
        /// <summary>
        /// Inserts new row in the table
        /// </summary>
        /// <param name="data">data to be inserted</param>
        /// <returns>index of the new row</returns>
        public RowId Insert(ref TRecord data)
        {
            this.ValidateModification();
            if (int.MaxValue - 1 <= this.table.Count)
            {
                throw new InvalidOperationException(Properties.Resources.ErrorTableTooBig(this.Name));
            }
            ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(this.snap.Count - 1);
            bool firstChange = (snapAddress.Page[snapAddress.Index].Version < this.SnapStore.Version);

            Debug.Assert(firstChange || snapAddress.Page[snapAddress.Index].Version == this.SnapStore.Version, "Impossible state: this should be the current transaction");
            Debug.Assert(firstChange || snapAddress.Page[snapAddress.Index].TableSize == this.table.Count, "Impossible state: wrong table size");
            Debug.Assert(firstChange || snapAddress.Page[snapAddress.Index].LogSize == this.log.Count, "Impossible state: wrong log size");
            this.table.PrepareAdd();
            if (firstChange)
            {
                this.snap.PrepareAdd();
            }
            RowId rowId;

            RuntimeHelpers.PrepareConstrainedRegions();
            try {} finally {
                rowId = new RowId(this.table.FixedAllocate());
                ValueList <Row> .Address rowAddress = this.table.ItemAddress(rowId.Value);
                rowAddress.Page[rowAddress.Index].Data = data;
                if (firstChange)
                {
                    // this is the first change in this transaction
                    Snap point = new Snap()
                    {
                        Version   = this.SnapStore.Version,
                        TableSize = this.table.Count,
                        LogSize   = this.log.Count
                    };
                    this.snap.FixedAdd(ref point);
                }
                else
                {
                    // this transaction already altered this table
                    snapAddress.Page[snapAddress.Index].TableSize = this.table.Count;
                }
            }
            return(rowId);
        }
        private void DeleteRow(RowId rowId)
        {
            int timestamp = TableSnapshot <TRecord> .Timestamp();

            foreach (List <IIndex <TRecord> > list in this.table.Indexes)
            {
                if (list != null)
                {
                    foreach (IIndex <TRecord> index in list)
                    {
                        if (index.Timestamp != timestamp)
                        {
                            index.Delete(rowId);
                            index.Timestamp = timestamp;
                        }
                    }
                }
            }
            this.table.Delete(rowId);
        }
        /// <summary>
        /// Reverts changes made in the provided transaction. Intended to be used in undo/redo.
        /// Warning! Assumes that higher stack functionality is responsible for providing correct version number,
        /// so there no gaps between versions in undo/redo operations
        /// </summary>
        /// <param name="version">Transaction to be reverted</param>
        public void Revert(int version)
        {
            this.ValidateModification();
            this.ValidateVersion(version);

            int pointIndex = this.snap.Count - 1;

            ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(pointIndex);
            while (version < snapAddress.Page[snapAddress.Index].Version)
            {
                snapAddress = this.snap.ItemAddress(--pointIndex);
            }
            if (version == snapAddress.Page[snapAddress.Index].Version)
            {
                int tableEnd = snapAddress.Page[snapAddress.Index].TableSize;
                int logEnd   = snapAddress.Page[snapAddress.Index].LogSize;
                snapAddress = this.snap.ItemAddress(--pointIndex);
                int tableStart = snapAddress.Page[snapAddress.Index].TableSize;
                int logStart   = snapAddress.Page[snapAddress.Index].LogSize;
                for (int i = tableStart; i < tableEnd; i++)
                {
                    ValueList <Row> .Address rowAddress = this.table.ItemAddress(i);
                    if (rowAddress.Page[rowAddress.Index].IsValid)
                    {
                        this.PushToLog(ref rowAddress.Page[rowAddress.Index], new RowId(i));
                        rowAddress.Page[rowAddress.Index].IsDeleted = true;
                        Debug.Assert(rowAddress.Page[rowAddress.Index].IsValid, "Invalid state: row with a history of changes can't be invalid");
                    }
                }
                for (int i = logStart; i < logEnd; i++)
                {
                    ValueList <Log> .Address logAddress = this.log.ItemAddress(i);
                    RowId rowId = logAddress.Page[logAddress.Index].RowId;
                    Debug.Assert(rowId.Value < tableStart, "Invalid rowId: changes are only possible to rows that already in the table, not just inserted ones");
                    ValueList <Row> .Address rowAddress = this.table.ItemAddress(rowId.Value);
                    this.PushToLog(ref rowAddress.Page[rowAddress.Index], rowId);
                    rowAddress.Page[rowAddress.Index].Data      = logAddress.Page[logAddress.Index].Data;
                    rowAddress.Page[rowAddress.Index].IsDeleted = logAddress.Page[logAddress.Index].IsDeleted;
                }
            }
        }
Beispiel #25
0
 /// <summary>
 /// Deletes row from table
 /// </summary>
 /// <param name="rowId"></param>
 public void Delete(RowId rowId)
 {
     this.ValidateModification();
     if (this.table.PrimaryKey != null)
     {
         HashSet <ChildRow> deleteList = new HashSet <ChildRow>();
         deleteList.Add(new ChildRow(this, rowId));
         foreach (IForeignKey fk in this.table.Children)
         {
             fk.OnParentDelete(rowId, deleteList);
         }
         foreach (ChildRow childRow in deleteList)
         {
             childRow.Table.DeleteRow(childRow.RowId);
         }
     }
     else
     {
         this.DeleteRow(rowId);
     }
 }
        public RowId Insert(ref TRecord data)
        {
            this.ValidateModification();
            RowId rowId     = this.table.Insert(ref data);
            int   timestamp = TableSnapshot <TRecord> .Timestamp();

            foreach (List <IIndex <TRecord> > list in this.table.Indexes)
            {
                if (list != null)
                {
                    foreach (IIndex <TRecord> index in list)
                    {
                        if (index.Timestamp != timestamp)
                        {
                            index.Insert(rowId);
                            index.Timestamp = timestamp;
                        }
                    }
                }
            }
            return(rowId);
        }
Beispiel #27
0
        private void SplitChild(RowId rowId, int child)
        {
            Node node;

            this.table.GetLatestData(rowId, out node);
            Debug.Assert(node.Count < this.keyFields.Length, "The node should be none full");
            // make a room for a new key that will bubble up from child node
            for (int i = node.Count - 1; child <= i; i--)
            {
                this.keyFields[i + 1].SetValue(ref node, this.keyFields[i].GetValue(ref node));
            }
            // make a room for a new child
            for (int i = node.Count; child < i; i--)
            {
                this.childFields[i + 1].SetValue(ref node, this.childFields[i].GetValue(ref node));
            }

            RowId childId = this.childFields[child].GetValue(ref node);
            Node  oldChild;

            this.table.GetLatestData(childId, out oldChild);
            Debug.Assert(oldChild.Count == this.keyFields.Length, "The node should be full in order to be split");

            Node newChild = new Node()
            {
                IsLeaf = oldChild.IsLeaf
            };

            this.Move(ref oldChild, this.MinDegree + 1, ref newChild);
            oldChild.Count--;
            Debug.Assert(oldChild.Count == newChild.Count && newChild.Count == this.MinDegree);
            node.Count++;
            this.keyFields[child].SetValue(ref node, this.keyFields[this.MinDegree].GetValue(ref oldChild));
            this.keyFields[this.MinDegree].SetValue(ref oldChild, default(Key));
            this.childFields[child + 1].SetValue(ref node, this.table.Insert(ref newChild));
            this.table.SetData(childId, ref oldChild);
            this.table.SetData(rowId, ref node);
        }
Beispiel #28
0
        private void InsertNoneFull(RowId rowId, Key key)
        {
            Node node;

            this.table.GetLatestData(rowId, out node);
            Debug.Assert(node.Count < this.keyFields.Length, "Node should be none full to perform the InserNoneFull");
            int i = node.Count - 1;

            if (node.IsLeaf)
            {
                while (0 <= i && this.keyFields[i].Compare(key, this.keyFields[i].GetValue(ref node)) < 0)
                {
                    this.keyFields[i + 1].SetValue(ref node, this.keyFields[i].GetValue(ref node));
                    i--;
                }
                this.keyFields[i + 1].SetValue(ref node, key);
                node.Count++;
                this.table.SetData(rowId, ref node);
            }
            else
            {
                while (0 <= i && this.keyFields[i].Compare(key, this.keyFields[i].GetValue(ref node)) < 0)
                {
                    i--;
                }
                i++;
                RowId childId = this.childFields[i].GetValue(ref node);
                if (this.table.GetLatestField <int>(childId, this.countField) == this.keyFields.Length)
                {
                    this.SplitChild(rowId, i);
                    if (this.keyFields[i].Compare(key, this.table.GetLatestField <Key>(rowId, this.keyFields[i])) > 0)
                    {
                        i++;
                    }
                }
                this.InsertNoneFull(this.table.GetLatestField <RowId>(rowId, this.childFields[i]), key);
            }
        }
 /// <summary>
 /// Rolls back current transaction
 /// </summary>
 public void Rollback()
 {
     this.ValidateModification();
     ValueList <Snap> .Address snapAddress = this.snap.ItemAddress(this.snap.Count - 1);
     if (snapAddress.Page[snapAddress.Index].Version == this.SnapStore.Version)
     {
         Debug.Assert(1 < this.snap.Count, "Only real transactions can be rolled back");
         // this table was modified in this transaction
         // it is impossible to completely rollback current changes, so just get old data back and delete new rows and undelete deleted ones.
         int tableEnd = snapAddress.Page[snapAddress.Index].TableSize;
         int logEnd   = snapAddress.Page[snapAddress.Index].LogSize;
         if (tableEnd != this.table.Count || logEnd != this.log.Count)
         {
             // if rolling back due to exception then ensure snap showing right data.
             snapAddress.Page[snapAddress.Index].TableSize = tableEnd = this.table.Count;
             snapAddress.Page[snapAddress.Index].LogSize   = logEnd = this.log.Count;
         }
         snapAddress = this.snap.ItemAddress(this.snap.Count - 2);
         int tableStart = snapAddress.Page[snapAddress.Index].TableSize;
         int logStart   = snapAddress.Page[snapAddress.Index].LogSize;
         for (int i = tableStart; i < tableEnd; i++)
         {
             ValueList <Row> .Address rowAddress = this.table.ItemAddress(i);
             rowAddress.Page[rowAddress.Index].IsDeleted = true;
             // Row must be freshly inserted but can be deleted in the same transaction, so check here that it is invalid now.
             Debug.Assert(!rowAddress.Page[rowAddress.Index].IsValid, "Invalid state: row should be freshly inserted one");
         }
         for (int i = logStart; i < logEnd; i++)
         {
             ValueList <Log> .Address logAddress = this.log.ItemAddress(i);
             RowId rowId = logAddress.Page[logAddress.Index].RowId;
             Debug.Assert(rowId.Value < tableStart, "Invalid rowId: changes are only possible to rows that already in the table, not just inserted ones");
             ValueList <Row> .Address rowAddress = this.table.ItemAddress(rowId.Value);
             rowAddress.Page[rowAddress.Index].Data      = logAddress.Page[logAddress.Index].Data;
             rowAddress.Page[rowAddress.Index].IsDeleted = logAddress.Page[logAddress.Index].IsDeleted;
         }
     }
 }
Beispiel #30
0
        public bool Remove(TField value)
        {
            int  version = this.SnapStore.Version;
            Hash hash    = new Hash(value, this.Size(version));

            for (int i = 0; i < hash.Size; i++)
            {
                RowId  bucketIndex = hash.Bucket(i);
                Bucket bucket;
                this.table.GetLatestData(bucketIndex, out bucket);
                if (bucket.IsEmpty)
                {
                    return(false);
                }
                if (!bucket.IsFree && bucket.Hash == hash.Code && this.valueField.Compare(bucket.Value, value) == 0)
                {
                    this.table.SetField <RowId>(bucketIndex, RowIdField.Field, bucket.HasCollision ? Bucket.Deleted : Bucket.Empty);
                    this.SetCount(this.Count(version) - 1);
                    return(true);
                }
            }
            return(false);
        }