Skip to content

Fire sqlite3_update_hook on clustered PRIMARY KEY tables - #2432

Merged
timsehn merged 2 commits into
masterfrom
fix/clustered-pk-update-hook
Aug 26, 2026
Merged

Fire sqlite3_update_hook on clustered PRIMARY KEY tables#2432
timsehn merged 2 commits into
masterfrom
fix/clustered-pk-update-hook

Conversation

@timsehn

@timsehn timsehn commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

sqlite3_update_hook now fires for auto-clustered non-INTEGER PRIMARY KEY tables (for example TEXT PRIMARY KEY), using the same synthetic SQL rowid as SELECT rowid / last_insert_rowid().

Explicit WITHOUT ROWID stays silent, matching stock SQLite.

INTEGER PRIMARY KEY is unchanged (still OP_Insert / OP_Delete).

Why

DoltLite stores those tables as WITHOUT ROWID (VisibleRowid still set). Writes used OP_IdxInsert, which had no update-hook, and OP_Delete only called the hook when HasRowid.

Tests

  • C regression clustered_pk_update_hook: INTEGER PK events, TEXT PK INSERT/UPDATE/DELETE with matching rowids, explicit WITHOUT ROWID silent.
  • hook.test 34/34.

Fixes #2427.

@itoqa

itoqa Bot commented Aug 25, 2026

Copy link
Copy Markdown

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

Summary

Coverage spans normal database writes and reads across integer, text, composite, clustered, and rowid-free tables, including callback behavior, row identity consistency, updates, deletes, and row transfers. It also exercises edge and adversarial conditions such as duplicate or failed writes, no-op statements, concurrent writers, key changes, and compatibility between clustered and ordinary tables.

Safe to merge — the exercised database behaviors are healthy across normal, edge-case, failure-recovery, concurrency, and compatibility scenarios, with no PR-attributable regressions or unresolved failures. No merge blocker is indicated by this run.

Tests run by Ito

View full run

Result Severity Type Description
General The database kept each generated row ID consistent across inserts, updates, deletes, callbacks, and later reads.
General Copying a row from the source table sent an INSERT callback for the destination table. The callback row ID matched the destination row, and the source table stayed unchanged.
General A duplicate insert was rejected, and it did not create a false callback. A later valid insert created exactly one callback and saved the new row.
General Changing the key from a to b succeeds and reports one update for the row with its new synthetic row ID. The earlier failure came from expecting an internal delete callback that the public update-hook contract does not require.
General Two writers changed clustered rows at the same time, and every callback kept the row ID for its own change.
Hook Adding, changing, and removing a row from a clustered table sends the expected notifications. Each notification uses the same nonzero row ID that the table exposes to SQL.
Hook Rows with text keys 'a' and 'b' received different, nonzero row identities, and each update-hook callback matched the identity returned by a row query.
Integer The database reports INSERT, UPDATE, and DELETE for the integer-keyed row, and every event uses rowid 1.
Rev Composite-key writes completed successfully. Each row got a different, nonzero row ID, and the insert, update, and delete callbacks reported the same IDs as SELECT rowid.
Rev The clustered DML regression passed all 12 assertions. The latest inserted row ID stayed correct after updates, deletes, reads, no-op statements, and the next insert.
Rev No-op updates and deletes produced no callbacks, and a rejected duplicate insert did not create a false callback. A later valid insert produced exactly the expected event, with all 12 checks passing.
Rev The stock build completed successfully and kept the expected behavior for ordinary integer keys and tables without stored row IDs. The matching prolly regression also passed all 12 checks.
Transfer Copying rows into the destination table triggered one insert callback for each row. Each callback used the same row ID shown by the destination query, and the source rows stayed unchanged.
Without Inserting, changing, and deleting rows in an explicit WITHOUT ROWID table completed successfully without producing update-hook events.

Tip

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

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

DoltLite performance vs PR base

  • Baseline: 10170ed82c1b12414db8d1b29d2fe9ea2a72fd88
  • Candidate: f8f2825089888c8dd4b10442e3dbf66c1ad04e84
  • Overall ratio: 0.995x
  • Gate result: PASS
  • Gates: individual > 1.50x with more than 10.00ms regression; section, suite, or overall > 1.25x with the same minimum delta
  • vc individual gate: > 2.00x with more than 50.00ms regression
  • Confirmed failed gates: none
  • Automatic retries: none
Suite Workloads Baseline total Candidate total Ratio Result
blobpk 69 11.43s 11.30s 0.988x PASS
compositepk 69 11.43s 11.36s 0.994x PASS
int 69 10.26s 10.33s 1.007x PASS
textpk 69 11.28s 11.19s 0.992x PASS
vc 13 702.77ms 697.97ms 0.993x PASS
blobpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 33.99ms 33.86ms -124us 0.996x PASS
mem_reads oltp_range_select 13.39ms 13.39ms -5us 1.000x PASS
mem_reads oltp_sum_range 13.27ms 13.21ms -59us 0.996x PASS
mem_reads oltp_order_range 3.12ms 3.14ms +23us 1.007x PASS
mem_reads oltp_distinct_range 4.32ms 4.22ms -96us 0.978x PASS
mem_reads oltp_index_scan 5.57ms 5.69ms +121us 1.022x PASS
mem_reads select_random_points 19.65ms 19.46ms -187us 0.990x PASS
mem_reads select_random_ranges 5.13ms 5.18ms +48us 1.009x PASS
mem_reads covering_index_scan 4.33ms 4.29ms -44us 0.990x PASS
mem_reads groupby_scan 35.30ms 35.97ms +672us 1.019x PASS
mem_reads index_join 8.90ms 8.89ms -7us 0.999x PASS
mem_reads index_join_scan 5.54ms 5.57ms +33us 1.006x PASS
mem_reads types_table_scan 1.24s 1.21s -32.47ms 0.974x PASS
mem_reads table_scan 1.37s 1.36s -10.62ms 0.992x PASS
mem_reads oltp_read_only 129.56ms 128.71ms -847us 0.993x PASS
mem_writes oltp_bulk_insert 342.86ms 340.25ms -2.60ms 0.992x PASS
mem_writes oltp_insert 38.11ms 38.04ms -65us 0.998x PASS
mem_writes oltp_update_index 133.59ms 132.61ms -981us 0.993x PASS
mem_writes oltp_update_non_index 76.32ms 75.26ms -1.06ms 0.986x PASS
mem_writes oltp_delete_insert 103.99ms 104.50ms +516us 1.005x PASS
mem_writes oltp_write_only 61.69ms 62.07ms +381us 1.006x PASS
mem_writes types_delete_insert 52.25ms 51.09ms -1.16ms 0.978x PASS
mem_writes oltp_read_write 137.94ms 136.83ms -1.12ms 0.992x PASS
file_reads oltp_point_select 55.90ms 55.26ms -637us 0.989x PASS
file_reads oltp_range_select 15.61ms 15.46ms -145us 0.991x PASS
file_reads oltp_sum_range 15.66ms 15.34ms -323us 0.979x PASS
file_reads oltp_order_range 3.43ms 3.40ms -31us 0.991x PASS
file_reads oltp_distinct_range 4.55ms 4.51ms -45us 0.990x PASS
file_reads oltp_index_scan 8.13ms 8.14ms +12us 1.001x PASS
file_reads select_random_points 22.16ms 21.85ms -317us 0.986x PASS
file_reads select_random_ranges 7.40ms 7.40ms +6us 1.001x PASS
file_reads covering_index_scan 6.81ms 6.91ms +96us 1.014x PASS
file_reads groupby_scan 35.76ms 36.39ms +637us 1.018x PASS
file_reads index_join 10.56ms 10.91ms +348us 1.033x PASS
file_reads index_join_scan 5.81ms 5.83ms +21us 1.004x PASS
file_reads types_table_scan 1.24s 1.21s -30.94ms 0.975x PASS
file_reads table_scan 1.36s 1.36s -6.64ms 0.995x PASS
file_reads oltp_read_only 162.91ms 159.02ms -3.89ms 0.976x PASS
file_writes oltp_bulk_insert 354.42ms 349.48ms -4.95ms 0.986x PASS
file_writes oltp_insert 45.15ms 44.80ms -354us 0.992x PASS
file_writes oltp_update_index 149.62ms 149.74ms +120us 1.001x PASS
file_writes oltp_update_non_index 91.35ms 88.42ms -2.94ms 0.968x PASS
file_writes oltp_delete_insert 116.12ms 116.02ms -98us 0.999x PASS
file_writes oltp_write_only 71.92ms 71.29ms -626us 0.991x PASS
file_writes types_delete_insert 60.60ms 59.83ms -767us 0.987x PASS
file_writes oltp_read_write 150.82ms 147.06ms -3.76ms 0.975x PASS
ac_reads oltp_point_select 56.09ms 55.52ms -568us 0.990x PASS
ac_reads oltp_range_select 15.46ms 15.52ms +50us 1.003x PASS
ac_reads oltp_sum_range 15.51ms 15.43ms -82us 0.995x PASS
ac_reads oltp_order_range 3.42ms 3.41ms -14us 0.996x PASS
ac_reads oltp_distinct_range 4.58ms 4.50ms -77us 0.983x PASS
ac_reads oltp_index_scan 8.17ms 8.20ms +24us 1.003x PASS
ac_reads select_random_points 22.50ms 22.21ms -293us 0.987x PASS
ac_reads select_random_ranges 7.39ms 7.39ms -3us 1.000x PASS
ac_reads covering_index_scan 6.84ms 6.92ms +80us 1.012x PASS
ac_reads groupby_scan 36.20ms 36.51ms +313us 1.009x PASS
ac_reads index_join 10.86ms 11.07ms +205us 1.019x PASS
ac_reads index_join_scan 5.89ms 5.95ms +61us 1.010x PASS
ac_reads types_table_scan 1.27s 1.25s -16.58ms 0.987x PASS
ac_reads table_scan 1.40s 1.39s -14.31ms 0.990x PASS
ac_reads oltp_read_only 163.91ms 162.40ms -1.51ms 0.991x PASS
ac_writes oltp_bulk_insert_ac 56.64ms 56.75ms +115us 1.002x PASS
ac_writes oltp_insert_ac 72.77ms 73.05ms +279us 1.004x PASS
ac_writes oltp_update_index_ac 83.72ms 83.20ms -522us 0.994x PASS
ac_writes oltp_update_non_index_ac 64.48ms 64.54ms +58us 1.001x PASS
ac_writes oltp_delete_insert_ac 75.89ms 74.48ms -1.41ms 0.981x PASS
ac_writes oltp_write_only_ac 74.12ms 73.87ms -255us 0.997x PASS
ac_writes types_delete_insert_ac 64.10ms 65.45ms +1.35ms 1.021x PASS
ac_writes oltp_read_write_ac 79.68ms 84.32ms +4.64ms 1.058x PASS
compositepk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 36.08ms 36.87ms +786us 1.022x PASS
mem_reads oltp_range_select 20.76ms 20.44ms -323us 0.984x PASS
mem_reads oltp_sum_range 20.02ms 20.07ms +53us 1.003x PASS
mem_reads oltp_order_range 3.86ms 3.87ms +10us 1.003x PASS
mem_reads oltp_distinct_range 5.04ms 5.04ms -5us 0.999x PASS
mem_reads oltp_index_scan 5.52ms 5.53ms +12us 1.002x PASS
mem_reads select_random_points 29.91ms 29.93ms +22us 1.001x PASS
mem_reads select_random_ranges 8.14ms 8.06ms -80us 0.990x PASS
mem_reads covering_index_scan 4.04ms 4.06ms +21us 1.005x PASS
mem_reads groupby_scan 41.09ms 41.87ms +786us 1.019x PASS
mem_reads index_join 10.17ms 9.84ms -333us 0.967x PASS
mem_reads index_join_scan 5.64ms 5.59ms -51us 0.991x PASS
mem_reads types_table_scan 1.24s 1.22s -19.21ms 0.984x PASS
mem_reads table_scan 1.34s 1.33s -5.04ms 0.996x PASS
mem_reads oltp_read_only 163.25ms 163.29ms +49us 1.000x PASS
mem_writes oltp_bulk_insert 329.36ms 332.14ms +2.78ms 1.008x PASS
mem_writes oltp_insert 34.94ms 34.62ms -319us 0.991x PASS
mem_writes oltp_update_index 118.04ms 119.02ms +978us 1.008x PASS
mem_writes oltp_update_non_index 75.34ms 74.76ms -588us 0.992x PASS
mem_writes oltp_delete_insert 94.39ms 94.78ms +386us 1.004x PASS
mem_writes oltp_write_only 57.51ms 57.68ms +174us 1.003x PASS
mem_writes types_delete_insert 50.92ms 50.78ms -137us 0.997x PASS
mem_writes oltp_read_write 151.91ms 151.60ms -308us 0.998x PASS
file_reads oltp_point_select 57.81ms 59.10ms +1.29ms 1.022x PASS
file_reads oltp_range_select 23.38ms 23.15ms -226us 0.990x PASS
file_reads oltp_sum_range 22.60ms 22.96ms +355us 1.016x PASS
file_reads oltp_order_range 4.19ms 4.15ms -47us 0.989x PASS
file_reads oltp_distinct_range 5.29ms 5.34ms +52us 1.010x PASS
file_reads oltp_index_scan 7.99ms 7.92ms -76us 0.990x PASS
file_reads select_random_points 32.80ms 32.78ms -20us 0.999x PASS
file_reads select_random_ranges 10.53ms 10.56ms +29us 1.003x PASS
file_reads covering_index_scan 6.48ms 6.57ms +94us 1.015x PASS
file_reads groupby_scan 42.15ms 42.07ms -80us 0.998x PASS
file_reads index_join 11.86ms 11.87ms +8us 1.001x PASS
file_reads index_join_scan 6.06ms 6.13ms +77us 1.013x PASS
file_reads types_table_scan 1.23s 1.21s -18.00ms 0.985x PASS
file_reads table_scan 1.34s 1.34s -7.89ms 0.994x PASS
file_reads oltp_read_only 195.06ms 196.29ms +1.23ms 1.006x PASS
file_writes oltp_bulk_insert 344.45ms 337.85ms -6.60ms 0.981x PASS
file_writes oltp_insert 40.38ms 40.24ms -139us 0.997x PASS
file_writes oltp_update_index 131.24ms 132.07ms +833us 1.006x PASS
file_writes oltp_update_non_index 90.78ms 90.23ms -548us 0.994x PASS
file_writes oltp_delete_insert 107.54ms 105.97ms -1.57ms 0.985x PASS
file_writes oltp_write_only 67.99ms 67.15ms -839us 0.988x PASS
file_writes types_delete_insert 56.78ms 56.20ms -583us 0.990x PASS
file_writes oltp_read_write 161.72ms 162.90ms +1.18ms 1.007x PASS
ac_reads oltp_point_select 57.72ms 58.38ms +663us 1.011x PASS
ac_reads oltp_range_select 23.22ms 23.36ms +144us 1.006x PASS
ac_reads oltp_sum_range 22.75ms 22.99ms +239us 1.011x PASS
ac_reads oltp_order_range 4.13ms 4.14ms +3us 1.001x PASS
ac_reads oltp_distinct_range 5.30ms 5.29ms -8us 0.998x PASS
ac_reads oltp_index_scan 7.93ms 7.90ms -30us 0.996x PASS
ac_reads select_random_points 32.70ms 33.09ms +399us 1.012x PASS
ac_reads select_random_ranges 10.45ms 10.57ms +121us 1.012x PASS
ac_reads covering_index_scan 6.41ms 6.50ms +94us 1.015x PASS
ac_reads groupby_scan 41.65ms 42.36ms +713us 1.017x PASS
ac_reads index_join 11.87ms 11.85ms -19us 0.998x PASS
ac_reads index_join_scan 6.04ms 6.10ms +58us 1.010x PASS
ac_reads types_table_scan 1.23s 1.22s -17.37ms 0.986x PASS
ac_reads table_scan 1.35s 1.34s -3.75ms 0.997x PASS
ac_reads oltp_read_only 196.18ms 196.95ms +767us 1.004x PASS
ac_writes oltp_bulk_insert_ac 57.14ms 56.96ms -179us 0.997x PASS
ac_writes oltp_insert_ac 74.80ms 75.86ms +1.07ms 1.014x PASS
ac_writes oltp_update_index_ac 83.71ms 83.69ms -16us 1.000x PASS
ac_writes oltp_update_non_index_ac 64.83ms 66.92ms +2.09ms 1.032x PASS
ac_writes oltp_delete_insert_ac 73.66ms 73.13ms -529us 0.993x PASS
ac_writes oltp_write_only_ac 77.95ms 77.47ms -486us 0.994x PASS
ac_writes types_delete_insert_ac 66.96ms 65.27ms -1.69ms 0.975x PASS
ac_writes oltp_read_write_ac 83.50ms 83.90ms +395us 1.005x PASS
int details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 27.12ms 26.39ms -735us 0.973x PASS
mem_reads oltp_range_select 10.98ms 10.89ms -87us 0.992x PASS
mem_reads oltp_sum_range 10.75ms 10.99ms +244us 1.023x PASS
mem_reads oltp_order_range 2.82ms 2.85ms +30us 1.011x PASS
mem_reads oltp_distinct_range 3.98ms 3.93ms -50us 0.987x PASS
mem_reads oltp_index_scan 4.73ms 4.72ms -13us 0.997x PASS
mem_reads select_random_points 10.80ms 10.87ms +68us 1.006x PASS
mem_reads select_random_ranges 3.94ms 3.91ms -29us 0.993x PASS
mem_reads covering_index_scan 4.05ms 4.03ms -26us 0.994x PASS
mem_reads groupby_scan 34.23ms 34.78ms +549us 1.016x PASS
mem_reads index_join 7.47ms 7.39ms -74us 0.990x PASS
mem_reads index_join_scan 4.54ms 4.50ms -40us 0.991x PASS
mem_reads types_table_scan 1.17s 1.17s -6.38ms 0.995x PASS
mem_reads table_scan 1.28s 1.33s +40.41ms 1.031x PASS
mem_reads oltp_read_only 112.39ms 113.27ms +883us 1.008x PASS
mem_writes oltp_bulk_insert 242.13ms 238.83ms -3.30ms 0.986x PASS
mem_writes oltp_insert 27.13ms 26.85ms -281us 0.990x PASS
mem_writes oltp_update_index 92.67ms 92.06ms -604us 0.993x PASS
mem_writes oltp_update_non_index 53.34ms 53.04ms -294us 0.994x PASS
mem_writes oltp_delete_insert 73.96ms 72.40ms -1.56ms 0.979x PASS
mem_writes oltp_write_only 47.26ms 47.01ms -258us 0.995x PASS
mem_writes types_delete_insert 36.50ms 35.59ms -911us 0.975x PASS
mem_writes oltp_read_write 108.30ms 107.14ms -1.16ms 0.989x PASS
file_reads oltp_point_select 47.86ms 47.87ms +4us 1.000x PASS
file_reads oltp_range_select 13.12ms 13.06ms -59us 0.996x PASS
file_reads oltp_sum_range 13.18ms 13.12ms -58us 0.996x PASS
file_reads oltp_order_range 3.11ms 3.12ms +6us 1.002x PASS
file_reads oltp_distinct_range 4.27ms 4.20ms -71us 0.983x PASS
file_reads oltp_index_scan 7.18ms 7.23ms +43us 1.006x PASS
file_reads select_random_points 13.20ms 13.26ms +57us 1.004x PASS
file_reads select_random_ranges 6.05ms 6.10ms +42us 1.007x PASS
file_reads covering_index_scan 6.65ms 6.64ms -4us 0.999x PASS
file_reads groupby_scan 34.35ms 34.81ms +464us 1.014x PASS
file_reads index_join 9.33ms 9.29ms -39us 0.996x PASS
file_reads index_join_scan 4.84ms 4.99ms +143us 1.030x PASS
file_reads types_table_scan 1.19s 1.19s -8.06ms 0.993x PASS
file_reads table_scan 1.31s 1.35s +32.38ms 1.025x PASS
file_reads oltp_read_only 145.49ms 145.88ms +385us 1.003x PASS
file_writes oltp_bulk_insert 250.13ms 245.58ms -4.55ms 0.982x PASS
file_writes oltp_insert 31.42ms 31.30ms -121us 0.996x PASS
file_writes oltp_update_index 102.35ms 98.90ms -3.45ms 0.966x PASS
file_writes oltp_update_non_index 65.70ms 65.67ms -31us 1.000x PASS
file_writes oltp_delete_insert 79.97ms 81.08ms +1.11ms 1.014x PASS
file_writes oltp_write_only 53.72ms 54.08ms +359us 1.007x PASS
file_writes types_delete_insert 41.83ms 41.14ms -693us 0.983x PASS
file_writes oltp_read_write 112.35ms 112.20ms -145us 0.999x PASS
ac_reads oltp_point_select 47.84ms 47.48ms -370us 0.992x PASS
ac_reads oltp_range_select 13.00ms 13.05ms +50us 1.004x PASS
ac_reads oltp_sum_range 13.19ms 13.03ms -163us 0.988x PASS
ac_reads oltp_order_range 3.11ms 3.10ms -14us 0.995x PASS
ac_reads oltp_distinct_range 4.25ms 4.20ms -58us 0.986x PASS
ac_reads oltp_index_scan 7.18ms 7.20ms +26us 1.004x PASS
ac_reads select_random_points 13.04ms 13.08ms +38us 1.003x PASS
ac_reads select_random_ranges 6.04ms 6.08ms +39us 1.006x PASS
ac_reads covering_index_scan 6.51ms 6.62ms +114us 1.018x PASS
ac_reads groupby_scan 34.89ms 34.88ms -11us 1.000x PASS
ac_reads index_join 9.17ms 9.31ms +146us 1.016x PASS
ac_reads index_join_scan 4.82ms 4.89ms +71us 1.015x PASS
ac_reads types_table_scan 1.17s 1.17s +714us 1.001x PASS
ac_reads table_scan 1.28s 1.32s +33.18ms 1.026x PASS
ac_reads oltp_read_only 143.42ms 142.81ms -609us 0.996x PASS
ac_writes oltp_bulk_insert_ac 54.34ms 55.45ms +1.11ms 1.020x PASS
ac_writes oltp_insert_ac 69.70ms 68.67ms -1.03ms 0.985x PASS
ac_writes oltp_update_index_ac 83.38ms 82.69ms -690us 0.992x PASS
ac_writes oltp_update_non_index_ac 64.78ms 62.38ms -2.41ms 0.963x PASS
ac_writes oltp_delete_insert_ac 74.11ms 70.79ms -3.32ms 0.955x PASS
ac_writes oltp_write_only_ac 73.33ms 74.57ms +1.24ms 1.017x PASS
ac_writes types_delete_insert_ac 66.31ms 65.06ms -1.25ms 0.981x PASS
ac_writes oltp_read_write_ac 82.74ms 84.03ms +1.29ms 1.016x PASS
textpk details
Section Test Baseline Candidate Delta Ratio Result
mem_reads oltp_point_select 33.62ms 33.07ms -551us 0.984x PASS
mem_reads oltp_range_select 13.09ms 13.03ms -55us 0.996x PASS
mem_reads oltp_sum_range 13.29ms 13.07ms -214us 0.984x PASS
mem_reads oltp_order_range 3.14ms 3.10ms -34us 0.989x PASS
mem_reads oltp_distinct_range 4.23ms 4.20ms -28us 0.993x PASS
mem_reads oltp_index_scan 5.43ms 5.39ms -37us 0.993x PASS
mem_reads select_random_points 19.70ms 19.69ms -9us 1.000x PASS
mem_reads select_random_ranges 5.08ms 5.08ms +6us 1.001x PASS
mem_reads covering_index_scan 4.25ms 4.24ms -13us 0.997x PASS
mem_reads groupby_scan 35.47ms 36.12ms +645us 1.018x PASS
mem_reads index_join 8.50ms 8.38ms -125us 0.985x PASS
mem_reads index_join_scan 5.50ms 5.31ms -199us 0.964x PASS
mem_reads types_table_scan 1.23s 1.22s -16.59ms 0.987x PASS
mem_reads table_scan 1.35s 1.33s -14.00ms 0.990x PASS
mem_reads oltp_read_only 128.78ms 127.43ms -1.35ms 0.990x PASS
mem_writes oltp_bulk_insert 347.78ms 346.11ms -1.67ms 0.995x PASS
mem_writes oltp_insert 38.47ms 37.99ms -473us 0.988x PASS
mem_writes oltp_update_index 136.09ms 134.77ms -1.32ms 0.990x PASS
mem_writes oltp_update_non_index 78.10ms 77.77ms -331us 0.996x PASS
mem_writes oltp_delete_insert 104.34ms 104.05ms -293us 0.997x PASS
mem_writes oltp_write_only 61.67ms 61.15ms -521us 0.992x PASS
mem_writes types_delete_insert 52.01ms 51.64ms -367us 0.993x PASS
mem_writes oltp_read_write 137.82ms 137.28ms -541us 0.996x PASS
file_reads oltp_point_select 55.59ms 55.40ms -193us 0.997x PASS
file_reads oltp_range_select 15.64ms 15.42ms -217us 0.986x PASS
file_reads oltp_sum_range 15.80ms 15.56ms -238us 0.985x PASS
file_reads oltp_order_range 3.44ms 3.41ms -29us 0.992x PASS
file_reads oltp_distinct_range 4.56ms 4.54ms -16us 0.996x PASS
file_reads oltp_index_scan 8.09ms 8.09ms -5us 0.999x PASS
file_reads select_random_points 22.61ms 22.22ms -384us 0.983x PASS
file_reads select_random_ranges 7.42ms 7.42ms -2us 1.000x PASS
file_reads covering_index_scan 6.91ms 6.90ms -10us 0.999x PASS
file_reads groupby_scan 35.89ms 36.50ms +610us 1.017x PASS
file_reads index_join 10.70ms 10.49ms -201us 0.981x PASS
file_reads index_join_scan 6.20ms 5.84ms -362us 0.942x PASS
file_reads types_table_scan 1.23s 1.21s -17.61ms 0.986x PASS
file_reads table_scan 1.35s 1.33s -17.24ms 0.987x PASS
file_reads oltp_read_only 160.99ms 159.67ms -1.32ms 0.992x PASS
file_writes oltp_bulk_insert 358.28ms 355.58ms -2.70ms 0.992x PASS
file_writes oltp_insert 45.10ms 44.87ms -228us 0.995x PASS
file_writes oltp_update_index 152.55ms 152.96ms +407us 1.003x PASS
file_writes oltp_update_non_index 92.18ms 91.34ms -842us 0.991x PASS
file_writes oltp_delete_insert 118.98ms 118.14ms -843us 0.993x PASS
file_writes oltp_write_only 73.01ms 72.39ms -616us 0.992x PASS
file_writes types_delete_insert 60.91ms 60.87ms -37us 0.999x PASS
file_writes oltp_read_write 147.89ms 147.41ms -480us 0.997x PASS
ac_reads oltp_point_select 55.66ms 55.71ms +49us 1.001x PASS
ac_reads oltp_range_select 15.51ms 15.50ms -9us 0.999x PASS
ac_reads oltp_sum_range 15.66ms 15.37ms -287us 0.982x PASS
ac_reads oltp_order_range 3.46ms 3.42ms -43us 0.988x PASS
ac_reads oltp_distinct_range 4.55ms 4.52ms -23us 0.995x PASS
ac_reads oltp_index_scan 8.06ms 8.13ms +61us 1.008x PASS
ac_reads select_random_points 22.33ms 22.13ms -193us 0.991x PASS
ac_reads select_random_ranges 7.33ms 7.42ms +85us 1.012x PASS
ac_reads covering_index_scan 6.84ms 6.91ms +71us 1.010x PASS
ac_reads groupby_scan 35.83ms 36.75ms +921us 1.026x PASS
ac_reads index_join 10.77ms 10.54ms -235us 0.978x PASS
ac_reads index_join_scan 5.91ms 5.81ms -106us 0.982x PASS
ac_reads types_table_scan 1.22s 1.22s -5.64ms 0.995x PASS
ac_reads table_scan 1.34s 1.34s -4.75ms 0.996x PASS
ac_reads oltp_read_only 161.84ms 160.99ms -844us 0.995x PASS
ac_writes oltp_bulk_insert_ac 54.04ms 54.88ms +845us 1.016x PASS
ac_writes oltp_insert_ac 65.81ms 66.76ms +946us 1.014x PASS
ac_writes oltp_update_index_ac 81.91ms 80.85ms -1.06ms 0.987x PASS
ac_writes oltp_update_non_index_ac 63.74ms 64.31ms +568us 1.009x PASS
ac_writes oltp_delete_insert_ac 72.87ms 72.53ms -348us 0.995x PASS
ac_writes oltp_write_only_ac 72.32ms 72.81ms +489us 1.007x PASS
ac_writes types_delete_insert_ac 64.99ms 63.98ms -1.01ms 0.984x PASS
ac_writes oltp_read_write_ac 78.96ms 79.05ms +88us 1.001x PASS
vc details
Section Test Baseline Candidate Delta Ratio Result
vc status_clean_many_tables 65.18ms 64.44ms -744us 0.989x PASS
vc status_dirty_many_tables 67.42ms 67.04ms -380us 0.994x PASS
vc diff_regular_working_one_table 58.99ms 59.38ms +393us 1.007x PASS
vc diff_regular_working_many_tables 72.80ms 70.69ms -2.10ms 0.971x PASS
vc diff_stat_working_many_tables 71.58ms 71.06ms -522us 0.993x PASS
vc diff_schema_working_many_tables 72.17ms 72.15ms -19us 1.000x PASS
vc branch_list_many_branches 20.19ms 20.03ms -168us 0.992x PASS
vc branch_create_delete 22.44ms 22.42ms -24us 0.999x PASS
vc checkout_branch_clean 48.96ms 48.21ms -748us 0.985x PASS
vc merge_data_no_conflicts 25.52ms 25.63ms +108us 1.004x PASS
vc merge_schema_no_conflicts 20.65ms 20.61ms -45us 0.998x PASS
vc merge_data_conflicts 77.55ms 78.24ms +690us 1.009x PASS
vc merge_data_conflicts_with_resolve 79.32ms 78.07ms -1.25ms 0.984x PASS

All relative performance gates passed.

Tim and others added 2 commits August 26, 2026 07:40
Non-INTEGER PRIMARY KEY tables are stored as WITHOUT ROWID, so INSERT
and UPDATE go through OP_IdxInsert and never reached the OP_Insert
update hook. DELETE already used OP_Delete but skipped the hook when
HasRowid was false.

For VisibleRowid clustered tables, attach the Table to the PK
OP_IdxInsert and invoke the hook with the synthetic SQL rowid. DELETE
captures sqlite3BtreeSqlRowid before the btree remove. Explicit
WITHOUT ROWID stays silent, matching stock.

Fixes #2427.

Co-Authored-By: Grok 4.6 <noreply@x.ai>
P4_TABLE on the PK IdxInsert stole the unpacked nMem count. Zeroing
x.nMem made WITHOUT ROWID CASCADE UPDATE compare the full packed
record, miss the existing PK, and insert duplicate child rows
(fkey8-7.4). Restore nMem from KeyInfo.nKeyField when P3 names the
unpacked key.

Co-Authored-By: Grok 4.6 <noreply@x.ai>
@timsehn
timsehn force-pushed the fix/clustered-pk-update-hook branch from 2612321 to 6694f8d Compare August 26, 2026 11:40
@timsehn

timsehn commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator Author

CI sqlite-regression-ddl-schema failed on fkey8-7.4: ON UPDATE CASCADE against a clustered (non-INTEGER) PRIMARY KEY duplicated child rows instead of rewriting the FK.

Cause: OP_IdxInsert attached P4_TABLE for the update hook and zeroed the unpacked key (x.nMem). WITHOUT ROWID then compared the full packed record, missed the existing PK, and inserted a second row.

Fix in 6694f8d: keep P4_TABLE for the hook, but restore unpacked nMem from KeyInfo.nKeyField when P3 names the key registers. Rebased onto current master. Local fkey8 (38/38), hook.test (34/34), and clustered_pk_update_hook (20/20) pass.

@itoqa

itoqa Bot commented Aug 26, 2026

Copy link
Copy Markdown

Ito QA test results

History reset (rebase or force-push detected). Starting test narrative over.

Commit: 6694f8d: 13 test cases ran, 13 passed ✅.

Summary

Coverage spans core data-change behavior, including inserts, updates, deletes, key changes, nested and cross-database writes, bulk transfers, and row identity reporting. It also exercises edge cases and concurrency, verifying correct notifications under overlapping operations and silence where notifications are not expected.

Safe to merge — the exercised behaviors completed successfully with no PR-attributable regressions or unresolved failures, leaving no merge blocker identified.

Tests run by Ito

View full run

Result Severity Type Description
General Two connections safely ran clustered inserts and updates across 20 rounds. Successful callbacks reported the correct nonzero row identities, and insert callbacks matched each connection's last inserted row ID.
General Twenty rounds of overlapping updates and deletes completed without a wrong callback or false event. Each successful operation reported the correct nonzero row identity, and the final table matched the committed operations.
Clustered Adding, changing, and removing a row sends the expected notifications with the same nonzero row identity.
Clustered Clustered table inserts, updates, and deletes completed successfully without an installed update hook, and no callback events were recorded.
Integer Integer primary-key inserts, updates, and deletes reported the same row ID of 1 to the callback, as expected.
Rev Changing a clustered table's text key keeps the row and reports one update event with the same synthetic row ID. The focused native regression suite passed all 20 assertions.
Rev Parent updates completed successfully, related child rows were changed correctly, and trigger-created audit writes did not add extra update notifications.
Rev Rows with two-part primary keys received distinct, nonzero row IDs, and later updates and deletes reported the same IDs for the affected rows.
Rev Updates to the attached aux table were reported as aux events, while the main table remained correctly identified as main.
Rev The clustered insert checks passed. The last inserted row ID matched the row ID returned for the inserted row, including the multi-row insert, and all three insert callbacks were recorded.
Silent Inserting, changing, and deleting rows in an explicitly hidden-rowid table all succeed without producing update-hook events.
Transfer Copying three rows into the clustered destination emits one INSERT callback for each row. Every callback names table t and has a distinct nonzero rowid.
Transfer Copying two rows into an explicitly hidden-rowid table succeeds, and the table produces no insert callbacks.

Tip

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

@github-actions

Copy link
Copy Markdown
Contributor

DoltLite source coverage

Metric Covered Total Coverage
Lines 52565 61947 84.85%
Branches 24256 35888 67.59%
Functions 2474 2690 91.97%

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

Per-file coverage (97 files)
File Lines Branches Functions
src/btree_orig_api.c 87.91% 77.27% 89.53%
src/chunk_file.c 100.00% 100.00% 100.00%
src/chunk_index.c 84.27% 67.76% 100.00%
src/chunk_refs.c 84.99% 68.91% 79.37%
src/chunk_staging.c 93.75% 80.33% 100.00%
src/chunk_store.c 91.00% 75.09% 100.00%
src/chunk_store_commit.c 86.75% 67.65% 100.00%
src/chunk_store_lock.c 93.98% 76.36% 100.00%
src/chunk_store_refs_api.c 91.17% 76.75% 100.00%
src/chunk_wal.c 81.88% 58.05% 87.10%
src/doltlite.c 100.00% 100.00% 100.00%
src/doltlite_add.c 85.50% 69.20% 100.00%
src/doltlite_ancestor.c 89.19% 68.92% 100.00%
src/doltlite_at.c 83.39% 64.20% 100.00%
src/doltlite_blame.c 80.86% 61.14% 96.43%
src/doltlite_branch.c 88.30% 80.66% 100.00%
src/doltlite_branches.c 93.75% 75.93% 93.75%
src/doltlite_checkout.c 77.42% 62.46% 96.15%
src/doltlite_cherry_pick.c 77.89% 58.62% 71.43%
src/doltlite_chunk_walk.c 88.60% 64.19% 100.00%
src/doltlite_cmd.c 81.09% 75.19% 100.00%
src/doltlite_commit.c 94.41% 76.87% 100.00%
src/doltlite_commit_ancestors.c 92.09% 72.83% 92.31%
src/doltlite_commit_cmd.c 75.49% 68.21% 100.00%
src/doltlite_config.c 78.57% 70.93% 100.00%
src/doltlite_conflicts.c 84.27% 62.50% 93.75%
src/doltlite_constraint_violations.c 82.46% 57.54% 91.11%
src/doltlite_core.c 90.77% 71.24% 100.00%
src/doltlite_creds.c 84.09% 53.78% 92.00%
src/doltlite_dbpage.c 93.45% 78.33% 91.67%
src/doltlite_diff.c 88.56% 68.99% 96.55%
src/doltlite_diff_stat.c 94.17% 76.01% 95.65%
src/doltlite_diff_table.c 93.96% 69.76% 97.06%
src/doltlite_docs.c 83.94% 71.43% 94.74%
src/doltlite_gc.c 77.37% 57.14% 96.55%
src/doltlite_hashof.c 76.95% 64.97% 94.44%
src/doltlite_history.c 86.93% 76.40% 100.00%
src/doltlite_http_remote.c 82.22% 57.34% 92.31%
src/doltlite_ignore.c 77.57% 62.25% 82.14%
src/doltlite_log.c 96.81% 73.24% 92.86%
src/doltlite_merge.c 95.93% 70.72% 100.00%
src/doltlite_merge_cmd.c 88.81% 72.63% 100.00%
src/doltlite_merge_constraints.c 86.15% 63.64% 95.00%
src/doltlite_merge_constraints_check.c 86.81% 67.79% 100.00%
src/doltlite_merge_constraints_fk.c 78.00% 56.64% 100.00%
src/doltlite_merge_constraints_notnull.c 80.45% 67.92% 100.00%
src/doltlite_merge_constraints_strict.c 75.96% 58.70% 100.00%
src/doltlite_merge_constraints_unique.c 85.99% 62.82% 100.00%
src/doltlite_merge_pass1.c 92.38% 73.59% 100.00%
src/doltlite_merge_pass2.c 83.59% 67.46% 100.00%
src/doltlite_merge_predetect.c 86.38% 71.15% 95.45%
src/doltlite_merge_rebuild.c 99.15% 78.91% 100.00%
src/doltlite_merge_rows.c 80.50% 63.23% 100.00%
src/doltlite_merge_schema.c 91.62% 71.91% 97.06%
src/doltlite_merge_status.c 91.98% 73.53% 92.31%
src/doltlite_patch.c 94.36% 70.80% 98.15%
src/doltlite_rebase.c 85.32% 59.28% 100.00%
src/doltlite_record.c 78.67% 59.36% 95.24%
src/doltlite_ref.c 95.52% 75.36% 100.00%
src/doltlite_remote.c 84.71% 66.04% 96.00%
src/doltlite_remote_sql.c 66.60% 60.73% 93.55%
src/doltlite_remotesrv.c 75.76% 63.77% 90.77%
src/doltlite_reset.c 87.48% 72.76% 100.00%
src/doltlite_revert.c 80.51% 69.44% 100.00%
src/doltlite_schema_diff.c 94.42% 71.13% 96.97%
src/doltlite_schemas.c 70.86% 48.75% 90.91%
src/doltlite_status.c 91.21% 72.41% 97.56%
src/doltlite_tag.c 86.84% 70.31% 94.12%
src/doltlite_tests.c 84.22% 69.61% 91.11%
src/doltlite_tls.c 85.04% 61.82% 92.31%
src/doltlite_verify_constraints.c 79.15% 66.44% 100.00%
src/doltlite_workspace.c 91.84% 70.27% 100.00%
src/pager_shim.c 56.96% 64.76% 32.86%
src/prolly_btree.c 86.00% 66.56% 90.70%
src/prolly_btree_catalog.c 82.81% 69.06% 97.14%
src/prolly_btree_cursor.c 84.15% 62.09% 96.00%
src/prolly_btree_cursor_count.c 80.05% 56.74% 100.00%
src/prolly_btree_cursor_payload.c 75.69% 54.48% 95.65%
src/prolly_btree_cursor_seek.c 76.68% 66.20% 82.35%
src/prolly_btree_mutation.c 87.89% 68.20% 97.83%
src/prolly_btree_orig.c 93.75% 71.43% 93.15%
src/prolly_btree_state.c 93.19% 65.93% 100.00%
src/prolly_btree_txn.c 81.84% 69.30% 98.08%
src/prolly_cache.c 93.27% 69.12% 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.67% 80.47% 100.00%
src/prolly_diff.c 52.79% 39.04% 60.87%
src/prolly_hash.c 93.33% 80.00% 100.00%
src/prolly_hashset.c 90.48% 80.56% 100.00%
src/prolly_mutate.c 80.78% 69.11% 100.00%
src/prolly_mutmap.c 93.87% 80.52% 100.00%
src/prolly_node.c 88.91% 73.81% 100.00%
src/prolly_three_way_diff.c 95.79% 85.37% 100.00%
src/prolly_three_way_merge.c 81.92% 67.41% 91.67%
src/prolly_xxhash.c 100.00% 100.00% 100.00%
src/sortkey.c 93.42% 81.64% 100.00%

Download HTML and LCOV artifacts from this workflow run.

@timsehn
timsehn merged commit 3617aa5 into master Aug 26, 2026
67 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.

sqlite3_update_hook is silent on clustered TEXT PRIMARY KEY tables

1 participant