public void AddPreAllocatedBuffers(NewPageAllocator tablePageAllocator, bool includeDetails) { if (PreAllocatedBuffers != null) { throw new InvalidOperationException("Pre allocated buffers already defined"); } PreAllocatedBuffers = StorageReportGenerator.GetReport(tablePageAllocator, includeDetails); AllocatedSpaceInBytes += PreAllocatedBuffers.AllocatedSpaceInBytes; if (includeDetails) { var allocationTree = PreAllocatedBuffers.AllocationTree; UsedSizeInBytes += (long)(allocationTree.AllocatedSpaceInBytes * allocationTree.Density); } }
/// <summary> /// A table is stored inside a tree, and has the following keys in it /// /// - active-section -> page number - the page number of the current active small data section /// - inactive-sections -> fixed size tree with no content where the keys are the page numbers of inactive small raw data sections /// - large-values -> fixed size tree with no content where the keys are the page numbers of the large values /// - for each index: /// - If can fit into fixed size tree, use that. /// - Otherwise, create a tree (whose key would be the indexed field value and the value would /// be a fixed size tree of the ids of all the matching values) /// - stats -> header information about the table (number of entries, etc) /// - schemas -> schema definition for the table /// /// </summary> public void Create(Transaction tx, Slice name, ushort?sizeInPages) { if (_primaryKey == null && _indexes.Count == 0 && _fixedSizeIndexes.Count == 0) { throw new InvalidOperationException($"Cannot create table {name} without a primary key and no indexes"); } var tableTree = tx.CreateTree(name, RootObjectType.Table); if (tableTree.State.NumberOfEntries > 0) { return; // this was already created } // Create raw data. This is where we will actually store the documents using (var rawDataActiveSection = ActiveRawDataSmallSection.Create(tx.LowLevelTransaction, name, TableType, sizeInPages)) { long val = rawDataActiveSection.PageNumber; Slice pageNumber; using ( Slice.External(tx.Allocator, (byte *)&val, sizeof(long), ByteStringType.Immutable, out pageNumber)) { tableTree.Add(ActiveSectionSlice, pageNumber); } byte *ptr; using (tableTree.DirectAdd(StatsSlice, sizeof(TableSchemaStats), out ptr)) { var stats = (TableSchemaStats *)ptr; stats->NumberOfEntries = 0; } var tablePageAllocator = new NewPageAllocator(tx.LowLevelTransaction, tableTree); tablePageAllocator.Create(); var globalPageAllocator = new NewPageAllocator(tx.LowLevelTransaction, tx.LowLevelTransaction.RootObjects); globalPageAllocator.Create(); if (_primaryKey != null) { if (_primaryKey.IsGlobal == false) { using (var indexTree = Tree.Create(tx.LowLevelTransaction, tx, _primaryKey.Name, isIndexTree: true, newPageAllocator: tablePageAllocator)) { using (tableTree.DirectAdd(_primaryKey.Name, sizeof(TreeRootHeader), out ptr)) { indexTree.State.CopyTo((TreeRootHeader *)ptr); } } } else { tx.CreateTree(_primaryKey.Name.ToString(), isIndexTree: true, newPageAllocator: globalPageAllocator); } } foreach (var indexDef in _indexes.Values) { if (indexDef.IsGlobal == false) { using (var indexTree = Tree.Create(tx.LowLevelTransaction, tx, indexDef.Name, isIndexTree: true, newPageAllocator: tablePageAllocator)) { using (tableTree.DirectAdd(indexDef.Name, sizeof(TreeRootHeader), out ptr)) { indexTree.State.CopyTo((TreeRootHeader *)ptr); } } } else { tx.CreateTree(indexDef.Name.ToString(), isIndexTree: true, newPageAllocator: globalPageAllocator); } } // Serialize the schema into the table's tree var serializer = SerializeSchema(); using (tableTree.DirectAdd(SchemasSlice, serializer.Length, out ptr)) { fixed(byte *source = serializer) { Memory.Copy(ptr, source, serializer.Length); } } } }