Skip to content

Fail closed on two corrupt node shapes the readers accepted - #2007

Merged
timsehn merged 2 commits into
masterfrom
fix/prolly-node-cursor-corrupt-input
Aug 7, 2026
Merged

Fail closed on two corrupt node shapes the readers accepted#2007
timsehn merged 2 commits into
masterfrom
fix/prolly-node-cursor-corrupt-input

Conversation

@timsehn

@timsehn timsehn commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Last two of the three prolly-core items from the deep review (after #2002). Both are hostile-input hardening in the read path — the writer produces neither shape, and every other structural check passes them.

1. An internal node pointing at an empty leaf

descendToExtremeLeaf set idx = pChild->node.nItems - 1, which is -1 when walking right, and prollyCursorNext/Prev then marked the cursor valid over a node whose key array is a null pointer. The next key read dereferences it.

prollyCursorFirst and Last already refused this through finalizeExtremeCursor, so only the stepping paths were exposed. Measured on a crafted two-child tree (one populated leaf, one empty):

before after
prollyCursorNext past the populated leaf SQLITE_OK, cursor marked valid SQLITE_CORRUPT
prollyCursorLast SQLITE_OK, silently EOF SQLITE_CORRUPT

The check sits on the descent, so an empty root leaf — which is just an empty table, not corruption — still reads as EOF. prolly_cursor_empty_leaf_root pins that and still passes.

2. Offset arrays not starting at zero

Readers reach key data two ways: through the offset array (prollyNodeKey) and by striding from pKeyData for fixed-width keys (prollyNodeIntKey, i*8). A node with aKeyOff = [8, 16] satisfies the bounds check, monotonicity, the INTKEY stride check (16-8 == 8) and the total-size check — and the two readers then disagree about which bytes are the key. Silently wrong data rather than a rejected node.

Both arrays must now start at 0.

Testing

Two cases in test/doltlite_regression_test_c.c, beside the existing prolly_cursor_corrupt_node and prolly_node_corruption cases and following their hand-built-node style.

Fail-before/pass-after, same test file both runs:

  • prolly_node_first_offset_validation2/4 → 4/4
  • prolly_cursor_empty_leaf_under_internal8/10 → 10/10

Each includes a guard that the fix is not just rejecting everything: a well-formed node with zero-start offsets still parses, and First on the crafted tree still returns a valid cursor.

Validation

  • test/doltlite_regression_test_c.sh — 310,193 tests, 0 failures
  • test/run_c_tests.sh — 26/26 gated c-tests
  • test/run_doltlite_tests.sh — 99/99 suites
  • testfixture storage bucket — 45,265 passing, 0 unexpected failures (the 2 bigfile terminations are environmental and pre-existing, confirmed against a control build during Bound the chunk store WAL offset before trusting it #1987)
  • testfixture core-sql bucket — the only failures are 7 shell/loadext/zipfile files, and I diffed the exact failure sets against a control build without these changes: identical, 53 each

A note on that last point, since it nearly fooled me: my first bucket runs reported ~2300 failures with "no such table" everywhere. That was a stale testfixture — I had rebuilt doltlite and the library but not it. Rebuilt, affinity2 went from 5 failures to 26/26 clean. Worth remembering that this binary needs an explicit rebuild.

🤖 Generated with Claude Code

Both are hostile-input hardening in the read path: the writer produces
neither shape, and every other structural check passes them.

An internal node pointing at an empty leaf. descendToExtremeLeaf set
idx to nItems-1, which is -1 when walking right, and
prollyCursorNext/Prev then marked the cursor valid over a node whose key
array is a null pointer -- the next key read dereferences it.
prollyCursorFirst and Last already refused it through
finalizeExtremeCursor, so only the stepping paths were exposed. The
check goes on the descent, so an empty root leaf, which is just an empty
table, still reads as EOF.

Offset arrays not starting at zero. Readers reach key data two ways,
through the offset array and by striding from pKeyData for fixed-width
keys, as prollyNodeIntKey does with i*8. A first offset of 8 with a
stride of 8 satisfies the bounds, monotonicity, width and total-size
checks, and the two readers then disagree about which bytes are the key
-- silently wrong data rather than a rejected node.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@itoqa

itoqa Bot commented Aug 6, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: dec0a96: 14 test cases ran, 14 passed ✅.

Summary

Coverage focuses on safe handling of malformed and empty stored data, including traversal in both directions, cache loading, scanning, cursor cleanup, and validation of valid data alongside corruption cases. The exercised behavior is primarily defensive edge-case and adversarial data handling, with normal empty-data and valid-record behavior also confirmed.

Safe to merge — all exercised behaviors passed, including corruption detection, normal end-of-data handling, valid-record access, and safe cursor cleanup, with no PR-attributable regressions or unresolved failures. Any unrelated observations do not present a merge blocker.

Tests run by Ito

View full run

Result Severity Type Description
Corruption Loading a malformed data node returned a corruption error and did not add the node to the cache. All 4 native checks passed.
Corruption The malformed-record scan stopped with a corruption error instead of completing as if the data were valid. All 11 native assertions passed, and no changes were emitted from the bad data.
Corruption Moving through malformed data in either direction returned an error instead of pretending the scan was finished. The native regression case completed all 10 checks successfully.
Cursor The cursor reaches the first item, then reports database corruption when moving into the empty child instead of treating it as a valid row or normal end of data.
Cursor An empty table opens normally, and moving to the first or last position returns EOF without an error.
Cursor Moving from a populated leaf into an empty child returns a corruption error, and the cursor is closed safely without exposing a row.
Harness The native regression case passed all 10 assertions. Moving forward from the populated leaf and checking the rightmost path both handled the empty child as corruption without crashing.
Harness The empty-root control stayed a valid empty table, and the empty-child case returned the expected corruption result. Both cases passed independently in sequence.
Harness Both named regression cases were found and completed successfully through the shell wrapper and the direct test binary. All four invocations exited successfully, so the new cases are registered and discoverable.
Harness Two complete sessions passed every malformed-offset, empty-root, and empty-child check. The test finished cleanly with no crash or cleanup failure.
Offset Malformed node data with a nonzero first value offset is rejected as corrupt, while a valid zero-start node still parses and exposes one item.
Offset A valid node with both offset lists starting at zero was accepted and exposed exactly one item with usable integer-key data.
Offset A malformed data node with a shifted key offset is rejected before it can expose inconsistent key data.
Offset An empty non-root node is rejected with a corruption error before any parsed data is exposed.

Tip

Reply with @itoqa to send us feedback on this test run.

The previous run's macOS and Windows builds were cancelled after several
hours of queueing rather than failing, and re-running the failed jobs did
not requeue them.
@itoqa

itoqa Bot commented Aug 6, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Reportdec0a9611cf0d7: 26 test cases ran, 25 passing ✅, 1 additional finding ⚠️.

Diff Summary

Coverage focuses on safe database reading across normal and hostile data conditions: valid records and scans, empty tables, tree-boundary movement, malformed serialized data, sparse reads, and cleanup after corruption. Overall behavior is healthy, with corruption generally surfaced safely while valid data remains readable and ordinary empty-table end-of-file behavior is preserved.

Safe to merge — the only failure is a medium-severity, pre-existing cursor-state safety issue explicitly marked unrelated to this PR, with no regressions or PR-attributable failures. It is appropriate as a flag for later rather than a merge blocker.

Tests run by Ito

View full run

Result State Severity Type Description
Passing Corruption Malformed offset data is rejected with SQLITE_CORRUPT, while a valid node with zero-start offsets still loads successfully.
Passing Corruption A SQL scan stops with a corruption error when it reaches malformed data instead of finishing successfully or returning a partial result as complete.
Passing Corruption Moving across the malformed child reports a corruption error in both directions instead of pretending the scan reached the end.
Passing Corruption Malformed data from a sparse read is rejected, and rejected entries are not kept for later use. The available parser control passed all four checks; the requested named case was not registered in the native test runner.
Passing Cursor Moving past the last item in a populated leaf returns a corruption error instead of treating the empty child as a valid row or ordinary end of file.
Passing Cursor An empty root opens normally. Moving to the first or last position returns end of file without exposing a row.
Passing Cursor Reverse traversal into an empty child stops with a corruption error instead of treating the invalid child as a usable cursor.
Passing Cursor A large populated tree was scanned in order, and the cursor reached ordinary EOF only after the final item.
Passing Cursor A blob-key lookup crossed an internal boundary, found the requested key in the right leaf, and kept the cursor valid.
Passing Harness The regression case reached the populated item, then correctly reported corruption when traversal entered an empty child from either direction. All 10 assertions passed.
Passing Harness The empty table reaches normal end-of-file, while an empty child inside a larger tree is rejected as corruption. Both neighboring checks ran independently and passed.
Passing Harness Both requested checks were found by name and ran successfully. The empty-child check passed all 10 assertions, and the first-offset check passed all 4 assertions.
Passing Harness Two repeated native sessions checked malformed offsets, valid zero-start data, an empty root, and an empty child. All assertions passed, and both sessions exited cleanly without a crash or cleanup failure.
Passing Harness The native regression case rejects malformed key and value offsets and still accepts the valid zero-start control. All four checks passed.
Passing Harness The malformed offset checks passed all four assertions and the process exited cleanly without a crash or allocator error.
Passing Harness Both named regression cases were found and ran their intended checks. The empty-child cursor case passed 10 checks, and the first-offset case passed 4 checks.
Passing Harness The malformed and valid fixtures behaved as expected in two complete native regression sessions. Both sessions passed all 310193 assertions and ended without a crash or allocator diagnostic.
Passing Offset The native parser rejected a malformed node whose first value offset was 1, instead of reading shifted value data or accepting the node.
Passing Offset A valid one-item integer record was accepted and remained available for reading.
Passing Offset A malformed integer-key node is rejected before it can expose inconsistent key data, while valid zero-start data still works.
Passing Offset An empty node below the root is rejected as corrupt before any reader can use it.
Passing Offset The valid zero-start node was accepted and exposed its single item, while the malformed offset checks also passed.
Passing Offset A malformed node with a value offset starting at one was rejected as corruption, while the valid zero-offset control still parsed successfully.
Passing Offset Malformed node data is rejected with a corruption error instead of being read.
Passing Offset The parser rejected a malformed node whose first key offset started at 8, while the valid zero-start control still parsed and exposed one item.
⚠️ Additional Finding Medium severity Cursor After the cursor reaches the final row, stepping into an empty child returns SQLITE_CORRUPT but does not make the cursor unusable. Reading the next key or value can therefore operate on a child with no rows.
Additional Findings Details

These findings are unrelated to the current changes but were observed during testing.

🟡 Corrupt cursor stays marked as usable
  • Severity: Medium Medium severity
  • Description: After the cursor reaches the final row, stepping into an empty child returns SQLITE_CORRUPT but does not make the cursor unusable. Reading the next key or value can therefore operate on a child with no rows.
  • Impact: When a database contains a malformed child node, moving through its rows can leave the cursor looking valid. A later read may crash or access an invalid row instead of safely reporting the corruption.
  • Steps to Reproduce:
    1. Build the native storage-engine regression runner with the prolly implementation enabled.
    2. Create an internal tree with one populated leaf followed by an empty child, and position the cursor on the populated leaf.
    3. Call the forward step that crosses into the empty child and confirm it returns SQLITE_CORRUPT.
    4. Call a key or value accessor after that error and verify that the cursor is not still reported as valid and no row is exposed.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: src/prolly_cursor.c:149-167 implements descendToExtremeLeaf. When the descended child has zero items, line 163 returns SQLITE_CORRUPT immediately. That function does not change cur->eState. In prollyCursorNext at src/prolly_cursor.c:270-307, the boundary path increments the parent child index, assigns cur->iLevel, calls descendToExtremeLeaf at line 299, and returns the error at line 300 before the success-path assignment cur->eState = PROLLY_CURSOR_VALID at line 301. Because the cursor was VALID before the call, it remains VALID on this error path. The accessors at lines 414-438 assert that eState is VALID and then use the current leaf and index; prollyCursorKey, prollyCursorIntKey, and prollyCursorValue can consequently read the failed empty-child state. The targeted fix is to set eState to PROLLY_CURSOR_INVALID (and release or otherwise clear the failed descent state as required by the cursor ownership rules) before returning SQLITE_CORRUPT from the failed stepping path. The separate level-zero empty-root path remains ordinary EOF and must not be changed.
Evidence Package

Tip

Reply with @itoqa to send us feedback on this test run.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

DoltLite source coverage

Metric Covered Total Coverage
Lines 43148 51121 84.40%
Branches 18948 28286 66.99%
Functions 2101 2300 91.35%

Merged 162 pooled raw profiles from the distributed Linux correctness jobs.

Per-file coverage (91 files)
File Lines Branches Functions
src/btree_orig_api.c 85.87% 76.19% 87.18%
src/chunk_file.c 100.00% 100.00% 100.00%
src/chunk_index.c 88.76% 76.47% 100.00%
src/chunk_refs.c 99.35% 85.48% 100.00%
src/chunk_staging.c 87.50% 77.05% 94.12%
src/chunk_store.c 91.01% 75.00% 100.00%
src/chunk_store_commit.c 86.32% 66.96% 100.00%
src/chunk_store_lock.c 94.25% 77.47% 100.00%
src/chunk_store_refs_api.c 91.52% 77.40% 100.00%
src/chunk_wal.c 94.24% 75.00% 92.86%
src/doltlite.c 100.00% 100.00% 100.00%
src/doltlite_add.c 85.19% 67.71% 100.00%
src/doltlite_ancestor.c 88.68% 66.11% 100.00%
src/doltlite_at.c 77.98% 59.93% 94.12%
src/doltlite_blame.c 80.70% 58.94% 96.43%
src/doltlite_branch.c 86.78% 80.20% 100.00%
src/doltlite_branches.c 93.51% 77.27% 92.86%
src/doltlite_checkout.c 76.38% 59.81% 100.00%
src/doltlite_cherry_pick.c 70.53% 55.08% 71.43%
src/doltlite_chunk_walk.c 88.60% 64.19% 100.00%
src/doltlite_cmd.c 69.23% 60.83% 93.33%
src/doltlite_commit.c 93.22% 73.08% 100.00%
src/doltlite_commit_ancestors.c 90.65% 68.89% 92.31%
src/doltlite_commit_cmd.c 75.72% 69.42% 100.00%
src/doltlite_config.c 77.54% 71.43% 100.00%
src/doltlite_conflicts.c 81.83% 58.92% 93.44%
src/doltlite_constraint_violations.c 81.56% 55.91% 92.86%
src/doltlite_core.c 91.69% 72.22% 100.00%
src/doltlite_creds.c 84.01% 54.75% 89.74%
src/doltlite_dbpage.c 92.68% 73.21% 91.67%
src/doltlite_diff.c 87.88% 69.04% 96.43%
src/doltlite_diff_stat.c 94.15% 75.38% 95.56%
src/doltlite_diff_table.c 93.78% 68.45% 97.14%
src/doltlite_gc.c 77.11% 56.01% 96.30%
src/doltlite_hashof.c 75.64% 62.86% 94.44%
src/doltlite_history.c 85.61% 70.55% 100.00%
src/doltlite_http_remote.c 82.22% 57.17% 92.31%
src/doltlite_ignore.c 90.06% 66.67% 100.00%
src/doltlite_log.c 94.38% 70.16% 92.31%
src/doltlite_merge.c 96.94% 74.49% 100.00%
src/doltlite_merge_cmd.c 88.89% 73.50% 100.00%
src/doltlite_merge_constraints.c 88.25% 67.65% 100.00%
src/doltlite_merge_constraints_check.c 92.68% 65.76% 100.00%
src/doltlite_merge_constraints_fk.c 77.09% 55.86% 85.71%
src/doltlite_merge_constraints_unique.c 86.50% 64.37% 100.00%
src/doltlite_merge_pass1.c 91.02% 74.87% 100.00%
src/doltlite_merge_pass2.c 62.90% 57.69% 100.00%
src/doltlite_merge_rows.c 88.25% 69.37% 100.00%
src/doltlite_merge_schema.c 84.75% 63.03% 96.55%
src/doltlite_merge_status.c 91.98% 73.53% 92.31%
src/doltlite_patch.c 94.92% 74.25% 97.83%
src/doltlite_rebase.c 85.57% 59.77% 100.00%
src/doltlite_record.c 75.90% 57.54% 92.86%
src/doltlite_ref.c 96.97% 77.27% 100.00%
src/doltlite_remote.c 81.98% 63.65% 95.92%
src/doltlite_remote_sql.c 63.89% 55.98% 92.86%
src/doltlite_remotesrv.c 75.94% 64.97% 90.62%
src/doltlite_reset.c 86.10% 71.53% 100.00%
src/doltlite_revert.c 79.29% 68.18% 100.00%
src/doltlite_schema_diff.c 92.38% 69.46% 96.88%
src/doltlite_schemas.c 68.97% 44.87% 90.91%
src/doltlite_status.c 92.46% 71.84% 97.22%
src/doltlite_tag.c 80.07% 56.62% 93.33%
src/doltlite_tls.c 85.04% 62.73% 92.31%
src/doltlite_verify_constraints.c 74.11% 58.46% 100.00%
src/doltlite_workspace.c 89.46% 65.50% 100.00%
src/pager_shim.c 58.00% 64.73% 32.37%
src/prolly_btree.c 81.21% 63.06% 86.40%
src/prolly_btree_catalog.c 80.81% 66.17% 96.67%
src/prolly_btree_cursor.c 86.32% 62.69% 97.92%
src/prolly_btree_cursor_count.c 78.78% 55.70% 100.00%
src/prolly_btree_cursor_payload.c 78.81% 60.38% 94.12%
src/prolly_btree_cursor_seek.c 73.88% 61.69% 82.35%
src/prolly_btree_mutation.c 85.54% 65.05% 97.92%
src/prolly_btree_orig.c 83.44% 35.71% 87.67%
src/prolly_btree_state.c 93.89% 67.48% 100.00%
src/prolly_btree_txn.c 82.70% 69.84% 98.04%
src/prolly_cache.c 93.24% 69.70% 100.00%
src/prolly_check.c 60.36% 62.96% 100.00%
src/prolly_chunker.c 94.04% 78.38% 100.00%
src/prolly_cursor.c 89.65% 81.25% 100.00%
src/prolly_diff.c 52.48% 38.62% 60.87%
src/prolly_hash.c 93.65% 80.00% 100.00%
src/prolly_hashset.c 90.48% 80.56% 100.00%
src/prolly_mutate.c 87.07% 78.08% 100.00%
src/prolly_mutmap.c 92.95% 80.04% 100.00%
src/prolly_node.c 89.31% 74.24% 100.00%
src/prolly_three_way_diff.c 95.58% 85.90% 100.00%
src/prolly_three_way_merge.c 80.78% 66.67% 91.67%
src/prolly_xxhash.c 100.00% 100.00% 100.00%
src/sortkey.c 93.06% 80.14% 100.00%

Download HTML and LCOV artifacts from this workflow run.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

DoltLite performance vs PR base

  • Baseline: 69832551d03778e273ae168d1577d223d24e628e
  • Candidate: 1b9a0300de9afc67e98bebdad473a16e54ae826c
  • Overall ratio: 1.005x
  • Gate result: PASS
  • Gates: individual > 1.25x with more than 5.00ms regression; section, suite, or overall > 1.15x with the same minimum delta
  • vc individual gate: > 1.50x with more than 25.00ms regression
  • Confirmed failed gates: none
  • Automatic retries: textpk cleared after 2 attempts; no gate failed every time
Suite Workloads Baseline total Candidate total Ratio Result
blobpk 69 11.88s 11.90s 1.001x PASS
compositepk 69 12.20s 12.20s 0.999x PASS
int 69 10.94s 10.95s 1.000x PASS
textpk 69 10.35s 10.58s 1.023x PASS
vc 13 911.71ms 918.81ms 1.008x PASS
blobpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 36.78ms 36.41ms -365us 0.990x PASS
mem_reads oltp_range_select 13.86ms 13.91ms +47us 1.003x PASS
mem_reads oltp_sum_range 13.88ms 13.96ms +72us 1.005x PASS
mem_reads oltp_order_range 3.15ms 3.10ms -42us 0.987x PASS
mem_reads oltp_distinct_range 4.24ms 4.21ms -31us 0.993x PASS
mem_reads oltp_index_scan 5.93ms 6.01ms +74us 1.012x PASS
mem_reads select_random_points 20.44ms 20.61ms +167us 1.008x PASS
mem_reads select_random_ranges 5.01ms 5.12ms +108us 1.022x PASS
mem_reads covering_index_scan 4.27ms 4.32ms +49us 1.011x PASS
mem_reads groupby_scan 34.00ms 33.82ms -173us 0.995x PASS
mem_reads index_join 8.65ms 8.79ms +140us 1.016x PASS
mem_reads index_join_scan 5.46ms 5.48ms +23us 1.004x PASS
mem_reads types_table_scan 1.26s 1.26s -4.32ms 0.997x PASS
mem_reads table_scan 1.35s 1.36s +11.02ms 1.008x PASS
mem_reads oltp_read_only 133.82ms 135.56ms +1.74ms 1.013x PASS
mem_writes oltp_bulk_insert 350.04ms 347.30ms -2.74ms 0.992x PASS
mem_writes oltp_insert 38.71ms 38.76ms +50us 1.001x PASS
mem_writes oltp_update_index 126.21ms 126.15ms -60us 1.000x PASS
mem_writes oltp_update_non_index 82.91ms 82.53ms -377us 0.995x PASS
mem_writes oltp_delete_insert 100.74ms 100.32ms -414us 0.996x PASS
mem_writes oltp_write_only 59.64ms 59.67ms +31us 1.001x PASS
mem_writes types_delete_insert 52.54ms 52.67ms +133us 1.003x PASS
mem_writes oltp_read_write 136.82ms 136.79ms -34us 1.000x PASS
file_reads oltp_point_select 60.70ms 61.06ms +358us 1.006x PASS
file_reads oltp_range_select 16.66ms 16.59ms -76us 0.995x PASS
file_reads oltp_sum_range 16.60ms 16.51ms -91us 0.995x PASS
file_reads oltp_order_range 3.46ms 3.44ms -26us 0.992x PASS
file_reads oltp_distinct_range 4.54ms 4.53ms -13us 0.997x PASS
file_reads oltp_index_scan 8.63ms 8.78ms +150us 1.017x PASS
file_reads select_random_points 24.01ms 24.80ms +791us 1.033x PASS
file_reads select_random_ranges 7.64ms 7.71ms +73us 1.010x PASS
file_reads covering_index_scan 6.92ms 6.98ms +62us 1.009x PASS
file_reads groupby_scan 34.67ms 34.46ms -206us 0.994x PASS
file_reads index_join 10.93ms 11.11ms +186us 1.017x PASS
file_reads index_join_scan 5.87ms 5.84ms -33us 0.994x PASS
file_reads types_table_scan 1.26s 1.25s -6.22ms 0.995x PASS
file_reads table_scan 1.35s 1.35s +1.75ms 1.001x PASS
file_reads oltp_read_only 171.12ms 172.50ms +1.38ms 1.008x PASS
file_writes oltp_bulk_insert 371.15ms 371.59ms +445us 1.001x PASS
file_writes oltp_insert 50.31ms 50.86ms +545us 1.011x PASS
file_writes oltp_update_index 159.89ms 159.75ms -135us 0.999x PASS
file_writes oltp_update_non_index 104.81ms 104.72ms -92us 0.999x PASS
file_writes oltp_delete_insert 126.02ms 126.70ms +675us 1.005x PASS
file_writes oltp_write_only 81.58ms 80.51ms -1.06ms 0.987x PASS
file_writes types_delete_insert 69.71ms 68.95ms -757us 0.989x PASS
file_writes oltp_read_write 157.02ms 157.00ms -16us 1.000x PASS
ac_reads oltp_point_select 60.79ms 60.77ms -18us 1.000x PASS
ac_reads oltp_range_select 16.57ms 16.49ms -74us 0.996x PASS
ac_reads oltp_sum_range 16.90ms 16.72ms -182us 0.989x PASS
ac_reads oltp_order_range 3.57ms 3.44ms -133us 0.963x PASS
ac_reads oltp_distinct_range 4.57ms 4.49ms -75us 0.984x PASS
ac_reads oltp_index_scan 8.73ms 8.85ms +122us 1.014x PASS
ac_reads select_random_points 24.22ms 24.05ms -174us 0.993x PASS
ac_reads select_random_ranges 7.59ms 7.67ms +72us 1.009x PASS
ac_reads covering_index_scan 7.01ms 6.98ms -36us 0.995x PASS
ac_reads groupby_scan 34.83ms 34.26ms -570us 0.984x PASS
ac_reads index_join 11.11ms 11.05ms -60us 0.995x PASS
ac_reads index_join_scan 5.87ms 5.94ms +68us 1.012x PASS
ac_reads types_table_scan 1.26s 1.26s -1.86ms 0.999x PASS
ac_reads table_scan 1.35s 1.36s +12.54ms 1.009x PASS
ac_reads oltp_read_only 170.54ms 169.89ms -651us 0.996x PASS
ac_writes oltp_bulk_insert_ac 95.67ms 98.89ms +3.22ms 1.034x PASS
ac_writes oltp_insert_ac 119.37ms 119.16ms -208us 0.998x PASS
ac_writes oltp_update_index_ac 130.32ms 135.27ms +4.95ms 1.038x PASS
ac_writes oltp_update_non_index_ac 111.00ms 110.87ms -133us 0.999x PASS
ac_writes oltp_delete_insert_ac 125.17ms 123.39ms -1.78ms 0.986x PASS
ac_writes oltp_write_only_ac 121.31ms 126.56ms +5.25ms 1.043x PASS
ac_writes types_delete_insert_ac 115.73ms 111.12ms -4.61ms 0.960x PASS
ac_writes oltp_read_write_ac 131.43ms 130.43ms -995us 0.992x PASS
compositepk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 40.37ms 40.51ms +136us 1.003x PASS
mem_reads oltp_range_select 21.40ms 21.48ms +74us 1.003x PASS
mem_reads oltp_sum_range 21.03ms 21.10ms +67us 1.003x PASS
mem_reads oltp_order_range 3.85ms 3.90ms +57us 1.015x PASS
mem_reads oltp_distinct_range 4.94ms 4.92ms -21us 0.996x PASS
mem_reads oltp_index_scan 6.20ms 6.10ms -101us 0.984x PASS
mem_reads select_random_points 32.10ms 31.93ms -162us 0.995x PASS
mem_reads select_random_ranges 8.95ms 9.01ms +62us 1.007x PASS
mem_reads covering_index_scan 4.29ms 4.16ms -133us 0.969x PASS
mem_reads groupby_scan 39.16ms 39.10ms -64us 0.998x PASS
mem_reads index_join 10.55ms 10.33ms -220us 0.979x PASS
mem_reads index_join_scan 5.55ms 5.61ms +61us 1.011x PASS
mem_reads types_table_scan 1.30s 1.29s -9.27ms 0.993x PASS
mem_reads table_scan 1.39s 1.39s +3.37ms 1.002x PASS
mem_reads oltp_read_only 170.23ms 169.15ms -1.08ms 0.994x PASS
mem_writes oltp_bulk_insert 362.88ms 358.11ms -4.78ms 0.987x PASS
mem_writes oltp_insert 36.78ms 36.80ms +19us 1.001x PASS
mem_writes oltp_update_index 116.71ms 115.70ms -1.01ms 0.991x PASS
mem_writes oltp_update_non_index 83.62ms 82.61ms -1.01ms 0.988x PASS
mem_writes oltp_delete_insert 95.81ms 94.86ms -950us 0.990x PASS
mem_writes oltp_write_only 57.57ms 58.08ms +515us 1.009x PASS
mem_writes types_delete_insert 54.12ms 54.76ms +637us 1.012x PASS
mem_writes oltp_read_write 154.84ms 154.82ms -14us 1.000x PASS
file_reads oltp_point_select 64.72ms 65.16ms +438us 1.007x PASS
file_reads oltp_range_select 23.98ms 24.21ms +232us 1.010x PASS
file_reads oltp_sum_range 24.12ms 24.06ms -57us 0.998x PASS
file_reads oltp_order_range 4.26ms 4.30ms +40us 1.009x PASS
file_reads oltp_distinct_range 5.41ms 5.35ms -63us 0.988x PASS
file_reads oltp_index_scan 9.20ms 9.11ms -87us 0.991x PASS
file_reads select_random_points 36.12ms 36.77ms +652us 1.018x PASS
file_reads select_random_ranges 11.74ms 11.89ms +152us 1.013x PASS
file_reads covering_index_scan 7.15ms 7.05ms -102us 0.986x PASS
file_reads groupby_scan 40.05ms 39.83ms -218us 0.995x PASS
file_reads index_join 13.29ms 13.19ms -104us 0.992x PASS
file_reads index_join_scan 6.16ms 6.17ms +5us 1.001x PASS
file_reads types_table_scan 1.30s 1.30s -3.49ms 0.997x PASS
file_reads table_scan 1.39s 1.39s +1.95ms 1.001x PASS
file_reads oltp_read_only 210.41ms 210.15ms -270us 0.999x PASS
file_writes oltp_bulk_insert 382.76ms 382.42ms -342us 0.999x PASS
file_writes oltp_insert 46.34ms 46.26ms -84us 0.998x PASS
file_writes oltp_update_index 144.98ms 145.39ms +411us 1.003x PASS
file_writes oltp_update_non_index 105.77ms 106.18ms +410us 1.004x PASS
file_writes oltp_delete_insert 120.77ms 120.48ms -296us 0.998x PASS
file_writes oltp_write_only 78.55ms 77.67ms -879us 0.989x PASS
file_writes types_delete_insert 69.32ms 68.99ms -329us 0.995x PASS
file_writes oltp_read_write 178.52ms 177.62ms -901us 0.995x PASS
ac_reads oltp_point_select 65.11ms 64.91ms -200us 0.997x PASS
ac_reads oltp_range_select 24.10ms 24.28ms +179us 1.007x PASS
ac_reads oltp_sum_range 24.00ms 24.12ms +120us 1.005x PASS
ac_reads oltp_order_range 4.30ms 4.36ms +59us 1.014x PASS
ac_reads oltp_distinct_range 5.33ms 5.32ms -11us 0.998x PASS
ac_reads oltp_index_scan 8.90ms 8.91ms +9us 1.001x PASS
ac_reads select_random_points 35.89ms 36.02ms +123us 1.003x PASS
ac_reads select_random_ranges 11.86ms 11.67ms -194us 0.984x PASS
ac_reads covering_index_scan 7.03ms 7.05ms +24us 1.003x PASS
ac_reads groupby_scan 40.19ms 40.08ms -107us 0.997x PASS
ac_reads index_join 12.90ms 12.98ms +78us 1.006x PASS
ac_reads index_join_scan 6.14ms 6.03ms -106us 0.983x PASS
ac_reads types_table_scan 1.29s 1.29s +799us 1.001x PASS
ac_reads table_scan 1.38s 1.38s +1.22ms 1.001x PASS
ac_reads oltp_read_only 207.13ms 208.48ms +1.36ms 1.007x PASS
ac_writes oltp_bulk_insert_ac 81.45ms 80.82ms -627us 0.992x PASS
ac_writes oltp_insert_ac 99.67ms 100.99ms +1.32ms 1.013x PASS
ac_writes oltp_update_index_ac 111.12ms 110.85ms -266us 0.998x PASS
ac_writes oltp_update_non_index_ac 92.88ms 91.25ms -1.63ms 0.982x PASS
ac_writes oltp_delete_insert_ac 103.79ms 103.64ms -151us 0.999x PASS
ac_writes oltp_write_only_ac 102.04ms 101.39ms -648us 0.994x PASS
ac_writes types_delete_insert_ac 97.06ms 100.60ms +3.54ms 1.037x PASS
ac_writes oltp_read_write_ac 109.45ms 112.91ms +3.46ms 1.032x PASS
int details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 26.98ms 27.23ms +248us 1.009x PASS
mem_reads oltp_range_select 11.73ms 11.84ms +116us 1.010x PASS
mem_reads oltp_sum_range 11.24ms 11.22ms -16us 0.999x PASS
mem_reads oltp_order_range 2.85ms 2.83ms -22us 0.992x PASS
mem_reads oltp_distinct_range 3.90ms 3.84ms -61us 0.984x PASS
mem_reads oltp_index_scan 4.70ms 4.71ms +9us 1.002x PASS
mem_reads select_random_points 10.82ms 10.74ms -85us 0.992x PASS
mem_reads select_random_ranges 3.91ms 3.95ms +34us 1.009x PASS
mem_reads covering_index_scan 3.99ms 3.99ms +1us 1.000x PASS
mem_reads groupby_scan 34.23ms 34.18ms -47us 0.999x PASS
mem_reads index_join 7.55ms 7.55ms -6us 0.999x PASS
mem_reads index_join_scan 4.58ms 4.56ms -19us 0.996x PASS
mem_reads types_table_scan 1.26s 1.26s -1.49ms 0.999x PASS
mem_reads table_scan 1.37s 1.37s +1.33ms 1.001x PASS
mem_reads oltp_read_only 114.19ms 115.32ms +1.13ms 1.010x PASS
mem_writes oltp_bulk_insert 239.81ms 241.43ms +1.62ms 1.007x PASS
mem_writes oltp_insert 28.16ms 27.98ms -182us 0.994x PASS
mem_writes oltp_update_index 104.14ms 104.22ms +77us 1.001x PASS
mem_writes oltp_update_non_index 58.40ms 58.45ms +57us 1.001x PASS
mem_writes oltp_delete_insert 77.67ms 77.36ms -309us 0.996x PASS
mem_writes oltp_write_only 44.27ms 44.90ms +626us 1.014x PASS
mem_writes types_delete_insert 39.54ms 39.21ms -330us 0.992x PASS
mem_writes oltp_read_write 103.71ms 103.87ms +155us 1.001x PASS
file_reads oltp_point_select 55.62ms 55.39ms -230us 0.996x PASS
file_reads oltp_range_select 14.68ms 14.71ms +33us 1.002x PASS
file_reads oltp_sum_range 14.29ms 14.30ms +16us 1.001x PASS
file_reads oltp_order_range 3.21ms 3.19ms -24us 0.993x PASS
file_reads oltp_distinct_range 4.14ms 4.25ms +108us 1.026x PASS
file_reads oltp_index_scan 7.90ms 7.89ms -8us 0.999x PASS
file_reads select_random_points 13.94ms 14.06ms +130us 1.009x PASS
file_reads select_random_ranges 6.86ms 6.73ms -127us 0.981x PASS
file_reads covering_index_scan 7.13ms 7.14ms +14us 1.002x PASS
file_reads groupby_scan 34.83ms 34.57ms -254us 0.993x PASS
file_reads index_join 9.47ms 9.61ms +140us 1.015x PASS
file_reads index_join_scan 5.03ms 4.99ms -40us 0.992x PASS
file_reads types_table_scan 1.26s 1.25s -1.27ms 0.999x PASS
file_reads table_scan 1.37s 1.37s -613us 1.000x PASS
file_reads oltp_read_only 154.59ms 156.52ms +1.93ms 1.012x PASS
file_writes oltp_bulk_insert 257.92ms 256.68ms -1.25ms 0.995x PASS
file_writes oltp_insert 35.42ms 35.31ms -115us 0.997x PASS
file_writes oltp_update_index 127.13ms 126.35ms -776us 0.994x PASS
file_writes oltp_update_non_index 80.58ms 80.13ms -446us 0.994x PASS
file_writes oltp_delete_insert 95.79ms 95.84ms +48us 1.001x PASS
file_writes oltp_write_only 64.53ms 64.25ms -279us 0.996x PASS
file_writes types_delete_insert 52.12ms 52.44ms +318us 1.006x PASS
file_writes oltp_read_write 123.30ms 123.10ms -196us 0.998x PASS
ac_reads oltp_point_select 55.70ms 55.58ms -112us 0.998x PASS
ac_reads oltp_range_select 14.69ms 14.73ms +37us 1.003x PASS
ac_reads oltp_sum_range 14.46ms 14.19ms -271us 0.981x PASS
ac_reads oltp_order_range 3.21ms 3.22ms +5us 1.002x PASS
ac_reads oltp_distinct_range 4.22ms 4.20ms -24us 0.994x PASS
ac_reads oltp_index_scan 7.90ms 7.87ms -37us 0.995x PASS
ac_reads select_random_points 14.05ms 13.93ms -119us 0.992x PASS
ac_reads select_random_ranges 6.76ms 6.85ms +91us 1.013x PASS
ac_reads covering_index_scan 7.05ms 7.10ms +45us 1.006x PASS
ac_reads groupby_scan 34.83ms 34.71ms -120us 0.997x PASS
ac_reads index_join 9.50ms 9.53ms +37us 1.004x PASS
ac_reads index_join_scan 5.00ms 4.98ms -16us 0.997x PASS
ac_reads types_table_scan 1.25s 1.26s +5.47ms 1.004x PASS
ac_reads table_scan 1.37s 1.37s +2.06ms 1.002x PASS
ac_reads oltp_read_only 155.12ms 154.73ms -387us 0.998x PASS
ac_writes oltp_bulk_insert_ac 58.58ms 58.57ms -11us 1.000x PASS
ac_writes oltp_insert_ac 75.36ms 80.02ms +4.66ms 1.062x PASS
ac_writes oltp_update_index_ac 97.25ms 91.52ms -5.73ms 0.941x PASS
ac_writes oltp_update_non_index_ac 70.37ms 70.24ms -125us 0.998x PASS
ac_writes oltp_delete_insert_ac 84.49ms 85.11ms +618us 1.007x PASS
ac_writes oltp_write_only_ac 81.92ms 80.10ms -1.82ms 0.978x PASS
ac_writes types_delete_insert_ac 74.64ms 71.68ms -2.96ms 0.960x PASS
ac_writes oltp_read_write_ac 87.43ms 87.44ms +6us 1.000x PASS
textpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 27.49ms 27.21ms -281us 0.990x PASS
mem_reads oltp_range_select 10.74ms 10.80ms +62us 1.006x PASS
mem_reads oltp_sum_range 10.64ms 10.57ms -64us 0.994x PASS
mem_reads oltp_order_range 2.51ms 2.51ms -2us 0.999x PASS
mem_reads oltp_distinct_range 3.29ms 3.27ms -12us 0.996x PASS
mem_reads oltp_index_scan 4.67ms 4.59ms -76us 0.984x PASS
mem_reads select_random_points 16.20ms 15.89ms -311us 0.981x PASS
mem_reads select_random_ranges 4.18ms 4.14ms -40us 0.990x PASS
mem_reads covering_index_scan 3.42ms 3.42ms -1us 1.000x PASS
mem_reads groupby_scan 27.75ms 27.62ms -129us 0.995x PASS
mem_reads index_join 6.75ms 6.98ms +226us 1.033x PASS
mem_reads index_join_scan 4.55ms 4.51ms -38us 0.992x PASS
mem_reads types_table_scan 972.93ms 972.42ms -506us 0.999x PASS
mem_reads table_scan 1.08s 1.06s -13.02ms 0.988x PASS
mem_reads oltp_read_only 101.28ms 100.91ms -378us 0.996x PASS
mem_writes oltp_bulk_insert 263.38ms 260.03ms -3.35ms 0.987x PASS
mem_writes oltp_insert 30.61ms 30.19ms -427us 0.986x PASS
mem_writes oltp_update_index 106.00ms 105.71ms -293us 0.997x PASS
mem_writes oltp_update_non_index 66.83ms 67.05ms +223us 1.003x PASS
mem_writes oltp_delete_insert 80.38ms 80.36ms -17us 1.000x PASS
mem_writes oltp_write_only 47.82ms 47.84ms +20us 1.000x PASS
mem_writes types_delete_insert 40.75ms 40.62ms -126us 0.997x PASS
mem_writes oltp_read_write 103.68ms 103.02ms -664us 0.994x PASS
file_reads oltp_point_select 50.35ms 50.34ms -14us 1.000x PASS
file_reads oltp_range_select 13.20ms 13.19ms -15us 0.999x PASS
file_reads oltp_sum_range 13.10ms 12.87ms -231us 0.982x PASS
file_reads oltp_order_range 2.82ms 2.81ms -6us 0.998x PASS
file_reads oltp_distinct_range 3.55ms 3.58ms +32us 1.009x PASS
file_reads oltp_index_scan 7.39ms 7.33ms -61us 0.992x PASS
file_reads select_random_points 18.75ms 18.47ms -285us 0.985x PASS
file_reads select_random_ranges 6.53ms 6.50ms -30us 0.995x PASS
file_reads covering_index_scan 6.17ms 6.24ms +61us 1.010x PASS
file_reads groupby_scan 28.14ms 28.09ms -47us 0.998x PASS
file_reads index_join 9.07ms 9.05ms -22us 0.998x PASS
file_reads index_join_scan 5.17ms 5.10ms -72us 0.986x PASS
file_reads types_table_scan 970.23ms 974.56ms +4.32ms 1.004x PASS
file_reads table_scan 1.08s 1.08s -3.64ms 0.997x PASS
file_reads oltp_read_only 135.18ms 135.06ms -121us 0.999x PASS
file_writes oltp_bulk_insert 343.17ms 349.39ms +6.23ms 1.018x PASS
file_writes oltp_insert 58.49ms 58.27ms -225us 0.996x PASS
file_writes oltp_update_index 202.84ms 233.38ms +30.54ms 1.151x PASS
file_writes oltp_update_non_index 138.83ms 184.38ms +45.55ms 1.328x TRANSIENT
file_writes oltp_delete_insert 168.14ms 164.37ms -3.77ms 0.978x PASS
file_writes oltp_write_only 118.12ms 118.94ms +825us 1.007x PASS
file_writes types_delete_insert 87.85ms 98.76ms +10.91ms 1.124x PASS
file_writes oltp_read_write 171.70ms 180.85ms +9.15ms 1.053x PASS
ac_reads oltp_point_select 51.07ms 51.12ms +53us 1.001x PASS
ac_reads oltp_range_select 13.46ms 13.34ms -122us 0.991x PASS
ac_reads oltp_sum_range 13.45ms 13.33ms -123us 0.991x PASS
ac_reads oltp_order_range 2.83ms 2.81ms -27us 0.990x PASS
ac_reads oltp_distinct_range 3.56ms 3.59ms +34us 1.010x PASS
ac_reads oltp_index_scan 7.43ms 7.39ms -35us 0.995x PASS
ac_reads select_random_points 19.04ms 18.87ms -166us 0.991x PASS
ac_reads select_random_ranges 6.49ms 6.55ms +56us 1.009x PASS
ac_reads covering_index_scan 6.20ms 6.19ms -9us 0.999x PASS
ac_reads groupby_scan 28.48ms 28.27ms -203us 0.993x PASS
ac_reads index_join 9.09ms 9.21ms +120us 1.013x PASS
ac_reads index_join_scan 5.12ms 5.05ms -73us 0.986x PASS
ac_reads types_table_scan 965.75ms 978.92ms +13.18ms 1.014x PASS
ac_reads table_scan 1.07s 1.08s +7.91ms 1.007x PASS
ac_reads oltp_read_only 134.46ms 135.17ms +703us 1.005x PASS
ac_writes oltp_bulk_insert_ac 107.33ms 80.54ms -26.79ms 0.750x PASS
ac_writes oltp_insert_ac 173.09ms 211.55ms +38.45ms 1.222x PASS
ac_writes oltp_update_index_ac 193.01ms 107.68ms -85.33ms 0.558x PASS
ac_writes oltp_update_non_index_ac 233.89ms 151.77ms -82.12ms 0.649x PASS
ac_writes oltp_delete_insert_ac 129.89ms 412.35ms +282.46ms 3.175x TRANSIENT
ac_writes oltp_write_only_ac 131.96ms 126.21ms -5.75ms 0.956x PASS
ac_writes types_delete_insert_ac 93.08ms 201.91ms +108.83ms 2.169x TRANSIENT
ac_writes oltp_read_write_ac 292.65ms 195.76ms -96.90ms 0.669x PASS
vc details
Section Test Baseline Candidate Delta Ratio Result
vc status_clean_many_tables 80.58ms 80.59ms +5us 1.000x PASS
vc status_dirty_many_tables 83.95ms 84.35ms +401us 1.005x PASS
vc diff_regular_working_one_table 77.30ms 77.18ms -118us 0.998x PASS
vc diff_regular_working_many_tables 88.89ms 89.75ms +857us 1.010x PASS
vc diff_stat_working_many_tables 89.35ms 90.25ms +897us 1.010x PASS
vc diff_schema_working_many_tables 90.16ms 90.55ms +389us 1.004x PASS
vc branch_list_many_branches 21.78ms 21.73ms -52us 0.998x PASS
vc branch_create_delete 24.30ms 24.46ms +165us 1.007x PASS
vc checkout_branch_clean 54.61ms 54.98ms +376us 1.007x PASS
vc merge_data_no_conflicts 28.40ms 28.62ms +220us 1.008x PASS
vc merge_schema_no_conflicts 21.75ms 21.70ms -50us 0.998x PASS
vc merge_data_conflicts 125.18ms 127.41ms +2.23ms 1.018x PASS
vc merge_data_conflicts_with_resolve 125.47ms 127.24ms +1.77ms 1.014x PASS

All relative performance gates passed.

@timsehn
timsehn merged commit c44ac6e into master Aug 7, 2026
59 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant