From 6207906027795e6a3e5235df743c32fe7e6d99d3 Mon Sep 17 00:00:00 2001 From: danielk1977 Date: Wed, 15 Aug 2007 17:08:46 +0000 Subject: [PATCH] Modify the crash-recovery test code in test6.c for 3.5. Also change some other code to use the new sqlite3_io_methods interface. Lots of things are broken now. (CVS 4228) FossilOrigin-Name: af9503daf3f7703fcddad754bc1dc9e179830b6e --- manifest | 28 +- manifest.uuid | 2 +- src/os.c | 74 ++-- src/os.h | 43 +-- src/os_unix.c | 159 ++++---- src/pager.c | 173 ++++----- src/sqlite.h.in | 4 +- src/test2.c | 9 +- src/test6.c | 948 ++++++++++++++++++++++++------------------------ src/vdbeaux.c | 8 +- 10 files changed, 717 insertions(+), 731 deletions(-) diff --git a/manifest b/manifest index 65cbbe048d..bf7b4faa69 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sa\sdebugging\smemory\sallocator.\s(CVS\s4227) -D 2007-08-15T17:07:57 +C Modify\sthe\scrash-recovery\stest\scode\sin\stest6.c\sfor\s3.5.\sAlso\schange\ssome\sother\scode\sto\suse\sthe\snew\ssqlite3_io_methods\sinterface.\sLots\sof\sthings\sare\sbroken\snow.\s(CVS\s4228) +D 2007-08-15T17:08:46 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -91,18 +91,18 @@ F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 F src/mem1.c ef73642a171d174cd556d0168f8edbceaf94933b F src/mem2.c b8173ddcca23e99829617185154a35a6f046b214 F src/mutex.c 667dae0de95f8fb92a3ffc8c3f20c0d26115a1a6 -F src/os.c 1f10b47acc1177fb9225edb4f5f0d25ed716f9cb -F src/os.h cea2e179bb33f4fc09dbb9fcd51b2246544bd2db +F src/os.c e2faefbe0f5a8ca5e3b1c49ee1b5c6cfa0f0e279 +F src/os.h 8eff07babf74e5bc3f895f8a6c7c294dad5ff997 F src/os_common.h a5c446d3b93f09f369d13bf217de4bed3437dd1c F src/os_os2.c cba4e96fadb949076c717108fe0599d1a3c2e446 F src/os_os2.h e5f17dd69333632bbc3112881ea407c37d245eb3 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3 -F src/os_unix.c 9d2a421acc607262e63ccf31e3fe86e5f2520af6 +F src/os_unix.c 05ad65c32b4937fd47b17b472955aa5dfc438074 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e F src/os_win.c d868d5f9e95ec9c1b9e2a30c54c996053db6dddd F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c cdf561d3ae4009be902df714da4518dc3522e206 +F src/pager.c 05ea9dcbc4de4e9d9ca332ca1f8a9ba65fe2cbf5 F src/pager.h 94110a5570dca30d54a883e880a3633b2e4c05ae F src/parse.y ad2ce25665be7f7303137f774a4e3e72e0d036ff F src/pragma.c 7914a6b9ea05f158800116dfcae11e52ab8e39c4 @@ -112,18 +112,18 @@ F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88 F src/select.c 3b167744fc375bddfddcef87feb18f5171737677 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb -F src/sqlite.h.in 025e9fd6c519f2945296a9db46ca9da4571c22d7 +F src/sqlite.h.in 165913eb3426fbaa8a2a51d87f84593bfe5bee15 F src/sqlite3ext.h a27bedc222df5e5f0f458ac99726d0483b953a91 F src/sqliteInt.h fc9f6e8d916e182c04983a089c4ce4057fac5003 F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa F src/table.c a8de75bcedf84d4060d804264b067ab3b1a3561d F src/tclsqlite.c 648e6f53041ce4974234d4963e71680926760925 F src/test1.c 94bd41c24a4d8d782e39c1275421511587d8b293 -F src/test2.c 24458b17ab2f3c90cbc1c8446bd7ffe69be62f88 +F src/test2.c 5c3edc610852a8f67990cd08c5d5dbb79e3f8db9 F src/test3.c a280931fb40222b7c90da45eea926459beee8904 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25 F src/test5.c c40a4cf43266c1c6da7bcb737d294304a177e6cc -F src/test6.c 2c4ed21787944bd8896cba80d4a544d8bed5473e +F src/test6.c 4d812a5ea1fe08693f4189bfc974b341102a3bea F src/test7.c 03fa8d787f6aebc6d1f72504d52f33013ad2c8e3 F src/test8.c 27a61c60f736066646a9e9ca21acdfdf0f3ea11e F src/test9.c c0f38f7795cc51d37db6c63874d90f40f10d0f0e @@ -147,7 +147,7 @@ F src/vdbe.c cf973bd1af5fbda845b0f759bb06eb19ff42e215 F src/vdbe.h 001c5b257567c1d3de7feb2203aac71d0d7b16a3 F src/vdbeInt.h c3514903cad9e36d6b3242be20261351d09db56c F src/vdbeapi.c 220b81132abaf0f620edb8da48799a77daef12a7 -F src/vdbeaux.c d626e0f8cd78b4280bcb7af25d5c5566348ba87a +F src/vdbeaux.c 8e6dbe3dac3bdd7d37c87ba553059b5251ba07e5 F src/vdbeblob.c bb30b3e387c35ba869949494b2736aff97159470 F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f F src/vdbemem.c ca4d3994507cb0a9504820293af69f5c778b4abd @@ -528,7 +528,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P c0fa3769790af199a4c8715c80bb8ff900730520 -R afa459b840a3b96de70924b91a0e1dbb -U drh -Z 9341f1481e29775e7a036e24546ed5fc +P 8d2d1c4ff9dca61f75e3048107ee9712d346a28c +R 7d9819ae0c3408fa1555b7b8d9a48850 +U danielk1977 +Z 41f21c80ce49e631fe5dbf7930bb9145 diff --git a/manifest.uuid b/manifest.uuid index ebda030826..ce612004a4 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8d2d1c4ff9dca61f75e3048107ee9712d346a28c \ No newline at end of file +af9503daf3f7703fcddad754bc1dc9e179830b6e \ No newline at end of file diff --git a/src/os.c b/src/os.c index 7cd1ab99dd..d55cfa7032 100644 --- a/src/os.c +++ b/src/os.c @@ -20,63 +20,65 @@ /* ** The following routines are convenience wrappers around methods -** of the OsFile object. This is mostly just syntactic sugar. All +** of the sqlite3_file object. This is mostly just syntactic sugar. All ** of this would be completely automatic if SQLite were coded using ** C++ instead of plain old C. */ -int sqlite3OsClose(OsFile **pId){ - OsFile *id; +int sqlite3OsClose(sqlite3_file **pId){ + int rc = SQLITE_OK; + sqlite3_file *id; if( pId!=0 && (id = *pId)!=0 ){ - return id->pMethod->xClose(pId); - }else{ - return SQLITE_OK; + rc = id->pMethods->xClose(id); + if( rc==SQLITE_OK ){ + *pId = 0; + } } + return rc; } -int sqlite3OsOpenDirectory(OsFile *id, const char *zName){ - return id->pMethod->xOpenDirectory(id, zName); +int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){ + return id->pMethods->xRead(id, pBuf, amt, offset); } -int sqlite3OsRead(OsFile *id, void *pBuf, int amt){ - return id->pMethod->xRead(id, pBuf, amt); +int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){ + return id->pMethods->xWrite(id, pBuf, amt, offset); } -int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){ - return id->pMethod->xWrite(id, pBuf, amt); +int sqlite3OsTruncate(sqlite3_file *id, i64 size){ + return id->pMethods->xTruncate(id, size); } -int sqlite3OsSeek(OsFile *id, i64 offset){ - return id->pMethod->xSeek(id, offset); +int sqlite3OsSync(sqlite3_file *id, int fullsync){ + return id->pMethods->xSync(id, fullsync); } -int sqlite3OsTruncate(OsFile *id, i64 size){ - return id->pMethod->xTruncate(id, size); +int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ + return id->pMethods->xFileSize(id, pSize); } -int sqlite3OsSync(OsFile *id, int fullsync){ - return id->pMethod->xSync(id, fullsync); +int sqlite3OsLock(sqlite3_file *id, int lockType){ + return id->pMethods->xLock(id, lockType); } -void sqlite3OsSetFullSync(OsFile *id, int value){ - id->pMethod->xSetFullSync(id, value); +int sqlite3OsUnlock(sqlite3_file *id, int lockType){ + return id->pMethods->xUnlock(id, lockType); } -int sqlite3OsFileSize(OsFile *id, i64 *pSize){ - return id->pMethod->xFileSize(id, pSize); +int sqlite3OsBreakLock(sqlite3_file *id){ + return id->pMethods->xBreakLock(id); } -int sqlite3OsLock(OsFile *id, int lockType){ - return id->pMethod->xLock(id, lockType); +int sqlite3OsCheckReservedLock(sqlite3_file *id){ + return id->pMethods->xCheckReservedLock(id); } -int sqlite3OsUnlock(OsFile *id, int lockType){ - return id->pMethod->xUnlock(id, lockType); -} -int sqlite3OsCheckReservedLock(OsFile *id){ - return id->pMethod->xCheckReservedLock(id); -} -int sqlite3OsSectorSize(OsFile *id){ - int (*xSectorSize)(OsFile*) = id->pMethod->xSectorSize; +int sqlite3OsSectorSize(sqlite3_file *id){ + int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize; return xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE; } +int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ + return id->pMethods->xDeviceCharacteristics(id); +} #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) /* These methods are currently only used for testing and debugging. */ - int sqlite3OsFileHandle(OsFile *id){ - return id->pMethod->xFileHandle(id); + int sqlite3OsFileHandle(sqlite3_file *id){ + /* return id->pMethods->xFileHandle(id); */ + return 0; } - int sqlite3OsLockState(OsFile *id){ - return id->pMethod->xLockState(id); + int sqlite3OsLockState(sqlite3_file *id){ + /* return id->pMethods->xLockState(id); */ + return 0; } #endif diff --git a/src/os.h b/src/os.h index 76a9f2c48e..30bc914433 100644 --- a/src/os.h +++ b/src/os.h @@ -348,27 +348,28 @@ extern unsigned int sqlite3_pending_byte; /* ** Prototypes for operating system interface routines. */ -int sqlite3OsClose(OsFile**); -int sqlite3OsOpenDirectory(OsFile*, const char*); -int sqlite3OsRead(OsFile*, void*, int amt); -int sqlite3OsWrite(OsFile*, const void*, int amt); -int sqlite3OsSeek(OsFile*, i64 offset); -int sqlite3OsTruncate(OsFile*, i64 size); -int sqlite3OsSync(OsFile*, int); -void sqlite3OsSetFullSync(OsFile *id, int setting); -int sqlite3OsFileSize(OsFile*, i64 *pSize); -int sqlite3OsLock(OsFile*, int); -int sqlite3OsUnlock(OsFile*, int); -int sqlite3OsCheckReservedLock(OsFile *id); -int sqlite3OsOpenReadWrite(const char*, OsFile**, int*); -int sqlite3OsOpenExclusive(const char*, OsFile**, int); -int sqlite3OsOpenReadOnly(const char*, OsFile**); +int sqlite3OsClose(sqlite3_file**); +int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset); +int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset); +int sqlite3OsTruncate(sqlite3_file*, i64 size); +int sqlite3OsSync(sqlite3_file*, int); +int sqlite3OsFileSize(sqlite3_file*, i64 *pSize); +int sqlite3OsLock(sqlite3_file*, int); +int sqlite3OsUnlock(sqlite3_file*, int); +int sqlite3OsBreakLock(sqlite3_file*); +int sqlite3OsCheckReservedLock(sqlite3_file *id); +int sqlite3OsSectorSize(sqlite3_file *id); +int sqlite3OsDeviceCharacteristics(sqlite3_file *id); + +int sqlite3OsOpenReadWrite(const char*, sqlite3_file**, int*); +int sqlite3OsOpenExclusive(const char*, sqlite3_file**, int); +int sqlite3OsOpenReadOnly(const char*, sqlite3_file**); + int sqlite3OsDelete(const char*); int sqlite3OsFileExists(const char*); char *sqlite3OsFullPathname(const char*); int sqlite3OsIsDirWritable(char*); int sqlite3OsSyncDirectory(const char*); -int sqlite3OsSectorSize(OsFile *id); int sqlite3OsTempFileName(char*); int sqlite3OsRandomSeed(char*); int sqlite3OsSleep(int ms); @@ -386,8 +387,8 @@ void *sqlite3OsDlsym(void*, const char*); int sqlite3OsDlclose(void*); #if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) - int sqlite3OsFileHandle(OsFile *id); - int sqlite3OsLockState(OsFile *id); + int sqlite3OsFileHandle(sqlite3_file *id); + int sqlite3OsLockState(sqlite3_file *id); #endif /* @@ -409,9 +410,9 @@ int sqlite3OsDlclose(void*); ** operating system interfaces or behaviors. */ struct sqlite3OsVtbl { - int (*xOpenReadWrite)(const char*, OsFile**, int*); - int (*xOpenExclusive)(const char*, OsFile**, int); - int (*xOpenReadOnly)(const char*, OsFile**); + int (*xOpenReadWrite)(const char*, sqlite3_file**, int*); + int (*xOpenExclusive)(const char*, sqlite3_file**, int); + int (*xOpenReadOnly)(const char*, sqlite3_file**); int (*xDelete)(const char*); int (*xFileExists)(const char*); diff --git a/src/os_unix.c b/src/os_unix.c index 91418914ce..dc223d929e 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -82,7 +82,7 @@ */ typedef struct unixFile unixFile; struct unixFile { - IoMethod const *pMethod; /* Always the first entry */ + sqlite3_io_methods const *pMethod; /* Always the first entry */ struct openCnt *pOpen; /* Info about all open fd's on this inode */ struct lockInfo *pLock; /* Info about locks on this inode */ #ifdef SQLITE_ENABLE_LOCKING_STYLE @@ -99,6 +99,7 @@ struct unixFile { #endif }; + /* ** Provide the ability to override some OS-layer functions during ** testing. This is used to simulate OS crashes to verify that @@ -106,13 +107,15 @@ struct unixFile { */ #ifdef SQLITE_CRASH_TEST extern int sqlite3CrashTestEnable; - extern int sqlite3CrashOpenReadWrite(const char*, OsFile**, int*); - extern int sqlite3CrashOpenExclusive(const char*, OsFile**, int); - extern int sqlite3CrashOpenReadOnly(const char*, OsFile**, int); -# define CRASH_TEST_OVERRIDE(X,A,B,C) \ - if(sqlite3CrashTestEnable){ return X(A,B,C); } + int sqlite3CrashFileWrap(sqlite3_file *, const char *, sqlite3_file **); + static int CRASH_TEST_OVERRIDE(const char *zFile, sqlite3_file **pId, int rc){ + if( rc==SQLITE_OK && sqlite3CrashTestEnable ){ + rc = sqlite3CrashFileWrap(*pId, zFile, pId); + } + return rc; + } #else -# define CRASH_TEST_OVERRIDE(X,A,B,C) /* no-op */ +# define CRASH_TEST_OVERRIDE(A,B,C) C #endif @@ -801,7 +804,7 @@ int sqlite3UnixFileExists(const char *zFilename){ /* Forward declaration */ static int allocateUnixFile( int h, /* File descriptor of the open file */ - OsFile **pId, /* Write the real file descriptor here */ + sqlite3_file **pId, /* Write the real file descriptor here */ const char *zFilename, /* Name of the file being opened */ int delFlag /* If true, make sure the file deletes on close */ ); @@ -821,12 +824,11 @@ static int allocateUnixFile( */ int sqlite3UnixOpenReadWrite( const char *zFilename, - OsFile **pId, + sqlite3_file **pId, int *pReadonly ){ int h; - CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadWrite, zFilename, pId, pReadonly); assert( 0==*pId ); h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, SQLITE_DEFAULT_FILE_PERMISSIONS); @@ -844,7 +846,10 @@ int sqlite3UnixOpenReadWrite( }else{ *pReadonly = 0; } - return allocateUnixFile(h, pId, zFilename, 0); + + return CRASH_TEST_OVERRIDE( + zFilename, pId, allocateUnixFile(h, pId, zFilename, 0) + ); } @@ -862,10 +867,13 @@ int sqlite3UnixOpenReadWrite( ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3UnixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ +int sqlite3UnixOpenExclusive( + const char *zFilename, + sqlite3_file **pId, + int delFlag +){ int h; - CRASH_TEST_OVERRIDE(sqlite3CrashOpenExclusive, zFilename, pId, delFlag); assert( 0==*pId ); h = open(zFilename, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, @@ -873,7 +881,9 @@ int sqlite3UnixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ if( h<0 ){ return SQLITE_CANTOPEN; } - return allocateUnixFile(h, pId, zFilename, delFlag); + return CRASH_TEST_OVERRIDE( + zFilename, pId, allocateUnixFile(h, pId, zFilename, delFlag) + ); } /* @@ -883,16 +893,17 @@ int sqlite3UnixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ ** ** On failure, return SQLITE_CANTOPEN. */ -int sqlite3UnixOpenReadOnly(const char *zFilename, OsFile **pId){ +int sqlite3UnixOpenReadOnly(const char *zFilename, sqlite3_file **pId){ int h; - CRASH_TEST_OVERRIDE(sqlite3CrashOpenReadOnly, zFilename, pId, 0); assert( 0==*pId ); h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY); if( h<0 ){ return SQLITE_CANTOPEN; } - return allocateUnixFile(h, pId, zFilename, 0); + return CRASH_TEST_OVERRIDE( + zFilename, pId, allocateUnixFile(h, pId, zFilename, 0) + ); } /* @@ -994,29 +1005,26 @@ int sqlite3UnixIsDirWritable(char *zBuf){ ** Seek to the offset in id->offset then read cnt bytes into pBuf. ** Return the number of bytes actually read. Update the offset. */ -static int seekAndRead(unixFile *id, void *pBuf, int cnt){ +static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ int got; i64 newOffset; TIMER_START; #if defined(USE_PREAD) - got = pread(id->h, pBuf, cnt, id->offset); + got = pread(id->h, pBuf, cnt, offset); SimulateIOError( got = -1 ); #elif defined(USE_PREAD64) - got = pread64(id->h, pBuf, cnt, id->offset); + got = pread64(id->h, pBuf, cnt, offset); SimulateIOError( got = -1 ); #else - newOffset = lseek(id->h, id->offset, SEEK_SET); + newOffset = lseek(id->h, offset, SEEK_SET); SimulateIOError( newOffset-- ); - if( newOffset!=id->offset ){ + if( newOffset!=offset ){ return -1; } got = read(id->h, pBuf, cnt); #endif TIMER_END; OSTRACE5("READ %-3d %5d %7lld %d\n", id->h, got, id->offset, TIMER_ELAPSED); - if( got>0 ){ - id->offset += got; - } return got; } @@ -1025,10 +1033,15 @@ static int seekAndRead(unixFile *id, void *pBuf, int cnt){ ** bytes were read successfully and SQLITE_IOERR if anything goes ** wrong. */ -static int unixRead(OsFile *id, void *pBuf, int amt){ +static int unixRead( + sqlite3_file *id, + void *pBuf, + int amt, + sqlite3_int64 offset +){ int got; assert( id ); - got = seekAndRead((unixFile*)id, pBuf, amt); + got = seekAndRead((unixFile*)id, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; }else if( got<0 ){ @@ -1043,26 +1056,23 @@ static int unixRead(OsFile *id, void *pBuf, int amt){ ** Seek to the offset in id->offset then read cnt bytes into pBuf. ** Return the number of bytes actually read. Update the offset. */ -static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){ +static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ int got; i64 newOffset; TIMER_START; #if defined(USE_PREAD) - got = pwrite(id->h, pBuf, cnt, id->offset); + got = pwrite(id->h, pBuf, cnt, offset); #elif defined(USE_PREAD64) - got = pwrite64(id->h, pBuf, cnt, id->offset); + got = pwrite64(id->h, pBuf, cnt, offset); #else - newOffset = lseek(id->h, id->offset, SEEK_SET); - if( newOffset!=id->offset ){ + newOffset = lseek(id->h, offset, SEEK_SET); + if( newOffset!=offset ){ return -1; } got = write(id->h, pBuf, cnt); #endif TIMER_END; - OSTRACE5("WRITE %-3d %5d %7lld %d\n", id->h, got, id->offset, TIMER_ELAPSED); - if( got>0 ){ - id->offset += got; - } + OSTRACE5("WRITE %-3d %5d %7lld %d\n", id->h, got, offset, TIMER_ELAPSED); return got; } @@ -1071,12 +1081,18 @@ static int seekAndWrite(unixFile *id, const void *pBuf, int cnt){ ** Write data from a buffer into a file. Return SQLITE_OK on success ** or some other error code on failure. */ -static int unixWrite(OsFile *id, const void *pBuf, int amt){ +static int unixWrite( + sqlite3_file *id, + const void *pBuf, + int amt, + sqlite3_int64 offset +){ int wrote = 0; assert( id ); assert( amt>0 ); - while( amt>0 && (wrote = seekAndWrite((unixFile*)id, pBuf, amt))>0 ){ + while( amt>0 && (wrote = seekAndWrite((unixFile*)id, offset, pBuf, amt))>0 ){ amt -= wrote; + offset += wrote; pBuf = &((char*)pBuf)[wrote]; } SimulateIOError(( wrote=(-1), amt=1 )); @@ -1205,7 +1221,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. */ -static int unixSync(OsFile *id, int dataOnly){ +static int unixSync(sqlite3_file *id, int dataOnly){ int rc; unixFile *pFile = (unixFile*)id; assert( pFile ); @@ -1272,7 +1288,7 @@ int sqlite3UnixSyncDirectory(const char *zDirname){ /* ** Truncate an open file to a specified size */ -static int unixTruncate(OsFile *id, i64 nByte){ +static int unixTruncate(sqlite3_file *id, i64 nByte){ int rc; assert( id ); rc = ftruncate(((unixFile*)id)->h, (off_t)nByte); @@ -1287,7 +1303,7 @@ static int unixTruncate(OsFile *id, i64 nByte){ /* ** Determine the current size of a file in bytes */ -static int unixFileSize(OsFile *id, i64 *pSize){ +static int unixFileSize(sqlite3_file *id, i64 *pSize){ int rc; struct stat buf; assert( id ); @@ -1306,7 +1322,7 @@ static int unixFileSize(OsFile *id, i64 *pSize){ ** non-zero. If the file is unlocked or holds only SHARED locks, then ** return zero. */ -static int unixCheckReservedLock(OsFile *id){ +static int unixCheckReservedLock(sqlite3_file *id){ int r = 0; unixFile *pFile = (unixFile*)id; @@ -1362,7 +1378,7 @@ static int unixCheckReservedLock(OsFile *id){ ** This routine will only increase a lock. Use the sqlite3OsUnlock() ** routine to lower a locking level. */ -static int unixLock(OsFile *id, int locktype){ +static int unixLock(sqlite3_file *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 @@ -1564,7 +1580,7 @@ end_lock: ** If the locking level of the file descriptor is already at or below ** the requested locking level, this routine is a no-op. */ -static int unixUnlock(OsFile *id, int locktype){ +static int unixUnlock(sqlite3_file *id, int locktype){ struct lockInfo *pLock; struct flock lock; int rc = SQLITE_OK; @@ -1650,44 +1666,42 @@ static int unixUnlock(OsFile *id, int locktype){ /* ** Close a file. */ -static int unixClose(OsFile **pId){ - unixFile *id = (unixFile*)*pId; - - if( !id ) return SQLITE_OK; - unixUnlock(*pId, NO_LOCK); - if( id->dirfd>=0 ) close(id->dirfd); - id->dirfd = -1; +static int unixClose(sqlite3_file *id){ + unixFile *pFile = (unixFile *)id; + if( !pFile ) return SQLITE_OK; + unixUnlock(id, NO_LOCK); + if( pFile->dirfd>=0 ) close(pFile->dirfd); + pFile->dirfd = -1; sqlite3OsEnterMutex(); - if( id->pOpen->nLock ){ + if( pFile->pOpen->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file ** descriptor to pOpen->aPending. It will be automatically closed when ** the last lock is cleared. */ int *aNew; - struct openCnt *pOpen = id->pOpen; + struct openCnt *pOpen = pFile->pOpen; aNew = realloc( pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); if( aNew==0 ){ /* If a malloc fails, just leak the file descriptor */ }else{ pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending] = id->h; + pOpen->aPending[pOpen->nPending] = pFile->h; pOpen->nPending++; } }else{ /* There are no outstanding locks so we can close the file immediately */ - close(id->h); + close(pFile->h); } - releaseLockInfo(id->pLock); - releaseOpenCnt(id->pOpen); + releaseLockInfo(pFile->pLock); + releaseOpenCnt(pFile->pOpen); sqlite3OsLeaveMutex(); - id->isOpen = 0; - OSTRACE2("CLOSE %-3d\n", id->h); + pFile->isOpen = 0; + OSTRACE2("CLOSE %-3d\n", pFile->h); OpenCounter(-1); sqlite3ThreadSafeFree(id); - *pId = 0; return SQLITE_OK; } @@ -2348,30 +2362,37 @@ static int unixLockState(OsFile *id){ ** a database and it's journal file) that the sector size will be the ** same for both. */ -static int unixSectorSize(OsFile *id){ +static int unixSectorSize(sqlite3_file *id){ return SQLITE_DEFAULT_SECTOR_SIZE; } +static int unixDeviceCharacteristics(sqlite3_file *id){ + return 0; +} + +static int unixBreakLock(sqlite3_file *id){ + assert(!"TODO: unixBreakLock()"); + return 0; +} + /* ** This vector defines all the methods that can operate on an OsFile ** for unix. */ -static const IoMethod sqlite3UnixIoMethod = { +static const sqlite3_io_methods sqlite3UnixIoMethod = { + 1, /* iVersion */ unixClose, - unixOpenDirectory, unixRead, unixWrite, - unixSeek, unixTruncate, unixSync, - unixSetFullSync, - unixFileHandle, unixFileSize, unixLock, unixUnlock, - unixLockState, unixCheckReservedLock, + unixBreakLock, unixSectorSize, + unixDeviceCharacteristics }; #ifdef SQLITE_ENABLE_LOCKING_STYLE @@ -2573,7 +2594,7 @@ static int allocateUnixFile( #else /* SQLITE_ENABLE_LOCKING_STYLE */ static int allocateUnixFile( int h, /* Open file descriptor on file being opened */ - OsFile **pId, /* Write the resul unixFile structure here */ + sqlite3_file **pId, /* Write the resul unixFile structure here */ const char *zFilename, /* Name of the file being opened */ int delFlag /* If true, delete the file on or before closing */ ){ @@ -2611,7 +2632,7 @@ static int allocateUnixFile( }else{ *pNew = f; pNew->pMethod = &sqlite3UnixIoMethod; - *pId = (OsFile*)pNew; + *pId = (sqlite3_file*)pNew; OpenCounter(+1); return SQLITE_OK; } diff --git a/src/pager.c b/src/pager.c index 1202b77954..35a2667da6 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.355 2007/08/11 00:26:21 drh Exp $ +** @(#) $Id: pager.c,v 1.356 2007/08/15 17:08:46 danielk1977 Exp $ */ #ifndef SQLITE_OMIT_DISKIO #include "sqliteInt.h" @@ -50,7 +50,7 @@ ** to print out file-descriptors. ** ** PAGERID() takes a pointer to a Pager struct as it's argument. The -** associated file-descriptor is returned. FILEHANDLEID() takes an OsFile +** associated file-descriptor is returned. FILEHANDLEID() takes an sqlite3_file ** struct as it's argument. */ #define PAGERID(p) ((int)(p->fd)) @@ -336,8 +336,8 @@ struct Pager { char *zFilename; /* Name of the database file */ char *zJournal; /* Name of the journal file */ char *zDirectory; /* Directory hold database and journal files */ - OsFile *fd, *jfd; /* File descriptors for database and journal */ - OsFile *stfd; /* File descriptor for the statement subjournal*/ + sqlite3_file *fd, *jfd; /* File descriptors for database and journal */ + sqlite3_file *stfd; /* File descriptor for the statement subjournal*/ BusyHandler *pBusyHandler; /* Pointer to sqlite.busyHandler */ PgHdr *pFirst, *pLast; /* List of free pages */ PgHdr *pFirstSynced; /* First free page with PgHdr.needSync==0 */ @@ -526,9 +526,9 @@ static void pager_resize_hash_table(Pager *pPager, int N){ ** ** All values are stored on disk as big-endian. */ -static int read32bits(OsFile *fd, u32 *pRes){ +static int read32bits(sqlite3_file *fd, i64 offset, u32 *pRes){ unsigned char ac[4]; - int rc = sqlite3OsRead(fd, ac, sizeof(ac)); + int rc = sqlite3OsRead(fd, ac, sizeof(ac), offset); if( rc==SQLITE_OK ){ *pRes = sqlite3Get4byte(ac); } @@ -544,10 +544,10 @@ static int read32bits(OsFile *fd, u32 *pRes){ ** Write a 32-bit integer into the given file descriptor. Return SQLITE_OK ** on success or an error code is something goes wrong. */ -static int write32bits(OsFile *fd, u32 val){ +static int write32bits(sqlite3_file *fd, i64 offset, u32 val){ char ac[4]; put32bits(ac, val); - return sqlite3OsWrite(fd, ac, 4); + return sqlite3OsWrite(fd, ac, 4, offset); } /* @@ -627,7 +627,7 @@ static void checkPage(PgHdr *pPg){ ** If no master journal file name is present *pzMaster is set to 0 and ** SQLITE_OK returned. */ -static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ +static int readMasterJournal(sqlite3_file *pJrnl, char **pzMaster){ int rc; u32 len; i64 szJ; @@ -640,26 +640,20 @@ static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ rc = sqlite3OsFileSize(pJrnl, &szJ); if( rc!=SQLITE_OK || szJ<16 ) return rc; - rc = sqlite3OsSeek(pJrnl, szJ-16); - if( rc!=SQLITE_OK ) return rc; - - rc = read32bits(pJrnl, &len); + rc = read32bits(pJrnl, szJ-16, &len); if( rc!=SQLITE_OK ) return rc; - rc = read32bits(pJrnl, &cksum); + rc = read32bits(pJrnl, szJ-12, &cksum); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(pJrnl, aMagic, 8); + rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8); if( rc!=SQLITE_OK || memcmp(aMagic, aJournalMagic, 8) ) return rc; - rc = sqlite3OsSeek(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 = sqlite3OsRead(pJrnl, *pzMaster, len, szJ-16-len); if( rc!=SQLITE_OK ){ sqliteFree(*pzMaster); *pzMaster = 0; @@ -700,7 +694,7 @@ static int readMasterJournal(OsFile *pJrnl, char **pzMaster){ ** 2000 2048 ** */ -static int seekJournalHdr(Pager *pPager){ +static void seekJournalHdr(Pager *pPager){ i64 offset = 0; i64 c = pPager->journalOff; if( c ){ @@ -710,7 +704,6 @@ static int seekJournalHdr(Pager *pPager){ assert( offset>=c ); assert( (offset-c)journalOff = offset; - return sqlite3OsSeek(pPager->jfd, pPager->journalOff); } /* @@ -735,11 +728,8 @@ static int writeJournalHdr(Pager *pPager){ pPager->stmtHdrOff = pPager->journalOff; } - rc = seekJournalHdr(pPager); - if( rc ) return rc; - + seekJournalHdr(pPager); pPager->journalHdr = pPager->journalOff; - pPager->journalOff += JOURNAL_HDR_SZ(pPager); /* FIX ME: ** @@ -760,17 +750,15 @@ static int writeJournalHdr(Pager *pPager){ /* The assumed sector size for this process */ put32bits(&zHeader[sizeof(aJournalMagic)+12], pPager->sectorSize); IOTRACE(("JHDR %p %lld %d\n", pPager, pPager->journalHdr, sizeof(zHeader))) - rc = sqlite3OsWrite(pPager->jfd, zHeader, sizeof(zHeader)); + rc = sqlite3OsWrite(pPager->jfd, zHeader, sizeof(zHeader),pPager->journalOff); + pPager->journalOff += JOURNAL_HDR_SZ(pPager); /* The journal header has been written successfully. Seek the journal ** file descriptor to the end of the journal header sector. */ if( rc==SQLITE_OK ){ IOTRACE(("JTAIL %p %lld\n", pPager, pPager->journalOff-1)) - rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff-1); - if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->jfd, "\000", 1); - } + rc = sqlite3OsWrite(pPager->jfd, "\000", 1, pPager->journalOff-1); } return rc; } @@ -799,28 +787,29 @@ static int readJournalHdr( ){ int rc; unsigned char aMagic[8]; /* A buffer to hold the magic header */ + i64 jrnlOff; - rc = seekJournalHdr(pPager); - if( rc ) return rc; - + seekJournalHdr(pPager); if( pPager->journalOff+JOURNAL_HDR_SZ(pPager) > journalSize ){ return SQLITE_DONE; } + jrnlOff = pPager->journalOff; - rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic)); + rc = sqlite3OsRead(pPager->jfd, aMagic, sizeof(aMagic), jrnlOff); if( rc ) return rc; + jrnlOff += sizeof(aMagic); if( memcmp(aMagic, aJournalMagic, sizeof(aMagic))!=0 ){ return SQLITE_DONE; } - rc = read32bits(pPager->jfd, pNRec); + rc = read32bits(pPager->jfd, jrnlOff, pNRec); if( rc ) return rc; - rc = read32bits(pPager->jfd, &pPager->cksumInit); + rc = read32bits(pPager->jfd, jrnlOff+4, &pPager->cksumInit); if( rc ) return rc; - rc = read32bits(pPager->jfd, pDbSize); + rc = read32bits(pPager->jfd, jrnlOff+8, pDbSize); if( rc ) return rc; /* Update the assumed sector-size to match the value used by @@ -829,12 +818,11 @@ static int readJournalHdr( ** is being called from within pager_playback(). The local value ** of Pager.sectorSize is restored at the end of that routine. */ - rc = read32bits(pPager->jfd, (u32 *)&pPager->sectorSize); + rc = read32bits(pPager->jfd, jrnlOff+12, (u32 *)&pPager->sectorSize); if( rc ) return rc; pPager->journalOff += JOURNAL_HDR_SZ(pPager); - rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff); - return rc; + return SQLITE_OK; } @@ -861,6 +849,7 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ int rc; int len; int i; + i64 jrnlOff; u32 cksum = 0; char zBuf[sizeof(aJournalMagic)+2*4]; @@ -877,21 +866,23 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){ ** the journal has already been synced. */ if( pPager->fullSync ){ - rc = seekJournalHdr(pPager); - if( rc!=SQLITE_OK ) return rc; + seekJournalHdr(pPager); } + jrnlOff = pPager->journalOff; pPager->journalOff += (len+20); - rc = write32bits(pPager->jfd, PAGER_MJ_PGNO(pPager)); + rc = write32bits(pPager->jfd, jrnlOff, PAGER_MJ_PGNO(pPager)); if( rc!=SQLITE_OK ) return rc; + jrnlOff += 4; - rc = sqlite3OsWrite(pPager->jfd, zMaster, len); + rc = sqlite3OsWrite(pPager->jfd, zMaster, len, jrnlOff); if( rc!=SQLITE_OK ) return rc; + jrnlOff += len; put32bits(zBuf, len); put32bits(&zBuf[4], cksum); memcpy(&zBuf[8], aJournalMagic, sizeof(aJournalMagic)); - rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic)); + rc = sqlite3OsWrite(pPager->jfd, zBuf, 8+sizeof(aJournalMagic), jrnlOff); pPager->needSync = !pPager->noSync; return rc; } @@ -1026,7 +1017,6 @@ static int pager_end_transaction(Pager *pPager){ if( pPager->journalOpen ){ if( pPager->exclusiveMode && (rc = sqlite3OsTruncate(pPager->jfd, 0))==SQLITE_OK ){; - sqlite3OsSeek(pPager->jfd, 0); pPager->journalOff = 0; pPager->journalStarted = 0; }else{ @@ -1111,7 +1101,12 @@ static void makeClean(PgHdr*); ** are not used in statement journals because statement journals do not ** need to survive power failures. */ -static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ +static int pager_playback_one_page( + Pager *pPager, + sqlite3_file *jfd, + i64 offset, + int useCksum +){ int rc; PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ @@ -1124,9 +1119,9 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ assert( jfd == (useCksum ? pPager->jfd : pPager->stfd) ); assert( aData ); - rc = read32bits(jfd, &pgno); + rc = read32bits(jfd, offset, &pgno); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(jfd, aData, pPager->pageSize); + rc = sqlite3OsRead(jfd, aData, pPager->pageSize, offset+4); if( rc!=SQLITE_OK ) return rc; pPager->journalOff += pPager->pageSize + 4; @@ -1142,7 +1137,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ return SQLITE_OK; } if( useCksum ){ - rc = read32bits(jfd, &cksum); + rc = read32bits(jfd, offset+pPager->pageSize+4, &cksum); if( rc ) return rc; pPager->journalOff += 4; if( pager_cksum(pPager, aData)!=cksum ){ @@ -1184,10 +1179,8 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ PAGERTRACE4("PLAYBACK %d page %d hash(%08x)\n", PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData)); if( pPager->state>=PAGER_EXCLUSIVE && (pPg==0 || pPg->needSync==0) ){ - rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize); - if( rc==SQLITE_OK ){ - rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize); - } + i64 offset = (pgno-1)*(i64)pPager->pageSize; + rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, offset); if( pPg ){ makeClean(pPg); } @@ -1235,7 +1228,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int useCksum){ static int pager_delmaster(const char *zMaster){ int rc; int master_open = 0; - OsFile *master = 0; + sqlite3_file *master = 0; char *zMasterJournal = 0; /* Contents of master journal file */ i64 nMasterJournal; /* Size of master journal file */ @@ -1261,7 +1254,7 @@ static int pager_delmaster(const char *zMaster){ rc = SQLITE_NOMEM; goto delmaster_out; } - rc = sqlite3OsRead(master, zMasterJournal, nMasterJournal); + rc = sqlite3OsRead(master, zMasterJournal, nMasterJournal, 0); if( rc!=SQLITE_OK ) goto delmaster_out; zJournal = zMasterJournal; @@ -1271,7 +1264,7 @@ static int pager_delmaster(const char *zMaster){ ** Open it and check if it points at the master journal. If ** so, return without deleting the master journal file. */ - OsFile *journal = 0; + sqlite3_file *journal = 0; int c; rc = sqlite3OsOpenReadOnly(zJournal, &journal); @@ -1424,7 +1417,6 @@ static int pager_playback(Pager *pPager, int isHot){ if( rc==SQLITE_DONE ) rc = SQLITE_OK; goto end_playback; } - sqlite3OsSeek(pPager->jfd, 0); pPager->journalOff = 0; /* This loop terminates either when the readJournalHdr() call returns @@ -1480,7 +1472,7 @@ static int pager_playback(Pager *pPager, int isHot){ /* Copy original pages out of the journal and back into the database file. */ for(i=0; ijfd, 1); + rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1); if( rc!=SQLITE_OK ){ if( rc==SQLITE_DONE ){ rc = SQLITE_OK; @@ -1567,7 +1559,6 @@ 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); nRec = pPager->stmtNRec; /* Copy original pages out of the statement journal and back into the @@ -1575,8 +1566,9 @@ static int pager_stmt_playback(Pager *pPager){ ** each record since power-failure recovery is not important to statement ** journals. */ - for(i=nRec-1; i>=0; i--){ - rc = pager_playback_one_page(pPager, pPager->stfd, 0); + for(i=0; ipageSize); + rc = pager_playback_one_page(pPager, pPager->stfd, offset, 0); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } @@ -1589,14 +1581,10 @@ 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); - if( rc!=SQLITE_OK ){ - goto end_stmt_playback; - } pPager->journalOff = pPager->stmtJSize; pPager->cksumInit = pPager->stmtCksum; while( pPager->journalOff < hdrOff ){ - rc = pager_playback_one_page(pPager, pPager->jfd, 1); + rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } @@ -1613,7 +1601,7 @@ static int pager_stmt_playback(Pager *pPager){ nJRec = (szJ - pPager->journalOff) / (pPager->pageSize+8); } for(i=nJRec-1; i>=0 && pPager->journalOff < szJ; i--){ - rc = pager_playback_one_page(pPager, pPager->jfd, 1); + rc = pager_playback_one_page(pPager, pPager->jfd, pPager->journalOff, 1); assert( rc!=SQLITE_DONE ); if( rc!=SQLITE_OK ) goto end_stmt_playback; } @@ -1693,7 +1681,7 @@ int sqlite3_opentemp_count = 0; ** The OS will automatically delete the temporary file when it is ** closed. */ -static int sqlite3PagerOpentemp(OsFile **pFd){ +static int sqlite3PagerOpentemp(sqlite3_file **pFd){ int cnt = 8; int rc; char zFile[SQLITE_TEMPNAME_SIZE]; @@ -1733,7 +1721,7 @@ int sqlite3PagerOpen( Pager *pPager = 0; char *zFullPathname = 0; int nameLen; /* Compiler is wrong. This is always initialized before use */ - OsFile *fd = 0; + sqlite3_file *fd = 0; int rc = SQLITE_OK; int i; int tempFile = 0; @@ -1970,11 +1958,8 @@ int sqlite3PagerReadFileheader(Pager *pPager, int N, unsigned char *pDest){ int rc = SQLITE_OK; memset(pDest, 0, N); if( MEMDB==0 ){ - disable_simulated_io_errors(); - sqlite3OsSeek(pPager->fd, 0); - enable_simulated_io_errors(); IOTRACE(("DBHDR %p 0 %d\n", pPager, N)) - rc = sqlite3OsRead(pPager->fd, pDest, N); + rc = sqlite3OsRead(pPager->fd, pDest, N, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } @@ -2381,6 +2366,8 @@ static int syncJournal(Pager *pPager){ } #endif { + i64 jrnlOff; + /* Write the nRec value into the journal file header. If in ** full-synchronous mode, sync the journal first. This ensures that ** all data has really hit the disk before nRec is updated to mark @@ -2392,15 +2379,10 @@ static int syncJournal(Pager *pPager){ rc = sqlite3OsSync(pPager->jfd, 0); if( rc!=0 ) return rc; } - rc = sqlite3OsSeek(pPager->jfd, - pPager->journalHdr + sizeof(aJournalMagic)); - if( rc ) return rc; - IOTRACE(("JHDR %p %lld %d\n", pPager, - pPager->journalHdr + sizeof(aJournalMagic), 4)) - rc = write32bits(pPager->jfd, pPager->nRec); - if( rc ) return rc; - rc = sqlite3OsSeek(pPager->jfd, pPager->journalOff); + jrnlOff = pPager->journalHdr + sizeof(aJournalMagic); + IOTRACE(("JHDR %p %lld %d\n", pPager, jrnlOff, 4)); + rc = write32bits(pPager->jfd, jrnlOff, pPager->nRec); if( rc ) return rc; } PAGERTRACE2("SYNC journal of %d\n", PAGERID(pPager)); @@ -2545,19 +2527,18 @@ static int pager_write_pagelist(PgHdr *pList){ pList = sort_pagelist(pList); while( pList ){ assert( pList->dirty ); - rc = sqlite3OsSeek(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 sqlite3PagerTruncate() was called to ** make the file smaller (presumably by auto-vacuum code). Do not write ** any such pages to the file. */ if( pList->pgno<=pPager->dbSize ){ + i64 offset = (pList->pgno-1)*(i64)pPager->pageSize; char *pData = CODEC2(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6); PAGERTRACE4("STORE %d page %d hash(%08x)\n", PAGERID(pPager), pList->pgno, pager_pagehash(pList)); IOTRACE(("PGOUT %p %d\n", pPager, pList->pgno)); - rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize); + rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset); PAGER_INCR(sqlite3_pager_writedb_count); PAGER_INCR(pPager->nWrite); if( pList->pgno==1 ){ @@ -2796,12 +2777,10 @@ int sqlite3PagerReleaseMemory(int nReq){ */ static int readDbPage(Pager *pPager, PgHdr *pPg, Pgno pgno){ int rc; + i64 offset; assert( MEMDB==0 ); - rc = sqlite3OsSeek(pPager->fd, (pgno-1)*(i64)pPager->pageSize); - if( rc==SQLITE_OK ){ - rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), - pPager->pageSize); - } + offset = (pgno-1)*(i64)pPager->pageSize; + rc = sqlite3OsRead(pPager->fd, PGHDR_TO_DATA(pPg), pPager->pageSize, offset); PAGER_INCR(sqlite3_pager_readdb_count); PAGER_INCR(pPager->nRead); IOTRACE(("PGIN %p %d\n", pPager, pgno)); @@ -2934,11 +2913,7 @@ static int pagerSharedLock(Pager *pPager){ if( pPager->dbSize>0 ){ IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers))); - rc = sqlite3OsSeek(pPager->fd, 24); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers)); + rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24); if( rc!=SQLITE_OK ){ return rc; } @@ -3332,9 +3307,11 @@ static int pager_open_journal(Pager *pPager){ } goto failed_to_open_journal; } +#if 0 sqlite3OsSetFullSync(pPager->jfd, pPager->full_fsync); sqlite3OsSetFullSync(pPager->fd, pPager->full_fsync); sqlite3OsOpenDirectory(pPager->jfd, pPager->zDirectory); +#endif pPager->journalOpen = 1; pPager->journalStarted = 0; pPager->needSync = 0; @@ -3583,7 +3560,7 @@ static int pager_write(PgHdr *pPg){ put32bits(pEnd, cksum); szPg = pPager->pageSize+8; put32bits(pData2, pPg->pgno); - rc = sqlite3OsWrite(pPager->jfd, pData2, szPg); + rc = sqlite3OsWrite(pPager->jfd, pData2, szPg, pPager->journalOff); IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, pPager->journalOff, szPg)); PAGER_INCR(sqlite3_pager_writej_count); @@ -3638,9 +3615,10 @@ static int pager_write(PgHdr *pPg){ PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); page_add_to_stmt_list(pPg); }else{ + i64 offset = pPager->stmtNRec*(4+pPager->pageSize); char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7)-4; put32bits(pData2, pPg->pgno); - rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize+4); + rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize+4, offset); PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno); if( rc!=SQLITE_OK ){ return rc; @@ -4215,7 +4193,6 @@ int sqlite3PagerStmtCommit(Pager *pPager){ PgHdr *pPg, *pNext; PAGERTRACE2("STMT-COMMIT %d\n", PAGERID(pPager)); if( !MEMDB ){ - sqlite3OsSeek(pPager->stfd, 0); /* sqlite3OsTruncate(pPager->stfd, 0); */ sqliteFree( pPager->aInStmt ); pPager->aInStmt = 0; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index c614201cf8..d378ed076c 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.223 2007/08/15 13:04:54 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.224 2007/08/15 17:08:46 danielk1977 Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -488,7 +488,7 @@ struct sqlite3_io_methods { int iVersion; int (*xClose)(sqlite3_file*); int (*xRead)(sqlite3_file*, void*, int iAmt, sqlite_int64 iOfst); - int (*xWrite)(sqlite3_file*, void*, int iAmt, sqlite_int64 iOfst); + int (*xWrite)(sqlite3_file*, const void*, int iAmt, sqlite_int64 iOfst); int (*xTruncate)(sqlite3_file*, sqlite_int64 size); int (*xSync)(sqlite3_file*, int flags); int (*xFileSize)(sqlite3_file*, sqlite_int64 *pSize); diff --git a/src/test2.c b/src/test2.c index 1398c18379..540957e348 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.43 2007/04/02 05:07:47 danielk1977 Exp $ +** $Id: test2.c,v 1.44 2007/08/15 17:08:46 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -546,12 +546,7 @@ static int fake_big_file( } offset = n; offset *= 1024*1024; - rc = sqlite3OsSeek(fd, offset); - if( rc ){ - Tcl_AppendResult(interp, "seek failed: ", errorName(rc), 0); - return TCL_ERROR; - } - rc = sqlite3OsWrite(fd, "Hello, World!", 14); + rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset); sqlite3OsClose(&fd); if( rc ){ Tcl_AppendResult(interp, "write failed: ", errorName(rc), 0); diff --git a/src/test6.c b/src/test6.c index 5b49156daf..534a09f1b0 100644 --- a/src/test6.c +++ b/src/test6.c @@ -21,7 +21,8 @@ #ifndef SQLITE_OMIT_DISKIO /* This file is a no-op if disk I/O is disabled */ -typedef struct crashFile crashFile; +typedef struct CrashFile CrashFile; +typedef struct CrashGlobal CrashGlobal; typedef struct WriteBuffer WriteBuffer; /* @@ -103,384 +104,41 @@ typedef struct WriteBuffer WriteBuffer; ** operation is one byte past the current end of the file, then option ** (1) is always selected. */ + +/* +** Each write operation in the write-list is represented by an instance +** of the following structure. +** +** If zBuf is 0, then this structure represents a call to xTruncate(), +** not xWrite(). In that case, iOffset is the size that the file is +** truncated to. +*/ struct WriteBuffer { - i64 iOffset; /* Byte offset of the start of this write() */ - int nBuf; /* Number of bytes written */ - u8 *zBuf; /* Pointer to copy of written data */ - crashFile *pFile; /* File this write() applies to */ + i64 iOffset; /* Byte offset of the start of this write() */ + int nBuf; /* Number of bytes written */ + u8 *zBuf; /* Pointer to copy of written data */ + CrashFile *pFile; /* File this write() applies to */ + + WriteBuffer *pNext; /* Next in CrashGlobal.pWriteList */ }; -/* -** crashFile is a subclass of OsFile that is taylored for -** the crash test module. -*/ -struct crashFile { - IoMethod const *pMethod; /* Must be first */ - u8 **apBlk; /* Array of blocks that have been written to. */ - int nBlk; /* Size of apBlock. */ - i64 offset; /* Next character to be read from the file */ - int nMaxWrite; /* Largest offset written to. */ - char *zName; /* File name */ - OsFile *pBase; /* The real file */ - crashFile *pNext; /* Next in a list of them all */ +struct CrashFile { + const sqlite3_io_methods *pMethod; /* Must be first */ + sqlite3_file *pRealFile; /* Underlying "real" file handle */ + const char *zName; }; -/* -** Size of a simulated disk block. Default is 512 bytes. -*/ -static int BLOCKSIZE = 512; -#define BLOCK_OFFSET(x) ((x) * BLOCKSIZE) +struct CrashGlobal { + WriteBuffer *pWriteList; /* Head of write-list */ + int iSectorSize; /* Value of simulated sector size */ + int iDeviceCharacteristics; /* Value of simulated device characteristics */ -/* -** 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]; + int iCrash; /* Crash on the iCrash'th call to xSync() */ + char zCrashFile[500]; /* Crash during an xSync() on this file */ +}; -/* -** A list of all open files. -*/ -static crashFile *pAllFiles = 0; - -/* -** 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; -} - -/* Forward reference */ -static void initFile(OsFile **pId, char const *zName, OsFile *pBase); - -/* -** Undo the work done by initFile. Delete the OsFile structure -** and unlink the structure from the pAllFiles list. -*/ -static void closeFile(crashFile **pId){ - crashFile *pFile = *pId; - if( pFile==pAllFiles ){ - pAllFiles = pFile->pNext; - }else{ - crashFile *p; - for(p=pAllFiles; p->pNext!=pFile; p=p->pNext ){ - assert( p ); - } - p->pNext = pFile->pNext; - } - sqliteFree(*pId); - *pId = 0; -} - -/* -** Read block 'blk' off of the real disk file and into the cache of pFile. -*/ -static int readBlockIntoCache(crashFile *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 = sqlite3OsFileSize(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 = sqlite3OsRead(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(crashFile *pFile, int crash){ - int i; - int nMax = pFile->nMaxWrite; - int rc = SQLITE_OK; - - 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 = sqlite3OsSeek(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 = sqlite3OsWrite(pFile->pBase, p, len); - } - } - sqliteFree(p); - } - } - sqliteFree(pFile->apBlk); - pFile->nBlk = 0; - pFile->apBlk = 0; - pFile->nMaxWrite = 0; - return rc; -} - -/* -** Write the cache to disk. -*/ -static int writeCache(crashFile *pFile){ - if( pFile->apBlk ){ - int c = crashRequired(pFile->zName); - if( c ){ - crashFile *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 **pId){ - crashFile *pFile = (crashFile*)*pId; - if( pFile ){ - /* printf("CLOSE %s (%d blocks)\n", pFile->zName, pFile->nBlk); */ - writeCache(pFile); - sqlite3OsClose(&pFile->pBase); - } - closeFile(&pFile); - *pId = 0; - return SQLITE_OK; -} - -static int crashSeek(OsFile *id, i64 offset){ - ((crashFile*)id)->offset = offset; - 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; - crashFile *pFile = (crashFile*)id; - - offset = pFile->offset; - 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 = sqlite3OsSeek(pFile->pBase, BLOCK_OFFSET(i) + off); - if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(pFile->pBase, zCsr, len); - if( rc!=SQLITE_OK ) return rc; - } - - zCsr += len; - } - assert( zCsr==&((u8 *)pBuf)[amt] ); - - pFile->offset = 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; - crashFile *pFile = (crashFile*)id; - - offset = pFile->offset; - 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] ); - pFile->offset = 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){ - return writeCache((crashFile*)id); -} - -/* -** 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){ - crashFile *pFile = (crashFile*)id; - pFile->nMaxWrite = nByte; - return sqlite3OsTruncate(pFile->pBase, 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){ - crashFile *pFile = (crashFile*)id; - int rc = sqlite3OsFileSize(pFile->pBase, pSize); - if( rc==SQLITE_OK && pSize && *pSizenMaxWrite ){ - *pSize = pFile->nMaxWrite; - } - return rc; -} +static CrashGlobal g = {0, SQLITE_DEFAULT_SECTOR_SIZE, 0, 0}; /* ** Set this global variable to 1 to enable crash testing. @@ -488,138 +146,387 @@ static int crashFileSize(OsFile *id, i64 *pSize){ int sqlite3CrashTestEnable = 0; /* -** 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. +** Flush the write-list as if xSync() had been called on file handle +** pFile. If isCrash is true, simulate a crash. */ -int sqlite3CrashOpenReadWrite(const char *zFilename, OsFile **pId,int *pRdonly){ - OsFile *pBase = 0; - int rc; +static int writeListSync(CrashFile *pFile, int isCrash){ + int rc = SQLITE_OK; + int iDc = g.iDeviceCharacteristics; - sqlite3CrashTestEnable = 0; - rc = sqlite3OsOpenReadWrite(zFilename, &pBase, pRdonly); - sqlite3CrashTestEnable = 1; - if( !rc ){ - initFile(pId, zFilename, pBase); - } - return rc; -} -int sqlite3CrashOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){ - OsFile *pBase = 0; - int rc; + WriteBuffer *pWrite; + WriteBuffer **ppPtr; - sqlite3CrashTestEnable = 0; - rc = sqlite3OsOpenExclusive(zFilename, &pBase, delFlag); - sqlite3CrashTestEnable = 1; - if( !rc ){ - initFile(pId, zFilename, pBase); + /* Set pFinal to point to the last element of the write-list that + ** is associated with file handle pFile. + */ + WriteBuffer *pFinal = 0; + if( !isCrash ){ + for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){ + if( pWrite->pFile==pFile ){ + pFinal = pWrite; + } + } } - return rc; -} -int sqlite3CrashOpenReadOnly(const char *zFilename, OsFile **pId, int NotUsed){ - OsFile *pBase = 0; - int rc; - sqlite3CrashTestEnable = 0; - rc = sqlite3OsOpenReadOnly(zFilename, &pBase); - sqlite3CrashTestEnable = 1; - if( !rc ){ - initFile(pId, zFilename, pBase); + ppPtr = &g.pWriteList; + for(pWrite=*ppPtr; rc==SQLITE_OK && pWrite; pWrite=*ppPtr){ + + /* (eAction==1) -> write block out normally, + ** (eAction==2) -> do nothing, + ** (eAction==3) -> trash sectors. + */ + int eAction = 0; + if( !isCrash ){ + eAction = 2; + if( (pWrite->pFile==pFile || iDc&SQLITE_IOCAP_SEQUENTIAL) ){ + eAction = 1; + } + }else{ + char random; + sqlite3Randomness(1, &random); + + if( iDc&SQLITE_IOCAP_ATOMIC || pWrite->zBuf==0 ){ + random &= 0x01; + } + + if( (random&0x06)==0x06 ){ + eAction = 3; + }else{ + eAction = ((random&0x01)?2:1); + } + } + + switch( eAction ){ + case 1: { /* Write out correctly */ + if( pWrite->zBuf ){ + rc = sqlite3OsWrite( + pFile->pRealFile, pWrite->zBuf, pWrite->nBuf, pWrite->iOffset + ); + }else{ + rc = sqlite3OsTruncate(pFile->pRealFile, pWrite->iOffset); + } + *ppPtr = pWrite->pNext; + sqliteFree(pWrite); + break; + } + case 2: { /* Do nothing */ + ppPtr = &pWrite->pNext; + break; + } + case 3: { /* Trash sectors */ + u8 *zGarbage; + sqlite3_int64 iFirst = (pWrite->iOffset%g.iSectorSize); + sqlite3_int64 iLast = (pWrite->iOffset+pWrite->nBuf-1)%g.iSectorSize; + + zGarbage = sqliteMalloc(g.iSectorSize); + if( zGarbage ){ + sqlite3_int64 i; + for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ + sqlite3Randomness(g.iSectorSize, zGarbage); + rc = sqlite3OsWrite( + pFile->pRealFile, i*g.iSectorSize, zGarbage, g.iSectorSize + ); + } + sqliteFree(zGarbage); + }else{ + rc = SQLITE_NOMEM; + } + + ppPtr = &pWrite->pNext; + break; + } + + default: + assert(!"Cannot happen"); + } + + if( pWrite==pFinal ) break; } + + if( rc==SQLITE_OK && isCrash ){ + exit(-1); + } + return rc; } /* -** OpenDirectory is a no-op +** Add an entry to the end of the write-list. */ -static int crashOpenDir(OsFile *id, const char *zName){ +static int writeListAppend( + sqlite3_file *pFile, + sqlite3_int64 iOffset, + const u8 *zBuf, + int nBuf +){ + WriteBuffer *pNew; + + assert((zBuf && nBuf) || (!nBuf && !zBuf)); + + pNew = (WriteBuffer *)sqliteMalloc(sizeof(WriteBuffer) + nBuf); + pNew->iOffset = iOffset; + pNew->nBuf = nBuf; + pNew->pFile = (CrashFile *)pFile; + if( zBuf ){ + pNew->zBuf = (u8 *)&pNew[1]; + memcpy(pNew->zBuf, zBuf, nBuf); + } + + if( g.pWriteList ){ + WriteBuffer *pEnd; + for(pEnd=g.pWriteList; pEnd->pNext; pEnd=pEnd->pNext); + pEnd->pNext = pNew; + }else{ + g.pWriteList = pNew; + } + return SQLITE_OK; } /* -** Locking primitives are passed through into the underlying -** file descriptor. +** Close a crash-file. */ -int crashLock(OsFile *id, int lockType){ - return sqlite3OsLock(((crashFile*)id)->pBase, lockType); -} -int crashUnlock(OsFile *id, int lockType){ - return sqlite3OsUnlock(((crashFile*)id)->pBase, lockType); -} -int crashCheckReservedLock(OsFile *id){ - return sqlite3OsCheckReservedLock(((crashFile*)id)->pBase); -} -void crashSetFullSync(OsFile *id, int setting){ - return; /* This is a no-op */ -} -int crashLockState(OsFile *id){ - return sqlite3OsLockState(((crashFile*)id)->pBase); +int cfClose(sqlite3_file *pFile){ + CrashFile *pCrash = (CrashFile *)pFile; + writeListSync(pCrash, 0); + sqlite3OsClose(&pCrash->pRealFile); + return SQLITE_OK; } /* -** Return the underlying file handle. +** Read data from a crash-file. */ -int crashFileHandle(OsFile *id){ -#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG) - return sqlite3OsFileHandle(((crashFile*)id)->pBase); -#endif - return 0; +int cfRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst){ + CrashFile *pCrash = (CrashFile *)pFile; + sqlite3_int64 iSize; + int rc; + WriteBuffer *pWrite; + + /* Check the file-size to see if this is a short-read */ + rc = sqlite3OsFileSize(pFile, &iSize); + if( rc!=SQLITE_OK ){ + return rc; + } + if( iSize<(iOfst+iAmt) ){ + return SQLITE_IOERR_SHORT_READ; + } + + /* Zero the output buffer */ + memset(zBuf, 0, iAmt); + + /* Read some data from the real file */ + rc = sqlite3OsFileSize(pCrash->pRealFile, &iSize); + if( rc==SQLITE_OK && iSize>iOfst ){ + int nRead = iAmt; + if( iSize<(iOfst+iAmt) ){ + nRead = iSize - iOfst; + } + rc = sqlite3OsRead(pCrash->pRealFile, zBuf, nRead, iOfst); + } + + /* Fill in the buffer by traversing the write-list */ + for(pWrite=g.pWriteList; rc==SQLITE_OK && pWrite; pWrite=pWrite->pNext){ + if( pWrite->pFile==pCrash ){ + int iWriteOffset; + int nWriteBuf; + u8 *zWriteBuf; + + iWriteOffset = pWrite->iOffset - iOfst; + nWriteBuf = pWrite->nBuf; + zWriteBuf = pWrite->zBuf; + if( iWriteOffset<0 ){ + nWriteBuf += iWriteOffset; + zWriteBuf -= iWriteOffset; + iWriteOffset = 0; + } + if( (iWriteOffset+nWriteBuf)>iAmt ){ + nWriteBuf = iAmt - iWriteOffset; + } + + if( pWrite->zBuf && nWriteBuf>0){ + /* Copy data to the buffer */ + memcpy(&((u8 *)zBuf)[iWriteOffset], zWriteBuf, nWriteBuf); + } + + if( pWrite->zBuf==0 && iWriteOffsetzName, g.zCrashFile) ){ + if( (--g.iCrash==0) ){ + isCrash = 1; + } + } + + return writeListSync(pCrash, isCrash); +} + +/* +** Return the current file-size of the crash-file. +*/ +int cfFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ + CrashFile *pCrash = (CrashFile *)pFile; + WriteBuffer *pWrite; + int rc; + sqlite_int64 iSize; + + rc = sqlite3OsFileSize(pCrash->pRealFile, &iSize); + if( rc!=SQLITE_OK ){ + return rc; + } + + for(pWrite=g.pWriteList; pWrite; pWrite=pWrite->pNext){ + sqlite_int64 iEnd = pWrite->nBuf+pWrite->iOffset; + if( pWrite->pFile==pCrash && (pWrite->zBuf==0 || iEnd>iSize) ){ + iSize = iEnd; + } + } + *pSize = iSize; + + return SQLITE_OK; +} + +/* +** Calls related to file-locks are passed on to the real file handle. +*/ +int cfLock(sqlite3_file *pFile, int eLock){ + return sqlite3OsLock(((CrashFile *)pFile)->pRealFile, eLock); +} +int cfUnlock(sqlite3_file *pFile, int eLock){ + return sqlite3OsUnlock(((CrashFile *)pFile)->pRealFile, eLock); +} +int cfCheckReservedLock(sqlite3_file *pFile){ + return sqlite3OsCheckReservedLock(((CrashFile *)pFile)->pRealFile); +} +int cfBreakLock(sqlite3_file *pFile){ + return sqlite3OsBreakLock(((CrashFile *)pFile)->pRealFile); +} + +/* +** The xSectorSize() and xDeviceCharacteristics() functions return +** the global values configured by the [sqlite_crashparams] tcl +* interface. +*/ +int cfSectorSize(sqlite3_file *pFile){ + return g.iSectorSize; +} +int cfDeviceCharacteristics(sqlite3_file *pFile){ + return g.iDeviceCharacteristics; +} + +static const sqlite3_io_methods CrashFileVtab = { + 1, /* iVersion */ + cfClose, /* xClose */ + cfRead, /* xRead */ + cfWrite, /* xWrite */ + cfTruncate, /* xTruncate */ + cfSync, /* xSync */ + cfFileSize, /* xFileSize */ + cfLock, /* xLock */ + cfUnlock, /* xUnlock */ + cfCheckReservedLock, /* xCheckReservedLock */ + cfBreakLock, /* xBreakLock */ + cfSectorSize, /* xSectorSize */ + cfDeviceCharacteristics /* xDeviceCharacteristics */ }; - /* -** Initialise the os_test.c specific fields of pFile. +** Open a crash-file file handle. The vfs pVfs is used to open +** the underlying real file. */ -static void initFile(OsFile **pId, char const *zName, OsFile *pBase){ - crashFile *pFile = sqliteMalloc(sizeof(crashFile) + strlen(zName)+1); - pFile->pMethod = &crashIoMethod; - pFile->nMaxWrite = 0; - pFile->offset = 0; - pFile->nBlk = 0; - pFile->apBlk = 0; - pFile->zName = (char *)(&pFile[1]); - strcpy(pFile->zName, zName); - pFile->pBase = pBase; - pFile->pNext = pAllFiles; - pAllFiles = pFile; - *pId = (OsFile*)pFile; +int sqlite3CrashFileOpen( + sqlite3_vfs *pVfs, + const char *zName, + sqlite3_file *pFile, + int flags, + int *pOutFlags +){ + CrashFile *pWrapper = (CrashFile *)pFile; + int rc = SQLITE_NOMEM; + sqlite3_file *pReal; + pReal = (sqlite3_file *)sqliteMalloc(pVfs->szOsFile); + if( pReal ){ + pWrapper->pMethod = &CrashFileVtab; + pWrapper->zName = zName; + rc = pVfs->xOpen(pVfs->pAppData, zName, pReal, flags, pOutFlags); + if( rc==SQLITE_OK ){ + pWrapper->pRealFile = pFile; + }else{ + sqliteFree(pReal); + } + } + return rc; +} + +int sqlite3CrashFileWrap( + sqlite3_file *pFile, + const char *zName, + sqlite3_file **ppWrapper +){ + CrashFile *pWrapper; + pWrapper = (CrashFile *)sqliteMalloc(sizeof(CrashFile)+strlen(zName)+1); + if( !pWrapper ){ + return SQLITE_NOMEM; + } + + pWrapper->pMethod = &CrashFileVtab; + pWrapper->pRealFile = pFile; + pWrapper->zName = &pWrapper[1]; + memcpy(pWrapper->zName, zName, strlen(zName)+1); + + *ppWrapper = (sqlite3_file *)pWrapper; + return SQLITE_OK; } +int sqlite3CrashFileSize(){ + return (int)sizeof(CrashFile); +} + /* -** tclcmd: sqlite_crashparams DELAY CRASHFILE ?BLOCKSIZE? +** tclcmd: sqlite_crashparams ?OPTIONS? DELAY CRASHFILE ** ** This procedure implements a TCL command that enables crash testing ** in testfixture. Once enabled, crash testing cannot be disabled. +** +** Available options are "-characteristics" and "-sectorsize". Both require +** an argument. For -sectorsize, this is the simulated sector size in +** bytes. For -characteristics, the argument must be a list of io-capability +** flags to simulate. Valid flags are "atomic", "atomic512", "atomic1K", +** "atomic2K", "atomic4K", "atomic8K", "atomic16K", "atomic32K", +** "atomic64K", "sequential" and "safe_append". +** +** Example: +** +** sqlite_crashparams -sect 1024 -char {atomic sequential} ./test.db 1 +** */ static int crashParamsObjCmd( void * clientData, @@ -627,31 +534,110 @@ static int crashParamsObjCmd( int objc, Tcl_Obj *CONST objv[] ){ - int iDelay; - const char *zFile; - int nFile; + int i; - if( objc!=3 && objc!=4 ){ - Tcl_WrongNumArgs(interp, 1, objv, "DELAY CRASHFILE ?BLOCKSIZE?"); + int iDelay; + const char *zCrashFile; + int nCrashFile; + + int iDc = 0; + int iSectorSize = 0; + int setSectorsize = 0; + int setDeviceChar = 0; + + struct DeviceFlag { + char *zName; + int iValue; + } aFlag[] = { + { "atomic", SQLITE_IOCAP_ATOMIC }, + { "atomic512", SQLITE_IOCAP_ATOMIC512 }, + { "atomic1k", SQLITE_IOCAP_ATOMIC1K }, + { "atomic2k", SQLITE_IOCAP_ATOMIC2K }, + { "atomic4k", SQLITE_IOCAP_ATOMIC4K }, + { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, + { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, + { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, + { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, + { "sequential", SQLITE_IOCAP_SEQUENTIAL }, + { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, + { 0, 0 } + }; + + if( objc<3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "?OPTIONS? DELAY CRASHFILE"); return TCL_ERROR; } - if( Tcl_GetIntFromObj(interp, objv[1], &iDelay) ) return TCL_ERROR; - zFile = Tcl_GetStringFromObj(objv[2], &nFile); - if( nFile>=sizeof(zCrashFile)-1 ){ - Tcl_AppendResult(interp, "crash file name too big", 0); + + zCrashFile = sqlite3OsFullPathname(Tcl_GetString(objv[objc-1])); + nCrashFile = strlen(zCrashFile); + if( nCrashFile>=sizeof(g.zCrashFile) ){ + Tcl_AppendResult(interp, "Filename is too long: \"", zCrashFile, "\"", 0); return TCL_ERROR; } - setCrashParams(iDelay, zFile); - if( objc==4 ){ - int iBlockSize = 0; - if( Tcl_GetIntFromObj(interp, objv[3], &iBlockSize) ) return TCL_ERROR; - if( pAllFiles ){ - char *zErr = "Cannot modify blocksize after opening files"; - Tcl_SetResult(interp, zErr, TCL_STATIC); + if( Tcl_GetIntFromObj(interp, objv[objc-2], &iDelay) ){ + return TCL_ERROR; + } + + for(i=1; i<(objc-2); i+=2){ + int nOpt; + char *zOpt = Tcl_GetStringFromObj(objv[i], &nOpt); + + if( (nOpt>11 || nOpt<2 || strncmp("-sectorsize", zOpt, nOpt)) + && (nOpt>16 || nOpt<2 || strncmp("-characteristics", zOpt, nOpt)) + ){ + Tcl_AppendResult(interp, + "Bad option: \"", zOpt, + "\" - must be \"-characteristics\" or \"-sectorsize\"", 0 + ); return TCL_ERROR; } - setBlocksize(iBlockSize); + if( i==objc-3 ){ + Tcl_AppendResult(interp, "Option requires an argument: \"", zOpt, "\"",0); + return TCL_ERROR; + } + + if( zOpt[1]=='s' ){ + if( Tcl_GetIntFromObj(interp, objv[i+1], &iSectorSize) ){ + return TCL_ERROR; + } + setSectorsize = 1; + }else{ + int j; + Tcl_Obj **apObj; + int nObj; + if( Tcl_ListObjGetElements(interp, objv[i+1], &nObj, &apObj) ){ + return TCL_ERROR; + } + for(j=0; jaDb[0].pBt); - OsFile *master = 0; + sqlite3_file *master = 0; + i64 offset = 0; /* Select a master journal file name */ do { @@ -1164,7 +1165,8 @@ static int vdbeCommit(sqlite3 *db){ if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ needSync = 1; } - rc = sqlite3OsWrite(master, zFile, strlen(zFile)+1); + rc = sqlite3OsWrite(master, zFile, strlen(zFile)+1, offset); + offset += strlen(zFile)+1; if( rc!=SQLITE_OK ){ sqlite3OsClose(&master); sqlite3OsDelete(zMaster); @@ -1179,6 +1181,7 @@ static int vdbeCommit(sqlite3 *db){ ** the master journal file is store in so that it gets synced too. */ zMainFile = sqlite3BtreeGetDirname(db->aDb[0].pBt); +#if 0 rc = sqlite3OsOpenDirectory(master, zMainFile); if( rc!=SQLITE_OK || (needSync && (rc=sqlite3OsSync(master,0))!=SQLITE_OK) ){ @@ -1187,6 +1190,7 @@ static int vdbeCommit(sqlite3 *db){ sqliteFree(zMaster); return rc; } +#endif /* Sync all the db files involved in the transaction. The same call ** sets the master journal pointer in each individual journal. If