解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)

原文:解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)

解剖SQLSERVER 第五篇  OrcaMDF里读取Bits类型数据(译)

http://improve.dk/reading-bits-in-orcamdf/

Bits类型的存储跟SQLSERVER其他定长数据类型的存储很不一样。通常,所有定长列都会显示出来,一个条记录里定长数据部分的字段数据总是一个挨着一个

我们可以写入磁盘的最小数据单位是一个字节,存储位类型数据的天真的方法就是使用一整个(字节@)来存储每一个位,使用常用的格式去解释位类型数据是很简单的

,不过这会浪费一些空间 ,就像null位图,如果一个表只有3列,那么用一个字节来存储null位图会比较浪费,因为其他的5个位都没有用到

@:文章里是用位 ,这里应该是用字节吧

在记录的内部位类型是如何存储的?

一些位类型列的值是存储在一个字节中的,最大可以到8个位,通常,我们会有如下表定义

CREATE TABLE BitTest
(
    A bit
    B bit
    C bit
    D int
)

记录的定长部分数据需要占用5个字节,4个字节存储int 列 ,而另一个字节存储A 、B、C这三列位类型的数据,只用了字节里面的3个位

我们再添加一些列

CREATE TABLE BitTest
(
    A bit
    B bit
    C bit
    D int
    E bit
    F bit
    G bit
    H smallint
    I bit
    J bit
    K bit
)

E到G列按道理来说应该存储在D列的后面,但是他们会继续使用第一个 bit byte,直到第一个 bit byte使用完所有的位空间为止

下面的图显示了H列(smallint )直接存储在D列的后面,而在D列后面是存储K列的新bit byte,因为第一个bit byte已经满了

当读取行记录里的位类型时我们需要知道的状态

很明显,我们一次不能只读取一个字段的值,我们读取固定长度数据类型的时候还需要读取定长数据偏移指针

我们需要一些能在读取的时候指示我们当前读取到字节中哪一个位属于哪一个字段的状态,然后我们读取一个新的bit byte

我来介绍一下RecordReadState类

public class RecordReadState
{
    // We start out having consumed all bits as none have been read
    private int currentBitIndex = 8;
    private byte bits;

    public void LoadBitByte(byte bits)
    {
        this.bits = bits;
        currentBitIndex = 0;
    }

    public bool AllBitsConsumed
    {
        get { return currentBitIndex == 8; }
    }

    public bool GetNextBit()
    {
        return (bits & (1 << currentBitIndex++)) != 0;
    }
}

RecordReadState 类当前只需要处理bits,但是将来我可能还要创建一个BitReadState 类用来保存读取状态

RecordReadState 类保存了一个字节用来当作指针指出下一个可用的位在字节的哪个地方,如果字节已经用完了存储满了所有的位数据

(currentBixIndex = 8 (0-7 being the available bits)),方法AllBitsConsumed 就会返回true,指示我们需要读取一个新的 bit byte

GetNextBit方法只是简单的从 bit byte中读取当前的bit ,然后将currentBitIndex(bit index)的值加1

demo

using NUnit.Framework;
using OrcaMDF.Core.Engine.Records;
namespace OrcaMDF.Core.Tests.Engine.Records
{
    [TestFixture]
public class RecordReadStateTests
{
        [Test]
public void General()
{
var state = new RecordReadState();
// No bits available
Assert.IsTrue(state.AllBitsConsumed);
state.LoadBitByte(0xD2); // 11010010
// Bits available
Assert.IsFalse(state.AllBitsConsumed);
// Reading bit values
Assert.IsFalse(state.GetNextBit());
Assert.IsTrue(state.GetNextBit());
Assert.IsFalse(state.GetNextBit());
Assert.IsFalse(state.GetNextBit());
Assert.IsTrue(state.GetNextBit());
Assert.IsFalse(state.GetNextBit());
Assert.IsTrue(state.GetNextBit());
// One bit left
Assert.IsFalse(state.AllBitsConsumed);
Assert.IsTrue(state.GetNextBit());
// Bits exhausted, ready for next byte
Assert.IsTrue(state.AllBitsConsumed);
}
}
}

SqlBit实现

一旦我们实现了状态的读取,我们就可以实现SqlBit 类型

public class SqlBit : ISqlType
{
    private readonly RecordReadState readState;

    public SqlBit(RecordReadState readState)
    {
        this.readState = readState;
    }

    public bool IsVariableLength
    {
        get { return false; }
    }

    public short? FixedLength
    {
        get
        {
            if (readState.AllBitsConsumed)
                return 1;

            return 0;
        }
    }

    public object GetValue(byte[] value)
    {
        if(readState.AllBitsConsumed && value.Length != 1)
            throw new ArgumentException("All bits consumed, invalid value length: " + value.Length);

        if (value.Length == 1)
            readState.LoadBitByte(value[0]);

        return readState.GetNextBit();
    }
}

SqlBit 在构造函数里传入一个read state,read state指示当前记录读取操作的范围。需要注意的是固定长度需要依据read state里的当前AllBitsConsumed值

如果字节里面所有位都被占用,那么意味着需要读取整个字节,如果if (readState.AllBitsConsumed)返回0表示不需要读取整个字节,但是GetValue方法依然会被调用

GetValue方法会验证一种情况:readState.AllBitsConsumed 返回真,证明 bit byte是有数据存储在里面,但是value.Length返回的长度是0,那证明有问题了

如果我们读到一个值,我们会请求 read state 去装载一个新的bit byte ,之后,我们可以调用GetNextBit 方法返回 read state的当前bit

相关测试

using NUnit.Framework;
using OrcaMDF.Core.Engine.Records;
using OrcaMDF.Core.Engine.SqlTypes;

namespace OrcaMDF.Core.Tests.Engine.SqlTypes
{
    [TestFixture]
    public class SqlBitTests
    {
        [Test]
        public void GetValue()
        {
            var readState = new RecordReadState();
            var type = new SqlBit(readState);

            // No bytes read - length is one
            Assert.AreEqual(1, type.FixedLength);

            // Load byte and check length is 0
            readState.LoadBitByte(0xD2);
            Assert.AreEqual(0, type.FixedLength);

            Assert.IsFalse((bool)type.GetValue(new byte[0]));
            Assert.IsTrue((bool)type.GetValue(new byte[0]));
            Assert.IsFalse((bool)type.GetValue(new byte[0]));
            Assert.IsFalse((bool)type.GetValue(new byte[0]));
            Assert.IsTrue((bool)type.GetValue(new byte[0]));
            Assert.IsFalse((bool)type.GetValue(new byte[0]));
            Assert.IsTrue((bool)type.GetValue(new byte[0]));

            // One bit left - length should still be 0
            Assert.AreEqual(0, type.FixedLength);

            Assert.IsTrue((bool)type.GetValue(new byte[0]));

            // All bits consumed - length should be 1
            Assert.AreEqual(1, type.FixedLength);
        }
    }
}

第五篇完

时间: 2024-11-20 00:36:57

解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)的相关文章

解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)

解剖SQLSERVER 第四篇  OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几种不同的date相关类型,当前OrcaMDF 支持三种最常用的date类型:date,datetime,smalldatetime SqlDate实现 date 类型在三种类型之中是最简单的,他是一个3个字节的定长类型,存储了日期值它支持的日期范围从0001-01-01到9999-12-31 默认值

解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译)

原文:解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译) 解剖SQLSERVER 第十篇  OrcaMDF Studio 发布+ 特性重温(译) http://improve.dk/orcamdf-studio-release-feature-recap/ 自从我上次作了一个OrcaMDF特性概述之后,两个半月过去了. 只是两个半月过去了自从我上次一个OrcaMDF特性概述.从那时起我一直在忙着参加SQLSERVER的最顶级的三个会议:SQLBits.SQLPA

解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译)

原文:解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译) 解剖SQLSERVER 第八篇  OrcaMDF 现在支持多数据文件的数据库(译) http://improve.dk/orcamdf-now-supports-databases-with-multiple-data-files/ OrcaMDF 其中一个最新特性是支持多数据文件的数据库.这在解析上面需要作出相关的小改变,实际上大部分都是bug 修复代码 由于之前只支持单个数据文件而引起的.然而这确实需要一

解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译)

原文:解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译) 解剖SQLSERVER 第七篇  OrcaMDF 特性概述(译) http://improve.dk/orcamdf-feature-recap/ 时间过得真快,这已经过了大概四个月了自从我最初介绍我的宠物项目OrcaMDF. 自从项目开始到现在,OrcaMDF发生了很多变化,功能更强了,因此我想提供一个概述对目前OrcaMDF的功能的概述以及我对OrcaMDF未来的计划 页面类型 OrcaMDF 当前支持以下页面的数据完整解

解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译)

原文:解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译) 解剖SQLSERVER 第六篇  对OrcaMDF的系统测试里避免regressions (译) http://improve.dk/avoiding-regressions-in-orcamdf-by-system-testing/ 当我继续添加新功能和新的数据结构支持进去OrcaMDF软件的时候,bug的风险不断增加 特别是当我开发一个很大的未知功能时,我不能预估结构和该结构的关联,为了降低风

解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译)

原文:解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译) 解剖SQLSERVER 第十三篇    Integers在行压缩和页压缩里的存储格式揭秘(译) http://improve.dk/the-anatomy-of-row-amp-page-compressed-integers/ 当解决OrcaMDF对行压缩的支持的时候,视图解析整数的时候遇到了一些挑战. 和正常的未压缩整数存储不同的是这些都是可变长度--这意味着1个整数的值50只占用1个字节,而不是

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译)

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译) http://improve.dk/corrupting-databases-purpose-using-orcamdf-corruptor/ 有时候你必须先作恶,后行善.情况就是 当你想磨练你的数据库修复技能 我现在添加了一个Corruptor 类到OrcaMDF里面 去测试新的RawDatabase 的功能.Corruptor 就跟他的名字一样--他会故意损坏数据库文件 Corruptor 本身

解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)

原文:解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译) 解剖SQLSERVER 第十一篇    对SQLSERVER的多个版本进行自动化测试(译) http://improve.dk/automated-testing-of-orcamdf-against-multiple-sql-server-versions/ 自从我发布了OrcaMDF Studio,我已经意识到SQL2005和SQL2008之间的一些系统表的差异. 这些差异导致OrcaMDF 解析失败

解剖SQLSERVER 第三篇 数据类型的实现(译)

解剖SQLSERVER 第三篇  数据类型的实现(译) http://improve.dk/implementing-data-types-in-orcamdf/ 实现对SQLSERVER数据类型的解析在OrcaMDF 软件里面是一件比较简单的事,只需要实现ISqlType 接口 public interface ISqlType { bool IsVariableLength { get; } short? FixedLength { get; } object GetValue(byte[]