Skip to content

Merge oracle coverage for schema objects; fix both-drop resurrection#1663

Merged
timsehn merged 1 commit into
masterfrom
test/merge-oracle-schema-objects
Jul 16, 2026
Merged

Merge oracle coverage for schema objects; fix both-drop resurrection#1663
timsehn merged 1 commit into
masterfrom
test/merge-oracle-schema-objects

Conversation

@timsehn

@timsehn timsehn commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Fills the merge × schema-objects oracle gap (follow-on to the #1657 add-oracle coverage).

Coverage added (19 scenarios, live-Dolt comparison)

  • Views: add ours/theirs, both-add identical (clean) and different (conflict), modify theirs, both-modify different (conflict), modify-vs-drop (conflict), both-drop, and in-transaction conflict inspection (dolt_conflicts count parity under BEGIN).
  • Triggers: add-theirs fires post-merge, both-add identical fires once, both-add different (conflict), modify-vs-drop (conflict), both-drop stops firing. Trigger bodies can't share one script across dialects, so these use new oracle_dual_poststate/oracle_dual_error helpers with per-system setups and a shared post-state probe.
  • Indexes: add-theirs, both-add same/different names, same-name different definition (conflict), and drop-vs-data-change (dual setup for DROP INDEX dialect).

Bug found and fixed

merge_view_both_drop and merge_trigger_both_drop failed on master: when both branches dropped a view or trigger, mergedSchemaChoice fell through to the ancestor entry and the merged catalog resurrected the dropped object. Dolt keeps it dropped. Fixed by returning no entry when both sides changed the name and neither side still has it — the same chokepoint serves hidden autoindex rows, so those can't resurrect either.

Tests

Both new both-drop scenarios fail before the fix and pass after. Full local regression green: battery 88/88 suites, C corpus 309,830/309,830, merge oracle 67/67, schema_merge 37/37, fk_merge 39/39, conflicts 9/9, index×VC matrix 38/38.

🤖 Generated with Claude Code

The merge oracle had no schema-object scenarios. Add nineteen: view
add/modify/drop on either or both sides, trigger firing after merge,
index merges including same-name divergence, and in-transaction
conflict inspection, with dual per-dialect setups where trigger bodies
and DROP INDEX cannot share one script.

The both-sides-dropped scenarios caught a real bug: the merged catalog
rebuild fell back to the ancestor schema entry when a view or trigger
was deleted on both branches, resurrecting the dropped object in the
merge commit. Dolt keeps it dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

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 90 200 PASS
status_dirty_many_tables 93 200 PASS
diff_regular_working_one_table 85 150 PASS
diff_regular_working_many_tables 98 200 PASS
diff_stat_working_many_tables 98 200 PASS
diff_schema_working_many_tables 99 200 PASS
branch_list_many_branches 24 100 PASS
branch_create_delete 26 100 PASS
checkout_branch_clean 58 200 PASS
merge_data_no_conflicts 31 150 PASS
merge_schema_no_conflicts 23 100 PASS
merge_data_conflicts 129 250 PASS
merge_data_conflicts_with_resolve 128 250 PASS

@github-actions

Copy link
Copy Markdown

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 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 33,010 40,560 1.23
oltp_range_select 18,488 21,525 1.16
oltp_sum_range 17,763 20,700 1.17
oltp_order_range 3,528 3,863 1.09
oltp_distinct_range 4,633 4,837 1.04
oltp_index_scan 4,727 6,132 1.30
select_random_points 28,003 32,066 1.15
select_random_ranges 7,720 9,071 1.18
covering_index_scan 4,171 4,410 1.06
groupby_scan 36,148 38,651 1.07
index_join 8,203 10,690 1.30
index_join_scan 4,051 5,548 1.37
types_table_scan 1,125,593 1,424,732 1.27
table_scan 1,395,621 1,574,033 1.13
oltp_read_only 154,866 173,129 1.12
Average 1.18

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 247,124 367,030 1.49
oltp_insert 19,311 37,554 1.94
oltp_update_index 71,055 124,337 1.75
oltp_update_non_index 52,682 86,246 1.64
oltp_delete_insert 51,932 100,954 1.94
oltp_write_only 28,079 60,144 2.14
types_delete_insert 33,413 57,639 1.73
oltp_read_write 108,169 163,527 1.51
Average 1.77

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 103,880 65,751 0.63
oltp_range_select 26,406 24,392 0.92
oltp_sum_range 25,695 23,727 0.92
oltp_order_range 4,292 4,220 0.98
oltp_distinct_range 5,497 5,228 0.95
oltp_index_scan 11,978 9,075 0.76
select_random_points 38,683 37,757 0.98
select_random_ranges 15,137 11,869 0.78
covering_index_scan 11,447 7,082 0.62
groupby_scan 36,994 38,861 1.05
index_join 12,080 12,823 1.06
index_join_scan 5,015 5,923 1.18
types_table_scan 1,063,876 1,395,511 1.31
table_scan 1,323,121 1,550,520 1.17
oltp_read_only 258,048 209,693 0.81
Average 0.94

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 259,012 380,236 1.47
oltp_insert 25,441 46,320 1.82
oltp_update_index 94,919 142,127 1.50
oltp_update_non_index 75,751 105,429 1.39
oltp_delete_insert 74,757 119,261 1.60
oltp_write_only 50,437 79,014 1.57
types_delete_insert 50,623 69,561 1.37
oltp_read_write 131,974 182,062 1.38
Average 1.51

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 58,160 65,595 1.13
oltp_range_select 21,111 24,429 1.16
oltp_sum_range 20,595 23,695 1.15
oltp_order_range 3,885 4,238 1.09
oltp_distinct_range 5,005 5,265 1.05
oltp_index_scan 7,409 9,018 1.22
select_random_points 32,754 37,725 1.15
select_random_ranges 10,473 11,802 1.13
covering_index_scan 6,881 7,200 1.05
groupby_scan 36,562 38,501 1.05
index_join 9,896 12,916 1.31
index_join_scan 4,520 5,848 1.29
types_table_scan 1,041,885 1,390,519 1.33
table_scan 1,229,979 1,531,424 1.25
oltp_read_only 180,399 206,950 1.15
Average 1.17

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 24,180 82,408 3.41
oltp_insert_ac 25,483 101,404 3.98
oltp_update_index_ac 27,473 113,059 4.12
oltp_update_non_index_ac 24,505 93,344 3.81
oltp_delete_insert_ac 29,009 104,192 3.59
oltp_write_only_ac 26,624 103,805 3.90
types_delete_insert_ac 21,730 90,599 4.17
oltp_read_write_ac 31,187 108,559 3.48
Average 3.81

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: 6x / 5x)

All tests within ceilings.

@github-actions

Copy link
Copy Markdown

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 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 29,959 35,245 1.18
oltp_range_select 13,427 13,485 1.00
oltp_sum_range 11,725 12,762 1.09
oltp_order_range 3,097 3,206 1.04
oltp_distinct_range 4,153 4,287 1.03
oltp_index_scan 4,486 5,875 1.31
select_random_points 17,497 19,700 1.13
select_random_ranges 4,159 5,267 1.27
covering_index_scan 4,519 4,385 0.97
groupby_scan 33,877 34,809 1.03
index_join 6,861 8,915 1.30
index_join_scan 4,348 5,643 1.30
types_table_scan 1,127,983 1,265,358 1.12
table_scan 1,302,865 1,371,146 1.05
oltp_read_only 118,778 129,616 1.09
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 240,554 336,533 1.40
oltp_insert 20,476 39,225 1.92
oltp_update_index 68,433 129,745 1.90
oltp_update_non_index 49,935 83,926 1.68
oltp_delete_insert 49,217 102,393 2.08
oltp_write_only 28,180 62,870 2.23
types_delete_insert 32,746 52,429 1.60
oltp_read_write 80,476 131,827 1.64
Average 1.81

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 112,904 63,885 0.57
oltp_range_select 21,335 16,518 0.77
oltp_sum_range 19,847 16,077 0.81
oltp_order_range 3,859 3,550 0.92
oltp_distinct_range 4,869 4,553 0.94
oltp_index_scan 12,754 9,097 0.71
select_random_points 25,454 23,271 0.91
select_random_ranges 12,588 8,158 0.65
covering_index_scan 13,036 7,733 0.59
groupby_scan 34,444 35,404 1.03
index_join 11,366 11,389 1.00
index_join_scan 5,164 6,162 1.19
types_table_scan 1,128,166 1,248,645 1.11
table_scan 1,316,059 1,361,452 1.03
oltp_read_only 238,342 170,207 0.71
Average 0.86

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 258,469 359,501 1.39
oltp_insert 30,682 52,670 1.72
oltp_update_index 103,749 165,486 1.60
oltp_update_non_index 75,794 109,885 1.45
oltp_delete_insert 80,382 133,313 1.66
oltp_write_only 53,336 87,443 1.64
types_delete_insert 51,127 71,587 1.40
oltp_read_write 110,592 156,693 1.42
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 58,656 63,934 1.09
oltp_range_select 16,915 16,706 0.99
oltp_sum_range 15,648 16,126 1.03
oltp_order_range 3,592 3,561 0.99
oltp_distinct_range 4,485 4,554 1.02
oltp_index_scan 7,659 9,317 1.22
select_random_points 20,774 23,463 1.13
select_random_ranges 7,144 8,359 1.17
covering_index_scan 8,205 7,734 0.94
groupby_scan 34,232 35,321 1.03
index_join 8,844 11,489 1.30
index_join_scan 4,919 6,356 1.29
types_table_scan 1,135,359 1,256,269 1.11
table_scan 1,328,374 1,380,715 1.04
oltp_read_only 158,682 172,171 1.09
Average 1.10

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 16,022 64,231 4.01
oltp_insert_ac 17,975 84,100 4.68
oltp_update_index_ac 19,945 96,493 4.84
oltp_update_non_index_ac 16,774 74,914 4.47
oltp_delete_insert_ac 17,672 87,010 4.92
oltp_write_only_ac 18,399 86,793 4.72
types_delete_insert_ac 15,706 76,226 4.85
oltp_read_write_ac 23,921 92,311 3.86
Average 4.54

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: 6x / 5x)

All tests within ceilings.

@github-actions

Copy link
Copy Markdown

Sysbench-Style Benchmark: Doltlite vs SQLite

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 24,176 29,532 1.22
oltp_range_select 9,591 12,557 1.31
oltp_sum_range 9,219 12,030 1.30
oltp_order_range 2,478 2,890 1.17
oltp_distinct_range 3,538 3,858 1.09
oltp_index_scan 3,949 5,233 1.33
select_random_points 9,877 10,905 1.10
select_random_ranges 2,864 4,009 1.40
covering_index_scan 4,113 4,144 1.01
groupby_scan 29,322 32,179 1.10
index_join 5,934 7,933 1.34
index_join_scan 3,460 4,527 1.31
types_table_scan 1,050,687 1,395,560 1.33
table_scan 1,219,407 1,514,228 1.24
oltp_read_only 102,887 120,403 1.17
Average 1.23

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 180,973 253,104 1.40
oltp_insert 15,714 29,688 1.89
oltp_update_index 51,418 95,046 1.85
oltp_update_non_index 36,486 61,506 1.69
oltp_delete_insert 45,512 71,049 1.56
oltp_write_only 22,323 44,994 2.02
types_delete_insert 25,029 40,246 1.61
oltp_read_write 68,529 110,216 1.61
Average 1.70

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 92,871 54,105 0.58
oltp_range_select 17,784 15,090 0.85
oltp_sum_range 16,182 14,741 0.91
oltp_order_range 3,294 3,175 0.96
oltp_distinct_range 4,367 4,211 0.96
oltp_index_scan 10,790 8,085 0.75
select_random_points 17,153 13,663 0.80
select_random_ranges 9,812 6,533 0.67
covering_index_scan 11,160 7,028 0.63
groupby_scan 30,250 32,570 1.08
index_join 10,351 10,058 0.97
index_join_scan 4,558 5,134 1.13
types_table_scan 1,103,050 1,407,111 1.28
table_scan 1,222,699 1,519,291 1.24
oltp_read_only 199,224 153,634 0.77
Average 0.91

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 199,585 274,340 1.37
oltp_insert 21,855 37,255 1.70
oltp_update_index 83,910 118,904 1.42
oltp_update_non_index 59,322 83,200 1.40
oltp_delete_insert 67,763 95,074 1.40
oltp_write_only 43,876 68,043 1.55
types_delete_insert 41,568 54,173 1.30
oltp_read_write 91,156 134,194 1.47
Average 1.45

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 47,668 53,930 1.13
oltp_range_select 14,025 16,208 1.16
oltp_sum_range 13,602 15,129 1.11
oltp_order_range 3,802 3,646 0.96
oltp_distinct_range 4,428 4,409 1.00
oltp_index_scan 6,674 8,458 1.27
select_random_points 14,491 14,202 0.98
select_random_ranges 5,784 6,753 1.17
covering_index_scan 7,695 7,118 0.93
groupby_scan 32,346 34,353 1.06
index_join 8,085 10,333 1.28
index_join_scan 4,275 5,399 1.26
types_table_scan 1,073,373 1,388,363 1.29
table_scan 1,186,112 1,518,153 1.28
oltp_read_only 142,579 157,884 1.11
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 21,409 80,895 3.78
oltp_insert_ac 25,129 108,712 4.33
oltp_update_index_ac 26,117 116,286 4.45
oltp_update_non_index_ac 24,348 88,837 3.65
oltp_delete_insert_ac 25,256 100,412 3.98
oltp_write_only_ac 23,523 97,405 4.14
types_delete_insert_ac 22,769 93,106 4.09
oltp_read_write_ac 28,077 110,178 3.92
Average 4.04

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: 6x / 5x)

All tests within ceilings.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

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 6× / 5×.

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 31,569 38,324 1.21
oltp_range_select 13,073 14,622 1.12
oltp_sum_range 12,719 14,370 1.13
oltp_order_range 3,084 3,151 1.02
oltp_distinct_range 4,048 4,171 1.03
oltp_index_scan 4,557 6,401 1.40
select_random_points 19,729 21,863 1.11
select_random_ranges 4,211 5,428 1.29
covering_index_scan 5,007 4,913 0.98
groupby_scan 32,214 33,564 1.04
index_join 6,907 9,377 1.36
index_join_scan 4,423 5,297 1.20
types_table_scan 1,109,464 1,388,073 1.25
table_scan 1,410,155 1,539,100 1.09
oltp_read_only 129,209 140,389 1.09
Average 1.15

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 234,046 361,419 1.54
oltp_insert 23,286 40,634 1.74
oltp_update_index 78,692 143,167 1.82
oltp_update_non_index 50,646 90,118 1.78
oltp_delete_insert 54,698 107,644 1.97
oltp_write_only 30,333 64,033 2.11
types_delete_insert 34,105 57,290 1.68
oltp_read_write 95,284 145,881 1.53
Average 1.77

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 101,520 63,453 0.63
oltp_range_select 21,406 17,376 0.81
oltp_sum_range 20,877 16,816 0.81
oltp_order_range 4,027 3,558 0.88
oltp_distinct_range 4,973 4,549 0.91
oltp_index_scan 12,134 9,158 0.75
select_random_points 28,957 25,579 0.88
select_random_ranges 11,423 7,841 0.69
covering_index_scan 13,350 7,498 0.56
groupby_scan 33,455 34,259 1.02
index_join 11,976 11,409 0.95
index_join_scan 5,650 5,832 1.03
types_table_scan 1,253,751 1,417,269 1.13
table_scan 1,520,502 1,549,853 1.02
oltp_read_only 236,677 177,338 0.75
Average 0.86

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 249,326 395,415 1.59
oltp_insert 43,311 53,265 1.23
oltp_update_index 119,166 175,739 1.47
oltp_update_non_index 90,888 114,058 1.25
oltp_delete_insert 91,761 135,634 1.48
oltp_write_only 98,671 85,421 0.87
types_delete_insert 56,031 76,555 1.37
oltp_read_write 145,409 165,809 1.14
Average 1.30

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,172 62,694 1.14
oltp_range_select 17,098 17,235 1.01
oltp_sum_range 15,342 17,002 1.11
oltp_order_range 3,508 3,469 0.99
oltp_distinct_range 4,507 4,657 1.03
oltp_index_scan 7,575 9,160 1.21
select_random_points 21,808 25,869 1.19
select_random_ranges 6,903 7,861 1.14
covering_index_scan 8,407 7,338 0.87
groupby_scan 32,999 34,187 1.04
index_join 10,204 11,408 1.12
index_join_scan 5,151 6,077 1.18
types_table_scan 1,248,830 1,433,303 1.15
table_scan 1,541,438 1,558,950 1.01
oltp_read_only 168,506 179,687 1.07
Average 1.08

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 20,985 75,952 3.62
oltp_insert_ac 24,653 92,069 3.73
oltp_update_index_ac 26,856 110,221 4.10
oltp_update_non_index_ac 21,771 89,951 4.13
oltp_delete_insert_ac 24,665 101,329 4.11
oltp_write_only_ac 23,638 103,093 4.36
types_delete_insert_ac 21,677 92,139 4.25
oltp_read_write_ac 29,393 106,661 3.63
Average 3.99

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: 6x / 5x)

All tests within ceilings.

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