From: drh <> Date: Wed, 13 Nov 2024 16:08:02 +0000 (+0000) Subject: Add the test/fork-test.c test program. X-Git-Tag: major-relase~168 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=31c160ab8f7b0c8f4a1a19ea5d9c6abe5083b37c;p=thirdparty%2Fsqlite.git Add the test/fork-test.c test program. FossilOrigin-Name: 0611e2b0cf3f33c28cc9ff6c5da7ebba2033bcbda5b1072a30021a3e1fb4e738 --- diff --git a/manifest b/manifest index eeed2863da..3fd0f2dc1e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_FCNTL_NULL_IO\sfile-control. -D 2024-11-13T14:58:35.917 +C Add\sthe\stest/fork-test.c\stest\sprogram. +D 2024-11-13T16:08:02.102 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d @@ -1154,6 +1154,7 @@ F test/fkey7.test 64fb28da03da5dfe3cdef5967aa7e832c2507bf7fb8f0780cacbca1f2338d0 F test/fkey8.test 51deda7f1a1448bca95875e4a6e1a3a75b4bd7215e924e845bd60de60e4d84bf F test/fkey_malloc.test 594a7ea1fbab553c036c70813cd8bd9407d63749 F test/fordelete.test ba98f14446b310f9c9d935b97ec748753d0144a28b356ba30d1f4f6958fdde5c +F test/fork-test.c 9ac2e6423a1d38df3d6be0e8ac15608b545de21e2b19d9d876254c5931b63edb F test/format4.test eeae341953db8b6bda7f549044797c3278a6cc345d11ada81471671b654f8ef4 F test/fp-speed-1.c b37de94eba034e1703668816225f54510ec60fb0685406608cc707afe6b8234d F test/fpconv1.test d5d8aa0c427533006c112fb1957cdd1ea68c1d0709470dabb9ca02c2e4c06ad8 @@ -2198,8 +2199,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P a4e976a030851357049e672bbc0ff66d9cc152b3d5f8e03fff36a7c6f060a755 -R cbde25c924d15d902cdf437d5236e99e +P f0e917fcf51b59f8ccfe5b9341937341d0e6016eb275d6c33dcb10b0b301a9da +R 6602510d12e6c22a0b1d041f1323efab U drh -Z 9feaba1d713c9ff220e94d51317a455c +Z a609b555f7a83a611caf3857959d5dfd # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 5cd62d912c..c86cacfebd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -f0e917fcf51b59f8ccfe5b9341937341d0e6016eb275d6c33dcb10b0b301a9da +0611e2b0cf3f33c28cc9ff6c5da7ebba2033bcbda5b1072a30021a3e1fb4e738 diff --git a/test/fork-test.c b/test/fork-test.c new file mode 100644 index 0000000000..8a9b4b4afa --- /dev/null +++ b/test/fork-test.c @@ -0,0 +1,310 @@ +/* +** The program demonstrates how a child process created using fork() +** can continue to use SQLite for a database that the parent had opened and +** and was writing into when the fork() occurred. +** +** This program executes the following steps: +** +** 1. Create a new database file. Open it, and populate it. +** 2. Start a transaction and make changes. +** ^-- close the transaction prior to fork() if --commit-before-fork +** 3. Fork() +** 4. In the child, close the database connection. Special procedures +** are needed to close the database connection in the child. See the +** implementation below. +** 5. Commit the transaction in the parent. +** 6. Verify that the transaction committed in the parent. +** 7. In the child, after a delay to allow time for (5) and (6), +** open a new database connection and verify that the transaction +** committed by (5) is seen. +** 8. Add make further changes and commit them in the child, using the +** new database connection. +** 9. In the parent, after a delay to account for (8), verify that +** the new transaction added by (8) can be seen. +** +** Usage: +** +** fork-test FILENAME [options] +** +** Options: +** +** --wal Run the database in WAL mode +** --vfstrace Enable VFS tracing for debugging +** --commit-before-fork COMMIT prior to the fork() in step 3 +** --delay-after-4 N Pause for N seconds after step 4 +** +** How To Compile: +** +** gcc -O0 -g -Wall -I$(SQLITESRC) \ +** f1.c $(SQLITESRC)/ext/misc/vfstrace.c $(SQLITESRC/sqlite3.c \ +** -ldl -lpthread -lm +** +** Test procedure: +** +** (1) Run "fork-test x1.db". Verify no I/O errors occur and that +** both parent and child see all three rows in the t1 table. +** +** (2) Repeat (1) adding the --wal option. +** +** (3) Repeat (1) and (2) adding the --commit-before-fork option. +** +** (4) Repeat all prior steps adding the --delay-after-4 option with +** a timeout of 15 seconds or so. Then, while both parent and child +** are paused, run the CLI against the x1.db database from a separate +** window and verify that all the correct file locks are still working +** correctly. +** +** Take-Aways: +** +** * If a process has open SQLite database connections when it fork()s, +** the child can call exec() and all it well. Nothing special needs +** to happen. +** +** * If a process has open SQLite database connections when it fork()s, +** the child can do anything that does not involve using SQLite without +** causing problems in the parent. No special actions are needed in +** the child. +** +** * If a process has open SQLite database connections when it fork()s, +** the child can call sqlite3_close() on those database connections +** as long as there were no pending write transactions when the fork() +** occurred. +** +** * If a process has open SQLite database connections that are in the +** middle of a write transaction and then the processes fork()s, the +** child process should close the database connections using the +** procedures demonstrated in Step 4 below before trying to do anything +** else with SQLite. +** +** * If a child process can safely close SQLite database connections that +** it inherited via fork() using the procedures shown in Step 4 below +** even if the database connections were not involved in a write +** transaction at the time of the fork(). The special procedures are +** required if a write transaction was active. They are optional +** otherwise. No harm results from using the special procedures when +** they are not necessary. +** +** * Child processes that use SQLite should open their own database +** connections. They should not attempt to use a database connection +** that is inherited from the parent. +*/ +#include +#include +#include +#include +#include +#include +#include +#include "sqlite3.h" + +/* +** Process ID of the parent +*/ +static pid_t parentPid = 0; + +/* +** Return either "parent" or "child", as appropriate. +*/ +static const char *whoAmI(void){ + return getpid()==parentPid ? "parent" : "child"; +} + +/* +** This is an sqlite3_exec() callback routine that prints all results. +*/ +static int execCallback(void *pNotUsed, int nCol, char **aVal, char **aCol){ + int i; + const char *zWho = whoAmI(); + for(i=0; ipMethods && pJrnl->pMethods->xFileControl ){ + pJrnl->pMethods->xFileControl(pJrnl, SQLITE_FCNTL_NULL_IO, 0); + } + } + sqlite3_close(db); + /* + ** End of special close procedures for SQLite database connections + ** inherited via fork(). + ***********************************************************************/ + + printf("%s: database connection closed\n", whoAmI()); fflush(stdout); + }else{ + /* Pause the parent briefly to give the child a chance to close its + ** database connection */ + sleep(1); + } + + if( nDelayAfter4>0 ){ + printf("%s: Delay for %d seconds\n", whoAmI(), nDelayAfter4); + fflush(stdout); + sleep(nDelayAfter4); + printf("%s: Continue after %d delay\n", whoAmI(), nDelayAfter4); + fflush(stdout); + } + + /** Step 5 **/ + if( child!=0 ){ + printf("Step 5:\n"); + if( !bCommitBeforeFork ) sqlExec(db, "COMMIT", 0); + sqlExec(db, "SELECT x FROM t1;", 1); + } + + + /** Step 7 **/ + if( child==0 ){ + sleep(2); + printf("Steps 7 and 8:\n"); + rc = sqlite3_open(zFilename, &db); + if( rc ){ + printf("Child unable to reopen the database. rc = %d\n", rc); + exit(1); + } + sqlExec(db, "SELECT * FROM t1;", 1); + + /** Step 8 **/ + sqlExec(db, "INSERT INTO t1 VALUES('Third row');", 0); + sqlExec(db, "SELECT * FROM t1;", 1); + sleep(1); + return 0; + } + c2 = wait(&status); + printf("Process %d finished with status %d\n", c2, status); + + /** Step 9 */ + if( child!=0 ){ + printf("Step 9:\n"); + sqlExec(db, "SELECT * FROM t1;", 1); + } + + return 0; +}