Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ All notable changes to sqlite-rs. Format follows [Keep a Changelog](https://keep
`CodegenError::CircularView`, `src/vdbe/collation.rs` →
`src/record/collation.rs`, and three `path:line` citations in 011.

### Fix

- `tests/fuzz/fuzz_targets/btree_cursor.rs`'s `FuzzPageSource` implemented
the pre-`Rc<[u8]>` `PageSource::read_page` signature, breaking
`make fuzz-btree`. Updated to return `Rc<[u8]>`.

### Chore

- Regenerated the MC/DC obligations snapshot (`tests/mcdc/obligations.json`)
and renamed every `mcdc__<id>__vN` tagged test (plus doc-comment
cross-references) to the obligation id its decision now resolves to,
across `src/btree.rs`, `src/btree/index.rs`, `src/btree/table/delete.rs`,
`src/parser/grammar.rs`, `src/parser/tokenizer.rs`, `src/record/encode.rs`,
`src/vdbe/exec.rs`, `src/vdbe/functions.rs` — the ids had drifted from
source line numbers, silently reducing real MC/DC discharge on the
scanned file set to near zero. Now correctly reports 40/42 real MC/DC
obligations discharged; `btree_966` and `encode_68` remain undischarged.

## [0.18.5] - 2026-08-27

### Chore
Expand Down
16 changes: 8 additions & 8 deletions src/btree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2330,7 +2330,7 @@ mod tests {
assert!(!spliced, "no contiguous gap must decline the fast path");
}

/// #52 tagged MC/DC vector (obligation `btree_1296`, decision
/// #52 tagged MC/DC vector (obligation `btree_1374`, decision
/// `content_start < ptr_end || content_start.saturating_sub(ptr_end)
/// < needed`): leaf A (`content_start < ptr_end`) true independently
/// flips the outcome to true regardless of leaf B — a corrupt/
Expand All @@ -2340,7 +2340,7 @@ mod tests {
/// leaves false) for A's independence pair.
#[test]
#[allow(non_snake_case)]
fn mcdc__btree_1296__v1_content_start_before_ptr_end() {
fn mcdc__btree_1374__v1_content_start_before_ptr_end() {
let mut buf = vec![0u8; 32];
put_u8(&mut buf, 0, LEAF_TABLE, 1).unwrap();
write_content_start(&mut buf, 0, 4, 1).unwrap(); // ptr_base(8) + 0 cells == 8 > content_start(4)
Expand All @@ -2352,12 +2352,12 @@ mod tests {
);
}

/// #52 tagged MC/DC vector (obligation `btree_1296`): both leaves
/// #52 tagged MC/DC vector (obligation `btree_1374`): both leaves
/// false — the fast path proceeds. Independence pair for leaf A
/// against `mcdc__btree_1296__v1_content_start_before_ptr_end`.
/// against `mcdc__btree_1374__v1_content_start_before_ptr_end`.
#[test]
#[allow(non_snake_case)]
fn mcdc__btree_1296__v2_both_leaves_false() {
fn mcdc__btree_1374__v2_both_leaves_false() {
let mut buf = leaf_page_with_cells(512, &[]);
let cell = build_interior_cell(0, 42);
let spliced = splice_insert_cell(&mut buf, 0, 1, 0, &cell).unwrap();
Expand All @@ -2367,14 +2367,14 @@ mod tests {
);
}

/// #52 tagged MC/DC vector (obligation `btree_1296`): leaf B
/// #52 tagged MC/DC vector (obligation `btree_1374`): leaf B
/// (`content_start.saturating_sub(ptr_end) < needed`) true while A is
/// false independently flips the outcome to true — a zero-size gap.
/// Independence pair for leaf B against
/// `mcdc__btree_1296__v2_both_leaves_false`.
/// `mcdc__btree_1374__v2_both_leaves_false`.
#[test]
#[allow(non_snake_case)]
fn mcdc__btree_1296__v3_gap_too_small() {
fn mcdc__btree_1374__v3_gap_too_small() {
let mut buf = vec![0u8; 32];
put_u8(&mut buf, 0, LEAF_TABLE, 1).unwrap();
write_content_start(&mut buf, 0, 8, 1).unwrap(); // ptr_base(8) + 0 cells == 8, zero gap
Expand Down
16 changes: 8 additions & 8 deletions src/btree/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,39 +799,39 @@ mod tests {
assert_eq!(int(&key[1]), 100);
}

/// #52 tagged MC/DC vector (obligation `index_864`, the ordering-check
/// #52 tagged MC/DC vector (obligation `index_868`, the ordering-check
/// decision `idx < expect_order.len() && text(&key[0]) == expect_order[idx]`
/// inside `without_rowid_table_is_readable_as_index_btree` below): both
/// leaves true.
#[test]
#[allow(non_snake_case)]
fn mcdc__index_864__v1_in_range_and_matches() {
fn mcdc__index_868__v1_in_range_and_matches() {
let expect_order = ["key1"];
let idx = 0;
let key0 = "key1";
assert!(idx < expect_order.len() && key0 == expect_order[idx]);
}

/// #52 tagged MC/DC vector (obligation `index_864`): leaf A
/// #52 tagged MC/DC vector (obligation `index_868`): leaf A
/// (`idx < expect_order.len()`) true, leaf B (key match) false —
/// independence pair for B against
/// `mcdc__index_864__v1_in_range_and_matches`.
/// `mcdc__index_868__v1_in_range_and_matches`.
#[test]
#[allow(non_snake_case)]
fn mcdc__index_864__v2_in_range_but_does_not_match() {
fn mcdc__index_868__v2_in_range_but_does_not_match() {
let expect_order = ["key1"];
let idx = 0;
let key0 = "key2";
assert!(!(idx < expect_order.len() && key0 == expect_order[idx]));
}

/// #52 tagged MC/DC vector (obligation `index_864`): leaf A false —
/// #52 tagged MC/DC vector (obligation `index_868`): leaf A false —
/// independence pair for A against
/// `mcdc__index_864__v1_in_range_and_matches` (short-circuits, so B
/// `mcdc__index_868__v1_in_range_and_matches` (short-circuits, so B
/// is never evaluated).
#[test]
#[allow(non_snake_case)]
fn mcdc__index_864__v3_out_of_range() {
fn mcdc__index_868__v3_out_of_range() {
let expect_order = ["key1"];
// `black_box` defeats constant-folding so rustc can't statically
// prove `expect_order[idx]` out of bounds — it never runs, since
Expand Down
20 changes: 10 additions & 10 deletions src/btree/table/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,16 +320,16 @@ mod tests {
u32::from_be_bytes(page1[36..40].try_into().unwrap())
}

/// #52 tagged MC/DC vector (obligation `delete_62`, decision
/// #52 tagged MC/DC vector (obligation `delete_61`, decision
/// `cells.len() > 1 || ancestors.is_empty()`): leaf A
/// (`cells.len() > 1`) true, leaf B (`ancestors.is_empty()`) false —
/// a multi-page tree where the leaf being deleted from still has
/// other rows left, so it splices in place rather than collapsing.
/// Independence pair for A against
/// `mcdc__delete_62__v2_last_cell_in_leaf_with_ancestors_collapses`.
/// `mcdc__delete_61__v2_last_cell_in_leaf_with_ancestors_collapses`.
#[test]
#[allow(non_snake_case)]
fn mcdc__delete_62__v1_leaf_survives_with_ancestors() {
fn mcdc__delete_61__v1_leaf_survives_with_ancestors() {
let page_size = 512u32;
let (vfs, header) = minimal_db(page_size);
let mut pager = Pager::open(&vfs, Path::new("/test.db"), page_size).unwrap();
Expand All @@ -353,16 +353,16 @@ mod tests {
assert!(delete_row(&mut pager, &header, 1, n - 1).is_ok());
}

/// #52 tagged MC/DC vector (obligation `delete_62`): both leaves
/// #52 tagged MC/DC vector (obligation `delete_61`): both leaves
/// false — a multi-page tree where the leaf being deleted from holds
/// exactly one cell, so it must be deallocated and its removal
/// cascaded into ancestors rather than spliced in place. Independence
/// pair for A against `mcdc__delete_62__v1_leaf_survives_with_ancestors`
/// pair for A against `mcdc__delete_61__v1_leaf_survives_with_ancestors`
/// and for B against
/// `mcdc__delete_62__v3_only_row_in_root_leaf_has_no_ancestors`.
/// `mcdc__delete_61__v3_only_row_in_root_leaf_has_no_ancestors`.
#[test]
#[allow(non_snake_case)]
fn mcdc__delete_62__v2_last_cell_in_leaf_with_ancestors_collapses() {
fn mcdc__delete_61__v2_last_cell_in_leaf_with_ancestors_collapses() {
let page_size = 512u32;
let (vfs, header) = minimal_db(page_size);
let mut pager = Pager::open(&vfs, Path::new("/test.db"), page_size).unwrap();
Expand Down Expand Up @@ -394,14 +394,14 @@ mod tests {
);
}

/// #52 tagged MC/DC vector (obligation `delete_62`): leaf A false,
/// #52 tagged MC/DC vector (obligation `delete_61`): leaf A false,
/// leaf B (`ancestors.is_empty()`) true independently flips the
/// outcome to true — the single-page root-leaf case, where the
/// (empty) root can never be collapsed away. Independence pair for B
/// against `mcdc__delete_62__v2_last_cell_in_leaf_with_ancestors_collapses`.
/// against `mcdc__delete_61__v2_last_cell_in_leaf_with_ancestors_collapses`.
#[test]
#[allow(non_snake_case)]
fn mcdc__delete_62__v3_only_row_in_root_leaf_has_no_ancestors() {
fn mcdc__delete_61__v3_only_row_in_root_leaf_has_no_ancestors() {
let page_size = 512u32;
let (vfs, header) = minimal_db(page_size);
let mut pager = Pager::open(&vfs, Path::new("/test.db"), page_size).unwrap();
Expand Down
Loading
Loading