Example #1
0
        /// <summary>
        /// Get a field name passing mapper type and returns property type
        /// </summary>
        private string GetTypeField(Type type, string property, out Type propertyType)
        {
            // lets get mapping bettwen .NET class and BsonDocument
            var            map = _mapper.GetPropertyMapper(type);
            PropertyMapper prop;

            if (map.TryGetValue(property, out prop))
            {
                propertyType = prop.PropertyType;

                return(prop.FieldName);
            }
            else
            {
                throw LiteException.PropertyNotMapped(property);
            }
        }
Example #2
0
        private static PropertyInfo SelectProperty(IEnumerable <PropertyInfo> props, params Func <PropertyInfo, bool>[] predicates)
        {
            foreach (var predicate in predicates)
            {
                var prop = props.FirstOrDefault(predicate);

                if (prop != null)
                {
                    if (!prop.CanRead || !prop.CanWrite)
                    {
                        throw LiteException.PropertyReadWrite(prop);
                    }

                    return(prop);
                }
            }

            return(null);
        }
        /// <summary>
        /// Override read page decrypting data from disk
        /// </summary>
        public override byte[] ReadPage(uint pageID)
        {
            var buffer = base.ReadPage(pageID);

            // when read header, checks passoword
            if (pageID == 0)
            {
                // I know, header page will be double read (it's the price for isolated concerns)
                var header = (HeaderPage)BasePage.ReadPage(buffer);

                if (header.DbParams.Password.BinaryCompareTo(_password) != 0)
                {
                    throw LiteException.DatabaseWrongPassword();
                }

                return(buffer);
            }

            return(_crypto.Decrypt(buffer));
        }
Example #4
0
        /// <summary>
        /// Create a new permanent index in all documents inside this collections if index not exists already. Returns true if index was created or false if already exits
        /// </summary>
        /// <param name="field">Document field name (case sensitive)</param>
        /// <param name="options">All index options</param>
        public bool EnsureIndex(string field, IndexOptions options)
        {
            if (string.IsNullOrEmpty(field))
            {
                throw new ArgumentNullException("field");
            }
            if (options == null)
            {
                throw new ArgumentNullException("options");
            }
            if (field == "_id")
            {
                return(false);                // always exists
            }
            if (!CollectionIndex.IndexPattern.IsMatch(field))
            {
                throw LiteException.InvalidFormat("IndexField", field);
            }

            return(_engine.EnsureIndex(_name, field, options));
        }
Example #5
0
        /// <summary>
        /// Add a new collection. Check if name the not exists
        /// </summary>
        public CollectionPage Add(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }
            if (!CollectionPage.NamePattern.IsMatch(name))
            {
                throw LiteException.InvalidFormat("CollectionName", name);
            }

            // get header marked as dirty because I will use header after (and NewPage can get another header instance)
            var header = _pager.GetPage <HeaderPage>(0, true);

            // check limit count (8 bytes per collection = 4 to string length, 4 for uint pageID)
            if (header.CollectionPages.Sum(x => x.Key.Length + 8) + name.Length + 8 >= CollectionPage.MAX_COLLECTIONS_SIZE)
            {
                throw LiteException.CollectionLimitExceeded(CollectionPage.MAX_COLLECTIONS_SIZE);
            }

            // get new collection page (marked as dirty)
            var col = _pager.NewPage <CollectionPage>();

            // add this page to header page collection
            header.CollectionPages.Add(name, col.PageID);

            col.CollectionName = name;

            // create PK index
            var pk = _indexer.CreateIndex(col);

            pk.Field   = "_id";
            pk.Options = new IndexOptions {
                Unique = true
            };

            return(col);
        }
Example #6
0
        /// <summary>
        /// Drop an index from a collection
        /// </summary>
        public bool DropIndex(string colName, string field)
        {
            if (field == "_id")
            {
                throw LiteException.IndexDropId();
            }

            return(this.Transaction <bool>(colName, false, (col) =>
            {
                // no collection, no index
                if (col == null)
                {
                    return false;
                }

                // mark collection page as dirty before changes
                _pager.SetDirty(col);

                // search for index reference
                var index = col.GetIndex(field);

                // no index, no drop
                if (index == null)
                {
                    return false;
                }

                _log.Write(Logger.COMMAND, "drop index on '{0}' :: '{1}'", colName, field);

                // delete all data pages + indexes pages
                _indexer.DropIndex(index);

                // clear index reference
                index.Clear();

                return true;
            }));
        }
        /// <summary>
        /// Insert a new node index inside an collection index.
        /// </summary>
        private IndexNode AddNode(CollectionIndex index, BsonValue key, byte level)
        {
            // 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
            };

            // 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.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)
                    _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)
                    {
                        _pager.SetDirty(next.Page);
                        next.Prev[i] = node.Position;
                    }
                }
            }

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

            return(node);
        }
Example #8
0
        /// <summary>
        /// Implement update command to a document inside a collection
        /// </summary>
        public int Update(string colName, IEnumerable <BsonDocument> docs)
        {
            return(this.Transaction <int>(colName, false, (col) =>
            {
                // no collection, no updates
                if (col == null)
                {
                    return 0;
                }

                var count = 0;

                foreach (var doc in docs)
                {
                    // normalize id before find
                    var id = doc["_id"].Normalize(col.PK.Options);

                    // validate id for null, min/max values
                    if (id.IsNull || id.IsMinValue || id.IsMaxValue)
                    {
                        throw LiteException.InvalidDataType("_id", id);
                    }

                    _log.Write(Logger.COMMAND, "update document on '{0}' :: _id = ", colName, id);

                    // find indexNode from pk index
                    var indexNode = _indexer.Find(col.PK, id, false, Query.Ascending);

                    // if not found document, no updates
                    if (indexNode == null)
                    {
                        continue;
                    }

                    // serialize document in bytes
                    var bytes = BsonSerializer.Serialize(doc);

                    // update data storage
                    var dataBlock = _data.Update(col, indexNode.DataBlock, bytes);

                    // delete/insert indexes - do not touch on PK
                    foreach (var index in col.GetIndexes(false))
                    {
                        var key = doc.Get(index.Field);

                        var node = _indexer.GetNode(dataBlock.IndexRef[index.Slot]);

                        // check if my index node was changed
                        if (node.Key.CompareTo(key) != 0)
                        {
                            // remove old index node
                            _indexer.Delete(index, node.Position);

                            // and add a new one
                            var newNode = _indexer.AddNode(index, key);

                            // point my index to data object
                            newNode.DataBlock = dataBlock.Position;

                            // set my block page as dirty before change
                            _pager.SetDirty(dataBlock.Page);

                            // point my dataBlock
                            dataBlock.IndexRef[index.Slot] = newNode.Position;
                        }
                    }

                    _cache.CheckPoint();

                    count++;
                }

                return count;
            }));
        }
Example #9
0
        /// <summary>
        /// Read all properties from a type - store in a static cache - exclude: Id and [BsonIgnore]
        /// </summary>
        public static Dictionary <string, PropertyMapper> GetProperties(Type type, Func <string, string> resolvePropertyName)
        {
            var dict      = new Dictionary <string, PropertyMapper>(StringComparer.OrdinalIgnoreCase);
            var id        = GetIdProperty(type);
            var ignore    = typeof(BsonIgnoreAttribute);
            var idAttr    = typeof(BsonIdAttribute);
            var fieldAttr = typeof(BsonFieldAttribute);
            var indexAttr = typeof(BsonIndexAttribute);
            var props     = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);

            foreach (var prop in props)
            {
                // ignore indexer property
                if (prop.GetIndexParameters().Length > 0)
                {
                    continue;
                }

                // ignore not read/write
                if (!prop.CanRead || !prop.CanWrite)
                {
                    continue;
                }

                // [BsonIgnore]
                if (prop.IsDefined(ignore, false))
                {
                    continue;
                }

                // check if property has [BsonField]
                var bsonField = prop.IsDefined(fieldAttr, false);

                // create getter/setter IL function
                var getter = CreateGetMethod(type, prop, bsonField);
                var setter = CreateSetMethod(type, prop, bsonField);

                // if not getter or setter - no mapping
                if (getter == null)
                {
                    continue;
                }

                var name = id != null && id.Equals(prop) ? "_id" : resolvePropertyName(prop.Name);

                // check if property has [BsonField] with a custom field name
                if (bsonField)
                {
                    var field = (BsonFieldAttribute)prop.GetCustomAttributes(fieldAttr, false).FirstOrDefault();
                    if (field != null && field.Name != null)
                    {
                        name = field.Name;
                    }
                }

                // check if property has [BsonId] to get with was setted AutoId = true
                var autoId = (BsonIdAttribute)prop.GetCustomAttributes(idAttr, false).FirstOrDefault();

                // checks if this proerty has [BsonIndex]
                var index = (BsonIndexAttribute)prop.GetCustomAttributes(indexAttr, false).FirstOrDefault();

                // if is _id field, do not accept index definition
                if (name == "_id")
                {
                    index = null;
                }

                // test if field name is OK (avoid to check in all instances) - do not test internal classes, like DbRef
                if (BsonDocument.IsValidFieldName(name) == false)
                {
                    throw LiteException.InvalidFormat(prop.Name, name);
                }

                // create a property mapper
                var p = new PropertyMapper
                {
                    AutoId       = autoId == null ? true : autoId.AutoId,
                    FieldName    = name,
                    PropertyName = prop.Name,
                    PropertyType = prop.PropertyType,
                    IndexOptions = index == null ? null : index.Options,
                    Getter       = getter,
                    Setter       = setter
                };

                dict.Add(prop.Name, p);
            }

            return(dict);
        }
Example #10
0
        /// <summary>
        /// Create a new instance from a Type
        /// </summary>
        public static object CreateInstance(Type type)
        {
            lock (_cacheCtor)
            {
                try
                {
                    CreateObject c = null;

                    if (_cacheCtor.TryGetValue(type, out c))
                    {
                        return(c());
                    }
                    else
                    {
                        if (type.IsClass)
                        {
                            var dynMethod = new DynamicMethod("_", type, null);
                            var il        = dynMethod.GetILGenerator();
                            il.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes));
                            il.Emit(OpCodes.Ret);
                            c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
                            _cacheCtor.Add(type, c);
                        }
                        else if (type.IsInterface) // some know interfaces
                        {
                            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IList <>))
                            {
                                return(CreateInstance(GetGenericListOfType(UnderlyingTypeOf(type))));
                            }
                            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ICollection <>))
                            {
                                return(CreateInstance(GetGenericListOfType(UnderlyingTypeOf(type))));
                            }
                            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IEnumerable <>))
                            {
                                return(CreateInstance(GetGenericListOfType(UnderlyingTypeOf(type))));
                            }
                            else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary <,>))
                            {
                                var k = type.GetGenericArguments()[0];
                                var v = type.GetGenericArguments()[1];
                                return(CreateInstance(GetGenericDictionaryOfType(k, v)));
                            }
                            else
                            {
                                throw LiteException.InvalidCtor(type);
                            }
                        }
                        else // structs
                        {
                            var dynMethod = new DynamicMethod("_", typeof(object), null);
                            var il        = dynMethod.GetILGenerator();
                            var lv        = il.DeclareLocal(type);
                            il.Emit(OpCodes.Ldloca_S, lv);
                            il.Emit(OpCodes.Initobj, type);
                            il.Emit(OpCodes.Ldloc_0);
                            il.Emit(OpCodes.Box, type);
                            il.Emit(OpCodes.Ret);
                            c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
                            _cacheCtor.Add(type, c);
                        }

                        return(c());
                    }
                }
                catch (Exception)
                {
                    throw LiteException.InvalidCtor(type);
                }
            }
        }
        private BsonValue Serialize(Type type, object obj, int depth)
        {
            if (++depth > MAX_DEPTH)
            {
                throw LiteException.DocumentMaxDepth(MAX_DEPTH);
            }

            if (obj == null)
            {
                return(BsonValue.Null);
            }

            Func <object, BsonValue> custom;

            // if is already a bson value
            if (obj is BsonValue)
            {
                return(new BsonValue((BsonValue)obj));
            }

            // test string - mapper has some special options
            else if (obj is String)
            {
                var str = this.TrimWhitespace ? (obj as String).Trim() : (String)obj;

                if (this.EmptyStringToNull && str.Length == 0)
                {
                    return(BsonValue.Null);
                }
                else
                {
                    return(new BsonValue(str));
                }
            }
            // basic Bson data types (cast datatype for better performance optimization)
            else if (obj is Int32)
            {
                return(new BsonValue((Int32)obj));
            }
            else if (obj is Int64)
            {
                return(new BsonValue((Int64)obj));
            }
            else if (obj is Double)
            {
                return(new BsonValue((Double)obj));
            }
            else if (obj is Byte[])
            {
                return(new BsonValue((Byte[])obj));
            }
            else if (obj is ObjectId)
            {
                return(new BsonValue((ObjectId)obj));
            }
            else if (obj is Guid)
            {
                return(new BsonValue((Guid)obj));
            }
            else if (obj is Boolean)
            {
                return(new BsonValue((Boolean)obj));
            }
            else if (obj is DateTime)
            {
                return(new BsonValue((DateTime)obj));
            }
            // basic .net type to convert to bson
            else if (obj is Int16 || obj is UInt16 || obj is Byte)
            {
                return(new BsonValue(Convert.ToInt32(obj)));
            }
            else if (obj is UInt32 || obj is UInt64)
            {
                return(new BsonValue(Convert.ToInt64(obj)));
            }
            else if (obj is Single || obj is Decimal)
            {
                return(new BsonValue(Convert.ToDouble(obj)));
            }
            else if (obj is Char || obj is Enum)
            {
                return(new BsonValue(obj.ToString()));
            }
            // check if is a custom type
            else if (_customSerializer.TryGetValue(type, out custom) || _customSerializer.TryGetValue(obj.GetType(), out custom))
            {
                return(custom(obj));
            }
            // for dictionary
            else if (obj is IDictionary)
            {
                var itemType = type.GetGenericArguments()[1];

                return(this.SerializeDictionary(itemType, obj as IDictionary, depth));
            }
            // check if is a list or array
            else if (obj is IEnumerable)
            {
                return(this.SerializeArray(Reflection.GetListItemType(obj), obj as IEnumerable, depth));
            }
            // otherwise serialize as a plain object
            else
            {
                return(this.SerializeObject(type, obj, depth));
            }
        }