diff --git a/Makefile.in b/Makefile.in index 78684bbd52..1d67c30350 100644 --- a/Makefile.in +++ b/Makefile.in @@ -189,6 +189,7 @@ TESTSRC = \ $(TOP)/src/test3.c \ $(TOP)/src/test4.c \ $(TOP)/src/test5.c \ + $(TOP)/src/test6.c \ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbe.c \ @@ -425,15 +426,8 @@ testfixture$(TEXE): $(TOP)/src/tclsqlite.c libsqlite3.la $(TESTSRC) -o testfixture $(TESTSRC) $(TOP)/src/tclsqlite.c \ libsqlite3.la $(LIBTCL) -crashtest$(TEXE): $(TOP)/src/tclsqlite.c libsqlite3.la $(TESTSRC) $(TOP)/src/os_test.c - $(LTLINK) -DOS_TEST=1 -DTCLSH=1 -DSQLITE_TEST=1 \ - -o crashtest \ - $(TESTSRC) $(TOP)/src/os_test.c $(TOP)/src/tclsqlite.c \ - libsqlite3.la $(LIBTCL) - - -fulltest: testfixture$(TEXE) sqlite3$(TEXE) crashtest$(TEXE) +fulltest: testfixture$(TEXE) sqlite3$(TEXE) ./testfixture $(TOP)/test/all.test test: testfixture$(TEXE) sqlite3$(TEXE) diff --git a/main.mk b/main.mk index ba90583fe3..e6b6ea9880 100644 --- a/main.mk +++ b/main.mk @@ -131,6 +131,7 @@ TESTSRC = \ $(TOP)/src/test3.c \ $(TOP)/src/test4.c \ $(TOP)/src/test5.c \ + $(TOP)/src/test6.c \ $(TOP)/src/utf.c \ $(TOP)/src/util.c \ $(TOP)/src/vdbe.c \ @@ -356,13 +357,7 @@ testfixture$(EXE): $(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC) $(TESTSRC) $(TOP)/src/tclsqlite.c \ libsqlite3.a $(LIBTCL) $(THREADLIB) -crashtest: $(TOP)/src/tclsqlite.c libsqlite3.a $(TESTSRC) $(TOP)/src/os_test.c - $(TCCX) $(TCL_FLAGS) -DOS_TEST=1 -DTCLSH=1 -DSQLITE_TEST=1 \ - -o crashtest \ - $(TESTSRC) $(TOP)/src/os_test.c $(TOP)/src/tclsqlite.c \ - libsqlite3.a $(LIBTCL) $(THREADLIB) - -fulltest: testfixture$(EXE) sqlite3$(EXE) crashtest +fulltest: testfixture$(EXE) sqlite3$(EXE) ./testfixture$(EXE) $(TOP)/test/all.test test: testfixture$(EXE) sqlite3$(EXE) @@ -557,7 +552,7 @@ install: sqlite3 libsqlite3.a sqlite3.h mv sqlite3.h /usr/include clean: - rm -f *.o sqlite3 libsqlite3.a sqlite3.h opcodes.* crashtest + rm -f *.o sqlite3 libsqlite3.a sqlite3.h opcodes.* rm -f lemon lempar.c parse.* sqlite*.tar.gz mkkeywordhash keywordhash.h rm -f $(PUBLISH) rm -f *.da *.bb *.bbg gmon.out diff --git a/manifest b/manifest index 4e5d27db94..4724112ce0 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ -C Ensure\sa\slow\ssystem\slimit\son\sthe\snumber\sof\sopen\sfiles\sdoes\snot\scause\smanydb.test\sto\sfail.\s(CVS\s2784) -D 2005-11-25T10:55:58 -F Makefile.in 7bed51530f32af7fe1945261c388d80998b39a12 +C Modify\sthe\sOS\slayer\sso\sthat\sI/O\sroutines\sare\scalled\sthrough\sa\svirtual\sfunction\ntable.\s\sThe\sos_test.c\smodule\shas\sbeen\sreplaced\sby\stest6.c.\s\sThe\scrash\stests\nare\sbusted\snow\sand\sneed\sto\sbe\sfixed.\s(CVS\s2785) +D 2005-11-26T00:25:01 +F Makefile.in 28a2772cd9e03ba758c2a052813092cdb9da73bf F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 F VERSION b818cce180263e590a00ad4509a713892c3eecea @@ -19,7 +19,7 @@ F doc/lemon.html f0f682f50210928c07e562621c3b7e8ab912a538 F doc/report1.txt a031aaf37b185e4fa540223cb516d3bccec7eeac F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826 -F main.mk c2c2f377be833dff5ec17840b9329d561ceec280 +F main.mk 1cf8c00ee4439d3332a2da771fe168ed8420c2d7 F mkdll.sh 5ec23622515d5bf8969404e80cfb5e220ddf0512 F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d F mkopcodeh.awk 071dbba4eaf56c8d643baf4604a043af35683316 @@ -50,18 +50,18 @@ F src/insert.c da031c3ed8e1675fac891990095d277c2ba6e205 F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b F src/main.c 97bb830cdbd378d1f87469618471f52d9d263d09 F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070 -F src/os.h c51f2747f7bd1840447e2c7d26db749604626814 +F src/os.h 64564ddc5b70f8968d1df5039810a2e68cebe769 F src/os_common.h d74a11728ad2444b6b695b94c28c06881f049e49 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3 -F src/os_unix.c 1c51efc1755b5413dc69c8500b78646babd8065d +F src/os_unix.c 57f47fd9172991550b2af15b18d8cc9ccb90d307 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e -F src/os_win.c fbccc85e7011174068c27d54256746321a1f0059 +F src/os_win.c 5d282b4fb2afdeefd0857357206510e72192302c F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c ca23cdff9e67a8e826e4796c60f06db6aab69b72 +F src/pager.c 561657a20ecbc5fa4c8e1fcfe019006553fd39a1 F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140 F src/parse.y e4d57c2fd5cc02f19822ec41f6dc2bfc9bc85609 -F src/pragma.c b40189967155a522433b8470f363192a927ba22c +F src/pragma.c e278b3f722379ab9d630a1569ac05f586f01f4db F src/prepare.c fc098db25d2a121affb08686cf04833fd50452d4 F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8 F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4 @@ -70,23 +70,24 @@ F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3 F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2 F src/sqliteInt.h 4148c9778e350014c2e27b332d7a2ef7278fe62e F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316 -F src/tclsqlite.c 4f274fae3d4a1863451a553dd8e5015747a5d91d -F src/test1.c 7a4e6f08ed6f7ce1e82949cb27954ab676f6e048 -F src/test2.c d55861d8ba5a8b434544da366ba6efe206e1ec97 +F src/tclsqlite.c a497c3adfd2c85da6a934331ec0041e47884fbcb +F src/test1.c 415c9c4571bf9ecbec3a7bba3c4338efd83fc137 +F src/test2.c 360cfa7fe17a4fe67f6d19699cd6d59fca4ff09f F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7 F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5 +F src/test6.c d86ca78abd3c37149add00efc6538128e776714c F src/tokenize.c bdb79702217af49eba44c2a3b4f5fc7bd9ed2917 F src/trigger.c aea0283a3ef729a3e9c8dc5dc1a11c9fcc0a12a7 F src/update.c fec7665138ccf2a2133f11dcd24c1134c6b33526 F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c F src/util.c 48fecbbef4391d102a23096d32f0d74173428406 -F src/vacuum.c baae8681282c7a03900043043dc7ce07d43b5a1e -F src/vdbe.c f90a0fff5dac2ae11f880fb54e78132640199c44 +F src/vacuum.c 3a9d801b642a2004e8a99efa8d19ceef3f79d8df +F src/vdbe.c 88a85e681522e82e14c8d08adacccbe4e96dd1c9 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13 F src/vdbeInt.h 0055c37eccbf3a189fd893a90f8eb6a5fa60c871 F src/vdbeapi.c 85bbe1d0243a89655433d60711b4bd71979b59cd -F src/vdbeaux.c eb5f7185a4a714b352c0b6dc804ad3180e03ec06 +F src/vdbeaux.c 3dca9c04c07dda17f0cb06b3a552e1e23106232f F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5 F src/vdbemem.c cd9609c1e7f71ec76d9840c84c3a57ebfa6539cf F src/where.c 9c260db859047a44fe8219716ee5f0d2bd647420 @@ -135,7 +136,7 @@ F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638 F test/conflict.test bc7b81670abfd6ca51cbcbf46ef23cd52784fafd F test/corrupt.test 18c7a995b1af76a8c8600b996257f2c7b7bff083 F test/corrupt2.test 88342570828f2b8cbbd8369eff3891f5c0bdd5ba -F test/crash.test 131259005bb5dbe776eba8de99408a493c69c06c +F test/crash.test 504a4eee0ce78f00d16a5a324e42964bd85164f5 F test/crashtest1.c 09c1c7d728ccf4feb9e481671e29dda5669bbcc2 F test/date.test 30ca15e608a45d868fd419c901795382efe27020 F test/default.test 252298e42a680146b1dd64f563b95bdf088d94fb @@ -218,7 +219,7 @@ F test/table.test ec0e6c2186bb8f6824f470caa118524dfd8fe057 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1 F test/tclsqlite.test 2da3e4b3a79b13c1511c9d0cd995e08f8362e782 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b -F test/tester.tcl bf4e288cd8b968e4514c0bdee27f4f31b21b7271 +F test/tester.tcl 1dbcac19ed71f68f24b7f5c6c134568d551092ba F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b @@ -321,7 +322,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P f9f5ecbb79f03ba8e7d6a036a562252319e82abc -R ccf6548dbbfa01c325708c34e3a65ec7 -U danielk1977 -Z 66667bbf44f1416dcd406944ed2382c1 +P dba2cc0d0e0c648c1a4798bc72913e3b19acba6e +R 0354953fb5e466e6ce44febcde77bb5c +U drh +Z e7c0bd1253129b8abe29fc40e7e0d238 diff --git a/manifest.uuid b/manifest.uuid index 65d090ae6f..948ee9f5e3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -dba2cc0d0e0c648c1a4798bc72913e3b19acba6e \ No newline at end of file +1f69aec285dd8e26bd739d5e44bb50fe03a9682a \ No newline at end of file diff --git a/src/os.h b/src/os.h index a161d134a6..3646308776 100644 --- a/src/os.h +++ b/src/os.h @@ -172,28 +172,32 @@ extern unsigned int sqlite3_pending_byte; #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 - -int sqlite3OsDelete(const char*); -int sqlite3OsFileExists(const char*); -int sqlite3OsOpenReadWrite(const char*, OsFile*, int*); -int sqlite3OsOpenExclusive(const char*, OsFile*, int); -int sqlite3OsOpenReadOnly(const char*, OsFile*); -int sqlite3OsOpenDirectory(const char*, OsFile*); -int sqlite3OsSyncDirectory(const char*); -int sqlite3OsTempFileName(char*); -int sqlite3OsIsDirWritable(char*); -int sqlite3OsClose(OsFile*); -int sqlite3OsRead(OsFile*, void*, int amt); -int sqlite3OsWrite(OsFile*, const void*, int amt); -int sqlite3OsSeek(OsFile*, i64 offset); -int sqlite3OsSync(OsFile*, int); -int sqlite3OsTruncate(OsFile*, i64 size); -int sqlite3OsFileSize(OsFile*, i64 *pSize); -char *sqlite3OsFullPathname(const char*); -int sqlite3OsLock(OsFile*, int); -int sqlite3OsUnlock(OsFile*, int); -int sqlite3OsCheckReservedLock(OsFile *id); - +/* +** A single global instance of the following structure holds pointers to the +** various disk I/O routines. +*/ +extern struct sqlite3IoVtbl { + int (*xDelete)(const char*); + int (*xFileExists)(const char*); + int (*xOpenReadWrite)(const char*, OsFile*, int*); + int (*xOpenExclusive)(const char*, OsFile*, int); + int (*xOpenReadOnly)(const char*, OsFile*); + int (*xOpenDirectory)(const char*, OsFile*); + int (*xSyncDirectory)(const char*); + int (*xTempFileName)(char*); + int (*xIsDirWritable)(char*); + int (*xClose)(OsFile*); + int (*xRead)(OsFile*, void*, int amt); + int (*xWrite)(OsFile*, const void*, int amt); + int (*xSeek)(OsFile*, i64 offset); + int (*xSync)(OsFile*, int); + int (*xTruncate)(OsFile*, i64 size); + int (*xFileSize)(OsFile*, i64 *pSize); + char *(*xFullPathname)(const char*); + int (*xLock)(OsFile*, int); + int (*xUnlock)(OsFile*, int); + int (*xCheckReservedLock)(OsFile *id); +} sqlite3Io; /* The interface for file I/O is above. Other miscellaneous functions ** are below */ @@ -204,4 +208,5 @@ int sqlite3OsCurrentTime(double*); void sqlite3OsEnterMutex(void); void sqlite3OsLeaveMutex(void); + #endif /* _SQLITE_OS_H_ */ diff --git a/src/os_unix.c b/src/os_unix.c index 2d6f404131..33185ed82c 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -481,7 +481,7 @@ static int findLockInfo( /* ** Delete the named file */ -int sqlite3OsDelete(const char *zFilename){ +static int unixDelete(const char *zFilename){ unlink(zFilename); return SQLITE_OK; } @@ -489,7 +489,7 @@ int sqlite3OsDelete(const char *zFilename){ /* ** Return TRUE if the named file exists. */ -int sqlite3OsFileExists(const char *zFilename){ +static int unixFileExists(const char *zFilename){ return access(zFilename, 0)==0; } @@ -506,7 +506,7 @@ int sqlite3OsFileExists(const char *zFilename){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id and *pReadonly unchanged. */ -int sqlite3OsOpenReadWrite( +static int unixOpenReadWrite( const char *zFilename, OsFile *id, int *pReadonly @@ -560,7 +560,7 @@ int sqlite3OsOpenReadWrite( ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +static int unixOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ int rc; assert( !id->isOpen ); if( access(zFilename, 0)==0 ){ @@ -599,7 +599,7 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ +static int unixOpenReadOnly(const char *zFilename, OsFile *id){ int rc; assert( !id->isOpen ); SET_THREADID(id); @@ -638,7 +638,7 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id unchanged. */ -int sqlite3OsOpenDirectory( +static int unixOpenDirectory( const char *zDirname, OsFile *id ){ @@ -668,7 +668,7 @@ char *sqlite3_temp_directory = 0; ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ -int sqlite3OsTempFileName(char *zBuf){ +static int unixTempFileName(char *zBuf){ static const char *azDirs[] = { 0, "/var/tmp", @@ -704,28 +704,28 @@ int sqlite3OsTempFileName(char *zBuf){ return SQLITE_OK; } -#ifndef SQLITE_OMIT_PAGER_PRAGMAS /* ** Check that a given pathname is a directory and is writable ** */ -int sqlite3OsIsDirWritable(char *zBuf){ +static int unixIsDirWritable(char *zBuf){ +#ifndef SQLITE_OMIT_PAGER_PRAGMAS struct stat buf; if( zBuf==0 ) return 0; if( zBuf[0]==0 ) return 0; if( stat(zBuf, &buf) ) return 0; if( !S_ISDIR(buf.st_mode) ) return 0; if( access(zBuf, 07) ) return 0; +#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ return 1; } -#endif /* SQLITE_OMIT_PAGER_PRAGMAS */ /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ +static int unixRead(OsFile *id, void *pBuf, int amt){ int got; assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); @@ -746,7 +746,7 @@ int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. */ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ +static int unixWrite(OsFile *id, const void *pBuf, int amt){ int wrote = 0; assert( id->isOpen ); assert( amt>0 ); @@ -769,7 +769,7 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ /* ** Move the read/write pointer in a file. */ -int sqlite3OsSeek(OsFile *id, i64 offset){ +static int unixSeek(OsFile *id, i64 offset){ assert( id->isOpen ); SEEK(offset/1024 + 1); #ifdef SQLITE_TEST @@ -863,7 +863,7 @@ static int full_fsync(int fd, int fullSync, int dataOnly){ ** the directory entry for the journal was never created) and the transaction ** will not roll back - possibly leading to database corruption. */ -int sqlite3OsSync(OsFile *id, int dataOnly){ +static int unixSync(OsFile *id, int dataOnly){ assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); TRACE2("SYNC %-3d\n", id->h); @@ -891,7 +891,7 @@ int sqlite3OsSync(OsFile *id, int dataOnly){ ** before making changes to individual journals on a multi-database commit. ** The F_FULLFSYNC option is not needed here. */ -int sqlite3OsSyncDirectory(const char *zDirname){ +static int unixSyncDirectory(const char *zDirname){ #ifdef SQLITE_DISABLE_DIRSYNC return SQLITE_OK; #else @@ -912,7 +912,7 @@ int sqlite3OsSyncDirectory(const char *zDirname){ /* ** Truncate an open file to a specified size */ -int sqlite3OsTruncate(OsFile *id, i64 nByte){ +static int unixTruncate(OsFile *id, i64 nByte){ assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); return ftruncate(id->h, nByte)==0 ? SQLITE_OK : SQLITE_IOERR; @@ -921,7 +921,7 @@ int sqlite3OsTruncate(OsFile *id, i64 nByte){ /* ** Determine the current size of a file in bytes */ -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ +static int unixFileSize(OsFile *id, i64 *pSize){ struct stat buf; assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); @@ -938,7 +938,7 @@ int sqlite3OsFileSize(OsFile *id, i64 *pSize){ ** non-zero. If the file is unlocked or holds only SHARED locks, then ** return zero. */ -int sqlite3OsCheckReservedLock(OsFile *id){ +static int unixCheckReservedLock(OsFile *id){ int r = 0; assert( id->isOpen ); @@ -1012,7 +1012,7 @@ static const char * locktypeName(int locktype){ ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ -int sqlite3OsLock(OsFile *id, int locktype){ +static int unixLock(OsFile *id, int locktype){ /* The following describes the implementation of the various locks and ** lock transitions in terms of the POSIX advisory shared and exclusive ** lock primitives (called read-locks and write-locks below, to avoid @@ -1208,7 +1208,7 @@ end_lock: ** is NO_LOCK. If the second argument is SHARED_LOCK, this routine ** might return SQLITE_IOERR instead of SQLITE_OK. */ -int sqlite3OsUnlock(OsFile *id, int locktype){ +static int unixUnlock(OsFile *id, int locktype){ struct lockInfo *pLock; struct flock lock; int rc = SQLITE_OK; @@ -1291,10 +1291,10 @@ int sqlite3OsUnlock(OsFile *id, int locktype){ /* ** Close a file. */ -int sqlite3OsClose(OsFile *id){ +static int unixClose(OsFile *id){ if( !id->isOpen ) return SQLITE_OK; if( CHECK_THREADID(id) ) return SQLITE_MISUSE; - sqlite3OsUnlock(id, NO_LOCK); + sqlite3Io.xUnlock(id, NO_LOCK); if( id->dirfd>=0 ) close(id->dirfd); id->dirfd = -1; sqlite3OsEnterMutex(); @@ -1333,7 +1333,7 @@ int sqlite3OsClose(OsFile *id){ ** The calling function is responsible for freeing this space once it ** is no longer needed. */ -char *sqlite3OsFullPathname(const char *zRelative){ +static char *unixFullPathname(const char *zRelative){ char *zFull = 0; if( zRelative[0]=='/' ){ sqlite3SetString(&zFull, zRelative, (char*)0); @@ -1351,6 +1351,33 @@ char *sqlite3OsFullPathname(const char *zRelative){ } +/* +** This is the structure that defines all of the I/O routines. +*/ +struct sqlite3IoVtbl sqlite3Io = { + unixDelete, + unixFileExists, + unixOpenReadWrite, + unixOpenExclusive, + unixOpenReadOnly, + unixOpenDirectory, + unixSyncDirectory, + unixTempFileName, + unixIsDirWritable, + unixClose, + unixRead, + unixWrite, + unixSeek, + unixSync, + unixTruncate, + unixFileSize, + unixFullPathname, + unixLock, + unixUnlock, + unixCheckReservedLock, +}; + + #endif /* SQLITE_OMIT_DISKIO */ /*************************************************************************** ** Everything above deals with file I/O. Everything that follows deals diff --git a/src/os_win.c b/src/os_win.c index ad874ae633..b3715c74f5 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -126,7 +126,7 @@ static char *unicodeToUtf8(const WCHAR *zWideFilename){ /* ** Delete the named file */ -int sqlite3OsDelete(const char *zFilename){ +static int winDelete(const char *zFilename){ WCHAR *zWide = utf8ToUnicode(zFilename); if( zWide ){ DeleteFileW(zWide); @@ -141,7 +141,7 @@ int sqlite3OsDelete(const char *zFilename){ /* ** Return TRUE if the named file exists. */ -int sqlite3OsFileExists(const char *zFilename){ +static int winFileExists(const char *zFilename){ int exists = 0; WCHAR *zWide = utf8ToUnicode(zFilename); if( zWide ){ @@ -166,7 +166,7 @@ int sqlite3OsFileExists(const char *zFilename){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id and *pReadonly unchanged. */ -int sqlite3OsOpenReadWrite( +static int winOpenReadWrite( const char *zFilename, OsFile *id, int *pReadonly @@ -251,7 +251,7 @@ int sqlite3OsOpenReadWrite( ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ +static int winOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ HANDLE h; int fileflags; WCHAR *zWide = utf8ToUnicode(zFilename); @@ -301,7 +301,7 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ +static int winOpenReadOnly(const char *zFilename, OsFile *id){ HANDLE h; WCHAR *zWide = utf8ToUnicode(zFilename); assert( !id->isOpen ); @@ -353,7 +353,7 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){ ** On failure, the function returns SQLITE_CANTOPEN and leaves ** *id unchanged. */ -int sqlite3OsOpenDirectory( +static int winOpenDirectory( const char *zDirname, OsFile *id ){ @@ -371,7 +371,7 @@ char *sqlite3_temp_directory = 0; ** Create a temporary file name in zBuf. zBuf must be big enough to ** hold at least SQLITE_TEMPNAME_SIZE characters. */ -int sqlite3OsTempFileName(char *zBuf){ +static int winTempFileName(char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -404,7 +404,7 @@ int sqlite3OsTempFileName(char *zBuf){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; - if( !sqlite3OsFileExists(zBuf) ) break; + if( !sqlite3Io.xFileExists(zBuf) ) break; } TRACE2("TEMP FILENAME: %s\n", zBuf); return SQLITE_OK; @@ -413,7 +413,7 @@ int sqlite3OsTempFileName(char *zBuf){ /* ** Close a file. */ -int sqlite3OsClose(OsFile *id){ +static int winClose(OsFile *id){ if( id->isOpen ){ TRACE2("CLOSE %d\n", id->h); CloseHandle(id->h); @@ -428,7 +428,7 @@ int sqlite3OsClose(OsFile *id){ ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ +static int winRead(OsFile *id, void *pBuf, int amt){ DWORD got; assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); @@ -447,7 +447,7 @@ int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. */ -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ +static int winWrite(OsFile *id, const void *pBuf, int amt){ int rc = 0; DWORD wrote; assert( id->isOpen ); @@ -475,7 +475,7 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ /* ** Move the read/write pointer in a file. */ -int sqlite3OsSeek(OsFile *id, i64 offset){ +static int winSeek(OsFile *id, i64 offset){ LONG upperBits = offset>>32; LONG lowerBits = offset & 0xffffffff; DWORD rc; @@ -495,7 +495,7 @@ int sqlite3OsSeek(OsFile *id, i64 offset){ /* ** Make sure all writes to a particular file are committed to disk. */ -int sqlite3OsSync(OsFile *id, int dataOnly){ +static int winSync(OsFile *id, int dataOnly){ assert( id->isOpen ); TRACE3("SYNC %d lock=%d\n", id->h, id->locktype); if( FlushFileBuffers(id->h) ){ @@ -509,7 +509,7 @@ int sqlite3OsSync(OsFile *id, int dataOnly){ ** Sync the directory zDirname. This is a no-op on operating systems other ** than UNIX. */ -int sqlite3OsSyncDirectory(const char *zDirname){ +static int winSyncDirectory(const char *zDirname){ SimulateIOError(SQLITE_IOERR); return SQLITE_OK; } @@ -517,7 +517,7 @@ int sqlite3OsSyncDirectory(const char *zDirname){ /* ** Truncate an open file to a specified size */ -int sqlite3OsTruncate(OsFile *id, i64 nByte){ +static int winTruncate(OsFile *id, i64 nByte){ LONG upperBits = nByte>>32; assert( id->isOpen ); TRACE3("TRUNCATE %d %lld\n", id->h, nByte); @@ -530,7 +530,7 @@ int sqlite3OsTruncate(OsFile *id, i64 nByte){ /* ** Determine the current size of a file in bytes */ -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ +static int winFileSize(OsFile *id, i64 *pSize){ DWORD upperBits, lowerBits; assert( id->isOpen ); SimulateIOError(SQLITE_IOERR); @@ -579,7 +579,7 @@ static int unlockReadLock(OsFile *id){ ** Check that a given pathname is a directory and is writable ** */ -int sqlite3OsIsDirWritable(char *zDirname){ +static int winIsDirWritable(char *zDirname){ int fileAttr; WCHAR *zWide; if( zDirname==0 ) return 0; @@ -620,12 +620,12 @@ int sqlite3OsIsDirWritable(char *zDirname){ ** RESERVED -> (PENDING) -> EXCLUSIVE ** PENDING -> EXCLUSIVE ** -** This routine will only increase a lock. The sqlite3OsUnlock() routine +** This routine will only increase a lock. The winUnlock() routine ** erases all locks at once and returns us immediately to locking level 0. ** It is not possible to lower the locking level one step at a time. You ** must go straight to locking level 0. */ -int sqlite3OsLock(OsFile *id, int locktype){ +static int winLock(OsFile *id, int locktype){ int rc = SQLITE_OK; /* Return code from subroutines */ int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set id->locktype to this value before exiting */ @@ -735,7 +735,7 @@ int sqlite3OsLock(OsFile *id, int locktype){ ** file by this or any other process. If such a lock is held, return ** non-zero, otherwise zero. */ -int sqlite3OsCheckReservedLock(OsFile *id){ +static int winCheckReservedLock(OsFile *id){ int rc; assert( id->isOpen ); if( id->locktype>=RESERVED_LOCK ){ @@ -763,7 +763,7 @@ int sqlite3OsCheckReservedLock(OsFile *id){ ** is NO_LOCK. If the second argument is SHARED_LOCK then this routine ** might return SQLITE_IOERR; */ -int sqlite3OsUnlock(OsFile *id, int locktype){ +static int winUnlock(OsFile *id, int locktype){ int type; int rc = SQLITE_OK; assert( id->isOpen ); @@ -798,7 +798,7 @@ int sqlite3OsUnlock(OsFile *id, int locktype){ ** The calling function is responsible for freeing this space once it ** is no longer needed. */ -char *sqlite3OsFullPathname(const char *zRelative){ +static char *winFullPathname(const char *zRelative){ char *zNotUsed; char *zFull; WCHAR *zWide; @@ -829,6 +829,32 @@ char *sqlite3OsFullPathname(const char *zRelative){ return zFull; } +/* +** This is the structure that defines all of the I/O routines. +*/ +struct sqlite3IoVtbl sqlite3Io = { + winDelete, + winFileExists, + winOpenReadWrite, + winOpenExclusive, + winOpenReadOnly, + winOpenDirectory, + winSyncDirectory, + winTempFileName, + winIsDirWritable, + winClose, + winRead, + winWrite, + winSeek, + winSync, + winTruncate, + winFileSize, + winFullPathname, + winLock, + winUnlock, + winCheckReservedLock, +}; + #endif /* SQLITE_OMIT_DISKIO */ /*************************************************************************** ** Everything above deals with file I/O. Everything that follows deals diff --git a/src/pager.c b/src/pager.c index 58918bbdba..07e728d800 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.217 2005/11/05 15:11:23 drh Exp $ +** @(#) $Id: pager.c,v 1.218 2005/11/26 00:25:03 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -413,7 +413,7 @@ static const unsigned char aJournalMagic[] = { static int read32bits(OsFile *fd, u32 *pRes){ u32 res; int rc; - rc = sqlite3OsRead(fd, &res, sizeof(res)); + rc = sqlite3Io.xRead(fd, &res, sizeof(res)); if( rc==SQLITE_OK ){ unsigned char ac[4]; memcpy(ac, &res, 4); @@ -433,7 +433,7 @@ static int write32bits(OsFile *fd, u32 val){ ac[1] = (val>>16) & 0xff; ac[2] = (val>>8) & 0xff; ac[3] = val & 0xff; - return sqlite3OsWrite(fd, ac, 4); + return sqlite3Io.xWrite(fd, ac, 4); } /* @@ -524,10 +524,10 @@ static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ *pzMaster = 0; - rc = sqlite3OsFileSize(pJrnl, &szJ); + rc = sqlite3Io.xFileSize(pJrnl, &szJ); if( rc!=SQLITE_OK || szJ<16 ) return rc; - rc = sqlite3OsSeek(pJrnl, szJ-16); + rc = sqlite3Io.xSeek(pJrnl, szJ-16); if( rc!=SQLITE_OK ) return rc; rc = read32bits(pJrnl, &len); @@ -536,17 +536,17 @@ static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ rc = read32bits(pJrnl, &cksum); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(pJrnl, aMagic, 8); + rc = sqlite3Io.xRead(pJrnl, aMagic, 8); if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc; - rc = sqlite3OsSeek(pJrnl, szJ-16-len); + rc = sqlite3Io.xSeek(pJrnl, szJ-16-len); if( rc!=SQLITE_OK ) return rc; *pzMaster = (char *)sqliteMalloc(len+1); if( !*pzMaster ){ return SQLITE_NOMEM; } - rc = sqlite3OsRead(pJrnl, *pzMaster, len); + rc = sqlite3Io.xRead(pJrnl, *pzMaster, len); if( rc!=SQLITE_OK ){ sqliteFree(*pzMaster); *pzMaster = 0; @@ -597,7 +597,7 @@ static int seekJournalHdr(Pager *pPager){ assert( offset>=c ); assert( (offset-c)journalOff = offset; - return sqlite3OsSeek(&pPager->jfd, pPager->journalOff); + return sqlite3Io.xSeek(&pPager->jfd, pPager->journalOff); } /* @@ -633,7 +633,7 @@ static int writeJournalHdr(Pager *pPager){ ** Actually maybe the whole journal header should be delayed until that ** point. Think about this. */ - rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); + rc = sqlite3Io.xWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); if( rc==SQLITE_OK ){ /* The nRec Field. 0xFFFFFFFF for no-sync journals. */ @@ -657,9 +657,9 @@ static int writeJournalHdr(Pager *pPager){ ** file descriptor to the end of the journal header sector. */ if( rc==SQLITE_OK ){ - rc = sqlite3OsSeek(&pPager->jfd, pPager->journalOff-1); + rc = sqlite3Io.xSeek(&pPager->jfd, pPager->journalOff-1); if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(&pPager->jfd, "\000", 1); + rc = sqlite3Io.xWrite(&pPager->jfd, "\000", 1); } } return rc; @@ -697,7 +697,7 @@ static int readJournalHdr( return SQLITE_DONE; } - rc = sqlite3OsRead(&pPager->jfd, aMagic, sizeof(aMagic)); + rc = sqlite3Io.xRead(&pPager->jfd, aMagic, sizeof(aMagic)); if( rc ) return rc; if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ @@ -723,7 +723,7 @@ static int readJournalHdr( if( rc ) return rc; pPager->journalOff += JOURNAL_HDR_SZ(pPager); - rc = sqlite3OsSeek(&pPager->jfd, pPager->journalOff); + rc = sqlite3Io.xSeek(&pPager->jfd, pPager->journalOff); return rc; } @@ -771,7 +771,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ rc = write32bits(&pPager->jfd, PAGER_MJ_PGNO(pPager)); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsWrite(&pPager->jfd, zMaster, len); + rc = sqlite3Io.xWrite(&pPager->jfd, zMaster, len); if( rc!=SQLITE_OK ) return rc; rc = write32bits(&pPager->jfd, len); @@ -780,7 +780,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ rc = write32bits(&pPager->jfd, cksum); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); + rc = sqlite3Io.xWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic)); pPager->needSync = !pPager->noSync; return rc; } @@ -858,7 +858,7 @@ static void pager_reset(Pager *pPager){ if( pPager->state>=PAGER_RESERVED ){ sqlite3pager_rollback(pPager); } - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; pPager->dbSize = -1; pPager->nRef = 0; @@ -907,13 +907,13 @@ static int pager_unwritelock(Pager *pPager){ } sqlite3pager_stmt_commit(pPager); if( pPager->stmtOpen ){ - sqlite3OsClose(&pPager->stfd); + sqlite3Io.xClose(&pPager->stfd); pPager->stmtOpen = 0; } if( pPager->journalOpen ){ - sqlite3OsClose(&pPager->jfd); + sqlite3Io.xClose(&pPager->jfd); pPager->journalOpen = 0; - sqlite3OsDelete(pPager->zJournal); + sqlite3Io.xDelete(pPager->zJournal); sqliteFree( pPager->aInJournal ); pPager->aInJournal = 0; for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){ @@ -930,7 +930,7 @@ static int pager_unwritelock(Pager *pPager){ assert( pPager->aInJournal==0 ); assert( pPager->dirtyCache==0 || pPager->useJournal==0 ); } - rc = sqlite3OsUnlock(&pPager->fd, SHARED_LOCK); + rc = sqlite3Io.xUnlock(&pPager->fd, SHARED_LOCK); pPager->state = PAGER_SHARED; pPager->origDbSize = 0; pPager->setMaster = 0; @@ -990,7 +990,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ rc = read32bits(jfd, &pgno); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(jfd, &aData, pPager->pageSize); + rc = sqlite3Io.xRead(jfd, &aData, pPager->pageSize); if( rc!=SQLITE_OK ) return rc; pPager->journalOff += pPager->pageSize + 4; @@ -1040,9 +1040,9 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ assert( pPager->state>=PAGER_EXCLUSIVE || pPg!=0 ); TRACE3("PLAYBACK %d page %d\n", PAGERID(pPager), pgno); if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){ - rc = sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); + rc = sqlite3Io.xSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(&pPager->fd, aData, pPager->pageSize); + rc = sqlite3Io.xWrite(&pPager->fd, aData, pPager->pageSize); } if( pPg ) pPg->dirty = 0; } @@ -1090,10 +1090,10 @@ static int pager_delmaster(const char *zMaster){ ** is running this routine also. Not that it makes too much difference. */ memset(&master, 0, sizeof(master)); - rc = sqlite3OsOpenReadOnly(zMaster, &master); + rc = sqlite3Io.xOpenReadOnly(zMaster, &master); if( rc!=SQLITE_OK ) goto delmaster_out; master_open = 1; - rc = sqlite3OsFileSize(&master, &nMasterJournal); + rc = sqlite3Io.xFileSize(&master, &nMasterJournal); if( rc!=SQLITE_OK ) goto delmaster_out; if( nMasterJournal>0 ){ @@ -1108,12 +1108,12 @@ static int pager_delmaster(const char *zMaster){ rc = SQLITE_NOMEM; goto delmaster_out; } - rc = sqlite3OsRead(&master, zMasterJournal, nMasterJournal); + rc = sqlite3Io.xRead(&master, zMasterJournal, nMasterJournal); if( rc!=SQLITE_OK ) goto delmaster_out; zJournal = zMasterJournal; while( (zJournal-zMasterJournal)dirty ) continue; if( (int)pPg->pgno <= pPager->origDbSize ){ - rc = sqlite3OsSeek(&pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1)); + rc = sqlite3Io.xSeek(&pPager->fd, pPager->pageSize*(i64)(pPg->pgno-1)); if( rc==SQLITE_OK ){ - rc = sqlite3OsRead(&pPager->fd, zBuf, pPager->pageSize); + rc = sqlite3Io.xRead(&pPager->fd, zBuf, pPager->pageSize); } TRACE3("REFETCH %d page %d\n", PAGERID(pPager), pPg->pgno); if( rc ) break; @@ -1205,7 +1205,7 @@ static int pager_reload_cache(Pager *pPager){ */ static int pager_truncate(Pager *pPager, int nPage){ assert( pPager->state>=PAGER_EXCLUSIVE ); - return sqlite3OsTruncate(&pPager->fd, pPager->pageSize*(i64)nPage); + return sqlite3Io.xTruncate(&pPager->fd, pPager->pageSize*(i64)nPage); } /* @@ -1273,7 +1273,7 @@ static int pager_playback(Pager *pPager){ ** the journal is empty. */ assert( pPager->journalOpen ); - rc = sqlite3OsFileSize(&pPager->jfd, &szJ); + rc = sqlite3Io.xFileSize(&pPager->jfd, &szJ); if( rc!=SQLITE_OK ){ goto end_playback; } @@ -1285,13 +1285,13 @@ static int pager_playback(Pager *pPager){ */ rc = readMasterJournal(&pPager->jfd, &zMaster); assert( rc!=SQLITE_DONE ); - if( rc!=SQLITE_OK || (zMaster && !sqlite3OsFileExists(zMaster)) ){ + if( rc!=SQLITE_OK || (zMaster && !sqlite3Io.xFileExists(zMaster)) ){ sqliteFree(zMaster); zMaster = 0; if( rc==SQLITE_DONE ) rc = SQLITE_OK; goto end_playback; } - sqlite3OsSeek(&pPager->jfd, 0); + sqlite3Io.xSeek(&pPager->jfd, 0); pPager->journalOff = 0; /* This loop terminates either when the readJournalHdr() call returns @@ -1334,7 +1334,7 @@ static int pager_playback(Pager *pPager){ pPager->dbSize = mxPg; } - /* rc = sqlite3OsSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */ + /* rc = sqlite3Io.xSeek(&pPager->jfd, JOURNAL_HDR_SZ(pPager)); */ if( rc!=SQLITE_OK ) goto end_playback; /* Copy original pages out of the journal and back into the database file. @@ -1407,7 +1407,7 @@ static int pager_stmt_playback(Pager *pPager){ #ifndef NDEBUG { i64 os_szJ; - rc = sqlite3OsFileSize(&pPager->jfd, &os_szJ); + rc = sqlite3Io.xFileSize(&pPager->jfd, &os_szJ); if( rc!=SQLITE_OK ) return rc; assert( szJ==os_szJ ); } @@ -1433,7 +1433,7 @@ static int pager_stmt_playback(Pager *pPager){ /* Figure out how many records are in the statement journal. */ assert( pPager->stmtInUse && pPager->journalOpen ); - sqlite3OsSeek(&pPager->stfd, 0); + sqlite3Io.xSeek(&pPager->stfd, 0); nRec = pPager->stmtNRec; /* Copy original pages out of the statement journal and back into the @@ -1455,7 +1455,7 @@ static int pager_stmt_playback(Pager *pPager){ ** If it is not zero, then Pager.stmtHdrOff is the offset to the start ** of the first journal header written during this statement transaction. */ - rc = sqlite3OsSeek(&pPager->jfd, pPager->stmtJSize); + rc = sqlite3Io.xSeek(&pPager->jfd, pPager->stmtJSize); if( rc!=SQLITE_OK ){ goto end_stmt_playback; } @@ -1515,7 +1515,7 @@ void sqlite3pager_set_cachesize(Pager *pPager, int mxPage){ ** or power failures by changing the number of syncs()s when writing ** the rollback journal. There are three levels: ** -** OFF sqlite3OsSync() is never called. This is the default +** OFF sqlite3Io.xSync() is never called. This is the default ** for temporary and transient files. ** ** NORMAL The journal is synced once before writes begin on the @@ -1566,8 +1566,8 @@ static int sqlite3pager_opentemp(char *zFile, OsFile *fd){ sqlite3_opentemp_count++; /* Used for testing and analysis only */ do{ cnt--; - sqlite3OsTempFileName(zFile); - rc = sqlite3OsOpenExclusive(zFile, fd, 1); + sqlite3Io.xTempFileName(zFile); + rc = sqlite3Io.xOpenExclusive(zFile, fd, 1); }while( cnt>0 && rc!=SQLITE_OK && rc!=SQLITE_NOMEM ); return rc; } @@ -1619,32 +1619,32 @@ int sqlite3pager_open( }else #endif { - zFullPathname = sqlite3OsFullPathname(zFilename); + zFullPathname = sqlite3Io.xFullPathname(zFilename); if( zFullPathname ){ - rc = sqlite3OsOpenReadWrite(zFullPathname, &fd, &readOnly); + rc = sqlite3Io.xOpenReadWrite(zFullPathname, &fd, &readOnly); } } }else{ rc = sqlite3pager_opentemp(zTemp, &fd); zFilename = zTemp; - zFullPathname = sqlite3OsFullPathname(zFilename); + zFullPathname = sqlite3Io.xFullPathname(zFilename); if( rc==SQLITE_OK ){ tempFile = 1; } } if( !zFullPathname ){ - sqlite3OsClose(&fd); + sqlite3Io.xClose(&fd); return SQLITE_NOMEM; } if( rc!=SQLITE_OK ){ - sqlite3OsClose(&fd); + sqlite3Io.xClose(&fd); sqliteFree(zFullPathname); return rc; } nameLen = strlen(zFullPathname); pPager = sqliteMalloc( sizeof(*pPager) + nameLen*3 + 30 ); if( pPager==0 ){ - sqlite3OsClose(&fd); + sqlite3Io.xClose(&fd); sqliteFree(zFullPathname); return SQLITE_NOMEM; } @@ -1660,9 +1660,6 @@ int sqlite3pager_open( sqliteFree(zFullPathname); strcpy(&pPager->zJournal[nameLen], "-journal"); pPager->fd = fd; -#if OS_UNIX - pPager->fd.pPager = pPager; -#endif pPager->journalOpen = 0; pPager->useJournal = useJournal && !memDb; pPager->noReadlock = noReadlock && readOnly; @@ -1773,8 +1770,8 @@ void enable_simulated_io_errors(void){ void sqlite3pager_read_fileheader(Pager *pPager, int N, unsigned char *pDest){ memset(pDest, 0, N); if( MEMDB==0 ){ - sqlite3OsSeek(&pPager->fd, 0); - sqlite3OsRead(&pPager->fd, pDest, N); + sqlite3Io.xSeek(&pPager->fd, 0); + sqlite3Io.xRead(&pPager->fd, pDest, N); clear_simulated_io_error(); } } @@ -1794,7 +1791,7 @@ int sqlite3pager_pagecount(Pager *pPager){ if( pPager->dbSize>=0 ){ n = pPager->dbSize; } else { - if( sqlite3OsFileSize(&pPager->fd, &n)!=SQLITE_OK ){ + if( sqlite3Io.xFileSize(&pPager->fd, &n)!=SQLITE_OK ){ pPager->errMask |= PAGER_ERR_DISK; return 0; } @@ -1926,7 +1923,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){ rc = SQLITE_OK; }else{ do { - rc = sqlite3OsLock(&pPager->fd, locktype); + rc = sqlite3Io.xLock(&pPager->fd, locktype); }while( rc==SQLITE_BUSY && sqlite3InvokeBusyHandler(pPager->pBusyHandler) ); if( rc==SQLITE_OK ){ pPager->state = locktype; @@ -1994,14 +1991,14 @@ int sqlite3pager_close(Pager *pPager){ sqlite3pager_rollback(pPager); enable_simulated_io_errors(); if( !MEMDB ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); } assert( pPager->errMask || pPager->journalOpen==0 ); break; } case PAGER_SHARED: { if( !MEMDB ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); } break; } @@ -2025,16 +2022,16 @@ int sqlite3pager_close(Pager *pPager){ TRACE2("CLOSE %d\n", PAGERID(pPager)); assert( pPager->errMask || (pPager->journalOpen==0 && pPager->stmtOpen==0) ); if( pPager->journalOpen ){ - sqlite3OsClose(&pPager->jfd); + sqlite3Io.xClose(&pPager->jfd); } sqliteFree(pPager->aInJournal); if( pPager->stmtOpen ){ - sqlite3OsClose(&pPager->stfd); + sqlite3Io.xClose(&pPager->stfd); } - sqlite3OsClose(&pPager->fd); + sqlite3Io.xClose(&pPager->fd); /* Temp files are automatically deleted by the OS ** if( pPager->tempFile ){ - ** sqlite3OsDelete(pPager->zFilename); + ** sqlite3Io.xDelete(pPager->zFilename); ** } */ @@ -2143,7 +2140,7 @@ static int syncJournal(Pager *pPager){ ** with the nRec computed from the size of the journal file. */ i64 jSz; - rc = sqlite3OsFileSize(&pPager->jfd, &jSz); + rc = sqlite3Io.xFileSize(&pPager->jfd, &jSz); if( rc!=0 ) return rc; assert( pPager->journalOff==jSz ); } @@ -2156,20 +2153,20 @@ static int syncJournal(Pager *pPager){ */ if( pPager->fullSync ){ TRACE2("SYNC journal of %d\n", PAGERID(pPager)); - rc = sqlite3OsSync(&pPager->jfd, 0); + rc = sqlite3Io.xSync(&pPager->jfd, 0); if( rc!=0 ) return rc; } - rc = sqlite3OsSeek(&pPager->jfd, + rc = sqlite3Io.xSeek(&pPager->jfd, pPager->journalHdr + sizeof(aJournalMagic)); if( rc ) return rc; rc = write32bits(&pPager->jfd, pPager->nRec); if( rc ) return rc; - rc = sqlite3OsSeek(&pPager->jfd, pPager->journalOff); + rc = sqlite3Io.xSeek(&pPager->jfd, pPager->journalOff); if( rc ) return rc; } TRACE2("SYNC journal of %d\n", PAGERID(pPager)); - rc = sqlite3OsSync(&pPager->jfd, pPager->fullSync); + rc = sqlite3Io.xSync(&pPager->jfd, pPager->fullSync); if( rc!=0 ) return rc; pPager->journalStarted = 1; } @@ -2213,7 +2210,7 @@ static int pager_write_pagelist(PgHdr *pList){ /* At this point there may be either a RESERVED or EXCLUSIVE lock on the ** database file. If there is already an EXCLUSIVE lock, the following - ** calls to sqlite3OsLock() are no-ops. + ** calls to sqlite3Io.xLock() are no-ops. ** ** Moving the lock from RESERVED to EXCLUSIVE actually involves going ** through an intermediate state PENDING. A PENDING lock prevents new @@ -2234,7 +2231,7 @@ static int pager_write_pagelist(PgHdr *pList){ while( pList ){ assert( pList->dirty ); - rc = sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize); + rc = sqlite3Io.xSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize); if( rc ) return rc; /* If there are dirty pages in the page cache with page numbers greater ** than Pager.dbSize, this means sqlite3pager_truncate() was called to @@ -2244,7 +2241,8 @@ static int pager_write_pagelist(PgHdr *pList){ if( pList->pgno<=pPager->dbSize ){ CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); TRACE3("STORE %d page %d\n", PAGERID(pPager), pList->pgno); - rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize); + rc = sqlite3Io.xWrite(&pPager->fd, PGHDR_TO_DATA(pList), + pPager->pageSize); CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0); TEST_INCR(pPager->nWrite); } @@ -2290,10 +2288,10 @@ static PgHdr *pager_get_all_dirty_pages(Pager *pPager){ */ static int hasHotJournal(Pager *pPager){ if( !pPager->useJournal ) return 0; - if( !sqlite3OsFileExists(pPager->zJournal) ) return 0; - if( sqlite3OsCheckReservedLock(&pPager->fd) ) return 0; + if( !sqlite3Io.xFileExists(pPager->zJournal) ) return 0; + if( sqlite3Io.xCheckReservedLock(&pPager->fd) ) return 0; if( sqlite3pager_pagecount(pPager)==0 ){ - sqlite3OsDelete(pPager->zJournal); + sqlite3Io.xDelete(pPager->zJournal); return 0; }else{ return 1; @@ -2370,9 +2368,9 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ ** second process will get to this point in the code and fail to ** obtain it's own EXCLUSIVE lock on the database file. */ - rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK); + rc = sqlite3Io.xLock(&pPager->fd, EXCLUSIVE_LOCK); if( rc!=SQLITE_OK ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; return rc; } @@ -2386,9 +2384,9 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ ** a write lock, so there is never any chance of two or more ** processes opening the journal at the same time. */ - rc = sqlite3OsOpenReadOnly(pPager->zJournal, &pPager->jfd); + rc = sqlite3Io.xOpenReadOnly(pPager->zJournal, &pPager->jfd); if( rc!=SQLITE_OK ){ - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; return SQLITE_BUSY; } @@ -2545,15 +2543,16 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){ }else{ int rc; assert( MEMDB==0 ); - rc = sqlite3OsSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); + rc = sqlite3Io.xSeek(&pPager->fd, (pgno-1)*(i64)pPager->pageSize); if( rc==SQLITE_OK ){ - rc = sqlite3OsRead(&pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize); + rc = sqlite3Io.xRead(&pPager->fd, PGHDR_TO_DATA(pPg), + pPager->pageSize); } TRACE3("FETCH %d page %d\n", PAGERID(pPager), pPg->pgno); CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3); if( rc!=SQLITE_OK ){ i64 fileSize; - if( sqlite3OsFileSize(&pPager->fd,&fileSize)!=SQLITE_OK + if( sqlite3Io.xFileSize(&pPager->fd,&fileSize)!=SQLITE_OK || fileSize>=pgno*pPager->pageSize ){ sqlite3pager_unref(PGHDR_TO_DATA(pPg)); return rc; @@ -2675,7 +2674,8 @@ static int pager_open_journal(Pager *pPager){ rc = SQLITE_NOMEM; goto failed_to_open_journal; } - rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile); + rc = sqlite3Io.xOpenExclusive(pPager->zJournal, &pPager->jfd, + pPager->tempFile); pPager->journalOff = 0; pPager->setMaster = 0; pPager->journalHdr = 0; @@ -2684,7 +2684,7 @@ static int pager_open_journal(Pager *pPager){ } SET_FULLSYNC(pPager->jfd, pPager->fullSync); SET_FULLSYNC(pPager->fd, pPager->fullSync); - sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd); + sqlite3Io.xOpenDirectory(pPager->zDirectory, &pPager->jfd); pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; @@ -2712,7 +2712,7 @@ static int pager_open_journal(Pager *pPager){ failed_to_open_journal: sqliteFree(pPager->aInJournal); pPager->aInJournal = 0; - sqlite3OsUnlock(&pPager->fd, NO_LOCK); + sqlite3Io.xUnlock(&pPager->fd, NO_LOCK); pPager->state = PAGER_UNLOCK; return rc; } @@ -2756,7 +2756,7 @@ int sqlite3pager_begin(void *pData, int exFlag){ pPager->state = PAGER_EXCLUSIVE; pPager->origDbSize = pPager->dbSize; }else{ - rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK); + rc = sqlite3Io.xLock(&pPager->fd, RESERVED_LOCK); if( rc==SQLITE_OK ){ pPager->state = PAGER_RESERVED; if( exFlag ){ @@ -2867,7 +2867,7 @@ int sqlite3pager_write(void *pData){ store32bits(cksum, pPg, pPager->pageSize); szPg = pPager->pageSize+8; store32bits(pPg->pgno, pPg, -4); - rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); + rc = sqlite3Io.xWrite(&pPager->jfd, &((char*)pData)[-4], szPg); pPager->journalOff += szPg; TRACE4("JOURNAL %d page %d needSync=%d\n", PAGERID(pPager), pPg->pgno, pPg->needSync); @@ -2916,7 +2916,8 @@ int sqlite3pager_write(void *pData){ }else{ store32bits(pPg->pgno, pPg, -4); CODEC(pPager, pData, pPg->pgno, 7); - rc = sqlite3OsWrite(&pPager->stfd,((char*)pData)-4, pPager->pageSize+4); + rc = sqlite3Io.xWrite(&pPager->stfd,((char*)pData)-4, + pPager->pageSize+4); TRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); CODEC(pPager, pData, pPg->pgno, 0); if( rc!=SQLITE_OK ){ @@ -3119,7 +3120,7 @@ int sqlite3pager_commit(Pager *pPager){ return SQLITE_OK; } if( pPager->dirtyCache==0 ){ - /* Exit early (without doing the time-consuming sqlite3OsSync() calls) + /* Exit early (without doing the time-consuming sqlite3Io.xSync() calls) ** if there have been no changes to the database file. */ assert( pPager->needSync==0 ); rc = pager_unwritelock(pPager); @@ -3278,11 +3279,11 @@ int sqlite3pager_stmt_begin(Pager *pPager){ assert( pPager->journalOpen ); pPager->aInStmt = sqliteMalloc( pPager->dbSize/8 + 1 ); if( pPager->aInStmt==0 ){ - sqlite3OsLock(&pPager->fd, SHARED_LOCK); + sqlite3Io.xLock(&pPager->fd, SHARED_LOCK); return SQLITE_NOMEM; } #ifndef NDEBUG - rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize); + rc = sqlite3Io.xFileSize(&pPager->jfd, &pPager->stmtJSize); if( rc ) goto stmt_begin_failed; assert( pPager->stmtJSize == pPager->journalOff ); #endif @@ -3315,8 +3316,8 @@ int sqlite3pager_stmt_commit(Pager *pPager){ PgHdr *pPg, *pNext; TRACE2("STMT-COMMIT %d\n", PAGERID(pPager)); if( !MEMDB ){ - sqlite3OsSeek(&pPager->stfd, 0); - /* sqlite3OsTruncate(&pPager->stfd, 0); */ + sqlite3Io.xSeek(&pPager->stfd, 0); + /* sqlite3Io.xTruncate(&pPager->stfd, 0); */ sqliteFree( pPager->aInStmt ); pPager->aInStmt = 0; } @@ -3519,7 +3520,7 @@ int sqlite3pager_sync(Pager *pPager, const char *zMaster, Pgno nTrunc){ /* Sync the database file. */ if( !pPager->noSync ){ - rc = sqlite3OsSync(&pPager->fd, 0); + rc = sqlite3Io.xSync(&pPager->fd, 0); } pPager->state = PAGER_SYNCED; diff --git a/src/pragma.c b/src/pragma.c index 93707f5301..318e807154 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.103 2005/11/03 02:15:03 drh Exp $ +** $Id: pragma.c,v 1.104 2005/11/26 00:25:03 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -392,7 +392,7 @@ void sqlite3Pragma( sqlite3VdbeAddOp(v, OP_Callback, 1, 0); } }else{ - if( zRight[0] && !sqlite3OsIsDirWritable(zRight) ){ + if( zRight[0] && !sqlite3Io.xIsDirWritable(zRight) ){ sqlite3ErrorMsg(pParse, "not a writable directory"); goto pragma_out; } diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 9baf582c43..f928faab7a 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -11,7 +11,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.133 2005/10/05 10:40:15 drh Exp $ +** $Id: tclsqlite.c,v 1.134 2005/11/26 00:25:03 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -2059,6 +2059,7 @@ int TCLSH_MAIN(int argc, char **argv){ extern int Sqlitetest3_Init(Tcl_Interp*); extern int Sqlitetest4_Init(Tcl_Interp*); extern int Sqlitetest5_Init(Tcl_Interp*); + extern int Sqlitetest6_Init(Tcl_Interp*); extern int Md5_Init(Tcl_Interp*); extern int Sqlitetestsse_Init(Tcl_Interp*); @@ -2067,6 +2068,7 @@ int TCLSH_MAIN(int argc, char **argv){ Sqlitetest3_Init(interp); Sqlitetest4_Init(interp); Sqlitetest5_Init(interp); + Sqlitetest6_Init(interp); Md5_Init(interp); #ifdef SQLITE_SSE Sqlitetestsse_Init(interp); diff --git a/src/test1.c b/src/test1.c index 0c0d4a4c93..402ff8c42c 100644 --- a/src/test1.c +++ b/src/test1.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test1.c,v 1.165 2005/11/25 10:38:22 danielk1977 Exp $ +** $Id: test1.c,v 1.166 2005/11/26 00:25:03 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -1412,29 +1412,6 @@ static int test_errstr( return TCL_OK; } -static int sqlite3_crashparams( - void * clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *CONST objv[] -){ -#ifdef OS_TEST - int delay; - if( objc!=3 ) goto bad_args; - if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR; - sqlite3SetCrashParams(delay, Tcl_GetString(objv[2])); -#endif - return TCL_OK; - -#ifdef OS_TEST -bad_args: - Tcl_AppendResult(interp, "wrong # args: should be \"", - Tcl_GetStringFromObj(objv[0], 0), " ", 0); - return TCL_ERROR; -#endif -} - - /* ** Usage: breakpoint ** @@ -2464,7 +2441,7 @@ static int test_sqlite3OsOpenReadWrite( } pFile = sqliteMalloc(sizeof(OsFile)); - rc = sqlite3OsOpenReadWrite(Tcl_GetString(objv[1]), pFile, &dummy); + rc = sqlite3Io.xOpenReadWrite(Tcl_GetString(objv[1]), pFile, &dummy); if( rc!=SQLITE_OK ){ sqliteFree(pFile); Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC); @@ -2496,7 +2473,7 @@ static int test_sqlite3OsClose( if( getFilePointer(interp, Tcl_GetString(objv[1]), &pFile) ){ return TCL_ERROR; } - rc = sqlite3OsClose(pFile); + rc = sqlite3Io.xClose(pFile); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC); return TCL_ERROR; @@ -2529,16 +2506,16 @@ static int test_sqlite3OsLock( } if( 0==strcmp("SHARED", Tcl_GetString(objv[2])) ){ - rc = sqlite3OsLock(pFile, SHARED_LOCK); + rc = sqlite3Io.xLock(pFile, SHARED_LOCK); } else if( 0==strcmp("RESERVED", Tcl_GetString(objv[2])) ){ - rc = sqlite3OsLock(pFile, RESERVED_LOCK); + rc = sqlite3Io.xLock(pFile, RESERVED_LOCK); } else if( 0==strcmp("PENDING", Tcl_GetString(objv[2])) ){ - rc = sqlite3OsLock(pFile, PENDING_LOCK); + rc = sqlite3Io.xLock(pFile, PENDING_LOCK); } else if( 0==strcmp("EXCLUSIVE", Tcl_GetString(objv[2])) ){ - rc = sqlite3OsLock(pFile, EXCLUSIVE_LOCK); + rc = sqlite3Io.xLock(pFile, EXCLUSIVE_LOCK); }else{ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), @@ -2574,7 +2551,7 @@ static int test_sqlite3OsUnlock( if( getFilePointer(interp, Tcl_GetString(objv[1]), &pFile) ){ return TCL_ERROR; } - rc = sqlite3OsUnlock(pFile, NO_LOCK); + rc = sqlite3Io.xUnlock(pFile, NO_LOCK); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC); return TCL_ERROR; @@ -2594,7 +2571,7 @@ static int test_sqlite3OsTempFileName( char zFile[SQLITE_TEMPNAME_SIZE]; int rc; - rc = sqlite3OsTempFileName(zFile); + rc = sqlite3Io.xTempFileName(zFile); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC); return TCL_ERROR; @@ -3186,7 +3163,6 @@ int Sqlitetest1_Init(Tcl_Interp *interp){ { "add_test_collate_needed", test_collate_needed, 0 }, { "add_test_function", test_function, 0 }, #endif - { "sqlite3_crashparams", sqlite3_crashparams, 0 }, { "sqlite3_test_errstr", test_errstr, 0 }, { "tcl_variable_type", tcl_variable_type, 0 }, }; diff --git a/src/test2.c b/src/test2.c index 1867fc8662..bb02af5da4 100644 --- a/src/test2.c +++ b/src/test2.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test2.c,v 1.35 2005/11/04 22:03:30 drh Exp $ +** $Id: test2.c,v 1.36 2005/11/26 00:25:04 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -533,20 +533,20 @@ static int fake_big_file( } if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; memset(&fd, 0, sizeof(fd)); - rc = sqlite3OsOpenReadWrite(argv[2], &fd, &readOnly); + rc = sqlite3Io.xOpenReadWrite(argv[2], &fd, &readOnly); if( rc ){ Tcl_AppendResult(interp, "open failed: ", errorName(rc), 0); return TCL_ERROR; } offset = n; offset *= 1024*1024; - rc = sqlite3OsSeek(&fd, offset); + rc = sqlite3Io.xSeek(&fd, offset); if( rc ){ Tcl_AppendResult(interp, "seek failed: ", errorName(rc), 0); return TCL_ERROR; } - rc = sqlite3OsWrite(&fd, "Hello, World!", 14); - sqlite3OsClose(&fd); + rc = sqlite3Io.xWrite(&fd, "Hello, World!", 14); + sqlite3Io.xClose(&fd); if( rc ){ Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0); return TCL_ERROR; diff --git a/src/test6.c b/src/test6.c new file mode 100644 index 0000000000..f0c5a06e0d --- /dev/null +++ b/src/test6.c @@ -0,0 +1,478 @@ +/* +** 2004 May 22 +** +** 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 contains code that modified the OS layer in order to simulate +** the effect on the database file of an OS crash or power failure. This +** is used to test the ability of SQLite to recover from those situations. +*/ +#if SQLITE_TEST /* This file is used for the testing only */ +#include "sqliteInt.h" +#include "os.h" +#include "tcl.h" + +/* +** A copy of the original sqlite3Io structure +*/ +static struct sqlite3IoVtbl origIo; + +/* +** The pAux part of OsFile points to this structure. +*/ +typedef struct OsTestFile OsTestFile; +struct OsTestFile { + u8 **apBlk; /* Array of blocks that have been written to. */ + int nBlk; /* Size of apBlock. */ + int nMaxWrite; /* Largest offset written to. */ + char *zName; /* File name */ + OsFile *pBase; /* Base class */ + OsTestFile *pNext; /* Next in a list of them all */ +}; + +/* +** Size of a simulated disk block +*/ +#define BLOCKSIZE 512 +#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE) + + +/* +** The following variables control when a simulated crash occurs. +** +** If iCrashDelay is non-zero, then zCrashFile contains (full path) name of +** a file that SQLite will call sqlite3OsSync() on. Each time this happens +** iCrashDelay is decremented. If iCrashDelay is zero after being +** decremented, a "crash" occurs during the sync() operation. +** +** In other words, a crash occurs the iCrashDelay'th time zCrashFile is +** synced. +*/ +static int iCrashDelay = 0; +static char zCrashFile[500]; + +/* +** Set the value of the two crash parameters. +*/ +static void setCrashParams(int iDelay, char const *zFile){ + sqlite3OsEnterMutex(); + assert( strlen(zFile)n ){ + n = strlen(zPath); + } + r = 0; + if( iCrashDelay>0 && strncmp(zPath, zCrashFile, n)==0 ){ + iCrashDelay--; + if( iCrashDelay<=0 ){ + r = 1; + } + } + sqlite3OsLeaveMutex(); + return r; +} + +/* +** A list of all open files. +*/ +static OsTestFile *pAllFiles = 0; + +/* +** Initialise the os_test.c specific fields of pFile. +*/ +static void initFile(OsFile *id, char const *zName){ + OsTestFile *pFile = sqliteMalloc(sizeof(OsTestFile) + strlen(zName)+1); + id->pAux = pFile; + pFile->nMaxWrite = 0; + pFile->nBlk = 0; + pFile->apBlk = 0; + pFile->zName = (char *)(&pFile[1]); + strcpy(pFile->zName, zName); + pFile->pBase = id; + pFile->pNext = pAllFiles; + pAllFiles = pFile; +} + +/* +** Undo the work done by initFile. Delete the OsTestFile structure +** and unlink the structure from the pAllFiles list. +*/ +static void closeFile(OsFile *id){ + OsTestFile *pFile = (OsTestFile*)id->pAux; + if( pFile==pAllFiles ){ + pAllFiles = pFile->pNext; + }else{ + OsTestFile *p; + for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){ + assert( p ); + } + p->pNext = pFile->pNext; + } + sqliteFree(pFile); +} + +/* +** Return the current seek offset from the start of the file. This +** is unix-only code. +*/ +static i64 osTell(OsTestFile *pFile){ + return lseek(pFile->pBase->h, 0, SEEK_CUR); +} + +/* +** Load block 'blk' into the cache of pFile. +*/ +static int cacheBlock(OsTestFile *pFile, int blk){ + if( blk>=pFile->nBlk ){ + int n = ((pFile->nBlk * 2) + 100 + blk); + /* if( pFile->nBlk==0 ){ printf("DIRTY %s\n", pFile->zName); } */ + pFile->apBlk = (u8 **)sqliteRealloc(pFile->apBlk, n * sizeof(u8*)); + if( !pFile->apBlk ) return SQLITE_NOMEM; + memset(&pFile->apBlk[pFile->nBlk], 0, (n - pFile->nBlk)*sizeof(u8*)); + pFile->nBlk = n; + } + + if( !pFile->apBlk[blk] ){ + i64 filesize; + int rc; + + u8 *p = sqliteMalloc(BLOCKSIZE); + if( !p ) return SQLITE_NOMEM; + pFile->apBlk[blk] = p; + + rc = origIo.xFileSize(pFile->pBase, &filesize); + if( rc!=SQLITE_OK ) return rc; + + if( BLOCK_OFFSET(blk)pBase, blk*BLOCKSIZE); + if( BLOCK_OFFSET(blk+1)>filesize ){ + len = filesize - BLOCK_OFFSET(blk); + } + if( rc!=SQLITE_OK ) return rc; + rc = origIo.xRead(pFile->pBase, p, len); + if( rc!=SQLITE_OK ) return rc; + } + } + + return SQLITE_OK; +} + +/* +** Write the cache of pFile to disk. If crash is non-zero, randomly +** skip blocks when writing. The cache is deleted before returning. +*/ +static int writeCache2(OsTestFile *pFile, int crash){ + int i; + int nMax = pFile->nMaxWrite; + i64 offset; + int rc = SQLITE_OK; + + offset = osTell(pFile); + for(i=0; inBlk; i++){ + u8 *p = pFile->apBlk[i]; + if( p ){ + int skip = 0; + int trash = 0; + if( crash ){ + char random; + sqlite3Randomness(1, &random); + if( random & 0x01 ){ + if( random & 0x02 ){ + trash = 1; +#ifdef TRACE_WRITECACHE +printf("Trashing block %d of %s\n", i, pFile->zName); +#endif + }else{ + skip = 1; +#ifdef TRACE_WRITECACHE +printf("Skiping block %d of %s\n", i, pFile->zName); +#endif + } + }else{ +#ifdef TRACE_WRITECACHE +printf("Writing block %d of %s\n", i, pFile->zName); +#endif + } + } + if( rc==SQLITE_OK ){ + rc = origIo.xSeek(pFile->pBase, BLOCK_OFFSET(i)); + } + if( rc==SQLITE_OK && !skip ){ + int len = BLOCKSIZE; + if( BLOCK_OFFSET(i+1)>nMax ){ + len = nMax-BLOCK_OFFSET(i); + } + if( len>0 ){ + if( trash ){ + sqlite3Randomness(len, p); + } + rc = origIo.xWrite(pFile->pBase, p, len); + } + } + sqliteFree(p); + } + } + sqliteFree(pFile->apBlk); + pFile->nBlk = 0; + pFile->apBlk = 0; + pFile->nMaxWrite = 0; + + if( rc==SQLITE_OK ){ + rc = origIo.xSeek(pFile->pBase, offset); + } + return rc; +} + +/* +** Write the cache to disk. +*/ +static int writeCache(OsTestFile *pFile){ + if( pFile->apBlk ){ + int c = crashRequired(pFile->zName); + if( c ){ + OsTestFile *p; +#ifdef TRACE_WRITECACHE + printf("\nCrash during sync of %s\n", pFile->zName); +#endif + for(p=pAllFiles; p; p=p->pNext){ + writeCache2(p, 1); + } + exit(-1); + }else{ + return writeCache2(pFile, 0); + } + } + return SQLITE_OK; +} + +/* +** Close the file. +*/ +static int crashClose(OsFile *id){ + if( id->pAux ) return SQLITE_OK; + if( id->isOpen ){ + /* printf("CLOSE %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ + writeCache((OsTestFile*)id->pAux); + origIo.xClose(id); + } + closeFile(id); + return SQLITE_OK; +} + +static int crashRead(OsFile *id, void *pBuf, int amt){ + i64 offset; /* The current offset from the start of the file */ + i64 end; /* The byte just past the last byte read */ + int blk; /* Block number the read starts on */ + int i; + u8 *zCsr; + int rc = SQLITE_OK; + OsTestFile *pFile = (OsTestFile*)id->pAux; + + offset = osTell(pFile); + end = offset+amt; + blk = (offset/BLOCKSIZE); + + zCsr = (u8 *)pBuf; + for(i=blk; i*BLOCKSIZE end ){ + len = len - (BLOCK_OFFSET(i+1)-end); + } + + if( inBlk && pFile->apBlk[i]){ + u8 *pBlk = pFile->apBlk[i]; + memcpy(zCsr, &pBlk[off], len); + }else{ + rc = origIo.xSeek(id, BLOCK_OFFSET(i) + off); + if( rc!=SQLITE_OK ) return rc; + rc = origIo.xRead(id, zCsr, len); + if( rc!=SQLITE_OK ) return rc; + } + + zCsr += len; + } + assert( zCsr==&((u8 *)pBuf)[amt] ); + + rc = origIo.xSeek(id, end); + return rc; +} + +static int crashWrite(OsFile *id, const void *pBuf, int amt){ + i64 offset; /* The current offset from the start of the file */ + i64 end; /* The byte just past the last byte written */ + int blk; /* Block number the write starts on */ + int i; + const u8 *zCsr; + int rc = SQLITE_OK; + OsTestFile *pFile = (OsTestFile*)id->pAux; + + offset = osTell(pFile); + end = offset+amt; + blk = (offset/BLOCKSIZE); + + zCsr = (u8 *)pBuf; + for(i=blk; i*BLOCKSIZEapBlk[i]; + assert( pBlk ); + + if( BLOCK_OFFSET(i) < offset ){ + off = offset-BLOCK_OFFSET(i); + } + len = BLOCKSIZE - off; + if( BLOCK_OFFSET(i+1) > end ){ + len = len - (BLOCK_OFFSET(i+1)-end); + } + memcpy(&pBlk[off], zCsr, len); + zCsr += len; + } + if( pFile->nMaxWritenMaxWrite = end; + } + assert( zCsr==&((u8 *)pBuf)[amt] ); + + rc = origIo.xSeek(id, end); + return rc; +} + +/* +** Sync the file. First flush the write-cache to disk, then call the +** real sync() function. +*/ +static int crashSync(OsFile *id, int dataOnly){ + int rc; + /* printf("SYNC %s (%d blocks)\n", (*id)->zName, (*id)->nBlk); */ + rc = writeCache((OsTestFile*)id->pAux); + if( rc!=SQLITE_OK ) return rc; + rc = origIo.xSync(id, dataOnly); + return rc; +} + +/* +** Truncate the file. Set the internal OsFile.nMaxWrite variable to the new +** file size to ensure that nothing in the write-cache past this point +** is written to disk. +*/ +static int crashTruncate(OsFile *id, i64 nByte){ + OsTestFile *pFile = (OsTestFile*)id->pAux; + pFile->nMaxWrite = nByte; + return origIo.xTruncate(id, nByte); +} + +/* +** Return the size of the file. If the cache contains a write that extended +** the file, then return this size instead of the on-disk size. +*/ +static int crashFileSize(OsFile *id, i64 *pSize){ + int rc = origIo.xFileSize(id, pSize); + OsTestFile *pFile = (OsTestFile*)id->pAux; + if( rc==SQLITE_OK && pSize && *pSizenMaxWrite ){ + *pSize = pFile->nMaxWrite; + } + return rc; +} + +/* +** The three functions used to open files. All that is required is to +** initialise the os_test.c specific fields and then call the corresponding +** os_unix.c function to really open the file. +*/ +static int crashOpenReadWrite(const char *zFilename, OsFile *id, int *pRdonly){ + initFile(id, zFilename); + return origIo.xOpenReadWrite(zFilename, id, pRdonly); +} +static int crashOpenExclusive(const char *zFilename, OsFile *id, int delFlag){ + initFile(id, zFilename); + return origIo.xOpenExclusive(zFilename, id, delFlag); +} +static int crashOpenReadOnly(const char *zFilename, OsFile *id){ + initFile(id, zFilename); + return origIo.xOpenReadOnly(zFilename, id); +} + +/* +** tclcmd: sqlite_crashparams DELAY CRASHFILE +** +** This procedure implements a TCL command that enables crash testing +** in testfixture. Once enabled, crash testing cannot be disabled. +*/ +static int crashParamsObjCmd( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int delay; + const char *zFile; + int nFile; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &delay) ) return TCL_ERROR; + zFile = Tcl_GetStringFromObj(objv[2], &nFile); + if( nFile>=sizeof(zCrashFile)-1 ){ + Tcl_AppendResult(interp, "crash file name too big", 0); + return TCL_ERROR; + } + setCrashParams(delay, zFile); + origIo = sqlite3Io; + sqlite3Io.xRead = crashRead; + sqlite3Io.xWrite = crashWrite; + sqlite3Io.xClose = crashClose; + sqlite3Io.xSync = crashSync; + sqlite3Io.xTruncate = crashTruncate; + sqlite3Io.xFileSize = crashFileSize; + sqlite3Io.xOpenReadWrite = crashOpenReadWrite; + sqlite3Io.xOpenExclusive = crashOpenExclusive; + sqlite3Io.xOpenReadOnly = crashOpenReadOnly; + return TCL_OK; +} + +/* +** This procedure registers the TCL procedures defined in this file. +*/ +int Sqlitetest6_Init(Tcl_Interp *interp){ + Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0); + return TCL_OK; +} + +#endif /* SQLITE_TEST */ diff --git a/src/vacuum.c b/src/vacuum.c index a8af62908f..bf9fd66b99 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -14,7 +14,7 @@ ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. ** -** $Id: vacuum.c,v 1.47 2005/11/04 22:03:30 drh Exp $ +** $Id: vacuum.c,v 1.48 2005/11/26 00:25:04 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -146,7 +146,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){ do { zTemp[nFilename] = '-'; randomName((unsigned char*)&zTemp[nFilename+1]); - } while( sqlite3OsFileExists(zTemp) ); + } while( sqlite3Io.xFileExists(zTemp) ); /* Attach the temporary database as 'vacuum_db'. The synchronous pragma ** can be set to 'off' for this file, as it is not recovered if a crash @@ -300,7 +300,7 @@ end_of_vacuum: rc = rc2; } if( zTemp ){ - sqlite3OsDelete(zTemp); + sqlite3Io.xDelete(zTemp); sqliteFree(zTemp); } sqliteFree( zSql ); diff --git a/src/vdbe.c b/src/vdbe.c index 143a869360..8684af553b 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.499 2005/11/24 14:34:36 drh Exp $ +** $Id: vdbe.c,v 1.500 2005/11/26 00:25:04 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -422,7 +422,7 @@ int sqlite3VdbeExec( } sqlite3VdbePrintOp(p->trace, pc, pOp); } - if( p->trace==0 && pc==0 && sqlite3OsFileExists("vdbe_sqltrace") ){ + if( p->trace==0 && pc==0 && sqlite3Io.xFileExists("vdbe_sqltrace") ){ sqlite3VdbePrintSql(p); } #endif diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 9aa22731fc..145664044d 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -273,7 +273,7 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){ /* If we never rollback a statement transaction, then statement ** transactions are not needed. So change every OP_Statement - ** opcode into an OP_Noop. This avoid a call to sqlite3OsOpenExclusive() + ** opcode into an OP_Noop. This avoid a call to sqlite3Io.xOpenExclusive() ** which can be expensive on some platforms. */ if( hasStatementBegin && !doesStatementRollback ){ @@ -755,7 +755,7 @@ void sqlite3VdbeMakeReady( #ifdef SQLITE_DEBUG if( (p->db->flags & SQLITE_VdbeListing)!=0 - || sqlite3OsFileExists("vdbe_explain") + || sqlite3Io.xFileExists("vdbe_explain") ){ int i; printf("VDBE Program Listing:\n"); @@ -764,7 +764,7 @@ void sqlite3VdbeMakeReady( sqlite3VdbePrintOp(stdout, i, &p->aOp[i]); } } - if( sqlite3OsFileExists("vdbe_trace") ){ + if( sqlite3Io.xFileExists("vdbe_trace") ){ p->trace = stdout; } #endif @@ -978,11 +978,11 @@ static int vdbeCommit(sqlite3 *db){ if( !zMaster ){ return SQLITE_NOMEM; } - }while( sqlite3OsFileExists(zMaster) ); + }while( sqlite3Io.xFileExists(zMaster) ); /* Open the master journal. */ memset(&master, 0, sizeof(master)); - rc = sqlite3OsOpenExclusive(zMaster, &master, 0); + rc = sqlite3Io.xOpenExclusive(zMaster, &master, 0); if( rc!=SQLITE_OK ){ sqliteFree(zMaster); return rc; @@ -1003,10 +1003,10 @@ static int vdbeCommit(sqlite3 *db){ if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ needSync = 1; } - rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1); + rc = sqlite3Io.xWrite(&master, zFile, strlen(zFile)+1); if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); - sqlite3OsDelete(zMaster); + sqlite3Io.xClose(&master); + sqlite3Io.xDelete(zMaster); sqliteFree(zMaster); return rc; } @@ -1018,11 +1018,11 @@ static int vdbeCommit(sqlite3 *db){ ** the master journal file is store in so that it gets synced too. */ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); - rc = sqlite3OsOpenDirectory(zMainFile, &master); + rc = sqlite3Io.xOpenDirectory(zMainFile, &master); if( rc!=SQLITE_OK || - (needSync && (rc=sqlite3OsSync(&master,0))!=SQLITE_OK) ){ - sqlite3OsClose(&master); - sqlite3OsDelete(zMaster); + (needSync && (rc=sqlite3Io.xSync(&master,0))!=SQLITE_OK) ){ + sqlite3Io.xClose(&master); + sqlite3Io.xDelete(zMaster); sqliteFree(zMaster); return rc; } @@ -1042,23 +1042,23 @@ static int vdbeCommit(sqlite3 *db){ if( pBt && sqlite3BtreeIsInTrans(pBt) ){ rc = sqlite3BtreeSync(pBt, zMaster); if( rc!=SQLITE_OK ){ - sqlite3OsClose(&master); + sqlite3Io.xClose(&master); sqliteFree(zMaster); return rc; } } } - sqlite3OsClose(&master); + sqlite3Io.xClose(&master); /* Delete the master journal file. This commits the transaction. After ** doing this the directory is synced again before any individual ** transaction files are deleted. */ - rc = sqlite3OsDelete(zMaster); + rc = sqlite3Io.xDelete(zMaster); assert( rc==SQLITE_OK ); sqliteFree(zMaster); zMaster = 0; - rc = sqlite3OsSyncDirectory(zMainFile); + rc = sqlite3Io.xSyncDirectory(zMainFile); if( rc!=SQLITE_OK ){ /* This is not good. The master journal file has been deleted, but ** the directory sync failed. There is no completely safe course of diff --git a/test/crash.test b/test/crash.test index 4058d33847..76ca0969ff 100644 --- a/test/crash.test +++ b/test/crash.test @@ -12,15 +12,12 @@ # # The focus of this file is testing the ability of the database to # uses its rollback journal to recover intact (no database corruption) -# from a power failure during the middle of a COMMIT. The special test -# module "crashtest" compiled with the special "os_test.c" backend is used. -# The os_test.c simulates the kind of file corruption that can occur -# when writes are happening at the moment of power loss. -# -# The special crash-test module with its os_test.c backend only works -# on Unix. +# from a power failure during the middle of a COMMIT. The OS interface +# modules are overloaded in a separate instance of testfixture using +# the modified I/O routines found in test6.c. These routines allow us +# to simulate the kind of file damage that occurs after a power failure. # -# $Id: crash.test,v 1.19 2005/09/17 17:05:19 drh Exp $ +# $Id: crash.test,v 1.20 2005/11/26 00:25:04 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -62,7 +59,7 @@ proc crashsql {crashdelay crashfile sql} { close $f set r [catch { - exec [file join . crashtest] crash.tcl >@stdout + exec [info nameofexec] crash.tcl >@stdout } msg] lappend r $msg } diff --git a/test/tester.tcl b/test/tester.tcl index adc85d5f6c..ab35a64f94 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -11,7 +11,7 @@ # This file implements some common TCL routines used for regression # testing the SQLite library # -# $Id: tester.tcl,v 1.51 2005/11/04 22:03:30 drh Exp $ +# $Id: tester.tcl,v 1.52 2005/11/26 00:25:04 drh Exp $ # Make sure tclsqlite3 was compiled correctly. Abort now with an # error message if not. @@ -281,7 +281,7 @@ proc crashsql {crashdelay crashfile sql} { close $f set r [catch { - exec [file join . crashtest] crash.tcl >@stdout + exec [info nameofexec] crash.tcl >@stdout } msg] lappend r $msg }