AryaWu/sqlite
0
1/*2** The program demonstrates how a child process created using fork()3** can continue to use SQLite for a database that the parent had opened and4** and was writing into when the fork() occurred.5**6** This program executes the following steps:7**8** 1. Create a new database file. Open it, and populate it.9** 2. Start a transaction and make changes.10** ^-- close the transaction prior to fork() if --commit-before-fork11** 3. Fork()12** 4. In the child, close the database connection. Special procedures13** are needed to close the database connection in the child. See the14** implementation below.15** 5. Commit the transaction in the parent.16** 6. Verify that the transaction committed in the parent.17** 7. In the child, after a delay to allow time for (5) and (6),18** open a new database connection and verify that the transaction19** committed by (5) is seen.20** 8. Add make further changes and commit them in the child, using the21** new database connection.22** 9. In the parent, after a delay to account for (8), verify that23** the new transaction added by (8) can be seen.24**25** Usage:26**27** fork-test FILENAME [options]28**29** Options:30**31** --wal Run the database in WAL mode32** --vfstrace Enable VFS tracing for debugging33** --commit-before-fork COMMIT prior to the fork() in step 334** --delay-after-4 N Pause for N seconds after step 435**36** How To Compile:37**38** gcc -O0 -g -Wall -I$(SQLITESRC) \39** f1.c $(SQLITESRC)/ext/misc/vfstrace.c $(SQLITESRC/sqlite3.c \40** -ldl -lpthread -lm41**42** Test procedure:43**44** (1) Run "fork-test x1.db". Verify no I/O errors occur and that45** both parent and child see all three rows in the t1 table.46**47** (2) Repeat (1) adding the --wal option.48**49** (3) Repeat (1) and (2) adding the --commit-before-fork option.50**51** (4) Repeat all prior steps adding the --delay-after-4 option with52** a timeout of 15 seconds or so. Then, while both parent and child53** are paused, run the CLI against the x1.db database from a separate54** window and verify that all the correct file locks are still working55** correctly.56**57** Take-Aways:58**59** * If a process has open SQLite database connections when it fork()s,60** the child can call exec() and all it well. Nothing special needs61** to happen.62**63** * If a process has open SQLite database connections when it fork()s,64** the child can do anything that does not involve using SQLite without65** causing problems in the parent. No special actions are needed in66** the child.67**68** * If a process has open SQLite database connections when it fork()s,69** the child can call sqlite3_close() on those database connections70** as long as there were no pending write transactions when the fork()71** occurred.72**73** * If a process has open SQLite database connections that are in the74** middle of a write transaction and then the processes fork()s, the75** child process should close the database connections using the76** procedures demonstrated in Step 4 below before trying to do anything77** else with SQLite.78**79** * If a child process can safely close SQLite database connections that80** it inherited via fork() using the procedures shown in Step 4 below81** even if the database connections were not involved in a write82** transaction at the time of the fork(). The special procedures are83** required if a write transaction was active. They are optional84** otherwise. No harm results from using the special procedures when85** they are not necessary.86**87** * Child processes that use SQLite should open their own database88** connections. They should not attempt to use a database connection89** that is inherited from the parent.90*/91#include <stdio.h>92#include <string.h>93#include <sys/types.h>94#include <sys/wait.h>95#include <unistd.h>96#include <stdlib.h>97#include <errno.h>98#include "sqlite3.h"99 100/*101** Process ID of the parent102*/103static pid_t parentPid = 0;104 105/*106** Return either "parent" or "child", as appropriate.107*/108static const char *whoAmI(void){109 return getpid()==parentPid ? "parent" : "child";110}111 112/*113** This is an sqlite3_exec() callback routine that prints all results.114*/115static int execCallback(void *pNotUsed, int nCol, char **aVal, char **aCol){116 int i;117 const char *zWho = whoAmI();118 for(i=0; i<nCol; i++){119 const char *zVal = aVal[i];120 const char *zCol = aCol[i];121 if( zVal==0 ) zVal = "NULL";122 if( zCol==0 ) zCol = "NULL";123 printf("%s: %s = %s\n", zWho, zCol, zVal);124 fflush(stdout);125 }126 return 0;127}128 129/*130** Execute one or more SQL statements.131*/132static void sqlExec(sqlite3 *db, const char *zSql, int bCallback){133 int rc;134 char *zErr = 0;135 printf("%s: %s\n", whoAmI(), zSql);136 fflush(stdout);137 rc = sqlite3_exec(db, zSql, bCallback ? execCallback : 0, 0, &zErr);138 if( rc || zErr!=0 ){139 printf("%s: %s: rc=%d: %s\n", 140 whoAmI(), zSql, rc, zErr);141 exit(1);142 }143}144 145/*146** Trace callback for the vfstrace extension.147*/148static int vfsTraceCallback(const char *zMsg, void *pNotUsed){149 printf("%s: %s", whoAmI(), zMsg);150 fflush(stdout);151 return 0;152}153 154/* External VFS module provided by ext/misc/vfstrace.c155*/156extern int vfstrace_register(157 const char *zTraceName, // Name of the newly constructed VFS158 const char *zOldVfsName, // Name of the underlying VFS159 int (*xOut)(const char*,void*), // Output routine. ex: fputs160 void *pOutArg, // 2nd argument to xOut. ex: stderr161 int makeDefault // Make the new VFS the default162);163 164 165int main(int argc, char **argv){166 sqlite3 *db;167 int rc;168 int i;169 int useWal = 0;170 const char *zFilename = 0;171 pid_t child = 0, c2;172 int status;173 int bCommitBeforeFork = 0;174 int nDelayAfter4 = 0;175 176 for(i=1; i<argc; i++){177 const char *z = argv[i];178 if( z[0]=='-' && z[1]=='-' && z[2]!=0 ) z++;179 if( strcmp(z, "-wal")==0 ){180 useWal = 1;181 }else if( strcmp(z, "-commit-before-fork")==0 ){182 bCommitBeforeFork = 1;183 }else if( strcmp(z, "-delay-after-4")==0 && i+1<argc ){184 i++;185 nDelayAfter4 = atoi(argv[i]);186 }else if( strcmp(z, "-vfstrace")==0 ){187 vfstrace_register("vfstrace", 0, vfsTraceCallback, 0, 1);188 }else if( z[0]=='-' ){189 printf("unknown option: \"%s\"\n", argv[i]);190 exit(1);191 }else if( zFilename!=0 ){192 printf("unknown argument: \"%s\"\n", argv[i]);193 exit(1);194 }else{195 zFilename = argv[i];196 }197 }198 if( zFilename==0 ){199 printf("Usage: %s FILENAME\n", argv[0]);200 return 1;201 }202 203 /** Step 1 **/204 printf("Step 1:\n");205 parentPid = getpid();206 unlink(zFilename);207 rc = sqlite3_open(zFilename, &db);208 if( rc ){209 printf("sqlite3_open() returns %d\n", rc);210 exit(1);211 }212 if( useWal ){213 sqlExec(db, "PRAGMA journal_mode=WAL;", 0);214 }215 sqlExec(db, "CREATE TABLE t1(x);", 0);216 sqlExec(db, "INSERT INTO t1 VALUES('First row');", 0);217 sqlExec(db, "SELECT x FROM t1;", 1);218 219 /** Step 2 **/220 printf("Step 2:\n");221 sqlExec(db, "BEGIN IMMEDIATE;", 0);222 sqlExec(db, "INSERT INTO t1 VALUES('Second row');", 0);223 sqlExec(db, "SELECT x FROM t1;", 1);224 if( bCommitBeforeFork ) sqlExec(db, "COMMIT", 0);225 226 /** Step 3 **/227 printf("Step 3:\n"); fflush(stdout);228 child = fork();229 if( child!=0 ){230 printf("Parent = %d\nChild = %d\n", getpid(), child);231 }232 233 /** Step 4 **/234 if( child==0 ){235 int k;236 printf("Step 4:\n"); fflush(stdout);237 238 /***********************************************************************239 ** The following block of code closes the database connection without240 ** rolling back or changing any files on disk. This is necessary to241 ** preservce the pending transaction in the parent. 242 */243 for(k=0; 1/*exit-by-break*/; k++){244 const char *zDbName = sqlite3_db_name(db, k);245 sqlite3_file *pJrnl = 0;246 if( k==1 ) continue;247 if( zDbName==0 ) break;248 sqlite3_file_control(db, zDbName, SQLITE_FCNTL_NULL_IO, 0);249 sqlite3_file_control(db, zDbName, SQLITE_FCNTL_JOURNAL_POINTER, &pJrnl);250 if( pJrnl && pJrnl->pMethods && pJrnl->pMethods->xFileControl ){251 pJrnl->pMethods->xFileControl(pJrnl, SQLITE_FCNTL_NULL_IO, 0);252 }253 } 254 sqlite3_close(db);255 /*256 ** End of special close procedures for SQLite database connections257 ** inherited via fork().258 ***********************************************************************/259 260 printf("%s: database connection closed\n", whoAmI()); fflush(stdout);261 }else{262 /* Pause the parent briefly to give the child a chance to close its263 ** database connection */264 sleep(1);265 }266 267 if( nDelayAfter4>0 ){268 printf("%s: Delay for %d seconds\n", whoAmI(), nDelayAfter4);269 fflush(stdout);270 sleep(nDelayAfter4);271 printf("%s: Continue after %d delay\n", whoAmI(), nDelayAfter4);272 fflush(stdout);273 }274 275 /** Step 5 **/276 if( child!=0 ){277 printf("Step 5:\n");278 if( !bCommitBeforeFork ) sqlExec(db, "COMMIT", 0);279 sqlExec(db, "SELECT x FROM t1;", 1);280 } 281 282 283 /** Step 7 **/284 if( child==0 ){285 sleep(2);286 printf("Steps 7 and 8:\n");287 rc = sqlite3_open(zFilename, &db);288 if( rc ){289 printf("Child unable to reopen the database. rc = %d\n", rc);290 exit(1);291 }292 sqlExec(db, "SELECT * FROM t1;", 1);293 294 /** Step 8 **/295 sqlExec(db, "INSERT INTO t1 VALUES('Third row');", 0);296 sqlExec(db, "SELECT * FROM t1;", 1);297 sleep(1);298 return 0;299 }300 c2 = wait(&status);301 printf("Process %d finished with status %d\n", c2, status);302 303 /** Step 9 */304 if( child!=0 ){305 printf("Step 9:\n");306 sqlExec(db, "SELECT * FROM t1;", 1);307 }308 309 return 0;310}311 