pagelens

Read-only SQLite database and WAL file analyzer written in MoonBit

sqlite
wal
database
forensics
parser
moon add geniuszby/pagelens@0.1.0
Download zip
Author
Version
0.1.0
License
Apache-2.0
Last updated
6 days ago
Downloads
2

Dependencies

README

#PageLens

PageLens is a read-only SQLite database file and write-ahead log (WAL) analyzer written primarily in MoonBit.

It parses bytes directly. It does not execute SQL and never modifies the database or WAL supplied by the user.

#Why PageLens

SQLite is often embedded behind an application, so a failed migration, a truncated copy, or an unexpected WAL can be hard to reason about without opening the database in a full SQL engine. PageLens exposes the on-disk structures themselves: headers, pages, B-trees, records, overflow chains, freelists, WAL transactions, and committed page overlays.

The project is useful for file-format learning, database fault diagnosis, consistency triage, recovery teaching, and deterministic local inspection. Its boundary is deliberately narrow: offline, read-only structural analysis.

#Features

  • Bounds-checked binary reader with big/little-endian integers, signed conversions, slicing, offset management, truncation errors, and SQLite varints.
  • Strict parsing of the 100-byte SQLite database header and all requested metadata fields.
  • Page addressing, all four B-tree page types, cell pointer arrays, freeblocks, fragmented bytes, payload layout, and child-page traversal.
  • SQLite record headers and serial types: NULL, signed integers, IEEE-754 floats, UTF-8/UTF-16 text, blobs, and the special integer constants.
  • Overflow payload sizing and guarded chain traversal with range, cycle, duplicate, and premature-end checks.
  • Freelist trunk/leaf traversal with count, loop, duplicate-reference, and header-consistency diagnostics.
  • WAL headers, both checksum byte orders, frame salts, rolling checksums, commit boundaries, transaction grouping, truncated tails, and uncommitted frames.
  • Immutable database/WAL snapshots at the newest valid commit, latest-frame page lookup, and byte-range change summaries.
  • INFO, WARNING, and ERROR consistency findings across database, B-tree, record, overflow, freelist, ownership, and WAL structures.
  • Human-readable and machine-readable JSON output.
  • Reproducible SQLite fixtures and CI smoke tests for every CLI command.

#Installation

Prerequisites:

  • A current stable MoonBit toolchain providing moon, moonc, and moonrun.
  • A native C toolchain when using the native CLI target.
  • Python 3 only when regenerating the example SQLite fixtures.

Clone and resolve the one declared dependency:

git clone https://github.com/geniuszby/pagelens.git cd pagelens moon update

After the first mooncakes.io release, the library can be added to another MoonBit module with:

moon add geniuszby/pagelens@0.1.0

#Quick Start

Generate the project-owned example databases, then inspect one:

python3 scripts/generate_fixtures.py moon run --target native cmd/pagelens -- inspect fixtures/generated/sample.db

On Windows, use python instead of python3 when that is the installed command. Fixture generation is optional when analyzing your own files.

#Usage

pagelens inspect <database> [--json] pagelens wal <wal-file> [--json] pagelens snapshot <database> [--wal <wal-file>] [--json] pagelens check <database> [--wal <wal-file>] [--json]

During development, replace pagelens with:

moon run --target native cmd/pagelens --

The snapshot and check commands look for database-path-wal by default. Pass --wal to select another file. Check succeeds without a WAL when the default file does not exist; snapshot requires a WAL.

#CLI

#Inspect a database

moon run --target native cmd/pagelens -- inspect app.db moon run --target native cmd/pagelens -- inspect app.db --json

The report includes every database-header field, physical page count, page 1 layout, schema B-tree reachability, freelist totals, and diagnostics.

#Inspect a WAL

moon run --target native cmd/pagelens -- wal app.db-wal

Each frame reports its page number, commit database size, salt validity, and rolling-checksum validity. Frames are grouped into committed transactions or an uncommitted tail.

#Build a committed snapshot

moon run --target native cmd/pagelens -- snapshot app.db moon run --target native cmd/pagelens -- snapshot app.db --wal saved.wal --json

Only valid frames at or before the newest valid commit boundary are overlaid. The command reports changed pages and byte ranges; it does not write a merged database.

#Check consistency

moon run --target native cmd/pagelens -- check app.db moon run --target native cmd/pagelens -- check app.db --wal app.db-wal --json

Exit status is 0 when no ERROR diagnostic exists, 1 for a completed check with errors, and 2 for invalid command usage or an input that cannot be parsed.

#Examples

The generator creates four license-clean cases under fixtures/generated:

  • sample.db: tables, index records, deleted space, UTF-8 text, blobs, and an overflow payload.
  • snapshot.db plus snapshot.db-wal: a committed five-frame WAL pair.
  • invalid-magic.db: a database with a damaged magic header.
  • truncated.db: a deliberately shortened database.

The current generated WAL produces five valid frames, two transactions, and three changed pages in the committed snapshot. See examples/README.md for copyable commands and expected facts. CI regenerates these files and runs all four commands.

#Architecture

file bytes -> BinaryReader -> database header / page layout -> B-tree cells / records / overflow / freelist -> WAL frames and commit boundaries -> immutable SnapshotView -> IntegrityReport -> text or JSON CLI reports

The core library accepts immutable Bytes values and has no filesystem write API. Filesystem access exists only in cmd/pagelens. Major source files map directly to responsibilities:

  • binary.mbt and error.mbt: safe primitives and typed errors.
  • header.mbt and page.mbt: database and page metadata.
  • btree.mbt, record.mbt, overflow.mbt, freelist.mbt: storage structures.
  • wal.mbt and snapshot.mbt: WAL validation and committed overlays.
  • checker.mbt and report.mbt: aggregate analysis and presentation.
  • cmd/pagelens: argument parsing and read-only file loading.

More detail is available in docs/architecture.md and docs/sqlite-format.md.

#Testing

Run the same checks used by CI:

moon fmt --check moon check --deny-warn moon build moon test --deny-warn python3 scripts/generate_fixtures.py moon run --target native cmd/pagelens -- check fixtures/generated/sample.db

The suite covers valid and damaged headers, empty/truncated/non-SQLite inputs, varint boundaries, all B-tree page classes, records and serial types, overflow chains, freelists, WAL checksums and commit boundaries, snapshots, error aggregation, and report rendering. See docs/testing.md.

#Build

Portable library build:

moon build --deny-warn

Native command-line build:

moon build --target native --deny-warn

Packaging preview:

moon package --list moon publish --dry-run

#Limitations

  • PageLens is not a SQL parser or execution engine.
  • It never writes, repairs, checkpoints, or recovers a database.
  • Rollback journals and the transient WAL-index/shm format are not parsed.
  • Pointer-map and lock-byte pages are reported as unclassified rather than decoded.
  • Encrypted or application-transformed SQLite pages are not decrypted.
  • Text decoding is loss-tolerant; the checker focuses on structural bounds and serial-type validity rather than proving semantic text correctness.
  • The first release provides structural recovery evidence, not a complete forensic or automated data-recovery product.

#Roadmap

  • Detailed pointer-map reports for auto-vacuum databases.
  • Schema-object listing and optional row-oriented exploration.
  • User-selectable historical committed WAL boundaries.
  • Streaming access for files too large to hold in one immutable byte buffer.
  • Stable library API documentation generated for mooncakes.io.

#Contributing

Open an issue before changing the supported file-format boundary. Contributions should include focused tests, preserve read-only behavior, pass formatting, check, build, and test, and document any new dependency and its license. Do not commit private databases or files containing personal information.

#License

PageLens is licensed under the Apache License 2.0. See LICENSE. The only runtime dependency is moonbitlang/x, also Apache-2.0. The parser is an original implementation based on SQLite's public file-format specification; it does not contain SQLite or third-party parser source. Full attribution and dependency notes are in docs/third-party-notices.md.

#
ParseError

pub(all) suberror ParseError {
UnexpectedEnd(Int, Int, Int, String)
InvalidRange(Int, Int, Int, String)
InvalidMagic(String, String)
InvalidValue(Int, String, String)
InvalidPageNumber(UInt64, UInt64)
IntegerOverflow(String)
Unsupported(String)
} derive(Eq,
Debug
)

Errors raised by byte-level and SQLite format parsers.

#
ParseError::message

fn ParseError::message(self : ParseError) -> String

#
BinaryReader

pub(all) struct BinaryReader {
data : Bytes
start : Int
limit : Int
position : Int
context : String
}

A bounded cursor over immutable bytes.

Every read checks the active range before indexing the input. A reader can be forked into a smaller range, which lets page and cell parsers enforce their own boundaries even when the underlying database buffer is larger.

#
BinaryReader::absolute_position

fn BinaryReader::absolute_position(self : BinaryReader) -> Int

#
BinaryReader::fork

fn BinaryReader::fork(self : BinaryReader, count : Int, context : String) -> BinaryReader raise ParseError

#
BinaryReader::is_empty

fn BinaryReader::is_empty(self : BinaryReader) -> Bool

#
BinaryReader::length

fn BinaryReader::length(self : BinaryReader) -> Int

#
BinaryReader::named

fn BinaryReader::named(data : Bytes, context : String) -> BinaryReader

#
BinaryReader::new

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

#
BinaryReader::peek_u8

fn BinaryReader::peek_u8(self : BinaryReader) -> Byte raise ParseError

#
BinaryReader::peek_varint

fn BinaryReader::peek_varint(self : BinaryReader) -> Varint raise ParseError

Decode a varint without changing the reader's position.

#
BinaryReader::range

fn BinaryReader::range(data : Bytes, offset : Int, length : Int, context : String) -> BinaryReader raise ParseError

#
BinaryReader::read_ascii

fn BinaryReader::read_ascii(self : BinaryReader, count : Int) -> String raise ParseError

#
BinaryReader::read_bytes

fn BinaryReader::read_bytes(self : BinaryReader, count : Int) -> Bytes raise ParseError

#
BinaryReader::read_f64_be

fn BinaryReader::read_f64_be(self : BinaryReader) -> Double raise ParseError

#
BinaryReader::read_i16_be

fn BinaryReader::read_i16_be(self : BinaryReader) -> Int raise ParseError

#
BinaryReader::read_i24_be

fn BinaryReader::read_i24_be(self : BinaryReader) -> Int raise ParseError

#
BinaryReader::read_i32_be

fn BinaryReader::read_i32_be(self : BinaryReader) -> Int64 raise ParseError

#
BinaryReader::read_i64_be

fn BinaryReader::read_i64_be(self : BinaryReader) -> Int64 raise ParseError

#
BinaryReader::read_i8

fn BinaryReader::read_i8(self : BinaryReader) -> Int raise ParseError

#
BinaryReader::read_int_be

fn BinaryReader::read_int_be(self : BinaryReader, width : Int) -> Int64 raise ParseError

Read a two's-complement signed big-endian integer using 1..8 bytes.

#
BinaryReader::read_u16_be

fn BinaryReader::read_u16_be(self : BinaryReader) -> Int raise ParseError

#
BinaryReader::read_u24_be

fn BinaryReader::read_u24_be(self : BinaryReader) -> Int raise ParseError

#
BinaryReader::read_u32_be

fn BinaryReader::read_u32_be(self : BinaryReader) -> UInt64 raise ParseError

#
BinaryReader::read_u32_le

fn BinaryReader::read_u32_le(self : BinaryReader) -> UInt64 raise ParseError

#
BinaryReader::read_u64_be

fn BinaryReader::read_u64_be(self : BinaryReader) -> UInt64 raise ParseError

#
BinaryReader::read_u8

fn BinaryReader::read_u8(self : BinaryReader) -> Byte raise ParseError

#
BinaryReader::read_uint_be

fn BinaryReader::read_uint_be(self : BinaryReader, width : Int) -> UInt64 raise ParseError

Read an unsigned big-endian integer using exactly width bytes.

#
BinaryReader::read_varint

fn BinaryReader::read_varint(self : BinaryReader) -> Varint raise ParseError

Decode SQLite's one-to-nine-byte unsigned varint representation.

#
BinaryReader::relative_position

fn BinaryReader::relative_position(self : BinaryReader) -> Int

#
BinaryReader::remaining

fn BinaryReader::remaining(self : BinaryReader) -> Int

#
BinaryReader::seek_absolute

fn BinaryReader::seek_absolute(self : BinaryReader, offset : Int) -> Unit raise ParseError

#
BinaryReader::seek_relative

fn BinaryReader::seek_relative(self : BinaryReader, offset : Int) -> Unit raise ParseError

#
BinaryReader::skip

fn BinaryReader::skip(self : BinaryReader, count : Int) -> Unit raise ParseError

#
BtreeCell

pub(all) struct BtreeCell {
page_number : UInt64
index : Int
offset : Int
page_type : PageType
left_child_page : UInt64?
rowid : Int64?
payload_size : Int
local_payload : Bytes
overflow_page : UInt64?
encoded_size : Int
} derive(Eq,
Debug
)

A decoded cell from one of SQLite's four B-tree page types.

#
BtreeCell::has_overflow

fn BtreeCell::has_overflow(self : BtreeCell) -> Bool

#
BtreeCell::local_payload_size

fn BtreeCell::local_payload_size(self : BtreeCell) -> Int

#
BtreePageHeader

pub(all) struct BtreePageHeader {
page_number : UInt64
page_offset : Int
header_offset : Int
page_type : PageType
first_freeblock : Int
cell_count : Int
cell_content_area : Int
fragmented_free_bytes : Int
right_most_pointer : UInt64?
cell_pointers : Array[Int]
} derive(Eq,
Debug
)

Parsed common header of a B-tree page.

#
BtreePageHeader::cell_content_start

fn BtreePageHeader::cell_content_start(self : BtreePageHeader) -> Int

#
BtreePageHeader::header_size

fn BtreePageHeader::header_size(self : BtreePageHeader) -> Int

#
BtreePageHeader::pointer_array_end

fn BtreePageHeader::pointer_array_end(self : BtreePageHeader) -> Int

#
BtreePageHeader::pointer_array_start

fn BtreePageHeader::pointer_array_start(self : BtreePageHeader) -> Int

#
BtreePageHeader::unallocated_bytes

fn BtreePageHeader::unallocated_bytes(self : BtreePageHeader) -> Int

#
BtreeStatistics

pub(all) struct BtreeStatistics {
root_page : UInt64
total_pages : Int
interior_pages : Int
leaf_pages : Int
table_pages : Int
index_pages : Int
total_cells : Int
maximum_depth : Int
maximum_children : Int
} derive(Eq,
Debug
)

Aggregate shape of one traversed SQLite B-tree.

#
BtreeStatistics::average_cells_per_page

fn BtreeStatistics::average_cells_per_page(self : BtreeStatistics) -> Double

#
BtreeStatistics::is_single_page

fn BtreeStatistics::is_single_page(self : BtreeStatistics) -> Bool

#
BtreeTraversal

pub(all) struct BtreeTraversal {
root_page : UInt64
visits : Array[BtreeVisit]
diagnostics : Array[Diagnostic]
} derive(Eq,
Debug
)

#
BtreeVisit

pub(all) struct BtreeVisit {
page_number : UInt64
depth : Int
page_type : PageType
cell_count : Int
child_pages : Array[UInt64]
} derive(Eq,
Debug
)

#
ChangedRange

pub(all) struct ChangedRange {
offset : Int
length : Int
} derive(Eq,
Debug
)

One contiguous byte range changed by a WAL frame.

#
DatabaseHeader

pub(all) struct DatabaseHeader {
page_size : Int
write_version : Int
read_version : Int
reserved_bytes : Int
max_embedded_payload_fraction : Int
min_embedded_payload_fraction : Int
leaf_payload_fraction : Int
file_change_counter : UInt64
database_size_pages : UInt64
first_freelist_trunk_page : UInt64
total_freelist_pages : UInt64
schema_cookie : UInt64
schema_format : UInt64
default_page_cache_size : Int64
largest_root_btree_page : UInt64
text_encoding : TextEncoding
user_version : UInt64
incremental_vacuum : UInt64
application_id : UInt64
version_valid_for : UInt64
sqlite_version_number : UInt64
} derive(Eq,
Debug
)

Parsed representation of SQLite's 100-byte database header.

#
DatabaseHeader::effective_database_pages

fn DatabaseHeader::effective_database_pages(self : DatabaseHeader, file_length : Int) -> UInt64

#
DatabaseHeader::is_wal_mode

fn DatabaseHeader::is_wal_mode(self : DatabaseHeader) -> Bool

#
DatabaseHeader::usable_page_size

fn DatabaseHeader::usable_page_size(self : DatabaseHeader) -> Int

#
DatabaseImage

pub(all) struct DatabaseImage {
data : Bytes
header : DatabaseHeader
page_count : UInt64
}

Immutable database image backed by the caller-provided bytes.

#
DatabaseImage::file_length

fn DatabaseImage::file_length(self : DatabaseImage) -> Int

#
DatabaseImage::open

fn DatabaseImage::open(data : Bytes) -> DatabaseImage raise ParseError

#
DatabaseImage::page_bytes

fn DatabaseImage::page_bytes(self : DatabaseImage, page_number : UInt64) -> Bytes raise ParseError

#
DatabaseImage::page_offset

fn DatabaseImage::page_offset(self : DatabaseImage, page_number : UInt64) -> Int raise ParseError

#
DatabaseImage::page_reader

fn DatabaseImage::page_reader(self : DatabaseImage, page_number : UInt64, context : String) -> BinaryReader raise ParseError

#
DatabaseImage::validate_page_number

fn DatabaseImage::validate_page_number(self : DatabaseImage, page_number : UInt64) -> Unit raise ParseError

#
DatabaseInspection

pub(all) struct DatabaseInspection {
header : DatabaseHeader
physical_page_count : UInt64
file_length : Int
page_one : BtreePageHeader
page_one_layout : PageLayout
schema_tree : BtreeTraversal
freelist : FreelistReport
diagnostics : Array[Diagnostic]
}

High-level database facts used by the CLI's text and JSON renderers.

#
Diagnostic

pub(all) struct Diagnostic {
severity : Severity
code : String
message : String
offset : Int?
page_number : UInt64?
} derive(Eq,
Debug
)

A non-fatal parser or validation observation.

#
Diagnostic::error

fn Diagnostic::error(code : String, message : String, offset? : Int, page_number? : UInt64) -> Diagnostic

#
Diagnostic::info

fn Diagnostic::info(code : String, message : String, offset? : Int, page_number? : UInt64) -> Diagnostic

#
Diagnostic::warning

fn Diagnostic::warning(code : String, message : String, offset? : Int, page_number? : UInt64) -> Diagnostic

#
Freeblock

pub(all) struct Freeblock {
offset : Int
next : Int
size : Int
} derive(Eq,
Debug
)

#
FreelistReport

pub(all) struct FreelistReport {
trunks : Array[FreelistTrunk]
trunk_pages : Array[UInt64]
leaf_pages : Array[UInt64]
diagnostics : Array[Diagnostic]
declared_page_count : UInt64
} derive(
Debug
)

A complete walk of the freelist rooted in the database header.

#
FreelistReport::observed_page_count

fn FreelistReport::observed_page_count(self : FreelistReport) -> Int

#
FreelistTrunk

pub(all) struct FreelistTrunk {
page_number : UInt64
next_trunk_page : UInt64
leaf_count : Int
leaf_pages : Array[UInt64]
} derive(Eq,
Debug
)

One SQLite freelist trunk page and the leaf page numbers stored on it.

#
IntegrityReport

pub(all) struct IntegrityReport {
diagnostics : Array[Diagnostic]
database_opened : Bool
pages_examined : Int
btree_pages : Int
btree_cells : Int
records_decoded : Int
overflow_pages : Int
freelist_pages : Int
wal_frames : Int
}

Aggregate outcome of PageLens's structural consistency pass.

#
IntegrityReport::count

fn IntegrityReport::count(self : IntegrityReport, severity : Severity) -> Int

#
IntegrityReport::error_count

fn IntegrityReport::error_count(self : IntegrityReport) -> Int

#
IntegrityReport::info_count

fn IntegrityReport::info_count(self : IntegrityReport) -> Int

#
IntegrityReport::is_ok

fn IntegrityReport::is_ok(self : IntegrityReport) -> Bool

#
IntegrityReport::warning_count

fn IntegrityReport::warning_count(self : IntegrityReport) -> Int

#
OverflowChain

pub(all) struct OverflowChain {
first_page : UInt64
pages : Array[UInt64]
payload : Bytes
diagnostics : Array[Diagnostic]
} derive(Eq,
Debug
)

#
PageDifference

pub(all) struct PageDifference {
page_number : UInt64
frame_index : Int
changed_bytes : Int
first_changed_offset : Int?
last_changed_offset : Int?
ranges : Array[ChangedRange]
} derive(Eq,
Debug
)

#
PageLayout

pub(all) struct PageLayout {
page_number : UInt64
header_bytes : Int
pointer_array_bytes : Int
unallocated_bytes : Int
cell_content_bytes : Int
freeblock_bytes : Int
fragmented_free_bytes : Int
usable_bytes : Int
freeblocks : Array[Freeblock]
} derive(Eq,
Debug
)

#
PageLayout::total_free_bytes

fn PageLayout::total_free_bytes(self : PageLayout) -> Int

#
PageType

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

The four B-tree page types defined by the SQLite file format.

#
PageType::flag

fn PageType::flag(self : PageType) -> Int

#
PageType::from_flag

fn PageType::from_flag(flag : Int, offset : Int) -> PageType raise ParseError

#
PageType::header_size

fn PageType::header_size(self : PageType) -> Int

#
PageType::is_interior

fn PageType::is_interior(self : PageType) -> Bool

#
PageType::is_table

fn PageType::is_table(self : PageType) -> Bool

#
PageType::label

fn PageType::label(self : PageType) -> String

#
RecordColumn

pub(all) struct RecordColumn {
index : Int
serial_type : UInt64
body_offset : Int
byte_length : Int
value : RecordValue
} derive(Eq,
Debug
)

#
RecordValue

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

One decoded value in a SQLite record body.

#
RecordValue::display

fn RecordValue::display(self : RecordValue) -> String

#
RecordValue::kind

fn RecordValue::kind(self : RecordValue) -> String

#
Severity

pub(all) enum Severity {
Info
Warning
Error
} derive(Compare, Eq,
Debug
)

Stable severity levels used by validation and consistency reports.

#
Severity::label

fn Severity::label(self : Severity) -> String

#
SnapshotPage

pub(all) struct SnapshotPage {
page_number : UInt64
source : SnapshotPageSource
bytes : Bytes
} derive(Eq,
Debug
)

#
SnapshotPageSource

pub(all) enum SnapshotPageSource {
DatabaseFile
WalFrame(Int)
} derive(Eq,
Debug
)

#
SnapshotStatistics

pub(all) struct SnapshotStatistics {
commit_frame : Int
logical_page_count : UInt64
changed_pages : Int
changed_bytes : Int
changed_ranges : Int
pages_added_by_wal : Int
largest_page_change : Int
} derive(Eq,
Debug
)

#
SnapshotStatistics::average_changed_bytes_per_page

fn SnapshotStatistics::average_changed_bytes_per_page(self : SnapshotStatistics) -> Double

#
SnapshotStatistics::is_unchanged

fn SnapshotStatistics::is_unchanged(self : SnapshotStatistics) -> Bool

#
SnapshotView

pub(all) struct SnapshotView {
database : DatabaseImage
wal : WalFile
max_frame : Int
logical_page_count : UInt64
}

An immutable database view at one committed WAL boundary.

#
SnapshotView::at_commit

fn SnapshotView::at_commit(database : DatabaseImage, wal : WalFile, frame_index : Int) -> SnapshotView raise ParseError

#
SnapshotView::changed_pages

fn SnapshotView::changed_pages(self : SnapshotView) -> Array[UInt64]

#
SnapshotView::difference_for_page

fn SnapshotView::difference_for_page(self : SnapshotView, page_number : UInt64) -> PageDifference? raise ParseError

#
SnapshotView::differences

fn SnapshotView::differences(self : SnapshotView) -> Array[PageDifference] raise ParseError

#
SnapshotView::page_size

fn SnapshotView::page_size(self : SnapshotView) -> Int

#
SnapshotView::read_page

fn SnapshotView::read_page(self : SnapshotView, page_number : UInt64) -> SnapshotPage raise ParseError

#
SnapshotView::uses_wal

fn SnapshotView::uses_wal(self : SnapshotView) -> Bool

#
SqliteRecord

pub(all) struct SqliteRecord {
header_size : Int
body_offset : Int
columns : Array[RecordColumn]
trailing_bytes : Int
} derive(Eq,
Debug
)

#
SqliteRecord::column_count

fn SqliteRecord::column_count(self : SqliteRecord) -> Int

#
SqliteRecord::value

fn SqliteRecord::value(self : SqliteRecord, index : Int) -> RecordValue?

#
TextEncoding

pub(all) enum TextEncoding {
Utf8
Utf16Le
Utf16Be
Unspecified
} derive(Eq,
Debug
)

Text encodings allowed by the SQLite database header.

#
TextEncoding::from_header

fn TextEncoding::from_header(value : UInt64) -> TextEncoding raise ParseError

#
TextEncoding::label

fn TextEncoding::label(self : TextEncoding) -> String

#
Varint

pub(all) struct Varint {
value : UInt64
length : Int
} derive(Eq,
Debug
)

Result of decoding one SQLite variable-length integer.

#
WalChecksum

pub(all) struct WalChecksum {
first : UInt64
second : UInt64
} derive(Eq,
Debug
)

#
WalChecksumOrder

pub(all) enum WalChecksumOrder {
BigEndian
LittleEndian
} derive(Eq,
Debug
)

Magic 0x377f0683 checksums words as big-endian; 0x377f0682 uses little-endian words. All structural fields remain big-endian.

#
WalFile

pub(all) struct WalFile {
header : WalHeader
frames : Array[WalFrame]
transactions : Array[WalTransaction]
diagnostics : Array[Diagnostic]
trailing_bytes : Int
last_commit_frame : Int?
} derive(
Debug
)

#
WalFile::committed_transaction_count

fn WalFile::committed_transaction_count(self : WalFile) -> Int

#
WalFile::latest_frame_for_page

fn WalFile::latest_frame_for_page(self : WalFile, page_number : UInt64, max_frame? : Int) -> WalFrame?

Return the newest valid occurrence of a page not newer than max_frame. Frame indexes are one-based, matching the CLI and SQLite documentation.

#
WalFile::page_histories

fn WalFile::page_histories(self : WalFile) -> Array[WalPageHistory]

#
WalFile::uncommitted_frame_count

fn WalFile::uncommitted_frame_count(self : WalFile) -> Int

#
WalFile::valid_frame_count

fn WalFile::valid_frame_count(self : WalFile) -> Int

#
WalFrame

pub(all) struct WalFrame {
index : Int
offset : Int
page_number : UInt64
database_size_after_commit : UInt64
salt_first : UInt64
salt_second : UInt64
checksum : WalChecksum
computed_checksum : WalChecksum
page_data : Bytes
salt_valid : Bool
checksum_valid : Bool
} derive(Eq,
Debug
)

#
WalFrame::is_commit

fn WalFrame::is_commit(self : WalFrame) -> Bool

#
WalFrame::is_valid

fn WalFrame::is_valid(self : WalFrame) -> Bool

#
WalHeader

pub(all) struct WalHeader {
magic : UInt64
format_version : UInt64
page_size : Int
checkpoint_sequence : UInt64
salt_first : UInt64
salt_second : UInt64
checksum : WalChecksum
computed_checksum : WalChecksum
checksum_order : WalChecksumOrder
checksum_valid : Bool
} derive(Eq,
Debug
)

#
WalPageHistory

pub(all) struct WalPageHistory {
page_number : UInt64
frame_indexes : Array[Int]
valid_frame_indexes : Array[Int]
committed_frame : Int?
uncommitted_frame_indexes : Array[Int]
} derive(Eq,
Debug
)

Every occurrence of one database page in a WAL, including frames that do not become visible in the newest committed snapshot.

#
WalPageHistory::has_uncommitted_change

fn WalPageHistory::has_uncommitted_change(self : WalPageHistory) -> Bool

#
WalPageHistory::latest_valid_frame

fn WalPageHistory::latest_valid_frame(self : WalPageHistory) -> Int?

#
WalPageHistory::occurrence_count

fn WalPageHistory::occurrence_count(self : WalPageHistory) -> Int

#
WalPageHistory::valid_occurrence_count

fn WalPageHistory::valid_occurrence_count(self : WalPageHistory) -> Int

#
WalTransaction

pub(all) struct WalTransaction {
ordinal : Int
first_frame : Int
last_frame : Int
frame_count : Int
committed : Bool
database_size_after_commit : UInt64?
} derive(Eq,
Debug
)

#
analyze_freelist

fn analyze_freelist(database : DatabaseImage) -> FreelistReport

Walk every trunk and validate page references, loops, duplicates and the freelist count declared by the 100-byte database header.

#
analyze_page_layout

fn analyze_page_layout(database : DatabaseImage, header : BtreePageHeader) -> PageLayout raise ParseError

fn banner() -> String

Return the project banner without performing file I/O.

#
bytes_equal

fn bytes_equal(left : Bytes, right : Bytes) -> Bool

#
bytes_hex

fn bytes_hex(data : Bytes) -> String

#
check_database

fn check_database(data : Bytes, wal_data? : Bytes) -> IntegrityReport

Run a read-only consistency pass. Parsing failures become ERROR diagnostics instead of escaping, which makes this function suitable for damaged files.

#
description

let description : String

Short description used by the CLI and package documentation.

#
inspect_database

fn inspect_database(data : Bytes) -> DatabaseInspection raise ParseError

#
join_payload

fn join_payload(local_bytes : Bytes, overflow : Bytes) -> Bytes

#
local_payload_size

fn local_payload_size(page_type : PageType, payload_size : Int, usable_size : Int) -> Int raise ParseError

Compute the local payload length using SQLite's spill formula.

#
materialize_cell_payload

fn materialize_cell_payload(database : DatabaseImage, cell : BtreeCell) -> Bytes raise ParseError

#
open_snapshot

fn open_snapshot(database : DatabaseImage, wal : WalFile) -> SnapshotView raise ParseError

Construct the newest fully committed view. Invalid and uncommitted WAL frames never influence the resulting snapshot.

#
parse_btree_cell

fn parse_btree_cell(database : DatabaseImage, header : BtreePageHeader, index : Int) -> BtreeCell raise ParseError

#
parse_btree_page_cells

fn parse_btree_page_cells(database : DatabaseImage, header : BtreePageHeader) -> Array[BtreeCell] raise ParseError

#
parse_btree_page_header

fn parse_btree_page_header(database : DatabaseImage, page_number : UInt64) -> BtreePageHeader raise ParseError

#
parse_cell_record

fn parse_cell_record(database : DatabaseImage, cell : BtreeCell) -> SqliteRecord raise ParseError

#
parse_database_header

fn parse_database_header(data : Bytes) -> DatabaseHeader raise ParseError

Parse and strictly validate the SQLite database header.

#
parse_freeblocks

fn parse_freeblocks(database : DatabaseImage, header : BtreePageHeader) -> Array[Freeblock] raise ParseError

#
parse_freelist_trunk

fn parse_freelist_trunk(database : DatabaseImage, page_number : UInt64) -> FreelistTrunk raise ParseError

Decode a single freelist trunk. Leaf pages are references only: their contents are intentionally not read because SQLite assigns them no format.

#
parse_record

fn parse_record(payload : Bytes, encoding : TextEncoding) -> SqliteRecord raise ParseError

Parse a complete SQLite record payload.

#
parse_wal

fn parse_wal(data : Bytes) -> WalFile raise ParseError

Parse a complete WAL, retaining invalid frames and attaching diagnostics so an analyst can distinguish structural damage from uncommitted tail data.

#
parse_wal_header

fn parse_wal_header(data : Bytes) -> WalHeader raise ParseError

#
read_overflow_chain

fn read_overflow_chain(database : DatabaseImage, first_page : UInt64, payload_bytes : Int) -> OverflowChain raise ParseError

Follow an overflow chain and return exactly payload_bytes bytes.

#
render_database_json

fn render_database_json(inspection : DatabaseInspection) -> String

#
render_database_text

fn render_database_text(inspection : DatabaseInspection) -> String

#
render_integrity_json

fn render_integrity_json(report : IntegrityReport) -> String

#
render_integrity_text

fn render_integrity_text(report : IntegrityReport) -> String

#
render_snapshot_json

fn render_snapshot_json(snapshot : SnapshotView) -> String raise ParseError

#
render_snapshot_text

fn render_snapshot_text(snapshot : SnapshotView) -> String raise ParseError

#
render_wal_json

fn render_wal_json(wal : WalFile) -> String

#
render_wal_text

fn render_wal_text(wal : WalFile) -> String

#
serial_type_length

fn serial_type_length(serial_type : UInt64) -> Int raise ParseError

Return the number of body bytes represented by a serial type.

#
summarize_btree

fn summarize_btree(traversal : BtreeTraversal) -> BtreeStatistics

#
summarize_snapshot

fn summarize_snapshot(snapshot : SnapshotView) -> SnapshotStatistics raise ParseError

#
traverse_btree

fn traverse_btree(database : DatabaseImage, root_page : UInt64, max_depth? : Int) -> BtreeTraversal raise ParseError

#
validate_database_header

fn validate_database_header(header : DatabaseHeader, file_length : Int) -> Array[Diagnostic]

Return non-fatal relationships that cannot be decided from one field alone.

#
validate_page_layout

fn validate_page_layout(database : DatabaseImage, header : BtreePageHeader) -> Array[Diagnostic]

Validate page-local layout facts without parsing cell payloads.

#
validate_record

fn validate_record(record : SqliteRecord) -> Array[Diagnostic]

#
version

let version : String

Semantic version of the PageLens library.

#
wal_checksum

fn wal_checksum(bytes : Bytes, order : WalChecksumOrder, initial_first? : UInt64, initial_second? : UInt64) -> WalChecksum raise ParseError

Apply SQLite's rolling two-word WAL checksum. Initial values allow frame checksums to continue the state established by the header and prior frames.