-C Fix\sOMIT_CONCURRENT\sbuilds\son\sthis\sbranch.
-D 2026-02-20T18:27:02.921
+C Add\sfurther\stests\sfor\sthe\sBEGIN\sCONCURRENT\sextension\son\sthis\sbranch.
+D 2026-02-23T11:42:16.203
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F test/concurrentB.test b85e8adc4c1acf9b1cc0e8c2012348dc116d4ce032bb699d039f10de9b4cca71
F test/concurrentC.test fd85071ee3792a1fe9c7faf5cdf70a338cdb1aeaa127ba6d1394db84b8fe0dbe
F test/concurrentD.test ded9c6e975af21652717afb87c8632395610372bc276d1efa701af05233f0c69
+F test/concurrentE.test 590d6f496bc0fd2fa354ec1a913dde2c88ad38ba382fe7b4263caa0fce2fca3e
F test/conflict.test 3307ffdf988e04b01c4e942d8aa369a977f085bf629f43a627c9a77f39d65926
F test/conflict2.test 5557909ce683b1073982f5d1b61dfb1d41e369533bfdaf003180c5bc87282dd1
F test/conflict3.test 81865d9599609aca394fb3b9cd5f561d4729ea5b176bece3644f6ecb540f88ac
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c
-P 9202cd04f74ae69bb82c6574c2259d270ae35c9476cccc2e20f3c1f9c8a6c995
-R 5a7bc9e12a953155b053b6e3af37cb59
+P 461907251e63055b6ecb56ae8b687c6cead6bf0870a78d50855c54568e2f2391
+R 2f5a7668700f2f9dbb7c7efd461f47e5
U dan
-Z 1f713f7a76b880e3040163159533c150
+Z 0c4d66a3f3d358a25605cdaaf8257b68
# Remove this line to create a well-formed Fossil manifest.
--- /dev/null
+# 2026 February 23
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#*************************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix concurrentE
+
+db close
+sqlite3_shutdown
+#sqlite3_config_sharedlog_maxsize 0
+reset_db
+
+expr srand(0)
+
+do_execsql_test 1.0 {
+ PRAGMA journal_mode = wal2;
+ PRAGMA page_size = 512;
+ CREATE TABLE jobs(
+ jobID INTEGER NOT NULL PRIMARY KEY,
+ parentJobID INTEGER NOT NULL DEFAULT 0,
+ state TEXT NOT NULL
+ );
+ CREATE INDEX jobs_idx ON jobs(parentJobID, state) WHERE parentJobID!=0;
+} {wal2}
+
+# Possible text values for jobs.state column
+#
+set lStateVal { FINISHED QUEUED RUNQUEUED RUNNING PAUSED }
+
+proc insert_job {db} {
+ set iParent [rand 0 10000000000]
+ $db eval { INSERT INTO jobs VALUES($iParent, 0, 'QUEUED'); }
+ set iParent [$db last_insert_rowid]
+ for {set ii 0} {$ii < 3} {incr ii} {
+ set iChild [rand 0 10000000000]
+ $db eval { INSERT INTO jobs VALUES($iChild, $iParent, 'QUEUED'); }
+ }
+}
+
+proc rand_state {} {
+ set lStateVal { FINISHED QUEUED RUNQUEUED RUNNING PAUSED }
+ lindex $lStateVal [rand 0 4]
+}
+db func rand_state rand_state
+
+proc rand {min max} {
+ expr {$min + int(abs(rand() * (1 + $max - $min)))}
+}
+db func rand rand
+
+# Each transaction is:
+#
+# 1. Select at random a child job to work on. Any child job with a state
+# other than 'FINISHED' can be worked on. If no child job can be
+# found, it is an error.
+#
+# 2. Open a transaction with BEGIN CONCURRENT.
+#
+# 3. Check that the child job is still available to work on.
+#
+# 4. Check parent job is not 'FINISHED' or missing. If it is, this is an
+# error.
+#
+# 5. Set child job state to a randomly selected state.
+#
+# 6. check if there are any children of the parent with state set to
+# anything other than FINISHED. If there are not, (a) set parent to
+# FINISHED, (b) add a new parent and its child jobs. All new jobs
+# entries are set to QUEUED state. jobid values are assigned randomly.
+#
+# 7. Commit transaction.
+#
+# This procedure does steps 1-6 of the above with
+#
+proc start_transaction {db} {
+ # Step 1: Choose a job to "work" on.
+ unset -nocomplain iChild iParent
+ $db eval {
+ WITH children(rownum, jobid, parentid, state) AS (
+ SELECT row_number() OVER (), jobid, parentjobid, state
+ FROM jobs WHERE parentJobId!=0 AND state!='FINISHED'
+ )
+ SELECT jobid AS iChild, parentid AS iParent, state FROM children
+ LIMIT 1
+ OFFSET rand( 0, (SELECT count(*) FROM children)-1 );
+ } { }
+ if {[info exists iChild]==0} {
+ error "Could not find a child to work on!!!"
+ }
+
+ # Step 2: Open transaction.
+ $db eval { BEGIN CONCURRENT }
+
+ # Step 3: Check that our job is still available to work on. If it
+ # is not, close the transaction just opened and return zero.
+ #
+ set child_ok 0
+ $db eval {
+ SELECT state IN ('QUEUED', 'RUNQUEUED', 'RUNNING', 'PAUSED')
+ FROM jobs
+ WHERE jobId=$iChild
+ } {
+ set child_ok 1
+ }
+ if {$child_ok==0} {
+ $db eval { ROLLBACK }
+ return 0
+ }
+
+ # Step 4: Check parent is not finished or deleted.
+ set parent_ok 0
+ $db eval {
+ SELECT
+ state IN ('QUEUED', 'RUNQUEUED', 'RUNNING', 'PAUSED')
+ FROM jobs
+ WHERE jobId=$iParent
+ } {
+ set parent_ok 1
+ }
+ if {$parent_ok==0} {
+ error "Parent job is not in expected state!"
+ }
+
+ # Step 5: Update child job.
+ $db eval {
+ UPDATE jobs SET state=rand_state() WHERE jobid=$iChild
+ }
+
+ # Step 6: Check if all children of parent are finished. If so,
+ # set parent to FINISHED and add a new parent + child jobs.
+ #
+ set iUnfinishedChild 0
+ $db eval {
+ SELECT jobId AS j FROM jobs
+ WHERE parentJobID!=0 AND parentJobID=$iParent AND
+ state IN ('QUEUED', 'RUNQUEUED', 'RUNNING', 'PAUSED')
+ LIMIT 1
+ } {
+ set iUnfinishedChild $j
+ }
+ if {$iUnfinishedChild==0} {
+ $db eval {
+ UPDATE jobs SET state = 'FINISHED' WHERE jobID = $iParent
+ }
+ insert_job $db
+ }
+
+ return 1
+}
+
+proc commit_transction {db} {
+ set rb [catch { $db eval COMMIT }]
+
+ if {$rb==0} {
+ incr ::O(commit)
+ } else {
+ catch { $db eval ROLLBACK }
+ incr ::O(rollback)
+ }
+}
+
+set ::O(commit) 0
+set ::O(rollback) 0
+
+set nInitialJob 8
+db eval BEGIN
+for {set ii 0} {$ii < $nInitialJob} {incr ii} {
+ insert_job db
+}
+db eval COMMIT
+
+set DBLIST [list db1 db2 db3 db4 db5 db6]
+foreach db $DBLIST {
+ sqlite3 $db test.db
+ $db func rand_state rand_state
+ $db func rand rand
+}
+
+for {set i 0} {$i < 1000} {incr i} {
+ foreach db $DBLIST {
+ start_transaction $db
+ }
+ foreach db $DBLIST {
+ commit_transction $db
+ }
+
+ db eval {
+ DELETE FROM jobs AS j WHERE
+ (parentjobid=0 AND state='FINISHED') OR
+ (SELECT state FROM jobs WHERE jobID=j.parentJobID)='FINISHED'
+ }
+
+ do_execsql_test 1.$i { PRAGMA integrity_check } {ok}
+}
+
+foreach db $DBLIST {
+ $db close
+}
+
+do_execsql_test 1.x.c=$O(commit).r=$O(rollback) { SELECT 1 } 1
+
+
+finish_test
+