public IndexRecord(byte[] bytes, Page page) : base(page) { parseStatusBitsA(new BitArray(new[] { bytes[0] })); // Index records don't contain fixed length header - it's stored in the page header FixedLengthData = bytes.Skip(1).Take(Page.Header.Pminlen - 1).ToArray(); PageId = new PagePointer(ArrayHelper.SliceArray(FixedLengthData, FixedLengthData.Length - 6, 6)); var offset = Page.Header.Pminlen; NumberOfColumns = BitConverter.ToInt16(bytes, offset); offset += 2; if (HasNullBitmap) { offset = ParseNullBitmap(bytes, ref offset); } if (HasVariableLengthColumns) { ParseVariableLengthColumns(bytes, ref offset); } }
public PageHeader(byte[] header) { if (header.Length != 96) { throw new ArgumentException("Header length must be 96."); } /* * Bytes Content * ----- ------- * 00 HeaderVersion (tinyint) * 01 Type (tinyint) * 02 TypeFlagBits (tinyint) * 03 Level (tinyint) * 04-05 FlagBits (smallint) * 06-07 IndexID (smallint) * 08-11 PreviousPageID (int) * 12-13 PreviousFileID (smallint) * 14-15 Pminlen (smallint) * 16-19 NextPageID (int) * 20-21 NextPageFileID (smallint) * 22-23 SlotCnt (smallint) * 24-27 ObjectID (int) * 28-29 FreeCnt (smallint) * 30-31 FreeData (smallint) * 32-35 PageID (int) * 36-37 FileID (smallint) * 38-39 ReservedCnt (smallint) * 40-43 Lsn1 (int) * 44-47 Lsn2 (int) * 48-49 Lsn3 (smallint) * 50-51 XactReserved (smallint) * 52-55 XdesIDPart2 (int) * 56-57 XdesIDPart1 (smallint) * 58-59 GhostRecCnt (smallint) * 60-63 Checksum/Tornbits (int) * 64-95 ? */ HeaderVersion = header[0]; Type = (PageType)header[1]; TypeFlagBits = header[2]; Level = header[3]; FlagBits = BitConverter.ToInt16(header, 4); IndexID = BitConverter.ToInt16(header, 6); PreviousPage = new PagePointer(BitConverter.ToInt16(header, 12), BitConverter.ToInt32(header, 8)); Pminlen = BitConverter.ToInt16(header, 14); NextPage = new PagePointer(BitConverter.ToInt16(header, 20), BitConverter.ToInt32(header, 16)); SlotCnt = BitConverter.ToInt16(header, 22); ObjectID = BitConverter.ToInt32(header, 24); FreeCnt = BitConverter.ToInt16(header, 28); FreeData = BitConverter.ToInt16(header, 30); Pointer = new PagePointer(BitConverter.ToInt16(header, 36), BitConverter.ToInt32(header, 32)); ReservedCnt = BitConverter.ToInt16(header, 38); Lsn = "(" + BitConverter.ToInt32(header, 40) + ":" + BitConverter.ToInt32(header, 44) + ":" + BitConverter.ToInt16(header, 48) + ")"; XactReserved = BitConverter.ToInt16(header, 50); XdesID = "(" + BitConverter.ToInt16(header, 56) + ":" + BitConverter.ToInt32(header, 52) + ")"; GhostRecCnt = BitConverter.ToInt16(header, 58); }
public PageHeader(byte[] header) { if (header.Length != 96) throw new ArgumentException("Header length must be 96."); /* Bytes Content ----- ------- 00 HeaderVersion (tinyint) 01 Type (tinyint) 02 TypeFlagBits (tinyint) 03 Level (tinyint) 04-05 FlagBits (smallint) 06-07 IndexID (smallint) 08-11 PreviousPageID (int) 12-13 PreviousFileID (smallint) 14-15 Pminlen (smallint) 16-19 NextPageID (int) 20-21 NextPageFileID (smallint) 22-23 SlotCnt (smallint) 24-27 ObjectID (int) 28-29 FreeCnt (smallint) 30-31 FreeData (smallint) 32-35 PageID (int) 36-37 FileID (smallint) 38-39 ReservedCnt (smallint) 40-43 Lsn1 (int) 44-47 Lsn2 (int) 48-49 Lsn3 (smallint) 50-51 XactReserved (smallint) 52-55 XdesIDPart2 (int) 56-57 XdesIDPart1 (smallint) 58-59 GhostRecCnt (smallint) 60-63 Checksum/Tornbits (int) 64-95 ? */ HeaderVersion = header[0]; Type = (PageType)header[1]; TypeFlagBits = header[2]; Level = header[3]; FlagBits = BitConverter.ToInt16(header, 4); IndexID = BitConverter.ToInt16(header, 6); PreviousPage = new PagePointer(BitConverter.ToInt16(header, 12), BitConverter.ToInt32(header, 8)); Pminlen = BitConverter.ToInt16(header, 14); NextPage = new PagePointer(BitConverter.ToInt16(header, 20), BitConverter.ToInt32(header, 16)); SlotCnt = BitConverter.ToInt16(header, 22); ObjectID = BitConverter.ToInt32(header, 24); FreeCnt = BitConverter.ToInt16(header, 28); FreeData = BitConverter.ToInt16(header, 30); Pointer = new PagePointer(BitConverter.ToInt16(header, 36), BitConverter.ToInt32(header, 32)); ReservedCnt = BitConverter.ToInt16(header, 38); Lsn = "(" + BitConverter.ToInt32(header, 40) + ":" + BitConverter.ToInt32(header, 44) + ":" + BitConverter.ToInt16(header, 48) + ")"; XactReserved = BitConverter.ToInt16(header, 50); XdesID = "(" + BitConverter.ToInt16(header, 56) + ":" + BitConverter.ToInt32(header, 52) + ")"; GhostRecCnt = BitConverter.ToInt16(header, 58); }
public ClusteredIndexRecord(byte[] bytes, Page page) : base(page) { parseStatusBitsA(new BitArray(new[] { bytes[0] })); // Index records don't contain fixed length header - it's stored in the page header FixedLengthData = bytes.Skip(1).Take(Page.Header.Pminlen - 1).ToArray(); PageId = new PagePointer(ArrayHelper.SliceArray(FixedLengthData, FixedLengthData.Length - 6, 6)); }
private void parseHeader() { /* * Bytes Content * ----- ------- * 00-03 SequenceNumber (int) * 04-13 ? * 14-15 Status (smallint) * 16-27 ? * 28-31 ObjectID (int) * 32-33 IndexID (smallint) * 34 PageCount (tinyint) * 35 ? * 36-39 StartPage PageID (int) * 40-41 StartPage FileID (smallint) * 42-45 Slot0 PageID (int) * 46-47 Slot0 FileID (smallint) * 48-51 Slot1 PageID (int) * 52-53 Slot1 FileID (smallint) * 54-57 Slot2 PageID (v) * 58-59 Slot2 FileID (smallint) * 60-63 Slot3 PageID (int) * 64-65 Slot3 FileID (smallint) * 66-69 Slot4 PageID (int) * 70-71 Slot4 FileID (smallint) * 72-75 Slot5 PageID (int) * 76-77 Slot5 FileID (smallint) * 78-81 Slot6 PageID (int) * 82-83 Slot6 FileID (smallint) * 84-87 Slot7 PageID (int) * 88-89 Slot7 FileID (smallint) */ byte[] header = Records[0].FixedLengthData; // Read iam header SequenceNumber = BitConverter.ToUInt32(header, 0); Status = BitConverter.ToInt16(header, 14); ObjectID = BitConverter.ToInt32(header, 28); IndexID = BitConverter.ToInt16(header, 32); PageCount = header[34]; StartPage = new PagePointer(BitConverter.ToInt16(header, 40), BitConverter.ToInt32(header, 36)); // Read single page slot allocations Slot0 = new PagePointer(BitConverter.ToInt16(header, 46), BitConverter.ToInt32(header, 42)); Slot1 = new PagePointer(BitConverter.ToInt16(header, 52), BitConverter.ToInt32(header, 48)); Slot2 = new PagePointer(BitConverter.ToInt16(header, 58), BitConverter.ToInt32(header, 54)); Slot3 = new PagePointer(BitConverter.ToInt16(header, 64), BitConverter.ToInt32(header, 60)); Slot4 = new PagePointer(BitConverter.ToInt16(header, 70), BitConverter.ToInt32(header, 66)); Slot5 = new PagePointer(BitConverter.ToInt16(header, 76), BitConverter.ToInt32(header, 72)); Slot6 = new PagePointer(BitConverter.ToInt16(header, 82), BitConverter.ToInt32(header, 78)); Slot7 = new PagePointer(BitConverter.ToInt16(header, 88), BitConverter.ToInt32(header, 84)); }
private void parseSysrowsets() { // Using a fixed allocation unit ID, we can look up the hobt AU and scan it var pageLoc = new PagePointer( sysallocunits .Where(x => x.auid == FixedSystemObjectAllocationUnits.sysrowsets) .Single() .pgfirst ); sysrowsets = scanner.ScanLinkedDataPages <sysrowset>(pageLoc, CompressionContext.NoCompression).ToList(); }
private void parseSyssingleobjrefs() { // Using a fixed object ID, we can look up the partition for sysscalartypes and scan the hobt AU from there long rowsetID = sysrowsets .Where(x => x.idmajor == (int)SystemObject.syssingleobjrefs && x.idminor == 1) .Single() .rowsetid; var pageLoc = new PagePointer( sysallocunits .Where(x => x.auid == rowsetID && x.type == 1) .Single() .pgfirst ); syssingleobjrefs = scanner.ScanLinkedDataPages <syssingleobjref>(pageLoc, CompressionContext.NoCompression).ToList(); }
internal static RecordEntityParser CreateEntityParserForPage(PagePointer loc, CompressionContext compression, Database database) { switch (compression.CompressionLevel) { case CompressionLevel.Page: throw new NotImplementedException("Page compression not yet supported."); case CompressionLevel.Row: return new CompressedRecordEntityParser(database.GetCompressedRecordPage(loc, compression)); case CompressionLevel.None: return new PrimaryRecordEntityParser(database.GetPrimaryRecordPage(loc, compression), compression); default: throw new ArgumentException("Unsupported compression level: " + compression.CompressionLevel); } }
private void parseSyscolpars() { // Using a fixed object ID, we can look up the partition for syscolpars and scan the hobt AU from there long rowsetID = SysRowsets .Where(x => x.idmajor == (int)SystemObject.syscolpars && x.idminor == 1) .Single() .rowsetid; var pageLoc = new PagePointer( SysAllocUnits .Where(x => x.auid == rowsetID && x.type == 1) .Single() .pgfirst ); SysColPars = _scanner.ScanLinkedDataPages <syscolpar>(pageLoc, CompressionContext.NoCompression).ToList(); }
internal static RecordEntityParser CreateEntityParserForPage(PagePointer loc, CompressionContext compression, Database database) { switch (compression.CompressionLevel) { case CompressionLevel.Page: throw new NotImplementedException("Page compression not yet supported."); case CompressionLevel.Row: return(new CompressedRecordEntityParser(database.GetCompressedRecordPage(loc, compression))); case CompressionLevel.None: return(new PrimaryRecordEntityParser(database.GetPrimaryRecordPage(loc, compression), compression)); default: throw new ArgumentException("Unsupported compression level: " + compression.CompressionLevel); } }
private void parseBootRecord() { /* * Bytes Content * ----- ------- * 0-1 Version (smallint) * 2-3 CreateVersion (smallint) * 4-31 ? * 32-35 Status (int) * 36-39 NextID (int) * 40-47 ? * 48-303 DatabaseName (nchar(128)) * 304-307 ? * 308-309 DBID (smallint) * 310-311 ? * 312-319 MaxDBTimeStamp (bigint) * 320-383 ? * 384-388 CollationId(long) * 389-511 ? * 512-515 FirstSysIndexes PageID (int) * 516-517 FirstSysIndexes FileID (smallint) * 518-1440 ? */ byte[] bootRecord = Records[0].FixedLengthData; Version = BitConverter.ToInt16(bootRecord, 0); CreateVersion = BitConverter.ToInt16(bootRecord, 2); Status = BitConverter.ToInt32(bootRecord, 32); NextID = BitConverter.ToInt32(bootRecord, 36); // Truncate name at first 0x2020 char DatabaseName = Encoding.Unicode.GetString(bootRecord, 48, 256); if (DatabaseName.IndexOf('†') > 0) { DatabaseName = DatabaseName.Substring(0, DatabaseName.IndexOf('†')); } DBID = BitConverter.ToInt16(bootRecord, 308); MaxDBTimeStamp = BitConverter.ToInt64(bootRecord, 312); CollationId = BitConverter.ToInt64(bootRecord, 384); FirstSysIndexes = new PagePointer(BitConverter.ToInt16(bootRecord, 516), BitConverter.ToInt32(bootRecord, 512)); }
public string CreateRestoreScriptWithRollback(string connectionString, PagePointer pagePointer) { using (var connection = new SqlConnection(connectionString)) { connection.Open(); using (var command = connection.CreateCommand()) { command.CommandText = "DBCC READPAGE (@dbName, @fileId, @pageId, 'V8192', 1)"; command.Parameters.AddWithValue("@dbName", connection.Database); command.Parameters.AddWithValue("@fileId", pagePointer.FileID); command.Parameters.AddWithValue("@pageId", pagePointer.PageID); var reader = command.ExecuteReader(); reader.Read(); var pageBytes = (byte[])reader["ValueDump"]; var backupPage = _database.GetPage(pagePointer).RawBytes; var rolloutScript = "-- PAGE UPDATE SCRIPT\r\n\r\n" + $"DBCC WRITEPAGE('{connection.Database}', {pagePointer.FileID}, {pagePointer.PageID}, 0, 8000, 0x{BitConverter.ToString(backupPage.Take(8000).ToArray()).Replace("-", string.Empty)}, 1);\r\n" + $"DBCC WRITEPAGE('{connection.Database}', {pagePointer.FileID}, {pagePointer.PageID}, 8000, 192, 0x{BitConverter.ToString(backupPage.Skip(8000).ToArray()).Replace("-", string.Empty)}, 1);"; var rollBackScript = "-- ROLLBACK SCRIPT\r\n\r\n" + $"-- DBCC WRITEPAGE('{connection.Database}', {pagePointer.FileID}, {pagePointer.PageID}, 0, 8000, 0x{BitConverter.ToString(pageBytes.Take(8000).ToArray()).Replace("-", string.Empty)}, 1);\r\n" + $"-- DBCC WRITEPAGE('{connection.Database}', {pagePointer.FileID}, {pagePointer.PageID}, 8000, 192, 0x{BitConverter.ToString(pageBytes.Skip(8000).ToArray()).Replace("-", string.Empty)}, 1);"; var resultScript = $"\r\nUSE [{connection.Database}]\r\nGO\r\nALTER DATABASE [{connection.Database}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;\r\nGO\r\n" + rolloutScript + "\r\n\r\n" + rollBackScript + $"\r\n\r\nALTER DATABASE [{connection.Database}] SET MULTI_USER\r\nGO"; return(resultScript); } } }
public static PagePointer GetGamPointerForPage(PagePointer loc) { // First gam page is at index 2 and every 511232 pages hereafter return(new PagePointer(loc.FileID, Math.Max(loc.PageID / 511232 * 511232, 2))); }
private void parseBootRecord() { /* Bytes Content ----- ------- 0-1 Version (smallint) 2-3 CreateVersion (smallint) 4-31 ? 32-35 Status (int) 36-39 NextID (int) 40-47 ? 48-303 DatabaseName (nchar(128)) 304-307 ? 308-309 DBID (smallint) 310-311 ? 312-319 MaxDBTimeStamp (bigint) 320-511 ? 512-515 FirstSysIndexes PageID (int) 516-517 FirstSysIndexes FileID (smallint) 518-1440 ? */ byte[] bootRecord = Records[0].FixedLengthData; Version = BitConverter.ToInt16(bootRecord, 0); CreateVersion = BitConverter.ToInt16(bootRecord, 2); Status = BitConverter.ToInt32(bootRecord, 32); NextID = BitConverter.ToInt32(bootRecord, 36); // Truncate name at first 0x2020 char DatabaseName = Encoding.Unicode.GetString(bootRecord, 48, 256); if (DatabaseName.IndexOf('†') > 0) DatabaseName = DatabaseName.Substring(0, DatabaseName.IndexOf('†')); DBID = BitConverter.ToInt16(bootRecord, 308); MaxDBTimeStamp = BitConverter.ToInt64(bootRecord, 312); FirstSysIndexes = new PagePointer(BitConverter.ToInt16(bootRecord, 516), BitConverter.ToInt32(bootRecord, 512)); }
public static PagePointer GetSgamPointerForPage(PagePointer loc) { // First gam page is at index 3 and every 511232 pages hereafter return new PagePointer(loc.FileID, Math.Max(loc.PageID / 511232 * 511232 + 1, 3)); }
public static PagePointer GetPfsPointerForPage(PagePointer loc) { // First pfs page is at index 1 and every 8088 pages hereafter return(new PagePointer(loc.FileID, Math.Max(loc.PageID / 8088 * 8088, 1))); }
public static PagePointer GetPfsPointerForPage(PagePointer loc) { // First pfs page is at index 1 and every 8088 pages hereafter return new PagePointer(loc.FileID, Math.Max(loc.PageID / 8088 * 8088, 1)); }
private void parseHeader() { /* Bytes Content ----- ------- 00-03 SequenceNumber (int) 04-13 ? 14-15 Status (smallint) 16-27 ? 28-31 ObjectID (int) 32-33 IndexID (smallint) 34 PageCount (tinyint) 35 ? 36-39 StartPage PageID (int) 40-41 StartPage FileID (smallint) 42-45 Slot0 PageID (int) 46-47 Slot0 FileID (smallint) 48-51 Slot1 PageID (int) 52-53 Slot1 FileID (smallint) 54-57 Slot2 PageID (v) 58-59 Slot2 FileID (smallint) 60-63 Slot3 PageID (int) 64-65 Slot3 FileID (smallint) 66-69 Slot4 PageID (int) 70-71 Slot4 FileID (smallint) 72-75 Slot5 PageID (int) 76-77 Slot5 FileID (smallint) 78-81 Slot6 PageID (int) 82-83 Slot6 FileID (smallint) 84-87 Slot7 PageID (int) 88-89 Slot7 FileID (smallint) */ byte[] header = Records[0].FixedLengthData; // Read iam header SequenceNumber = BitConverter.ToUInt32(header, 0); Status = BitConverter.ToInt16(header, 14); ObjectID = BitConverter.ToInt32(header, 28); IndexID = BitConverter.ToInt16(header, 32); PageCount = header[34]; StartPage = new PagePointer(BitConverter.ToInt16(header, 40), BitConverter.ToInt32(header, 36)); // Read single page slot allocations Slot0 = new PagePointer(BitConverter.ToInt16(header, 46), BitConverter.ToInt32(header, 42)); Slot1 = new PagePointer(BitConverter.ToInt16(header, 52), BitConverter.ToInt32(header, 48)); Slot2 = new PagePointer(BitConverter.ToInt16(header, 58), BitConverter.ToInt32(header, 54)); Slot3 = new PagePointer(BitConverter.ToInt16(header, 64), BitConverter.ToInt32(header, 60)); Slot4 = new PagePointer(BitConverter.ToInt16(header, 70), BitConverter.ToInt32(header, 66)); Slot5 = new PagePointer(BitConverter.ToInt16(header, 76), BitConverter.ToInt32(header, 72)); Slot6 = new PagePointer(BitConverter.ToInt16(header, 82), BitConverter.ToInt32(header, 78)); Slot7 = new PagePointer(BitConverter.ToInt16(header, 88), BitConverter.ToInt32(header, 84)); }