Beispiel #1
0
        /// <summary>
        /// Gets all node list from any index node (go forward and backward)
        /// </summary>
        public IEnumerable <IndexNode> GetNodeList(IndexNode node, bool includeInitial)
        {
            var next = node.NextNode;
            var prev = node.PrevNode;

            // returns some initial node
            if (includeInitial)
            {
                yield return(node);
            }

            // go forward
            while (next.IsEmpty == false)
            {
                var n = this.GetNode(next);
                next = n.NextNode;
                yield return(n);
            }

            // go backward
            while (prev.IsEmpty == false)
            {
                var p = this.GetNode(prev);
                prev = p.PrevNode;
                yield return(p);
            }
        }
Beispiel #2
0
        protected override void ReadContent(ByteReader reader)
        {
            this.Nodes = new Dictionary <ushort, IndexNode>(this.ItemCount);

            for (var i = 0; i < this.ItemCount; i++)
            {
                var index  = reader.ReadUInt16();
                var levels = reader.ReadByte();

                var node = new IndexNode(levels);

                node.Page      = this;
                node.Position  = new PageAddress(this.PageID, index);
                node.Slot      = reader.ReadByte();
                node.PrevNode  = reader.ReadPageAddress();
                node.NextNode  = reader.ReadPageAddress();
                node.KeyLength = reader.ReadUInt16();
                node.Key       = reader.ReadBsonValue(node.KeyLength);
                node.DataBlock = reader.ReadPageAddress();

                for (var j = 0; j < node.Prev.Length; j++)
                {
                    node.Prev[j] = reader.ReadPageAddress();
                    node.Next[j] = reader.ReadPageAddress();
                }

                this.Nodes.Add(node.Position.Index, node);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Create a new index and returns head page address (skip list)
        /// </summary>
        public CollectionIndex CreateIndex(CollectionIndex index)
        {
            // get a new index page for first index page
            var page = _pager.NewPage <IndexPage>();

            // create a empty node with full max level
            var node = new IndexNode(IndexNode.MAX_LEVEL_LENGTH)
            {
                Key = new IndexKey(null), Page = page
            };

            node.Position = new PageAddress(page.PageID, 0);

            // add as first node
            page.Nodes.Add(node.Position.Index, node);

            // add/remove indexPage on freelist if has space
            _pager.AddOrRemoveToFreeList(page.FreeBytes > BasePage.RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID);

            // point the head node to this new node position
            index.HeadNode = node.Position;

            index.Page.IsDirty = true;
            page.IsDirty       = true;

            return(index);
        }
Beispiel #4
0
        /// <summary>
        /// Delete node from this page and update counter and free space
        /// </summary>
        public void DeleteNode(IndexNode node)
        {
            this.ItemCount--;
            this.FreeBytes += node.Length;

            _nodes.Remove(node.Position.Index);
        }
Beispiel #5
0
        /// <summary>
        /// Add new index node into this page. Update counters and free space
        /// </summary>
        public void AddNode(IndexNode node)
        {
            var index = _nodes.NextIndex();

            node.Position = new PageAddress(this.PageID, index);

            this.ItemCount++;
            this.FreeBytes -= node.Length;

            _nodes.Add(index, node);
        }
Beispiel #6
0
        internal virtual void Delete(CollectionPage col, IndexNode node)
        {
            // read dataBlock
            var dataBlock = this.Database.Data.Read(node.DataBlock, false);

            // lets remove all indexes that point to this in dataBlock
            foreach (var index in col.GetIndexes(true))
            {
                this.Database.Indexer.Delete(index, dataBlock.IndexRef[index.Slot]);
            }

            // remove object data
            this.Database.Data.Delete(col, node.DataBlock);
        }
Beispiel #7
0
        /// <summary>
        /// Insert a new node index inside an collection index. Flip coin to know level
        /// </summary>
        public IndexNode AddNode(CollectionIndex index, BsonValue key, IndexNode last)
        {
            var level = this.FlipCoin();

            // set index collection with max-index level
            if (level > index.MaxLevel)
            {
                index.MaxLevel = level;

                _pager.SetDirty(index.Page);
            }

            // call AddNode with key value
            return(this.AddNode(index, key, level, last));
        }
Beispiel #8
0
        /// <summary>
        /// Go first/last occurence of this index value
        /// </summary>
        private IndexNode FindBoundary(CollectionIndex index, IndexNode cur, BsonValue value, int order, int level)
        {
            var last = cur;

            while (cur.Key.CompareTo(value) == 0)
            {
                last = cur;
                cur  = this.GetNode(cur.NextPrev(0, order));
                if (cur.IsHeadTail(index))
                {
                    break;
                }
            }

            return(last);
        }
Beispiel #9
0
        internal virtual void Delete(CollectionPage col, IndexNode node)
        {
            // read dataBlock
            var dataBlock = _engine.Data.Read(node.DataBlock, false);

            // lets remove all indexes that point to this in dataBlock
            for (byte i = 0; i < col.Indexes.Length; i++)
            {
                var index = col.Indexes[i];

                if (!index.IsEmpty)
                {
                    _engine.Indexer.Delete(index, dataBlock.IndexRef[i]);
                }
            }

            // remove object data
            _engine.Data.Delete(col, node.DataBlock);
        }
Beispiel #10
0
        /// <summary>
        /// Create a new index and returns head page address (skip list)
        /// </summary>
        public CollectionIndex CreateIndex(CollectionPage col)
        {
            // get index slot
            var index = col.GetFreeIndex();

            // get a new index page for first index page
            var page = _pager.NewPage <IndexPage>();

            // create a empty node with full max level
            var head = new IndexNode(IndexNode.MAX_LEVEL_LENGTH)
            {
                Key       = BsonValue.MinValue,
                KeyLength = (ushort)BsonValue.MinValue.GetBytesCount(false),
                Slot      = (byte)index.Slot,
                Page      = page,
                Position  = new PageAddress(page.PageID, 0)
            };

            // add as first node
            page.Nodes.Add(head.Position.Index, head);

            // update freebytes + item count (for head)
            page.UpdateItemCount();

            // set index page as dirty
            _pager.SetDirty(index.Page);

            // add indexPage on freelist if has space
            _pager.AddOrRemoveToFreeList(true, page, index.Page, ref index.FreeIndexPageID);

            // point the head/tail node to this new node position
            index.HeadNode = head.Position;

            // insert tail node
            var tail = this.AddNode(index, BsonValue.MaxValue, IndexNode.MAX_LEVEL_LENGTH, null);

            index.TailNode = tail.Position;

            return(index);
        }
Beispiel #11
0
        /// <summary>
        /// Insert a new node index inside an collection index.
        /// </summary>
        private IndexNode AddNode(CollectionIndex index, BsonValue key, byte level)
        {
            // creating a new index node
            var node = new IndexNode(level)
            {
                Key       = key,
                KeyLength = key.GetBytesCount()
            };

            if (node.KeyLength > MAX_INDEX_LENGTH)
            {
                throw LiteException.IndexKeyTooLong();
            }

            // get a free page to insert my index node
            var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length);

            node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex());
            node.Page     = page;

            // add index node to page
            page.Nodes.Add(node.Position.Index, node);

            // update freebytes + items count
            page.UpdateItemCount();

            // now, let's link my index node on right place
            var cur = this.GetNode(index.HeadNode);

            // scan from top left
            for (var i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--)
            {
                // for(; <while_not_this>; <do_this>) { ... }
                for (; cur.Next[i].IsEmpty == false; cur = this.GetNode(cur.Next[i]))
                {
                    // read next node to compare
                    var diff = this.GetNode(cur.Next[i]).Key.CompareTo(key);

                    // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty)
                    if (diff == 0 && index.Options.Unique)
                    {
                        throw LiteException.IndexDuplicateKey(index.Field, key);
                    }

                    if (diff == 1)
                    {
                        break;
                    }
                }

                if (i <= (level - 1)) // level == length
                {
                    // cur = current (imediatte before - prev)
                    // node = new inserted node
                    // next = next node (where cur is poiting)

                    node.Next[i] = cur.Next[i];
                    node.Prev[i] = cur.Position;
                    cur.Next[i]  = node.Position;

                    var next = this.GetNode(node.Next[i]);

                    if (next != null)
                    {
                        next.Prev[i] = node.Position;

                        next.Page.IsDirty = true;
                    }

                    cur.Page.IsDirty = true;
                }
            }

            // add/remove indexPage on freelist if has space
            _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.INDEX_RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID);

            page.IsDirty = true;

            return(node);
        }
Beispiel #12
0
        /// <summary>
        /// Insert a new node index inside an collection index.
        /// </summary>
        private IndexNode AddNode(CollectionIndex index, BsonValue key, byte level, IndexNode last)
        {
            // calc key size
            var keyLength = key.GetBytesCount(false);

            if (keyLength > MAX_INDEX_LENGTH)
            {
                throw LiteException.IndexKeyTooLong();
            }

            // creating a new index node
            var node = new IndexNode(level)
            {
                Key       = key,
                KeyLength = (ushort)keyLength,
                Slot      = (byte)index.Slot
            };

            // get a free page to insert my index node
            var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length);

            node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex());
            node.Page     = page;

            // add index node to page
            page.Nodes.Add(node.Position.Index, node);

            // update freebytes + items count
            page.UpdateItemCount();

            // now, let's link my index node on right place
            var cur = this.GetNode(index.HeadNode);

            // using as cache last
            IndexNode cache = null;

            // scan from top left
            for (var i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--)
            {
                // get cache for last node
                cache = cache != null && cache.Position.Equals(cur.Next[i]) ? cache : this.GetNode(cur.Next[i]);

                // for(; <while_not_this>; <do_this>) { ... }
                for (; cur.Next[i].IsEmpty == false; cur = cache)
                {
                    // get cache for last node
                    cache = cache != null && cache.Position.Equals(cur.Next[i]) ? cache : this.GetNode(cur.Next[i]);

                    // read next node to compare
                    var diff = cache.Key.CompareTo(key);

                    // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty)
                    if (diff == 0 && index.Unique)
                    {
                        throw LiteException.IndexDuplicateKey(index.Field, key);
                    }

                    if (diff == 1)
                    {
                        break;
                    }
                }

                if (i <= (level - 1)) // level == length
                {
                    // cur = current (imediatte before - prev)
                    // node = new inserted node
                    // next = next node (where cur is poiting)
                    _pager.SetDirty(cur.Page);

                    node.Next[i] = cur.Next[i];
                    node.Prev[i] = cur.Position;
                    cur.Next[i]  = node.Position;

                    var next = this.GetNode(node.Next[i]);

                    if (next != null)
                    {
                        next.Prev[i] = node.Position;
                        _pager.SetDirty(next.Page);
                    }
                }
            }

            // add/remove indexPage on freelist if has space
            _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.INDEX_RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID);

            // if last node exists, create a double link list
            if (last != null)
            {
                // link new node with last node
                if (last.NextNode.IsEmpty == false)
                {
                    // fix link pointer with has more nodes in list
                    var next = this.GetNode(last.NextNode);
                    next.PrevNode = node.Position;
                    last.NextNode = node.Position;
                    node.PrevNode = last.Position;
                    node.NextNode = next.Position;

                    _pager.SetDirty(next.Page);
                }
                else
                {
                    last.NextNode = node.Position;
                    node.PrevNode = last.Position;
                }

                // set last node page as dirty
                _pager.SetDirty(last.Page);
            }

            return(node);
        }
Beispiel #13
0
 /// <summary>
 /// Insert a new node index inside an collection index. Flip coin to know level
 /// </summary>
 public IndexNode AddNode(CollectionIndex index, BsonValue key, IndexNode last)
 {
     // call AddNode with key value
     return(this.AddNode(index, key, this.FlipCoin(), last));
 }
Beispiel #14
0
        /// <summary>
        /// Insert a new node index inside a index. Use skip list
        /// </summary>
        public IndexNode AddNode(CollectionIndex index, object value)
        {
            // create persist value - used on key
            var key = new IndexKey(value);

            var level = this.FlipCoin();

            // creating a new index node
            var node = new IndexNode(level)
            {
                Key = key
            };

            // get a free page to insert my index node
            var page = _pager.GetFreePage <IndexPage>(index.FreeIndexPageID, node.Length);

            node.Position = new PageAddress(page.PageID, page.Nodes.NextIndex());
            node.Page     = page;

            // add index node to page
            page.Nodes.Add(node.Position.Index, node);

            // now, let's link my index node on right place
            var cur = this.GetNode(index.HeadNode);

            // scan from top left
            for (int i = IndexNode.MAX_LEVEL_LENGTH - 1; i >= 0; i--)
            {
                // for(; <while_not_this>; <do_this>) { ... }
                for (; cur.Next[i].IsEmpty == false; cur = this.GetNode(cur.Next[i]))
                {
                    // read next node to compare
                    var diff = this.GetNode(cur.Next[i]).Key.CompareTo(key);

                    // if unique and diff = 0, throw index exception (must rollback transaction - others nodes can be dirty)
                    if (diff == 0 && index.Unique)
                    {
                        throw new LiteException(string.Format("Cannot insert duplicate key in unique index '{0}'. The duplicate value is '{1}'.", index.Field, value));
                    }

                    if (diff == 1)
                    {
                        break;
                    }
                }

                if (i <= (level - 1)) // level == length
                {
                    // cur = current (imediatte before - prev)
                    // node = new inserted node
                    // next = next node (where cur is poiting)

                    node.Next[i] = cur.Next[i];
                    node.Prev[i] = cur.Position;
                    cur.Next[i]  = node.Position;

                    var next = this.GetNode(node.Next[i]);

                    if (next != null)
                    {
                        next.Prev[i] = node.Position;

                        next.Page.IsDirty = true;
                    }

                    cur.Page.IsDirty = true;
                }
            }

            // add/remove indexPage on freelist if has space
            _pager.AddOrRemoveToFreeList(page.FreeBytes > IndexPage.RESERVED_BYTES, page, index.Page, ref index.FreeIndexPageID);

            page.IsDirty = true;

            return(node);
        }
        /// <summary>
        /// Wrtie any BsonValue. Use 1 byte for data type, 1 byte for length (optional), 0-255 bytes to value.
        /// For document or array, use BufferWriter
        /// </summary>
        public static void WriteIndexKey(this BufferSlice buffer, BsonValue value, int offset)
        {
            ENSURE(IndexNode.GetKeyLength(value, true) <= MAX_INDEX_KEY_LENGTH, $"index key must have less than {MAX_INDEX_KEY_LENGTH} bytes");

            if (value.IsString)
            {
                var str       = value.AsString;
                var strLength = (ushort)Encoding.UTF8.GetByteCount(str);

                ExtendedLengthHelper.WriteLength(BsonType.String, strLength, out var typeByte, out var lengthByte);

                buffer[offset++] = typeByte;
                buffer[offset++] = lengthByte;
                buffer.Write(str, offset);
            }
            else if (value.IsBinary)
            {
                var arr = value.AsBinary;

                ExtendedLengthHelper.WriteLength(BsonType.Binary, (ushort)arr.Length, out var typeByte, out var lengthByte);

                buffer[offset++] = typeByte;
                buffer[offset++] = lengthByte;
                buffer.Write(arr, offset);
            }
            else
            {
                buffer[offset++] = (byte)value.Type;

                switch (value.Type)
                {
                case BsonType.Null:
                case BsonType.MinValue:
                case BsonType.MaxValue:
                    break;

                case BsonType.Int32: buffer.Write(value.AsInt32, offset); break;

                case BsonType.Int64: buffer.Write(value.AsInt64, offset); break;

                case BsonType.Double: buffer.Write(value.AsDouble, offset); break;

                case BsonType.Decimal: buffer.Write(value.AsDecimal, offset); break;

                case BsonType.Document:
                    using (var w = new BufferWriter(buffer))
                    {
                        w.Skip(offset);     // skip offset from buffer
                        w.WriteDocument(value.AsDocument, true);
                    }
                    break;

                case BsonType.Array:
                    using (var w = new BufferWriter(buffer))
                    {
                        w.Skip(offset);     // skip offset from buffer
                        w.WriteArray(value.AsArray, true);
                    }
                    break;

                case BsonType.ObjectId: buffer.Write(value.AsObjectId, offset); break;

                case BsonType.Guid: buffer.Write(value.AsGuid, offset); break;

                case BsonType.Boolean: buffer[offset] = (value.AsBoolean) ? (byte)1 : (byte)0; break;

                case BsonType.DateTime: buffer.Write(value.AsDateTime, offset); break;

                default: throw new NotImplementedException();
                }
            }
        }