From: dan Date: Mon, 23 Feb 2026 11:42:16 +0000 (+0000) Subject: Add further tests for the BEGIN CONCURRENT extension on this branch. X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=7ef794170f7f625389f9a1ef2f8a1dbefe755591;p=thirdparty%2Fsqlite.git Add further tests for the BEGIN CONCURRENT extension on this branch. FossilOrigin-Name: eb09c021d3bfb55e0297180f8f76a0eee2233d8b09f4cd1e954b7ea501c66560 --- diff --git a/manifest b/manifest index 862aaca7c4..19ce29227c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -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 @@ -1011,6 +1011,7 @@ F test/concurrentA.test 2272fecbf128ac5a8d7b0c9cfe732c4935a5a1b915e237c93ef8dce3 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 @@ -2238,8 +2239,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee 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. diff --git a/manifest.uuid b/manifest.uuid index c3b9828d6b..a41cbf59e8 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -461907251e63055b6ecb56ae8b687c6cead6bf0870a78d50855c54568e2f2391 +eb09c021d3bfb55e0297180f8f76a0eee2233d8b09f4cd1e954b7ea501c66560 diff --git a/test/concurrentE.test b/test/concurrentE.test new file mode 100644 index 0000000000..f4d27327a4 --- /dev/null +++ b/test/concurrentE.test @@ -0,0 +1,214 @@ +# 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 +