moonsqlitefile

    Pure MoonBit read-only SQLite file format parser and inspector

    sqlite
    parser
    database
    inspector
    Download zip
    Author
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    10 hours ago
    Downloads
    4

    #MoonSQLiteFile

    CI

    纯 MoonBit 的 SQLite 3 数据库文件解析与检查库:直接解释磁盘格式,提供页面、记录和 schema API。适用于文件格式教学、离线数据库检查、跨后端的数据库结构浏览器。

    核心库只依赖 MoonBit 标准库,支持 Wasm、WasmGC、JS 与 native;Node CLI 宿主层只处理文件与进程 I/O。SQLite 引擎仅用于生成测试样本和对照结果。

    #已实现

    • 100 字节文件头验证、512–65536 字节页面、安全页访问。
    • 四类 B-tree 页头、cell pointer 与 freeblock 链检查。
    • 1–9 字节 varint、全部标准 serial types、64 位整数、浮点、NULL、BLOB。
    • UTF-8、UTF-16LE、UTF-16BE 严格解码。
    • 普通 rowid 表的多层 B-tree 遍历、父键范围与循环检查。
    • 索引及 WITHOUT ROWID 原始记录遍历,包含索引内部页记录,保留磁盘字段顺序。
    • 逐条回调扫描、明确完成状态、累计 payload 与总页数预算。
    • overflow 重组、sqlite_schema、表名解析与 freelist 检查。
    • JSON CLI、真实 SQLite 对照测试与 GitHub Actions CI。

    项目只读,不执行 SQL、不写数据库、不合并 WAL。索引记录的排序语义验证、SQL 列映射和全局页归属诊断尚未实现。输入应为安全获取的静态数据库副本;完整范围见 架构说明。

    #获取与运行

    要求 MoonBit release 工具链(本地验证 moon 0.1.20260920)、Node.js 22 或更新版本;对照验证还需要 Python 3.13。无 npm 依赖。

    git clone https://github.com/prowk/MoonSQLiteFile.git cd MoonSQLiteFile moon build --target js cmd/inspect node tools/inspect.cjs fixtures/core.sqlite header node tools/inspect.cjs fixtures/core.sqlite schema node tools/inspect.cjs fixtures/core.sqlite page 3 node tools/inspect.cjs fixtures/core.sqlite rows samples node tools/inspect.cjs fixtures/core.sqlite rows branches 5 node tools/inspect.cjs fixtures/core.sqlite freelist node tools/inspect.cjs fixtures/btree.sqlite records keyed 5 node tools/inspect.cjs fixtures/btree.sqlite index mixed_index 5 node tools/inspect.cjs fixtures/btree.sqlite scan 49 10

    成功时 stdout 输出一行 JSON;失败时 stderr 输出错误,退出码非零。rows 默认最多 100000 行,传 0 返回空数组。整数和 rowid 用十进制字符串、BLOB 用十六进制,避免 JS 丢失 64 位精度。

    samples 第一行 rowid 为 "-7",中文字段为 "中文 SQLite 🌙";branches 共 680 行;freelist 共 7 页。样本可重复生成,见 fixtures 文档。

    #库 API

    v0.1.0 已发布到 Mooncakes;本仓库当前实现 v0.2.0。模块名为 prowk/moonsqlitefile。消费包的 moon.pkg 导入:

    import {
    "prowk/moonsqlitefile" @sqlite,
    }

    宿主提供文件字节,库接收 Bytes:

    fn inspect(data : Bytes) -> Array[@sqlite.Row] raise @sqlite.SqliteError {
    let db = @sqlite.open_database(data)
    db.table_rows("samples", limit=10)
    }

    运行纯库示例:

    moon run --target js examples/basic

    输出 page_size=512, pages=1 与 schema entries=0。API 见 pkg.generated.mbti 和 架构说明。

    Row.values 是磁盘存储值,保持字段顺序:INTEGER PRIMARY KEY 通常为 Null,真实值位于 Row.rowid;REAL affinity 的值也可能存为 Integer。库不推断列名、主键别名或 SQL 默认值。

    v0.2 新增 table_records、index_records 和 scan_btree。WITHOUT ROWID 与索引的 BTreeRecord.rowid 为 None,字段保留在磁盘顺序;使用 scan_btree 的回调可逐条消费数据,并从 ScanSummary.completion 判断是否真正完成。

    open_source(&PageSource) 支持宿主按需提供字节,初始化只读取文件头。源必须保持静态快照;open_database(Bytes) 保留原有用法。同步数据源接口目前使用 Int 偏移,完整的大文件、异步 I/O 和 WAL overlay 尚未实现。

    #验证

    moon check --target all --deny-warn moon build --target all --deny-warn moon test --target all --deny-warn moon fmt --check python tools/generate_fixtures.py --check python tools/verify_oracle.py python tools/generate_btree_fixtures.py --check python tools/verify_btree_oracle.py python tools/verify_consumer.py

    测试覆盖四后端、真实 SQLite 查询、Unicode、overflow、损坏引用与资源预算。原始表读取验证 1063 行;索引及 WITHOUT ROWID 在三种页大小和文本编码中验证 4407 条记录,检查内部页记录的完整性和磁盘位置。CI 还验证实际发布包可由独立项目消费。Python 不参与核心运行时。

    确定性 mutation smoke 对 192 次单比特变更进行限额扫描,检查解析结果或 SqliteError 失败路径,覆盖文件头、页头、cell 和 overflow 等区域;该小型回归集不替代长期 fuzz 或完整损坏语料库。

    #赛事与来源

    依据 SQLite 官方磁盘格式规范 独立实现,未移植第三方解析器。已有 SQLite binding 用于执行 SQL,本项目直接检查文件结构,差异及十月规则来源见 赛事工程记录。

    开发由 Codex AI 辅助,参赛者需理解并维护成果;正式一页申报书须人工撰写。目前工程准备不代表已报名或验收通过,后续还需人工申报与报名材料提交。

    Apache-2.0 许可证,见 LICENSE 与 来源说明。

    PageSource

    pub(open) trait PageSource {
    fn byte_length(Self) -> Int
    fn read_range(Self, Int, Int) -> Bytes raise SqliteError
    }

    源必须在 Database 的使用期间保持同一份静态快照;不提供并发锁或事务语义。

    SqliteError

    pub(all) suberror SqliteError {
    Invalid(String)
    Unsupported(String)
    LimitExceeded(String)
    } derive(
    Debug
    )

    BTreeRecord

    pub(all) struct BTreeRecord {
    rowid : Int64?
    values : Array[Value]
    page_number : Int
    cell_offset : Int
    } derive(
    Debug
    )

    原始存储记录;index B-tree 没有独立 rowid,字段按磁盘顺序保留。

    BytesSource

    pub struct BytesSource {
    data : Bytes
    }

    BytesSource::byte_length

    fn BytesSource::byte_length(self : BytesSource) -> Int

    BytesSource::new

    fn BytesSource::new(data : Bytes) -> BytesSource

    BytesSource::read_range

    fn BytesSource::read_range(self : BytesSource, offset : Int, count : Int) -> Bytes raise SqliteError

    Database

    pub struct Database {
    source : &PageSource
    header : Header
    page_count : Int
    limits : Limits
    }

    Database::freelist

    fn Database::freelist(self : Database) -> Array[Int] raise SqliteError

    返回 freelist 中的 trunk 与 leaf 页号,并校验计数与循环。

    Database::header

    fn Database::header(self : Database) -> Header

    Database::index_records

    fn Database::index_records(self : Database, name : String, limit? : Int) -> Array[BTreeRecord] raise SqliteError

    Database::page

    fn Database::page(self : Database, number : Int) -> Page raise SqliteError

    Database::page_count

    fn Database::page_count(self : Database) -> Int

    Database::read_btree

    fn Database::read_btree(self : Database, root : Int, limit? : Int, max_total_payload_bytes? : UInt64) -> Array[BTreeRecord] raise SqliteError

    Database::read_index

    fn Database::read_index(self : Database, root : Int, limit? : Int) -> Array[BTreeRecord] raise SqliteError

    Database::read_page

    fn Database::read_page(self : Database, number : Int) -> Bytes raise SqliteError

    Database::read_table

    fn Database::read_table(self : Database, root : Int, limit? : Int) -> Array[Row] raise SqliteError

    以 rowid 顺序读取普通表;保留首版接口与原始存储值语义。

    Database::scan_btree

    fn Database::scan_btree(self : Database, root : Int, visit : (BTreeRecord) -> Bool raise SqliteError, limit? : Int, max_total_payload_bytes? : UInt64) -> ScanSummary raise SqliteError

    按 B-tree 顺序逐条回调,返回 false 可停止;无需在库中保存所有结果。

    Database::schema

    fn Database::schema(self : Database) -> Array[SchemaEntry] raise SqliteError

    Database::table_records

    fn Database::table_records(self : Database, name : String, limit? : Int) -> Array[BTreeRecord] raise SqliteError

    返回普通表或 WITHOUT ROWID 表的原始存储记录,不推断 SQL 列顺序。

    Database::table_rows

    fn Database::table_rows(self : Database, name : String, limit? : Int) -> Array[Row] raise SqliteError

    pub(all) struct Header {
    page_size : Int
    usable_size : Int
    write_version : Int
    read_version : Int
    change_counter : UInt
    declared_pages : UInt
    freelist_trunk : UInt
    freelist_pages : UInt
    schema_cookie : UInt
    schema_format : Int
    text_encoding : Int
    user_version : UInt
    application_id : UInt
    version_valid_for : UInt
    sqlite_version : UInt
    } derive(Eq,
    Debug
    )

    Header::equal

    fn Header::equal(Header, Header) -> Bool

    Header::not_equal

    fn Header::not_equal(x : Header, y : Header) -> Bool

    Header::to_repr

    Limits

    pub(all) struct Limits {
    max_rows : Int
    max_payload_bytes : Int
    max_pages : Int
    max_depth : Int
    } derive(
    Debug
    )

    Limits::default

    fn Limits::default() -> Limits

    Limits::to_repr

    Page

    pub(all) struct Page {
    number : Int
    kind : PageType
    first_freeblock : Int
    cell_count : Int
    content_start : Int
    fragmented_bytes : Int
    right_child : Int?
    cell_offsets : Array[Int]
    } derive(
    Debug
    )

    Page::to_repr

    PageType

    pub(all) enum PageType {
    TableLeaf
    TableInterior
    IndexLeaf
    IndexInterior
    } derive(Eq,
    Debug
    )

    PageType::equal

    fn PageType::equal(PageType, PageType) -> Bool

    PageType::not_equal

    fn PageType::not_equal(x : PageType, y : PageType) -> Bool

    PageType::to_repr

    Row

    pub(all) struct Row {
    rowid : Int64
    values : Array[Value]
    page_number : Int
    } derive(
    Debug
    )

    Row::to_repr

    ScanCompletion

    pub(all) enum ScanCompletion {
    Complete
    RecordLimit
    VisitorStopped
    } derive(Eq,
    Debug
    )

    ScanCompletion::equal

    ScanCompletion::not_equal

    fn ScanCompletion::not_equal(x : ScanCompletion, y : ScanCompletion) -> Bool

    ScanSummary

    pub(all) struct ScanSummary {
    records_read : Int
    pages_read : Int
    payload_bytes : UInt64
    completion : ScanCompletion
    } derive(
    Debug
    )

    SchemaEntry

    pub(all) struct SchemaEntry {
    object_type : String
    name : String
    table_name : String
    root_page : Int
    sql : String?
    } derive(
    Debug
    )

    Value

    pub(all) enum Value {
    Null
    Integer(Int64)
    Real(Double)
    Text(String)
    Blob(Bytes)
    } derive(Eq,
    Debug
    )

    SQLite 记录中的原始值,保留整数、浮点数与二进制数据的区别。

    Value::equal

    fn Value::equal(Value, Value) -> Bool

    Value::not_equal

    fn Value::not_equal(x : Value, y : Value) -> Bool

    Value::to_repr

    decode_record

    fn decode_record(payload : Bytes, encoding : Int) -> Array[Value] raise SqliteError

    解码完整的 SQLite record payload;编码编号与数据库文件头保持一致。

    decode_varint

    fn decode_varint(data : Bytes, offset : Int) -> (UInt64, Int) raise SqliteError

    open_database

    fn open_database(data : Bytes, limits? : Limits) -> Database raise SqliteError

    open_source

    fn open_source(source : &PageSource, limits? : Limits) -> Database raise SqliteError

    parse_header

    fn parse_header(data : Bytes) -> Header raise SqliteError