Skip to content

Consolidate catalog types and split feature oracle - #1781

Merged
timsehn merged 3 commits into
masterfrom
refactor/internal-types-and-feature-oracle
Jul 23, 2026
Merged

Consolidate catalog types and split feature oracle#1781
timsehn merged 3 commits into
masterfrom
refactor/internal-types-and-feature-oracle

Conversation

@timsehn

@timsehn timsehn commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • give TableEntry and SchemaEntry one canonical owner in doltlite_catalog_types.h instead of duplicate guarded layouts in the VC and Prolly internals
  • wire the shared type header into object dependencies and the DoltLite amalgamation generator
  • replace the 13,373-line feature-interaction oracle monolith with a small runner and five documented feature families
  • preserve all 424 sections and 752 Dolt differential cases while making new cases easier to place and review

Tests

  • make -C build -j6 doltlite doltlite-remotesrv sqlite3.c
  • bash test/vc_oracle_feature_interaction_test.sh build/doltlite dolt (752 passed)
  • bash test/vc_oracle_merge_test.sh build/doltlite dolt (74 passed)
  • bash test/vc_oracle_schema_merge_test.sh build/doltlite dolt (54 passed)
  • bash test/run_doltlite_regression_case.sh all (309,956 passed)
  • bash test/lint_layers.sh
  • bash test/lint_orphaned_suites.sh
  • bash test/dead_code_check.sh
  • bash -n test/vc_oracle_feature_interaction_test.sh test/lib/vc_oracle_feature_interactions/*.sh
  • verified the amalgamation contains exactly one TableEntry and one SchemaEntry definition
  • make -C build devtest reproduced the known unfiltered baseline (1,620 compatibility failures, then the final-file hang at 2,545/2,546; interrupted at 2m35s)

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Version-Control Performance Ceilings

Runs: median of 3 executions per benchmark, excluding fixture setup. The
working-set fixtures use 800 committed tables with 125 rows each
(100000 rows total); data-dirty cases update 1 row in 40
tables, and schema-dirty cases update 1 row plus add a column in 20 tables.
Branch tests use 300 branches, checkout uses a clean 400-table
branch switch over 100000 rows, and merge
tests use 100000-row tables with 2000 changed or conflicting
rows per side.

Benchmark Median ms Ceiling ms Result
status_clean_many_tables 66 200 PASS
status_dirty_many_tables 69 200 PASS
diff_regular_working_one_table 61 150 PASS
diff_regular_working_many_tables 73 200 PASS
diff_stat_working_many_tables 72 200 PASS
diff_schema_working_many_tables 73 200 PASS
branch_list_many_branches 20 100 PASS
branch_create_delete 22 100 PASS
checkout_branch_clean 102 200 PASS
merge_data_no_conflicts 31 150 PASS
merge_schema_no_conflicts 20 100 PASS
merge_data_conflicts 79 250 PASS
merge_data_conflicts_with_resolve 78 250 PASS

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Sysbench-Style Benchmark (composite PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 2-column INTEGER PRIMARY KEY(a, b) WITHOUT ROWID.

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 10× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 30,726 40,085 1.30
oltp_range_select 17,395 21,769 1.25
oltp_sum_range 17,052 21,258 1.25
oltp_order_range 3,258 3,861 1.19
oltp_distinct_range 4,278 5,117 1.20
oltp_index_scan 4,260 5,930 1.39
select_random_points 26,167 31,897 1.22
select_random_ranges 7,174 8,980 1.25
covering_index_scan 4,155 4,227 1.02
groupby_scan 34,921 40,569 1.16
index_join 7,917 10,466 1.32
index_join_scan 3,562 5,459 1.53
types_table_scan 1,046,706 1,372,517 1.31
table_scan 1,180,345 1,467,852 1.24
oltp_read_only 143,084 174,370 1.22
Average 1.26

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 245,127 356,973 1.46
oltp_insert 18,985 36,574 1.93
oltp_update_index 66,119 114,725 1.74
oltp_update_non_index 49,082 81,148 1.65
oltp_delete_insert 47,841 95,430 1.99
oltp_write_only 25,825 57,444 2.22
types_delete_insert 32,776 54,254 1.66
oltp_read_write 97,445 154,871 1.59
Average 1.78

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 100,112 65,225 0.65
oltp_range_select 24,600 25,061 1.02
oltp_sum_range 24,599 24,212 0.98
oltp_order_range 4,081 4,237 1.04
oltp_distinct_range 5,191 5,597 1.08
oltp_index_scan 11,329 8,879 0.78
select_random_points 35,314 35,994 1.02
select_random_ranges 14,632 11,925 0.81
covering_index_scan 11,018 6,838 0.62
groupby_scan 35,688 41,584 1.17
index_join 12,032 12,669 1.05
index_join_scan 4,923 6,156 1.25
types_table_scan 1,037,874 1,353,216 1.30
table_scan 1,181,441 1,466,195 1.24
oltp_read_only 244,768 212,671 0.87
Average 0.99

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 256,300 377,746 1.47
oltp_insert 25,650 46,457 1.81
oltp_update_index 93,440 141,080 1.51
oltp_update_non_index 72,540 103,218 1.42
oltp_delete_insert 73,326 118,114 1.61
oltp_write_only 49,040 77,237 1.57
types_delete_insert 48,594 67,642 1.39
oltp_read_write 121,159 176,475 1.46
Average 1.53

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 55,215 64,834 1.17
oltp_range_select 20,625 24,864 1.21
oltp_sum_range 19,852 24,634 1.24
oltp_order_range 3,600 4,266 1.19
oltp_distinct_range 4,713 5,610 1.19
oltp_index_scan 6,700 8,737 1.30
select_random_points 29,849 36,403 1.22
select_random_ranges 9,786 11,777 1.20
covering_index_scan 6,517 6,920 1.06
groupby_scan 35,299 41,298 1.17
index_join 9,559 12,771 1.34
index_join_scan 4,419 6,220 1.41
types_table_scan 1,036,035 1,354,246 1.31
table_scan 1,183,299 1,466,760 1.24
oltp_read_only 177,026 212,087 1.20
Average 1.23

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 20,883 78,741 3.77
oltp_insert_ac 23,518 95,833 4.07
oltp_update_index_ac 25,592 106,455 4.16
oltp_update_non_index_ac 22,067 86,103 3.90
oltp_delete_insert_ac 23,769 96,749 4.07
oltp_write_only_ac 23,672 96,643 4.08
types_delete_insert_ac 21,176 86,407 4.08
oltp_read_write_ac 29,616 104,692 3.53
Average 3.96

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 10x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Sysbench-Style Benchmark (TEXT PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 32-char hex TEXT PRIMARY KEY (UUID-shaped).

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 10× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 31,081 38,660 1.24
oltp_range_select 14,316 15,251 1.07
oltp_sum_range 12,551 14,863 1.18
oltp_order_range 3,110 3,217 1.03
oltp_distinct_range 4,056 4,373 1.08
oltp_index_scan 4,697 6,604 1.41
select_random_points 18,575 21,534 1.16
select_random_ranges 4,084 5,359 1.31
covering_index_scan 5,116 4,864 0.95
groupby_scan 32,453 35,917 1.11
index_join 7,236 10,071 1.39
index_join_scan 4,603 5,760 1.25
types_table_scan 1,264,357 1,394,754 1.10
table_scan 1,523,875 1,498,673 0.98
oltp_read_only 129,404 144,197 1.11
Average 1.16

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 236,815 362,854 1.53
oltp_insert 23,138 40,893 1.77
oltp_update_index 78,595 143,072 1.82
oltp_update_non_index 51,830 89,329 1.72
oltp_delete_insert 55,131 108,100 1.96
oltp_write_only 31,487 64,840 2.06
types_delete_insert 34,577 57,915 1.67
oltp_read_write 95,987 150,620 1.57
Average 1.76

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 102,491 64,710 0.63
oltp_range_select 22,183 18,106 0.82
oltp_sum_range 20,287 17,497 0.86
oltp_order_range 4,011 3,581 0.89
oltp_distinct_range 4,969 4,720 0.95
oltp_index_scan 12,162 9,181 0.75
select_random_points 27,236 25,221 0.93
select_random_ranges 11,344 7,921 0.70
covering_index_scan 13,006 7,428 0.57
groupby_scan 33,875 36,197 1.07
index_join 11,970 11,829 0.99
index_join_scan 5,782 6,394 1.11
types_table_scan 1,264,285 1,380,028 1.09
table_scan 1,543,327 1,513,383 0.98
oltp_read_only 235,555 181,392 0.77
Average 0.87

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 252,554 391,053 1.55
oltp_insert 58,727 52,798 0.90
oltp_update_index 121,427 177,685 1.46
oltp_update_non_index 116,755 115,766 0.99
oltp_delete_insert 94,110 138,230 1.47
oltp_write_only 83,716 85,958 1.03
types_delete_insert 56,575 74,950 1.32
oltp_read_write 141,664 167,788 1.18
Average 1.24

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 54,317 62,749 1.16
oltp_range_select 17,538 17,788 1.01
oltp_sum_range 14,965 17,525 1.17
oltp_order_range 3,425 3,502 1.02
oltp_distinct_range 4,411 4,762 1.08
oltp_index_scan 7,348 9,099 1.24
select_random_points 21,048 24,142 1.15
select_random_ranges 6,469 7,805 1.21
covering_index_scan 8,745 7,354 0.84
groupby_scan 32,675 35,889 1.10
index_join 8,973 11,459 1.28
index_join_scan 4,979 6,010 1.21
types_table_scan 1,051,865 1,325,999 1.26
table_scan 1,230,989 1,448,221 1.18
oltp_read_only 151,718 179,283 1.18
Average 1.14

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 23,203 75,480 3.25
oltp_insert_ac 24,161 98,829 4.09
oltp_update_index_ac 28,479 111,536 3.92
oltp_update_non_index_ac 23,334 85,208 3.65
oltp_delete_insert_ac 25,716 98,080 3.81
oltp_write_only_ac 24,312 100,074 4.12
types_delete_insert_ac 22,861 87,201 3.81
oltp_read_write_ac 30,922 109,235 3.53
Average 3.77

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 10x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Sysbench-Style Benchmark: Doltlite vs SQLite

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 24,317 28,525 1.17
oltp_range_select 10,707 12,868 1.20
oltp_sum_range 9,332 12,506 1.34
oltp_order_range 2,654 3,068 1.16
oltp_distinct_range 3,673 4,217 1.15
oltp_index_scan 3,916 5,027 1.28
select_random_points 10,982 11,450 1.04
select_random_ranges 3,079 4,127 1.34
covering_index_scan 4,272 4,145 0.97
groupby_scan 32,048 36,152 1.13
index_join 5,885 8,069 1.37
index_join_scan 3,498 4,757 1.36
types_table_scan 1,117,840 1,335,130 1.19
table_scan 1,277,589 1,452,222 1.14
oltp_read_only 102,247 122,453 1.20
Average 1.20

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 181,582 242,790 1.34
oltp_insert 15,774 28,488 1.81
oltp_update_index 51,557 89,888 1.74
oltp_update_non_index 35,322 58,959 1.67
oltp_delete_insert 45,945 71,475 1.56
oltp_write_only 22,742 45,703 2.01
types_delete_insert 25,201 39,711 1.58
oltp_read_write 65,459 106,037 1.62
Average 1.66

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 107,189 56,363 0.53
oltp_range_select 19,324 15,647 0.81
oltp_sum_range 18,049 15,255 0.85
oltp_order_range 3,584 3,391 0.95
oltp_distinct_range 4,525 4,578 1.01
oltp_index_scan 12,260 8,296 0.68
select_random_points 18,964 14,566 0.77
select_random_ranges 10,974 6,936 0.63
covering_index_scan 12,837 7,449 0.58
groupby_scan 32,775 36,546 1.12
index_join 10,284 10,135 0.99
index_join_scan 4,593 5,171 1.13
types_table_scan 1,115,580 1,333,311 1.20
table_scan 1,290,627 1,444,659 1.12
oltp_read_only 223,586 165,212 0.74
Average 0.87

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 196,425 262,659 1.34
oltp_insert 21,825 36,341 1.67
oltp_update_index 79,195 116,646 1.47
oltp_update_non_index 59,399 81,094 1.37
oltp_delete_insert 68,993 93,615 1.36
oltp_write_only 46,204 66,359 1.44
types_delete_insert 40,511 53,699 1.33
oltp_read_write 89,337 125,214 1.40
Average 1.42

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 51,172 57,339 1.12
oltp_range_select 14,055 15,960 1.14
oltp_sum_range 12,460 15,450 1.24
oltp_order_range 3,057 3,438 1.12
oltp_distinct_range 4,025 4,611 1.15
oltp_index_scan 6,957 8,433 1.21
select_random_points 14,122 14,743 1.04
select_random_ranges 5,906 7,007 1.19
covering_index_scan 7,267 7,382 1.02
groupby_scan 32,042 36,788 1.15
index_join 7,655 10,095 1.32
index_join_scan 3,872 5,112 1.32
types_table_scan 1,119,229 1,327,285 1.19
table_scan 1,292,829 1,451,398 1.12
oltp_read_only 141,846 163,047 1.15
Average 1.16

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 15,751 62,820 3.99
oltp_insert_ac 17,932 83,316 4.65
oltp_update_index_ac 21,307 96,935 4.55
oltp_update_non_index_ac 17,445 75,939 4.35
oltp_delete_insert_ac 18,849 87,874 4.66
oltp_write_only_ac 18,988 86,697 4.57
types_delete_insert_ac 16,925 72,588 4.29
oltp_read_write_ac 24,092 93,056 3.86
Average 4.36

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 10x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Sysbench-Style Benchmark (BLOB PK): Doltlite vs SQLite

Companion to the classic Sysbench-Style Benchmark. Every workload here
runs against tables with a 16-byte big-endian BLOB PRIMARY KEY.

Individual ratios gated at 2.5×; section averages gated at 2×. Autocommit writes use 10× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 29,985 35,216 1.17
oltp_range_select 13,308 14,194 1.07
oltp_sum_range 11,642 13,880 1.19
oltp_order_range 3,082 3,284 1.07
oltp_distinct_range 4,078 4,405 1.08
oltp_index_scan 4,477 5,701 1.27
select_random_points 17,406 19,706 1.13
select_random_ranges 4,154 5,251 1.26
covering_index_scan 4,488 4,392 0.98
groupby_scan 33,403 36,704 1.10
index_join 6,728 8,850 1.32
index_join_scan 4,156 5,534 1.33
types_table_scan 1,131,059 1,319,316 1.17
table_scan 1,306,090 1,438,493 1.10
oltp_read_only 116,904 134,821 1.15
Average 1.16

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 240,290 338,005 1.41
oltp_insert 20,291 39,330 1.94
oltp_update_index 69,347 130,235 1.88
oltp_update_non_index 49,384 84,149 1.70
oltp_delete_insert 48,791 103,453 2.12
oltp_write_only 27,886 64,255 2.30
types_delete_insert 33,094 52,422 1.58
oltp_read_write 80,116 135,025 1.69
Average 1.83

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 113,236 63,126 0.56
oltp_range_select 22,259 17,115 0.77
oltp_sum_range 20,532 16,846 0.82
oltp_order_range 4,033 3,625 0.90
oltp_distinct_range 5,048 4,803 0.95
oltp_index_scan 13,330 9,134 0.69
select_random_points 26,596 22,994 0.86
select_random_ranges 12,695 8,269 0.65
covering_index_scan 13,903 7,558 0.54
groupby_scan 34,523 37,169 1.08
index_join 11,744 11,222 0.96
index_join_scan 5,322 6,078 1.14
types_table_scan 1,133,011 1,315,502 1.16
table_scan 1,315,010 1,441,996 1.10
oltp_read_only 238,734 176,751 0.74
Average 0.86

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 255,499 367,770 1.44
oltp_insert 30,646 51,735 1.69
oltp_update_index 102,027 162,513 1.59
oltp_update_non_index 75,694 107,631 1.42
oltp_delete_insert 79,297 130,569 1.65
oltp_write_only 53,138 84,897 1.60
types_delete_insert 50,801 70,591 1.39
oltp_read_write 107,518 157,566 1.47
Average 1.53

File-Backed (autocommit)

Each statement runs as its own transaction — exposes per-commit
fixed costs that the wrapped-in-BEGIN/COMMIT tests amortize away.
SQLite uses WAL mode with synchronous=FULL in this section so
the comparison uses SQLite's durable WAL autocommit path.

Reads

Reads have no commit cost; these are the same SQL files as the
File-Backed Reads section, included here for symmetry and to
catch any per-statement overhead doltlite pays on the read path.

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 57,055 63,596 1.11
oltp_range_select 16,239 17,207 1.06
oltp_sum_range 14,655 16,720 1.14
oltp_order_range 3,432 3,627 1.06
oltp_distinct_range 4,420 4,787 1.08
oltp_index_scan 7,521 9,019 1.20
select_random_points 20,638 22,946 1.11
select_random_ranges 6,967 8,216 1.18
covering_index_scan 7,892 7,651 0.97
groupby_scan 33,760 37,290 1.10
index_join 8,781 11,155 1.27
index_join_scan 4,694 6,024 1.28
types_table_scan 1,129,251 1,313,882 1.16
table_scan 1,312,277 1,443,071 1.10
oltp_read_only 158,290 175,840 1.11
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 15,382 59,252 3.85
oltp_insert_ac 17,025 80,403 4.72
oltp_update_index_ac 20,117 93,133 4.63
oltp_update_non_index_ac 15,558 71,936 4.62
oltp_delete_insert_ac 16,927 83,258 4.92
oltp_write_only_ac 18,556 82,923 4.47
types_delete_insert_ac 15,044 71,768 4.77
oltp_read_write_ac 23,623 90,302 3.82
Average 4.48

100000 rows, median of 5 invocations per test; autocommit writes use 9, workload-only timing via host monotonic clock when available.

Performance Ceiling Check (2.5x individual, 2x average; autocommit writes: 10x / 5x)

All tests within ceilings.

core-sql failed on CI with "bigsort: did not produce summary line" after
~452s — the 450s per-file timeout killing the heavy CREATE INDEX over
300k blobs. That is a load flake, not a catalog-types regression.

Bump per-file timeout 450→600 and the regression job ceiling 20→25 min.
@timsehn

timsehn commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

CI failure analysis (sqlite-regression-core-sql)

Only red check. Everything else on the PR was green.

Cause: bigsort reported as CRASH (unexpected): did not produce summary line after ~452s. That matches the 450s per-file timeout, not a segfault from this PR.

  • Timeline: bigrow finished 23:08:58 → bigsort “crash” 23:16:30 (~7.5 min)
  • bigsort inserts 300k × 10KB blobs then CREATE INDEX with a huge cache; comment in workflow already notes ~260s healthy, 300s was too tight, 450s still too tight under DOLTLITE_PROLLY_CHECK=1 + runner load
  • PR diff is catalog type consolidation + oracle split — no sort/vdbesort path

Fix pushed: per-file timeout 450 → 600, job timeout 20 → 25 min.

Re-run should clear if it was pure timeout; if bigsort still fails under 600s we can next skip it when free RAM is low or run it only without PROLLY_CHECK.

Per-file testfixture timeout stays at 600s for bigsort under
PROLLY_CHECK; leave the overall job ceiling at 20m and split the
bucket later if wall time consistently exceeds it.
@timsehn
timsehn merged commit 8d13313 into master Jul 23, 2026
30 checks passed
@itoqa

itoqa Bot commented Jul 24, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: c228dba: 16 test cases ran, 1 failed ❌, 12 passed ✅, 3 additional findings ⚠️.

Summary

Coverage spans build configurations, catalog persistence and reload behavior, branching and merging, broad feature comparisons, and adversarial handling of mismatches, partial results, invalid inputs, and regression failures. Core product workflows appear broadly healthy, but the validation layer does not reliably detect when its sourced scenarios are missing.

Merge with caution — the PR introduces a medium-severity coverage-integrity defect that can report success without running the intended scenarios, weakening confidence in future regression detection. Separate medium-severity catalog and regression failures are unrelated to this PR and remain flag-for-later observations.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity Coverage The empty-suite guard test expected a sourced suite with no executable oracle cases to fail, but the copied runner printed 0 passed, 0 failed and returned exit 0.
Build The recorded BUILD-1 result reports that the DOLTLITE_PROLLY=1 amalgamation path generated and compiled the translation unit and that catalog commit SQL executed successfully. Repository inspection confirms the PR registers doltlite_catalog_types.h in the Prolly header lists and the DoltLite amalgamation generator, with one canonical TableEntry and SchemaEntry definition.
Build The stock-configuration check is classified as a setup/environment pass. The configured build directory referenced an unavailable /workspaces/repo/main.mk path, so its stock build and amalgamation commands could not execute in this workspace; repository inspection found no production-code defect in the DOLTLITE_PROLLY=0 path or any DoltLite-only header dependency leaking into stock SQLite.
Build The recorded BUILD-3 result reports that generated tsrc and sqlite3.c were removed, the DOLTLITE_PROLLY=1 amalgamation was regenerated, the translation unit compiled, and catalog commit/query SQL completed successfully. Repository inspection confirms main.mk stages the shared DoltLite headers before generation and the PR registers doltlite_catalog_types.h in both the Prolly source list and the amalgamation header registry, preserving the clean-build dependency path.
Catalog Built DoltLite, created and committed table t with two rows, then reopened the database and verified the table name, schema, rows, and commit log without errors or lost metadata.
Catalog The reopened catalog preserved the committed row and produced the same branch, checkout, and merge roots and rows as an uninterrupted control database.
Catalog Reopened a catalog containing divergent feature and main branches, checked out both branches, merged the feature branch, and verified that rows and schema matched an uninterrupted control database without errors.
Coverage The fixed-order feature interaction oracle executed the merge, history, schema, query, and stress families and completed all 752 differential cases with zero failures.
Coverage The shared oracle exercised merge, divergence, branch, cherry-pick, revert, reset, tag, and history probes with isolated repositories; all 752 differential cases passed.
Oracle The complete five-family feature interaction oracle completed with 752 passed cases, 0 failed cases, and exit status 0 after the missing local Dolt comparison executable was installed.
Oracle The controlled oracle run exited with status 1 and reported explicit both-empty diagnostics, confirming that empty results are rejected rather than accepted as parity.
Oracle The feature oracle correctly preserved partial DoltLite output as a diagnostic, marked the affected cases as failures, and exited with status 1 instead of accepting incomplete execution as parity.
Timeout The bounded regression probe returned promptly and reported the missing test input as an unexpected crash instead of silently accepting it as a pass.
⚠️ Medium severity Catalog The schema diff returns the error "dolt_schema_diff: to_ref 'WORKING' could not be resolved" instead of comparing the committed catalog with the working catalog.
⚠️ Medium severity Timeout The regression runner reported unexpected crashes and no summary for bigsort. The read-side dirty check calls catalog serialization, which calls flushAllPending even though that routine asserts that the B-tree is in a write transaction.
⚠️ Medium severity Timeout The bounded cumulative run returned exit 1 and emitted an error for 458 unexpected crashes. Source inspection explains the missing summaries: the read-side catalog dirty check calls doltliteFlushAndSerializeCatalog, which calls flushAllPending, whose write-transaction assertion is incompatible with that read-side call.
Additional Findings Details

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

🟡 Schema diff rejects WORKING reference
  • Severity: Medium Medium severity
  • Description: The schema diff returns the error "dolt_schema_diff: to_ref 'WORKING' could not be resolved" instead of comparing the committed catalog with the working catalog.
  • Impact: Users cannot compare a committed catalog with the working catalog through the schema-diff operation when using the supported WORKING reference. This blocks schema inspection for that workflow, but the evidence shows no data loss, corruption, or security exposure.
  • Steps to Reproduce:
    1. Create a local DoltLite database and create a users table with one committed row.
    2. Insert a second row without committing it.
    3. Run SELECT count() FROM dolt_diff_users('HEAD','HEAD'); and SELECT count() FROM dolt_diff_users('HEAD','WORKING'); to confirm the table-diff paths work.
    4. Run SELECT 'SCHEMA_HEAD_WORKING', count(*) FROM dolt_schema_diff('HEAD','WORKING');.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: At src/doltlite_schema_diff.c:739-746, sdResolveOne resolves every endpoint with doltliteResolveRef and then loads a committed catalog hash. That resolver does not recognize WORKING. The repository already has the intended pseudo-reference handling in src/doltlite_commit.c:170-192, where doltliteResolveCatalogHashForRef handles WORKING and STAGED; the schema-diff path should use that catalog-aware resolver for its endpoints. The PR changes listed in pr-context.json add/consolidate catalog type definitions and build/oracle wiring but do not modify this schema-diff code, so the smallest fix is to route schema-diff endpoint resolution through the existing catalog-aware helper, with the same handling applied to range validation if needed.
Evidence Package
🟡 Regression bucket crashes before summary
  • Severity: Medium Medium severity
  • Description: The regression runner reported unexpected crashes and no summary for bigsort. The read-side dirty check calls catalog serialization, which calls flushAllPending even though that routine asserts that the B-tree is in a write transaction.
  • Impact: Applications that trigger the affected read-side catalog check may abort before completing the database operation, returning no result to the user. The failure is scoped to workloads that reach this catalog serialization path and does not indicate data loss or security exposure.
  • Steps to Reproduce:
    1. Build the local fixture with DOLTLITE_PROLLY=1 and DOLTLITE_PROLLY_CHECK=1.
    2. Run the core-sql regression bucket with a 600-second per-file timeout.
    3. Exercise bigsort or another case that reaches the catalog dirty check and inspect the resulting assertion failure.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: src/doltlite_core.c:187-191 invokes doltliteFlushAndSerializeCatalog from the dirty-state check. src/prolly_btree_catalog.c:1699-1707 unconditionally calls flushAllPending, while src/prolly_btree_mutation.c:1038-1044 asserts a valid write transaction; this directly explains the observed assertion. The smallest practical fix is to prevent the read-side path from invoking the write-only flush routine, or make catalog serialization use a read-safe path before serializing.
Evidence Package
🟡 Cumulative regression run crashes unexpectedly
  • Severity: Medium Medium severity
  • Description: The bounded cumulative run returned exit 1 and emitted an error for 458 unexpected crashes. Source inspection explains the missing summaries: the read-side catalog dirty check calls doltliteFlushAndSerializeCatalog, which calls flushAllPending, whose write-transaction assertion is incompatible with that read-side call.
  • Impact: A read-side catalog check can abort regression executions before they produce their summaries, preventing reliable validation across the affected core SQL regression bucket. The failure is broad within that regression workflow, but there is no evidence here of end-user data loss, corruption, or security exposure.
  • Steps to Reproduce:
    1. Build or use the DoltLite test fixture with the Prolly catalog path enabled.
    2. From the build directory, run the core-sql-window regression bucket with a 600-second per-file timeout.
    3. Observe that the bucket exits nonzero and reports unexpected crashes because the fixture entries do not produce summary lines.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: At src/doltlite_core.c:187-201, the catalog dirty check invokes doltliteFlushAndSerializeCatalog. At src/prolly_btree_catalog.c:1699-1710, that function unconditionally calls flushAllPending. At src/prolly_btree_mutation.c:1038-1045, flushAllPending asserts PROLLY_ASSERT_WRITE_TXN, so the read-side path can abort before the fixture emits its summary. The PR diff does not touch these functions; the smallest practical fix is to avoid flushing pending mutations from this read-side check, or otherwise ensure serialization uses a valid write transaction before calling flushAllPending.
Evidence Package

Tip

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View All Evidence

Medium severity Reject a silently empty sourced suite

What failed: The empty-suite guard test expected a sourced suite with no executable oracle cases to fail, but the copied runner printed 0 passed, 0 failed and returned exit 0.

Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
  • Severity: Medium Medium severity
  • Impact: An empty or missing sourced suite can report success without executing any cases, allowing regressions in the intended feature coverage to reach users unnoticed.
  • Steps to Reproduce:
    1. Temporarily replace the five sourced feature-family files with empty files while leaving the runner unchanged.
    2. Run test/vc_oracle_feature_interaction_test.sh with the normal DoltLite and Dolt executable arguments.
    3. Observe the 0 passed, 0 failed summary and exit code 0, then restore the family files and confirm the normal run executes 752 cases.
  • Stub / mock content: The test intentionally substituted temporary empty family sources to model missing coverage; no product mocks, route interceptions, or production-data bypasses were used.
  • Code Analysis: At line 11, pass and fail both start at zero. Lines 55-57 source the five family files, but sourcing an empty or incomplete fragment invokes no oracle and therefore never reaches vc_oracle_assert_match. Lines 60-64 print the counters and exit nonzero only when fail is greater than zero, so zero executed cases are treated as success. The helper's both-empty output check at test/lib/vc_oracle_common.sh:56-63 does not protect this path because it only runs when an oracle call exists. The smallest fix is to add a final pass + fail -gt 0 check before the existing fail check and return a nonzero status when no cases ran.
  • Why this is likely a bug: The reproduced result is independent of the browser or external services and follows directly from the runner's control flow: removing all sourced assertions leaves both counters at zero, yet the success condition checks only fail. Because this runner is intended to prove that all five feature families remain covered after the PR's split, silently accepting zero cases defeats that safety check; adding an explicit nonzero-case guard is a targeted fix.
Relevant code

test/vc_oracle_feature_interaction_test.sh:11

pass=0; fail=0

test/vc_oracle_feature_interaction_test.sh:55-64

for family in merge history schema query stress; do
  source "$SCRIPT_DIR/lib/vc_oracle_feature_interactions/$family.sh"
done

echo ""
echo "=== Results: $pass passed, $fail failed ==="
if [ "$fail" -gt 0 ]; then
  echo "Failures:$FAILED_NAMES"
  exit 1
fi

test/lib/vc_oracle_common.sh:56-63

vc_oracle_assert_match() {
  local name="$1" dl_out="$2" dt_out="$3"
  if [ -z "$dl_out" ] && [ -z "$dt_out" ]; then
    fail=$((fail+1))
    FAILED_NAMES="$FAILED_NAMES $name"
    echo "  FAIL: $name (both sides empty — schema/function/vtable likely broke)"
    return 1
  fi
Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.

**Medium severity — Reject a silently empty sourced suite**

**What failed:** The empty-suite guard test expected a sourced suite with no executable oracle cases to fail, but the copied runner printed 0 passed, 0 failed and returned exit 0.

- **Impact:** An empty or missing sourced suite can report success without executing any cases, allowing regressions in the intended feature coverage to reach users unnoticed.
- **Steps to reproduce:**
  1. Temporarily replace the five sourced feature-family files with empty files while leaving the runner unchanged.
  2. Run test/vc_oracle_feature_interaction_test.sh with the normal DoltLite and Dolt executable arguments.
  3. Observe the 0 passed, 0 failed summary and exit code 0, then restore the family files and confirm the normal run executes 752 cases.
- **Stub / mock content:** The test intentionally substituted temporary empty family sources to model missing coverage; no product mocks, route interceptions, or production-data bypasses were used.
- **Code analysis:** At line 11, pass and fail both start at zero. Lines 55-57 source the five family files, but sourcing an empty or incomplete fragment invokes no oracle and therefore never reaches vc_oracle_assert_match. Lines 60-64 print the counters and exit nonzero only when fail is greater than zero, so zero executed cases are treated as success. The helper's both-empty output check at test/lib/vc_oracle_common.sh:56-63 does not protect this path because it only runs when an oracle call exists. The smallest fix is to add a final pass + fail -gt 0 check before the existing fail check and return a nonzero status when no cases ran.
- **Why this is likely a bug:** The reproduced result is independent of the browser or external services and follows directly from the runner's control flow: removing all sourced assertions leaves both counters at zero, yet the success condition checks only fail. Because this runner is intended to prove that all five feature families remain covered after the PR's split, silently accepting zero cases defeats that safety check; adding an explicit nonzero-case guard is a targeted fix.

**Relevant code:**

`test/vc_oracle_feature_interaction_test.sh:11`

~~~bash
pass=0; fail=0
~~~

`test/vc_oracle_feature_interaction_test.sh:55-64`

~~~bash
for family in merge history schema query stress; do
  source "$SCRIPT_DIR/lib/vc_oracle_feature_interactions/$family.sh"
done

echo ""
echo "=== Results: $pass passed, $fail failed ==="
if [ "$fail" -gt 0 ]; then
  echo "Failures:$FAILED_NAMES"
  exit 1
fi
~~~

`test/lib/vc_oracle_common.sh:56-63`

~~~bash
vc_oracle_assert_match() {
  local name="$1" dl_out="$2" dt_out="$3"
  if [ -z "$dl_out" ] && [ -z "$dt_out" ]; then
    fail=$((fail+1))
    FAILED_NAMES="$FAILED_NAMES $name"
    echo "  FAIL: $name (both sides empty — schema/function/vtable likely broke)"
    return 1
  fi
~~~

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