internal override IEnumerable <Row> GetEntities(DataExtractorHelper schema) { foreach (var record in page.Records) { var dataRow = schema.NewRow(); var readState = new RecordReadState(schema.Columns.Count(x => x.UnderlyingType == ColumnType.Bit)); int columnIndex = 0; foreach (DataColumn col in dataRow.Columns) { var sqlType = SqlTypeFactory.Create(col, readState, new CompressionContext(CompressionLevel.Row, true)); IVariableLengthDataProxy dataProxy = record.GetPhysicalColumnBytes(columnIndex); if (dataProxy == null) { dataRow[col] = null; } else { dataRow[col] = sqlType.GetValue(dataProxy.GetBytes().ToArray()); } columnIndex++; } yield return(dataRow); } }
/// <summary> /// Starts at the data page (loc) and follows the NextPage pointer chain till the end. /// </summary> internal IEnumerable <Row> ScanLinkedDataPages(PagePointer loc, DataExtractorHelper schema, CompressionContext compression, bool isSysTable) { while (PagePointer.Zero != loc && loc != null && loc.PageID > 0) { var recordParser = RecordEntityParser.CreateEntityParserForPage(loc, compression, Database, isSysTable); foreach (var dr in recordParser.GetEntities(schema)) { yield return(dr); } loc = recordParser.NextPage; } }
internal override IEnumerable <Row> GetEntities(DataExtractorHelper schema) { foreach (var record in page.Records) { // Don't process forwarded blob fragments as they should only be processed from the referenced record if (_recordsToSkip.Contains(record.Type) || record.IsGhostForwardedRecord) { continue; } short fixedOffset = 0; short variableColumnIndex = 0; var dataRow = schema.NewRow(); var readState = new RecordReadState(schema.BitColumnsCount); var bitColumnBytes = new byte[0]; foreach (var col in schema.Columns) { var sqlType = SqlTypeFactory.Create(col, readState, compression); object columnValue = null; // Sparse columns needs to retrieve their values from the sparse vector, contained in the very last // variable length column in the record. if (col.IsSparse) { // We may encounter records that don't have any sparse vectors, for instance if no sparse columns have values if (record.SparseVector != null) { // Column ID's are stored as ints in general. In the sparse vector though, they're stored as shorts. if (record.SparseVector.ColumnValues.ContainsKey((short)col.ColumnID)) { columnValue = sqlType.GetValue(record.SparseVector.ColumnValues[(short)col.ColumnID]); } } } else { var nonSparseIndex = schema.NonSparseIndexes[col.Name]; // Before we even try to parse the column & make a null bitmap lookup, ensure that it's present in the record. // There may be columns > record.NumberOfColumns caused by nullable columns added to the schema after the record was written. if (nonSparseIndex < record.NumberOfColumns && col.UnderlyingType != ColumnType.Computed) { if (sqlType.IsVariableLength) { // If there's either no null bitmap, or the null bitmap defines the column as non-null. if (!record.HasNullBitmap || !record.NullBitmap[nonSparseIndex]) { // If the current variable length column index exceeds the number of stored // variable length columns, the value is empty by definition (that is, 0 bytes, but not null). if (variableColumnIndex < record.NumberOfVariableLengthColumns) { var data = record.VariableLengthColumnData[variableColumnIndex].GetBytes()?.ToArray(); columnValue = sqlType.GetValue(data ?? new byte[0]); } else { columnValue = sqlType.GetValue(new byte[0]); } } variableColumnIndex++; } else { // Must cache type FixedLength as it may change after getting a value (e.g. SqlBit) var fixedLength = sqlType.FixedLength.Value; if ((!record.HasNullBitmap || !record.NullBitmap[nonSparseIndex]) && col.UnderlyingType != ColumnType.Bit) { var valueBytes = record.FixedLengthData.Skip(fixedOffset).Take(fixedLength).ToArray(); // We may run out of fixed length bytes. In certain conditions a null integer may have been added without // there being a null bitmap. In such a case, we detect the null condition by there not being enough fixed // length bytes to process. if (valueBytes.Length > 0) { columnValue = sqlType.GetValue(valueBytes); } } else if (col.UnderlyingType == ColumnType.Bit && !schema.IsDroppedColumn(col)) { if (readState.IsFirstBit) { bitColumnBytes = record.FixedLengthData.Skip(fixedOffset).Take(fixedLength).ToArray(); } var value = sqlType.GetValue(bitColumnBytes); columnValue = !record.HasNullBitmap || !record.NullBitmap[nonSparseIndex] ? value : null; } fixedOffset += fixedLength; } } else if (col.UnderlyingType == ColumnType.Computed) { columnValue = sqlType.GetValue(null); } else if (!col.IsNullable) { columnValue = schema.GetDefaultValue(col, sqlType); } } if (!schema.IsDroppedColumn(col)) { dataRow[col] = columnValue; } } yield return(dataRow); } }
private IEnumerable <Row> ScanPartition(long partitionID, int partitionNumber, Row schema, bool isSysTable = true) { // Lookup partition var partition = Database.Dmvs.Partitions .SingleOrDefault(p => p.PartitionID == partitionID && p.PartitionNumber == partitionNumber); if (partition == null) { throw new ArgumentException("Partition (" + partitionID + "." + partitionNumber + " does not exist."); } // Get allocation unit for in-row data var au = Database.Dmvs.SystemInternalsAllocationUnits .SingleOrDefault(x => x.ContainerID == partition.PartitionID && x.Type == 1); if (au == null) { throw new ArgumentException("Partition (" + partition.PartitionID + "." + partition.PartitionNumber + " has no HOBT allocation unit."); } // Before we can scan either heaps or indices, we need to know the compression level as that's set at the partition level, and not at the record/page level. // We also need to know whether the partition is using vardecimals. var compression = new CompressionContext((CompressionLevel)partition.DataCompression, MetaData.PartitionHasVardecimalColumns(partition.PartitionID)); var clusteredIndex = isSysTable ? null : Database.Dmvs.Indexes.SingleOrDefault(x => x.ObjectID == partition.ObjectID && x.Type == 1); var useClusteredIndex = isSysTable || clusteredIndex != null; var partitionColumns = isSysTable ? null : Database.Dmvs.SystemInternalsPartitionColumns.Where(x => x.PartitionID == partition.PartitionID).ToArray(); var defaultConstraints = isSysTable ? null : Database.Dmvs.SysDefaultConstraints.Where(x => x.ParentObjectId == partition.ObjectID).ToArray(); var schemaWrapper = new DataExtractorHelper(schema, Database.Dmvs, null, partitionColumns, defaultConstraints); // Heap tables won't have root pages, thus we can check whether a root page is defined for the HOBT allocation unit if (au.RootPagePointer != PagePointer.Zero && useClusteredIndex) { var currentPage = isSysTable ? au.FirstPagePointer : au.RootPagePointer; if (currentPage != au.FirstPagePointer) { while (true) { var ciPage = Database.GetClusteredIndexPage(currentPage, isSysTable); currentPage = ciPage.Records.Select(x => x.PageId).FirstOrDefault(); if (ciPage.Header.Level <= 1) { break; } } } // Index foreach (var row in ScanLinkedDataPages(currentPage, schemaWrapper, compression, isSysTable)) { yield return(row); } } else { // Heap foreach (var row in ScanHeap(au.FirstIamPagePointer, schemaWrapper, compression, isSysTable)) { yield return(row); } } }
/// <summary> /// Scans a heap beginning from the provided IAM page and onwards. /// </summary> private IEnumerable <Row> ScanHeap(PagePointer loc, DataExtractorHelper schema, CompressionContext compression, bool isSysTable) { // Traverse the linked list of IAM pages until the tail pointer is zero while (loc != PagePointer.Zero) { // Before scanning, check that the IAM page itself is allocated var pfsPage = Database.GetPfsPage(PfsPage.GetPfsPointerForPage(loc)); // If IAM page isn't allocated, there's nothing to return if (!pfsPage.GetPageDescription(loc.PageID).IsAllocated) { yield break; } var iamPage = Database.GetIamPage(loc, isSysTable); // Create an array with all of the header slot pointers var iamPageSlots = new [] { iamPage.Slot0, iamPage.Slot1, iamPage.Slot2, iamPage.Slot3, iamPage.Slot4, iamPage.Slot5, iamPage.Slot6, iamPage.Slot7 }; // Loop each header slot and yield the results, provided the header slot is allocated foreach (var slot in iamPageSlots.Where(x => x != PagePointer.Zero)) { var recordParser = RecordEntityParser.CreateEntityParserForPage(slot, compression, Database, isSysTable); foreach (var dr in recordParser.GetEntities(schema)) { yield return(dr); } } // Then loop through allocated extents and yield results foreach (var extent in iamPage.GetAllocatedExtents()) { // Get PFS page that tracks this extent var pfs = Database.GetPfsPage(PfsPage.GetPfsPointerForPage(extent.StartPage)); foreach (var pageLoc in extent.GetPagePointers()) { // Check if page is allocated according to PFS page var pfsDescription = pfs.GetPageDescription(pageLoc.PageID); if (!pfsDescription.IsAllocated) { continue; } var recordParser = RecordEntityParser.CreateEntityParserForPage(pageLoc, compression, Database, isSysTable); foreach (var dr in recordParser.GetEntities(schema)) { yield return(dr); } } } // Update current IAM chain location to the tail pointer loc = iamPage.Header.NextPage; } }
internal abstract IEnumerable <Row> GetEntities(DataExtractorHelper schema);