Skip to content

Keep unstaged views and triggers out of named-add commits#1652

Merged
timsehn merged 1 commit into
masterfrom
bug/view-trigger-staging
Jul 16, 2026
Merged

Keep unstaged views and triggers out of named-add commits#1652
timsehn merged 1 commit into
masterfrom
bug/view-trigger-staging

Conversation

@timsehn

@timsehn timsehn commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1648, the last open item from the schema-object staging survey.

Views and triggers are entry-less (schema rows only) and the row filter marks them always-wanted, so commit inclusion was decided entirely by which master root the staged catalog carried — a named dolt_add('t') with a schema change on t adopted the working master wholesale and dragged never-staged views/triggers into the commit; without the schema change they stayed out. Inconsistent, and contrary to Dolt (verified against real Dolt: named adds keep unstaged schema objects out; -a carries them when dolt_schemas is tracked).

The fix composes the staged master instead of adopting it (doltliteBuildNamedStageMasterRoot): table/index rows — including vtabs and shadows — come from the working master so the staged entries keep its numbering domain (the #1616 constraint that forced wholesale adoption in the first place), while view and trigger rows keep the previously staged state.

Verified semantics: the #1648 repro commits the table's schema change without the view/trigger; add -A and commit -am still carry views (Dolt's tracked-dolt_schemas analog); previously staged views survive later named adds; the working state keeps its uncommitted view after the commit; vtab shadow staging (#1647) unaffected.

Gating tests in doltlite_commit.sh fail on master (54/56) and pass with the fix (56/56).

Also split out while verifying: #1651dolt_status is blind to view/trigger-only changes (pre-existing; more visible now that named adds correctly exclude them).

Validation: battery 88/88, C regression 309,830/309,830, index×VC matrix 38/38, oracles vs real Dolt: add 27/27, commit 37/37, reset 43/43.

🤖 Generated with Claude Code

Views and triggers have no catalog entries, and the catalog row filter
marks entry-less rows as always wanted, so their inclusion in a commit
was decided by whichever master root the staged catalog carried: a
named dolt_add whose table had a schema change adopted the working
master wholesale and dragged unstaged view and trigger changes into
the commit, while the same add without a schema change left them out —
inconsistent, and Dolt keeps unstaged schema objects out of named adds.

The staged master is now composed instead of adopted: table and index
rows (including virtual tables and shadows) come from the working
master so the staged entries share its numbering domain, while view
and trigger rows keep the previously staged state. Staging surfaces
that legitimately carry views — add -A and commit -a, matching Dolt's
tracked-dolt_schemas behavior — are unchanged, previously staged views
survive later named adds, and the working state keeps its uncommitted
view after the commit.

Fixes #1648

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 86 150 PASS
diff_regular_working_many_tables 93 200 PASS
diff_stat_working_many_tables 93 200 PASS
diff_schema_working_many_tables 94 200 PASS
branch_list_many_branches 26 100 PASS
branch_create_delete 27 100 PASS
checkout_branch_clean 60 200 PASS
merge_data_no_conflicts 32 150 PASS
merge_schema_no_conflicts 25 100 PASS
merge_data_conflicts 131 250 PASS
merge_data_conflicts_with_resolve 129 250 PASS

@github-actions

Copy link
Copy Markdown

Sysbench-Style Benchmark: Doltlite vs SQLite

In-Memory

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 23,465 29,197 1.24
oltp_range_select 9,620 14,571 1.51
oltp_sum_range 9,203 12,263 1.33
oltp_order_range 2,530 2,904 1.15
oltp_distinct_range 3,526 3,866 1.10
oltp_index_scan 3,832 5,218 1.36
select_random_points 10,424 10,946 1.05
select_random_ranges 2,875 4,015 1.40
covering_index_scan 4,144 4,185 1.01
groupby_scan 29,126 32,777 1.13
index_join 5,957 7,819 1.31
index_join_scan 3,387 4,454 1.32
types_table_scan 1,069,119 1,281,328 1.20
table_scan 1,196,524 1,372,236 1.15
oltp_read_only 102,349 122,016 1.19
Average 1.23

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 181,401 251,626 1.39
oltp_insert 15,283 28,109 1.84
oltp_update_index 49,717 87,865 1.77
oltp_update_non_index 33,115 58,884 1.78
oltp_delete_insert 44,547 70,964 1.59
oltp_write_only 21,522 43,912 2.04
types_delete_insert 23,929 40,098 1.68
oltp_read_write 71,307 111,206 1.56
Average 1.71

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 91,367 53,051 0.58
oltp_range_select 16,636 17,078 1.03
oltp_sum_range 16,656 14,862 0.89
oltp_order_range 3,363 3,270 0.97
oltp_distinct_range 4,339 4,185 0.96
oltp_index_scan 10,704 8,029 0.75
select_random_points 16,563 13,460 0.81
select_random_ranges 9,834 6,435 0.65
covering_index_scan 11,089 6,964 0.63
groupby_scan 30,143 33,150 1.10
index_join 9,783 10,076 1.03
index_join_scan 4,357 4,825 1.11
types_table_scan 1,079,916 1,307,511 1.21
table_scan 1,189,516 1,381,826 1.16
oltp_read_only 213,253 160,115 0.75
Average 0.91

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 194,134 274,821 1.42
oltp_insert 22,215 35,781 1.61
oltp_update_index 76,893 121,079 1.57
oltp_update_non_index 60,372 85,122 1.41
oltp_delete_insert 69,076 98,455 1.43
oltp_write_only 44,443 65,335 1.47
types_delete_insert 41,317 54,728 1.32
oltp_read_write 98,832 136,489 1.38
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 48,117 54,868 1.14
oltp_range_select 13,295 17,272 1.30
oltp_sum_range 12,573 15,140 1.20
oltp_order_range 2,936 3,296 1.12
oltp_distinct_range 4,011 4,265 1.06
oltp_index_scan 6,607 8,314 1.26
select_random_points 13,329 14,346 1.08
select_random_ranges 5,393 6,488 1.20
covering_index_scan 6,604 6,894 1.04
groupby_scan 29,561 33,064 1.12
index_join 7,438 9,869 1.33
index_join_scan 3,894 4,802 1.23
types_table_scan 1,037,849 1,296,084 1.25
table_scan 1,171,489 1,372,872 1.17
oltp_read_only 134,187 160,405 1.20
Average 1.18

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 22,350 87,223 3.90
oltp_insert_ac 27,184 102,354 3.77
oltp_update_index_ac 27,423 117,573 4.29
oltp_update_non_index_ac 24,720 93,972 3.80
oltp_delete_insert_ac 25,811 109,763 4.25
oltp_write_only_ac 26,870 105,272 3.92
types_delete_insert_ac 23,578 98,880 4.19
oltp_read_write_ac 30,986 114,507 3.70
Average 3.98

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 (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 29,591 37,317 1.26
oltp_range_select 12,800 16,166 1.26
oltp_sum_range 11,715 13,950 1.19
oltp_order_range 2,865 3,131 1.09
oltp_distinct_range 3,940 4,239 1.08
oltp_index_scan 4,343 6,166 1.42
select_random_points 17,284 20,985 1.21
select_random_ranges 3,963 5,235 1.32
covering_index_scan 4,368 4,675 1.07
groupby_scan 31,301 34,274 1.09
index_join 6,816 9,101 1.34
index_join_scan 4,516 5,524 1.22
types_table_scan 1,079,639 1,264,661 1.17
table_scan 1,438,701 1,362,910 0.95
oltp_read_only 116,310 138,136 1.19
Average 1.19

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 227,808 350,871 1.54
oltp_insert 21,053 39,352 1.87
oltp_update_index 71,649 132,568 1.85
oltp_update_non_index 48,295 86,302 1.79
oltp_delete_insert 49,915 104,265 2.09
oltp_write_only 28,531 60,432 2.12
types_delete_insert 32,493 54,005 1.66
oltp_read_write 85,897 139,575 1.62
Average 1.82

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 98,021 61,327 0.63
oltp_range_select 19,838 18,718 0.94
oltp_sum_range 18,822 16,588 0.88
oltp_order_range 3,660 3,504 0.96
oltp_distinct_range 4,738 4,580 0.97
oltp_index_scan 11,339 9,100 0.80
select_random_points 25,300 24,861 0.98
select_random_ranges 10,687 7,626 0.71
covering_index_scan 11,568 7,228 0.62
groupby_scan 32,408 34,656 1.07
index_join 11,003 11,268 1.02
index_join_scan 5,501 6,000 1.09
types_table_scan 1,069,759 1,283,592 1.20
table_scan 1,261,544 1,355,111 1.07
oltp_read_only 215,434 175,412 0.81
Average 0.92

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 243,994 373,841 1.53
oltp_insert 47,469 50,862 1.07
oltp_update_index 113,186 164,852 1.46
oltp_update_non_index 97,464 111,138 1.14
oltp_delete_insert 93,811 131,238 1.40
oltp_write_only 95,442 83,444 0.87
types_delete_insert 57,186 73,400 1.28
oltp_read_write 143,673 162,133 1.13
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 52,811 61,649 1.17
oltp_range_select 16,489 18,720 1.14
oltp_sum_range 14,681 16,786 1.14
oltp_order_range 3,378 3,528 1.04
oltp_distinct_range 4,342 4,636 1.07
oltp_index_scan 7,450 9,077 1.22
select_random_points 21,195 24,947 1.18
select_random_ranges 6,545 7,685 1.17
covering_index_scan 7,749 7,244 0.93
groupby_scan 32,031 34,672 1.08
index_join 9,336 11,531 1.24
index_join_scan 5,099 5,971 1.17
types_table_scan 1,055,838 1,283,647 1.22
table_scan 1,232,682 1,360,489 1.10
oltp_read_only 149,345 174,727 1.17
Average 1.14

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 32,349 105,690 3.27
oltp_insert_ac 34,513 123,433 3.58
oltp_update_index_ac 35,254 164,535 4.67
oltp_update_non_index_ac 32,490 128,569 3.96
oltp_delete_insert_ac 34,558 132,822 3.84
oltp_write_only_ac 36,358 132,463 3.64
types_delete_insert_ac 36,346 125,932 3.46
oltp_read_write_ac 41,164 146,839 3.57
Average 3.75

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 (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 25,424 29,245 1.15
oltp_range_select 15,000 16,168 1.08
oltp_sum_range 13,208 15,511 1.17
oltp_order_range 2,876 3,029 1.05
oltp_distinct_range 3,719 3,917 1.05
oltp_index_scan 3,587 4,528 1.26
select_random_points 21,172 23,642 1.12
select_random_ranges 5,819 6,462 1.11
covering_index_scan 3,330 3,195 0.96
groupby_scan 29,990 31,658 1.06
index_join 6,368 7,966 1.25
index_join_scan 3,366 4,677 1.39
types_table_scan 878,536 995,043 1.13
table_scan 1,015,262 1,071,972 1.06
oltp_read_only 113,316 127,147 1.12
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 189,760 261,100 1.38
oltp_insert 14,855 27,282 1.84
oltp_update_index 52,681 89,127 1.69
oltp_update_non_index 41,133 64,226 1.56
oltp_delete_insert 39,553 74,779 1.89
oltp_write_only 22,144 44,675 2.02
types_delete_insert 25,455 39,771 1.56
oltp_read_write 74,007 112,514 1.52
Average 1.68

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 90,592 52,618 0.58
oltp_range_select 22,568 18,819 0.83
oltp_sum_range 20,134 17,995 0.89
oltp_order_range 3,744 3,370 0.90
oltp_distinct_range 4,494 4,259 0.95
oltp_index_scan 10,446 7,274 0.70
select_random_points 28,702 26,459 0.92
select_random_ranges 12,656 8,956 0.71
covering_index_scan 10,253 5,981 0.58
groupby_scan 30,480 32,026 1.05
index_join 9,957 9,959 1.00
index_join_scan 4,088 4,976 1.22
types_table_scan 874,514 964,959 1.10
table_scan 1,024,809 1,063,836 1.04
oltp_read_only 208,990 159,050 0.76
Average 0.88

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 240,192 385,978 1.61
oltp_insert 48,184 51,529 1.07
oltp_update_index 167,325 161,469 0.97
oltp_update_non_index 137,815 121,804 0.88
oltp_delete_insert 132,453 140,106 1.06
oltp_write_only 96,851 99,745 1.03
types_delete_insert 86,292 79,740 0.92
oltp_read_write 149,489 165,924 1.11
Average 1.08

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,204 52,516 1.11
oltp_range_select 18,035 18,877 1.05
oltp_sum_range 16,108 18,089 1.12
oltp_order_range 3,330 3,412 1.02
oltp_distinct_range 4,092 4,248 1.04
oltp_index_scan 6,217 7,267 1.17
select_random_points 24,672 26,713 1.08
select_random_ranges 8,296 8,927 1.08
covering_index_scan 6,019 5,996 1.00
groupby_scan 30,099 32,042 1.06
index_join 7,913 10,018 1.27
index_join_scan 3,751 4,975 1.33
types_table_scan 871,018 974,884 1.12
table_scan 1,013,004 1,066,540 1.05
oltp_read_only 145,625 158,278 1.09
Average 1.11

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 35,949 100,926 2.81
oltp_insert_ac 59,437 115,454 1.94
oltp_update_index_ac 32,133 109,335 3.40
oltp_update_non_index_ac 28,784 89,389 3.11
oltp_delete_insert_ac 30,073 110,600 3.68
oltp_write_only_ac 52,801 262,244 4.97
types_delete_insert_ac 29,069 130,092 4.48
oltp_read_write_ac 42,788 116,230 2.72
Average 3.39

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,954 34,707 1.16
oltp_range_select 13,221 13,437 1.02
oltp_sum_range 11,702 12,902 1.10
oltp_order_range 3,074 3,180 1.03
oltp_distinct_range 4,070 4,216 1.04
oltp_index_scan 4,472 5,805 1.30
select_random_points 17,498 19,691 1.13
select_random_ranges 4,157 5,226 1.26
covering_index_scan 4,499 4,351 0.97
groupby_scan 33,602 35,113 1.04
index_join 6,879 8,874 1.29
index_join_scan 4,229 5,621 1.33
types_table_scan 1,127,927 1,255,404 1.11
table_scan 1,298,645 1,365,737 1.05
oltp_read_only 117,151 129,229 1.10
Average 1.13

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 239,313 335,831 1.40
oltp_insert 20,371 39,258 1.93
oltp_update_index 68,818 129,406 1.88
oltp_update_non_index 48,961 83,717 1.71
oltp_delete_insert 49,847 102,365 2.05
oltp_write_only 28,064 62,670 2.23
types_delete_insert 32,321 52,108 1.61
oltp_read_write 80,149 130,930 1.63
Average 1.81

File-Backed

Reads

Test SQLite (us) Doltlite (us) Multiplier
oltp_point_select 112,023 62,606 0.56
oltp_range_select 21,329 16,321 0.77
oltp_sum_range 19,741 15,949 0.81
oltp_order_range 3,848 3,535 0.92
oltp_distinct_range 4,825 4,640 0.96
oltp_index_scan 12,640 9,057 0.72
select_random_points 25,714 22,890 0.89
select_random_ranges 12,236 8,107 0.66
covering_index_scan 13,487 7,668 0.57
groupby_scan 34,163 35,404 1.04
index_join 11,262 11,239 1.00
index_join_scan 5,211 6,068 1.16
types_table_scan 1,130,647 1,237,456 1.09
table_scan 1,307,540 1,352,797 1.03
oltp_read_only 236,284 169,722 0.72
Average 0.86

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert 256,140 359,674 1.40
oltp_insert 30,971 51,470 1.66
oltp_update_index 102,421 164,473 1.61
oltp_update_non_index 75,657 109,056 1.44
oltp_delete_insert 80,580 131,363 1.63
oltp_write_only 54,652 85,994 1.57
types_delete_insert 51,135 71,115 1.39
oltp_read_write 105,213 155,203 1.48
Average 1.52

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,072 63,319 1.11
oltp_range_select 16,481 16,344 0.99
oltp_sum_range 15,282 15,909 1.04
oltp_order_range 3,451 3,535 1.02
oltp_distinct_range 4,385 4,578 1.04
oltp_index_scan 7,565 9,011 1.19
select_random_points 20,650 22,760 1.10
select_random_ranges 7,015 8,105 1.16
covering_index_scan 7,770 7,662 0.99
groupby_scan 33,969 35,688 1.05
index_join 8,739 11,267 1.29
index_join_scan 4,957 6,124 1.24
types_table_scan 1,127,898 1,239,611 1.10
table_scan 1,304,565 1,354,316 1.04
oltp_read_only 156,628 170,465 1.09
Average 1.10

Writes

Test SQLite (us) Doltlite (us) Multiplier
oltp_bulk_insert_ac 16,943 64,998 3.84
oltp_insert_ac 18,047 85,555 4.74
oltp_update_index_ac 20,315 98,221 4.83
oltp_update_non_index_ac 16,619 74,659 4.49
oltp_delete_insert_ac 17,922 87,659 4.89
oltp_write_only_ac 17,992 84,883 4.72
types_delete_insert_ac 16,291 80,699 4.95
oltp_read_write_ac 23,830 94,374 3.96
Average 4.55

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.

@timsehn
timsehn merged commit 444f23c into master Jul 16, 2026
27 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.

View and trigger changes ride along into commits whenever the staged master is adopted from working

1 participant