/// <summary> /// Flushes a table header to disk /// </summary> /// <param name="Path"></param> /// <param name="Key"></param> internal static void Flush(string Path, TableHeader Element) { // Convert to a hash // byte[] b = new byte[TableHeader.SIZE]; TableHeader.ToHash(b, 0, Element); // Hit the disk // using (FileStream x = File.Open(Path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)) { x.Write(b, 0, b.Length); } }
/// <summary> /// Reads the table header from disk, but does NOT allocate in the Spike heap /// </summary> /// <param name="Path"></param> /// <returns></returns> internal static TableHeader Buffer(string Path) { byte[] buffer = new byte[TableHeader.SIZE]; using (FileStream x = File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.Read)) { x.Read(buffer, 0, TableHeader.SIZE); } TableHeader h = TableHeader.FromHash(buffer, 0); return(h); }
/// <summary> /// This method should be used for creating a table object from an existing table on disk /// </summary> /// <param name="Host"></param> /// <param name="Header"></param> /// <param name="ClusterKey"></param> public TreeTable(Host Host, TableHeader Header) : base(Host, Header) { if (Header.RootPageID == -1) { throw new ArgumentException("The root page ID cannot be null"); } // Get the sort key // Key k = Header.ClusterKey; // Get the root page ID // Page root = this.GetPage(Header.RootPageID); // Cluster // BinaryRecordTree.TreeAffinity x = this._Header.ClusterKeyState; this._Cluster = new BinaryRecordTree(this, k, this.Columns, root, this.Header, x); this._TableType = "CLUSTER_SCRIBE"; }
/// <summary> /// Copies a table to a new directory /// </summary> /// <param name="Element"></param> /// <param name="NewDir"></param> /// <param name="NewName"></param> public void CopyTable(Table Element, string NewDir, string NewName) { // Need to save the current copy to disk // this.FlushTable(Element.Key); // Save the key // string OldPath = Element.Header.Path; string NewPath = TableHeader.DeriveV1Path(NewDir, NewName); // Copy the file // File.Copy(OldPath, NewPath); // Buffer the header // TableHeader h = Buffer(NewPath); // Change the dir and name // h.Directory = NewDir; h.Name = NewName; // Save the header // Flush(NewPath, h); }
/// <summary> /// This method should be used for creating a table object from an existing table on disk /// </summary> /// <param name="Host"></param> /// <param name="Header"></param> /// <param name="ClusterKey"></param> public ShellTable(Host Host, TableHeader Header) : base(Host, Header) { this._TableType = "HEAP_SCRIBE"; }
/// <summary> /// Converts a TableHeader to a byte array /// </summary> /// <param name="Buffer"></param> /// <param name="Location"></param> /// <param name="Key"></param> public static void ToHash(byte[] Buffer, int Location, TableHeader Element) { // Check the size // if (Buffer.Length - Location < TableHeader.SIZE) { throw new Exception("Buffer is incorrect size"); } // Write the hash key // Array.Copy(BitConverter.GetBytes(HASH_KEY), 0, Buffer, Location + OFFSET_HASH_KEY, LEN_SIZE); // Write the name // Array.Copy(BitConverter.GetBytes(Element.Name.Length), 0, Buffer, Location + OFFSET_NAME_LEN, LEN_SIZE); Array.Copy(ASCIIEncoding.ASCII.GetBytes(Element.Name), 0, Buffer, Location + OFFSET_NAME, Element.Name.Length); // Write the directory // Array.Copy(BitConverter.GetBytes(Element.Directory.Length), 0, Buffer, Location + OFFSET_DIR_LEN, LEN_SIZE); Array.Copy(ASCIIEncoding.ASCII.GetBytes(Element.Directory), 0, Buffer, Location + OFFSET_DIR, Element.Directory.Length); // Write the extension // Array.Copy(BitConverter.GetBytes(Element.Extension.Length), 0, Buffer, Location + OFFSET_EXT_LEN, LEN_SIZE); Array.Copy(ASCIIEncoding.ASCII.GetBytes(Element.Extension), 0, Buffer, Location + OFFSET_EXT, Element.Extension.Length); // Write page count // Array.Copy(BitConverter.GetBytes(Element.PageCount), 0, Buffer, Location + OFFSET_PAGE_COUNT, LEN_SIZE); // Write record count // Array.Copy(BitConverter.GetBytes(Element.RecordCount), 0, Buffer, Location + OFFSET_RECORD_COUNT, 8); // Long integer // Write column count // Array.Copy(BitConverter.GetBytes(Element.Columns.Count), 0, Buffer, Location + OFFSET_COLUMN_COUNT, LEN_SIZE); // Write first page ID // Array.Copy(BitConverter.GetBytes(Element.OriginPageID), 0, Buffer, Location + OFFSET_FIRST_PAGE_ID, LEN_SIZE); // Write last page ID // Array.Copy(BitConverter.GetBytes(Element.TerminalPageID), 0, Buffer, Location + OFFSET_LAST_PAGE_ID, LEN_SIZE); // Write page size // Array.Copy(BitConverter.GetBytes(Element.PageSize), 0, Buffer, Location + OFFSET_PAGE_SIZE, LEN_SIZE); // Write radix page // Array.Copy(BitConverter.GetBytes(Element.RootPageID), 0, Buffer, Location + OFFSET_ROOT_PAGE_ID, LEN_SIZE); // Write key // Buffer[Location + OFFSET_SORT_KEY] = (byte)Element.ClusterKeyState; byte[] b = Element.ClusterKey.Bash(); Array.Copy(b, 0, Buffer, Location + OFFSET_SORT_KEY + 4, b.Length); // Write the index table // Array.Copy(BitConverter.GetBytes(Element.IndexHeaders.Count), 0, Buffer, Location + OFFSET_INDEX_COUNT, LEN_SIZE); for (int i = 0; i < Element.IndexHeaders.Count; i++) { int pos = Location + OFFSET_INDEX_TABLE + i * IndexHeader.SIZE_HASH; IndexHeader.Write(Buffer, pos, Element.IndexHeaders[i]); } // Write schema // for (int i = 0; i < Element.Columns.Count; i++) { Cell c = new Cell((short)(Element.Columns.ColumnSize(i) * (Element.Columns.ColumnNull(i) ? 1 : -1))); byte NameLen = (byte)Element.Columns.ColumnName(i).Length; byte Affinity = (byte)Element.Columns.ColumnAffinity(i); byte Size1 = c.B0; byte Size2 = c.B1; byte[] Name = ASCIIEncoding.ASCII.GetBytes(Element.Columns.ColumnName(i)); int ptr = Location + OFFSET_COLUMNS + i * COL_REC_LEN; Buffer[ptr + COL_NAME_LEN_PTR] = NameLen; Buffer[ptr + COL_AFFINITY] = Affinity; Buffer[ptr + COL_SIZE] = Size1; Buffer[ptr + COL_SIZE + 1] = Size2; //Buffer[ptr + COL_NULL] = Nullness; Array.Copy(Name, 0, Buffer, ptr + COL_NAME_PTR, Name.Length); } }
// Static Methods // /// <summary> /// Converts a byte array to a TableHeader /// </summary> /// <param name="Buffer"></param> /// <param name="Location"></param> /// <returns></returns> public static TableHeader FromHash(byte[] Buffer, int Location) { // Check the size // if (Buffer.Length - Location < TableHeader.SIZE) { throw new Exception("Buffer is incorrect size"); } // Check the hash key // if (BitConverter.ToInt32(Buffer, Location + OFFSET_HASH_KEY) != HASH_KEY) { throw new Exception("Invalid hash key"); } // Create // TableHeader h = new TableHeader(); int Len = 0; // Alias // Len = BitConverter.ToInt32(Buffer, Location + OFFSET_NAME_LEN); h.Name = ASCIIEncoding.ASCII.GetString(Buffer, Location + OFFSET_NAME, Len); // Directory // Len = BitConverter.ToInt32(Buffer, Location + OFFSET_DIR_LEN); h.Directory = ASCIIEncoding.ASCII.GetString(Buffer, Location + OFFSET_DIR, Len); // Extension // Len = BitConverter.ToInt32(Buffer, Location + OFFSET_EXT_LEN); h.Extension = ASCIIEncoding.ASCII.GetString(Buffer, Location + OFFSET_EXT, Len); // Page count // h.PageCount = BitConverter.ToInt32(Buffer, Location + OFFSET_PAGE_COUNT); // Row count // h.RecordCount = BitConverter.ToInt64(Buffer, Location + OFFSET_RECORD_COUNT); // Column Count // int ColCount = BitConverter.ToInt32(Buffer, Location + OFFSET_COLUMN_COUNT); // First Page // h.OriginPageID = BitConverter.ToInt32(Buffer, Location + OFFSET_FIRST_PAGE_ID); // Last Page // h.TerminalPageID = BitConverter.ToInt32(Buffer, Location + OFFSET_LAST_PAGE_ID); // Page PageSize // h.PageSize = BitConverter.ToInt32(Buffer, Location + OFFSET_PAGE_SIZE); // Radix Page // h.RootPageID = BitConverter.ToInt32(Buffer, Location + OFFSET_ROOT_PAGE_ID); // Key // h.ClusterKey = new Key(); h.ClusterKeyState = (BinaryRecordTree.TreeAffinity)Buffer[Location + OFFSET_SORT_KEY]; // gets the unique int KeyCount = BitConverter.ToInt32(Buffer, Location + OFFSET_SORT_KEY + 4); // gets the key size for (int i = 0; i < KeyCount; i++) { int loc = Location + OFFSET_SORT_KEY + 8 + 8 * i; int idx = BitConverter.ToInt32(Buffer, loc); KeyAffinity affinity = (KeyAffinity)BitConverter.ToInt32(Buffer, loc + 4); h.ClusterKey.Add(idx, affinity); } // Read the index table // int idx_cnt = BitConverter.ToInt32(Buffer, Location + OFFSET_INDEX_COUNT); for (int i = 0; i < idx_cnt; i++) { int pos = Location + OFFSET_INDEX_TABLE + i * IndexHeader.SIZE_HASH; IndexHeader idx_h = IndexHeader.Read(Buffer, pos); h.IndexHeaders.Add(idx_h); } // Load the columns // h.Columns = new Schema(); for (int i = 0; i < ColCount; i++) { int RecordOffset = Location + OFFSET_COLUMNS + i * COL_REC_LEN; int NameLen = (int)Buffer[RecordOffset]; string ColName = ASCIIEncoding.ASCII.GetString(Buffer, RecordOffset + COL_NAME_PTR, NameLen); CellAffinity ColType = (CellAffinity)Buffer[RecordOffset + COL_AFFINITY]; int ColSize = (int)(BitConverter.ToInt16(Buffer, RecordOffset + COL_SIZE)); bool ColNull = true; if (ColSize < 0) { ColSize = -ColSize; ColNull = false; } h.Columns.Add(ColName, ColType, ColSize, ColNull); } return(h); }
// Clones // public static Table Clone(Table Element, string NewDirectory, string NewName) { Element.Host.TableStore.DropTable(TableHeader.DeriveV1Path(NewDirectory, NewName)); return(new HeapTable(Element.Host, NewName, NewDirectory, Element.Columns, Element.PageSize)); }
/// <summary> /// /// </summary> /// <param name="Host"></param> /// <param name="Path"></param> /// <param name="Columns"></param> /// <param name="PageSize"></param> public HeapTable(Host Host, string Path, Schema Columns, int PageSize) : this(Host, TableHeader.ExtractName(Path), TableHeader.ExtractDir(Path), Columns, Page.DEFAULT_SIZE) { }
/// <summary> /// This method should be used for creating a table object from an existing table on disk /// </summary> /// <param name="Host"></param> /// <param name="Header"></param> /// <param name="ClusterKey"></param> public HeapTable(Host Host, TableHeader Header) : base(Host, Header) { this._Terminis = this.GetPage(this._Header.TerminalPageID); this._TableType = "HEAP_SCRIBE"; }
/// <summary> /// Gets a table, either from memory or disk /// </summary> /// <param name="Key"></param> /// <returns></returns> public Table RequestTable(string Key) { // #DEBUG# // this._Host.DebugPrint("TableStore.RequestTable({0})", Key); this._Host.DebugDepth++; if (this.TableIsInMemory(Key)) { // #DEBUG# // this._Host.DebugPrint("TableStore.RequestTable->Table in memory({0})", Key); return(this._TableStore[Key]); } else { // Get the header // TableHeader h = Buffer(Key); Table t; // Create the actual table // if (h.RootPageID == -1) { t = new HeapTable(this._Host, h); // #DEBUG# // this._Host.DebugPrint("TableStore.RequestTable->Table not in memory; buffering table as HEAP ({0})", Key); } else { t = new TreeTable(this._Host, h); // #DEBUG# // this._Host.DebugPrint("TableStore.RequestTable->Table not in memory; buffering table as CLUSTER ({0})", Key); } // Add to the table store // this.PushTable(t); // Need to buffer a block of pages and make sure these pages are not in memory // Check to see how many pages we can buffer // int MaxPages = (int)(this.FreeMemory / h.PageSize); int Pages = Math.Min(h.PageCount, MaxPages); // Buffer a block of pages // // #DEBUG# // this._Host.DebugPrint("TableStore.RequestTable->Buffering a block of pages ({0}); from {1} - {2} of {3}", Key, 0, Pages, h.PageCount); foreach (Page p in BufferBlock(h, 0, Pages)) { PageUID k = new PageUID(Key, p.PageID); if (this._PageStore.ContainsKey(k)) { this._PageStore[k] = p; } else { this._PageStore.Add(k, p); } } this._Host.DebugDepth--; return(t); } }