Merge the POWERSAFE_OVERWRITE features and the use of statvfs() from the

statvfs branch into trunk.

FossilOrigin-Name: 2370d70eb51d2259aaa8073d861ab79d6637cbd9
This commit is contained in:
drh 2011-12-23 02:07:10 +00:00
commit cc8d10a0fd
23 changed files with 568 additions and 254 deletions

View File

@ -1,5 +1,5 @@
C Add\sinterfaces\ssqlite3_uri_boolean()\sand\ssqlite3_uri_int64()\swhich\sare\nwrappers\saround\ssqlite3_uri_parameter()\scombined\swith\sinternal\sroutines\sfor\nconverting\sstrings\sto\sbooleans\sand\s64-bit\sintegers.
D 2011-12-23T00:07:33.075
C Merge\sthe\sPOWERSAFE_OVERWRITE\sfeatures\sand\sthe\suse\sof\sstatvfs()\sfrom\sthe\nstatvfs\sbranch\sinto\strunk.
D 2011-12-23T02:07:10.640
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -166,9 +166,9 @@ F src/os.c 28bbdab2170dfce84d86c45456a18eab1d0f99a9
F src/os.h 549b1a2e5e0ed1e1499f252dac126c4973e7379c
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_os2.c 4a75888ba3dfc820ad5e8177025972d74d7f2440
F src/os_unix.c ee4ea557de196798bafba1ac69f76a755035ebbb
F src/os_win.c 197d23ce8a0dff748e766e034bf95ff756dd3884
F src/pager.c 523c64f6ca707e820d5cf10ed8371238ecac8333
F src/os_unix.c 3a441671f35569df4b72641e928fdb1ab5cd8576
F src/os_win.c 569fe7448e781bfb8116aa79081df0eadf576fc6
F src/pager.c d03fb1de7bd724f0cdcae0aab0a733d89c94ac2f
F src/pager.h 5cd760857707529b403837d813d86b68938d6183
F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
F src/pcache.c 1fdd77978c1525d1ca4b9ef48eb80abca710cb4c
@ -182,19 +182,19 @@ F src/resolve.c 3d3e80a98f203ac6b9329e9621e29eda85ddfd40
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c a1d075db66a0ea42807353501b62997969e5be79
F src/shell.c aa4183d4a5243d8110b1d3d77faa4aea7e9c9c2d
F src/sqlite.h.in db834f87316a1422051a97cba5beac31c0cb1b2d
F src/sqlite.h.in 3d1a77e27b9d9979fb7ec84834a8423602d29dc5
F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
F src/sqliteInt.h 165409fa8adc8701148830804febeded3f2e4448
F src/sqliteInt.h b8fdd9c39c8d7f5c794f4ea917293d9c75b9aff2
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c de581e2e71f5e7f98366156afad83b4742ac6fe0
F src/test1.c 33b9c49f728cf28937eea7b246c28e89e3cf5e9e
F src/tclsqlite.c bd86070f52ae3f77a2e6b3b065ff03adb9140bfa
F src/test1.c 1b1e514e85ffe7152b02cba38bd0a1ce8cd56113
F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
F src/test5.c e1a19845625144caf038031234a12185e40d315c
F src/test6.c 3191b4ab964a144230ff9ef96c093520375c7b2a
F src/test6.c cf6ab27a59e1ab64b011bb251ba600131e803e59
F src/test7.c 2e0781754905c8adc3268d8f0967e7633af58843
F src/test8.c 99f70341d6ec480313775127f4cd14b4a47db557
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
@ -230,7 +230,7 @@ F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c 35022393dd54d147b998b6b7f7e945b01114d666
F src/test_vfs.c 27b7d9de40630f603b9e2cf9ef2a7c81d31c4515
F src/test_vfs.c 07157a0bbfe161cb5e32cad2079abd26cd611c4b
F src/test_vfstrace.c 065c7270a614254b2c68fbc7ba8d1fb1d5cbc823
F src/test_wholenumber.c 6129adfbe7c7444f2e60cc785927f3aa74e12290
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
@ -250,7 +250,7 @@ F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
F src/wal.c 5525f049dffd47ee860bf4ffbb8da4ebef78637d
F src/wal.c 66e2afeec77933f80c8098cfb3c0b29bc875de0f
F src/wal.h 42f8313f7aaf8913e2d1fdf7b47025c23491ea1d
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c af623942514571895818b9b7ae11db95ae3b3d88
@ -515,7 +515,7 @@ F test/incrblob3.test aedbb35ea1b6450c33b98f2b6ed98e5020be8dc7
F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
F test/incrvacuum2.test 62fbeb85459fe4e501684d8fb5b6e98a23e3b0c0
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
F test/incrvacuum_ioerr.test 22f208d01c528403240e05beecc41dc98ed01637
F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
@ -545,7 +545,7 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
F test/journal1.test 8b71ef1ed5798bdc0e6eb616d8694e2c2c188d4d
F test/journal2.test 29937bdbb253bbfd92057610120bdc0aa7e84a0a
F test/journal2.test ae06f566c28552c313ded3fee79a6c69e6d049b1
F test/journal3.test 6fd28532c88b447db844186bc190523108b6dbb4
F test/jrnlmode.test 9ee3a78f53d52cca737db69293d15dc41c0cbd36
F test/jrnlmode2.test 81610545a4e6ed239ea8fa661891893385e23a1d
@ -620,7 +620,7 @@ F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test 0d3040974bf402e19b7df4b783e447289d7ab394
F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
F test/pager1.test b936e80553652002fc47d89bfde7018801d8257d
F test/pager1.test 9e9f5f1c6d4df4831dbff213b1262ef94bf72118
F test/pager2.test 745b911dde3d1f24ae0870bd433dfa83d7c658c1
F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
F test/pagerfault.test 452f2cc23e3bfcfa935f4442aec1da4fe1dc0442
@ -704,9 +704,9 @@ F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796
F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd
F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/superlock.test 7b1167925e9d30a5d1f0701d24812fdda42c3a86
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test 2a922050dbee032f587249b070fb42692f5e1e22
F test/syscall.test 265cda616f56a297406728ee1e74c9b4a93aa6dd
F test/sysfault.test c79441d88d23696fbec7b147dba98d42a04f523f
F test/table.test a59d985ca366e39b17b175f387f9d5db5a18d4e2
F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
@ -872,7 +872,7 @@ F test/types.test bf816ce73c7dfcfe26b700c19f97ef4050d194ff
F test/types2.test 3555aacf8ed8dc883356e59efc314707e6247a84
F test/types3.test 99e009491a54f4dc02c06bdbc0c5eea56ae3e25a
F test/unique.test 083c7fff74695bcc27a71d75699deba3595bc9c2
F test/unixexcl.test 892937c53d0c16e76631674e38a0fce052ae5e9c
F test/unixexcl.test c82934b3fda907573c7dfccc4f4c9506e5537f36
F test/unordered.test f53095cee37851bf30130fa1bf299a8845e837bb
F test/update.test 8bc86fd7ef1a00014f76dc6a6a7c974df4aef172
F test/uri.test 0d289d32396bdbc491e9dc845f1a52e13f861e0b
@ -902,11 +902,11 @@ F test/vtabF.test fd5ad376f5a34fe0891df1f3cddb4fe7c3eb077e
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test c743be787e60c1242fa6cdf73b410e64b2977e25
F test/wal2.test 29e2cbe840582fc6efd0487b4f6337caed4b3e80
F test/wal3.test 29a6e8843e5f5fd13f33cb0407d2923107020d32
F test/wal.test edefe316b4125d7f68004ea53c5e73c398d436cc
F test/wal2.test f11883dd3cb7f647c5d2acfd7b5c6d4ba5770cc9
F test/wal3.test 6504bbf348b2d6dfade64a064f1050fd617e8706
F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
F test/wal5.test 1bbfaa316dc2a1d0d1fac3f4500c38a90055a41b
F test/wal5.test f58ed4b8b542f71c7441da12fbd769d99b362437
F test/wal6.test 2e3bc767d9c2ce35c47106148d43fcbd072a93b3
F test/wal7.test 2ae8f427d240099cc4b2dfef63cff44e2a68a1bd
F test/wal_common.tcl a98f17fba96206122eff624db0ab13ec377be4fe
@ -941,6 +941,7 @@ F test/whereC.test 13ff5ec0dba407c0e0c075980c75b3275a6774e5
F test/wherelimit.test 5e9fd41e79bb2b2d588ed999d641d9c965619b31
F test/win32lock.test b2a539e85ae6b2d78475e016a9636b4451dc7fb9
F test/zeroblob.test caaecfb4f908f7bc086ed238668049f96774d688
F test/zerodamage.test 7ef0680559163118705975be132933898d349674
F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
@ -985,7 +986,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
P 1c27d842163e27c39bbe9409f50657b9de9ade6e
R 712ad6ea662046687d3fa2e6906d477a
P 83d26b9a9115eadac9e59a33d608bca0ab2519e3 6191c5e45175f5c6040e891843b0725a929d6dd7
R 9e1524ff0804966ed866a28455ef991b
U drh
Z dfef488f9835a38d5955098b906ef223
Z 88029f358469c654bea2978d031e29f2

View File

@ -1 +1 @@
83d26b9a9115eadac9e59a33d608bca0ab2519e3
2370d70eb51d2259aaa8073d861ab79d6637cbd9

View File

@ -122,6 +122,10 @@
#ifndef SQLITE_OMIT_WAL
#include <sys/mman.h>
#endif
#ifndef MISSING_STATVFS
#include <sys/statvfs.h>
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
# include <sys/ioctl.h>
@ -211,6 +215,7 @@ struct unixFile {
int h; /* The file descriptor */
unsigned char eFileLock; /* The type of lock held on this fd */
unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
unsigned char szSector; /* Sectorsize/512 */
int lastErrno; /* The unix errno from last I/O error */
void *lockingContext; /* Locking style specific state */
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
@ -258,6 +263,7 @@ struct unixFile {
#else
# define UNIXFILE_DIRSYNC 0x00
#endif
#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
/*
** Include code that is common to all os_*.c files
@ -414,6 +420,14 @@ static struct unix_syscall {
{ "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
#if defined(MISSING_STATVFS)
{ "statvfs", (sqlite3_syscall_ptr)0, 0 },
#define osStatvfs ((int(*)(const char*,void*))aSyscall[20].pCurrent)
#else
{ "statvfs", (sqlite3_syscall_ptr)statvfs, 0 },
#define osStatvfs ((int(*)(const char*,struct statvfs*))aSyscall[20].pCurrent)
#endif
}; /* End of the overrideable system calls */
/*
@ -3498,6 +3512,22 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
return SQLITE_OK;
}
/*
** If *pArg is inititially negative then this is a query. Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
**
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
*/
static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
if( *pArg<0 ){
*pArg = (pFile->ctrlFlags & mask)!=0;
}else if( (*pArg)==0 ){
pFile->ctrlFlags &= ~mask;
}else{
pFile->ctrlFlags |= mask;
}
}
/*
** Information and control of an open file handle.
*/
@ -3524,14 +3554,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return rc;
}
case SQLITE_FCNTL_PERSIST_WAL: {
int bPersist = *(int*)pArg;
if( bPersist<0 ){
*(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
}else if( bPersist==0 ){
pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
}else{
pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
}
unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
@ -3572,17 +3599,46 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
** a database and its journal file) that the sector size will be the
** same for both.
*/
static int unixSectorSize(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return SQLITE_DEFAULT_SECTOR_SIZE;
static int unixSectorSize(sqlite3_file *pFile){
unixFile *p = (unixFile*)pFile;
if( p->szSector==0 ){
#ifdef MISSING_STATVFS
p->szSector = SQLITE_DEFAULT_SECTOR_SIZE/512;
#else
struct statvfs x;
int sz;
memset(&x, 0, sizeof(x));
osStatvfs(p->zPath, &x);
sz = (int)x.f_frsize;
if( sz<512 || sz>65536 || (sz&(sz-1))!=0 ){
sz = SQLITE_DEFAULT_SECTOR_SIZE;
}
p->szSector = sz/512;
#endif
}
return p->szSector*512;
}
/*
** Return the device characteristics for the file. This is always 0 for unix.
** Return the device characteristics for the file.
**
** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
** However, that choice is contraversial since technically the underlying
** file system does not always provide powersafe overwrites. (In other
** words, after a power-loss event, parts of the file that were never
** written might end up being altered.) However, non-PSOW behavior is very,
** very rare. And asserting PSOW makes a large reduction in the amount
** of required I/O for journaling, since a lot of padding is eliminated.
** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
** available to turn it off and URI query parameter available to turn it off.
*/
static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return 0;
static int unixDeviceCharacteristics(sqlite3_file *id){
unixFile *p = (unixFile*)id;
if( p->ctrlFlags & UNIXFILE_PSOW ){
return SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}else{
return 0;
}
}
#ifndef SQLITE_OMIT_WAL
@ -4565,10 +4621,12 @@ static int fillInUnixFile(
pNew->h = h;
pNew->pVfs = pVfs;
pNew->zPath = zFilename;
pNew->ctrlFlags = 0;
if( sqlite3_uri_boolean(zFilename, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
pNew->ctrlFlags |= UNIXFILE_PSOW;
}
if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
pNew->ctrlFlags = UNIXFILE_EXCL;
}else{
pNew->ctrlFlags = 0;
pNew->ctrlFlags |= UNIXFILE_EXCL;
}
if( isReadOnly ){
pNew->ctrlFlags |= UNIXFILE_RDONLY;
@ -6775,7 +6833,7 @@ int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
assert( ArraySize(aSyscall)==20 );
assert( ArraySize(aSyscall)==21 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){

View File

@ -59,7 +59,7 @@ struct winFile {
HANDLE h; /* Handle for accessing the file */
u8 locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
u8 bPersistWal; /* True to persist WAL files */
u8 ctrlFlags; /* Flags. See WINFILE_* below */
DWORD lastErrno; /* The Windows errno from the last I/O error */
DWORD sectorSize; /* Sector size of the device file is on */
winShm *pShm; /* Instance of shared memory on this file */
@ -74,6 +74,12 @@ struct winFile {
#endif
};
/*
** Allowed values for winFile.ctrlFlags
*/
#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
/*
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
* various Win32 API heap functions instead of our own.
@ -2125,6 +2131,22 @@ static int winUnlock(sqlite3_file *id, int locktype){
return rc;
}
/*
** If *pArg is inititially negative then this is a query. Set *pArg to
** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
**
** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
*/
static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
if( *pArg<0 ){
*pArg = (pFile->ctrlFlags & mask)!=0;
}else if( (*pArg)==0 ){
pFile->ctrlFlags &= ~mask;
}else{
pFile->ctrlFlags |= mask;
}
}
/*
** Control and query of the open file handle.
*/
@ -2160,12 +2182,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
case SQLITE_FCNTL_PERSIST_WAL: {
int bPersist = *(int*)pArg;
if( bPersist<0 ){
*(int*)pArg = pFile->bPersistWal;
}else{
pFile->bPersistWal = bPersist!=0;
}
winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
winModeBit(pFile, WINFILE_PSOW, (int*)pArg);
return SQLITE_OK;
}
case SQLITE_FCNTL_VFSNAME: {
@ -2212,8 +2233,9 @@ static int winSectorSize(sqlite3_file *id){
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
UNUSED_PARAMETER(id);
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
winFile *p = (winFile*)id;
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
#ifndef SQLITE_OMIT_WAL
@ -3178,6 +3200,9 @@ static int winOpen(
pFile->pVfs = pVfs;
pFile->pShm = 0;
pFile->zPath = zName;
if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
pFile->ctrlFlags |= WINFILE_PSOW;
}
pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
#if SQLITE_OS_WINCE

View File

@ -2515,23 +2515,36 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
** the value returned by the xSectorSize() method rounded up to 32 if
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it
** is greater than MAX_SECTOR_SIZE.
**
** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set
** the effective sector size to its minimum value (512). The purpose of
** pPager->sectorSize is to define the "blast radius" of bytes that
** might change if a crash occurs while writing to a single byte in
** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero
** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector
** size. For backwards compatibility of the rollback journal file format,
** we cannot reduce the effective sector size below 512.
*/
static void setSectorSize(Pager *pPager){
assert( isOpen(pPager->fd) || pPager->tempFile );
if( !pPager->tempFile ){
if( pPager->tempFile
|| (sqlite3OsDeviceCharacteristics(pPager->fd) &
SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
** call will segfault.
*/
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
}
if( pPager->sectorSize<32 ){
** call will segfault. */
pPager->sectorSize = 512;
}
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
assert( MAX_SECTOR_SIZE>=512 );
pPager->sectorSize = MAX_SECTOR_SIZE;
}else{
pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
if( pPager->sectorSize<32 ){
pPager->sectorSize = 512;
}
if( pPager->sectorSize>MAX_SECTOR_SIZE ){
assert( MAX_SECTOR_SIZE>=512 );
pPager->sectorSize = MAX_SECTOR_SIZE;
}
}
}

View File

@ -504,7 +504,14 @@ int sqlite3_exec(
** first then the size of the file is extended, never the other
** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
** information is written to disk in the same order as calls
** to xWrite().
** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
** after reboot following a crash or power loss, the value of
** each byte in a file is a value that was actually written
** into that byte at some point. In other words, a crash will
** not cause unwritten bytes of the file to change nor introduce
** randomness into a file nor zero out parts of the file, and any byte of
** a file that are never written will not change values due to
** writes to nearby bytes.
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@ -518,6 +525,7 @@ int sqlite3_exec(
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
/*
** CAPI3REF: File Locking Levels
@ -767,6 +775,15 @@ struct sqlite3_io_methods {
** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
**
** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
** xDeviceCharacteristics methods. The fourth parameter to
** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
** mode. If the integer is -1, then it is overwritten with the current
** zero-damage mode setting.
**
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current
@ -783,18 +800,19 @@ struct sqlite3_io_methods {
** pointer in case this file-control is not implemented. This file-control
** is intended for diagnostic use only.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_SET_LOCKPROXYFILE 3
#define SQLITE_LAST_ERRNO 4
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
#define SQLITE_FCNTL_SYNC_OMITTED 8
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
#define SQLITE_FCNTL_OVERWRITE 11
#define SQLITE_FCNTL_VFSNAME 12
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_SET_LOCKPROXYFILE 3
#define SQLITE_LAST_ERRNO 4
#define SQLITE_FCNTL_SIZE_HINT 5
#define SQLITE_FCNTL_CHUNK_SIZE 6
#define SQLITE_FCNTL_FILE_POINTER 7
#define SQLITE_FCNTL_SYNC_OMITTED 8
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
#define SQLITE_FCNTL_OVERWRITE 11
#define SQLITE_FCNTL_VFSNAME 12
#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
/*
** CAPI3REF: Mutex Handle

View File

@ -125,6 +125,14 @@
#endif
#endif
/*
** Powersafe overwrite is on by default. But can be turned off using
** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
*/
#ifndef SQLITE_POWERSAFE_OVERWRITE
# define SQLITE_POWERSAFE_OVERWRITE 1
#endif
/*
** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
** It determines whether or not the features related to

View File

@ -3001,6 +3001,14 @@ static int DbMain(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}else{
flags &= ~SQLITE_OPEN_FULLMUTEX;
}
}else if( strcmp(zArg, "-uri")==0 ){
int b;
if( Tcl_GetBooleanFromObj(interp, objv[i+1], &b) ) return TCL_ERROR;
if( b ){
flags |= SQLITE_OPEN_URI;
}else{
flags &= ~SQLITE_OPEN_URI;
}
}else{
Tcl_AppendResult(interp, "unknown option: ", zArg, (char*)0);
return TCL_ERROR;

View File

@ -5235,6 +5235,38 @@ static int file_control_persist_wal(
return TCL_OK;
}
/*
** tclcmd: file_control_powersafe_overwrite DB PSOW-FLAG
**
** This TCL command runs the sqlite3_file_control interface with
** the SQLITE_FCNTL_POWERSAFE_OVERWRITE opcode.
*/
static int file_control_powersafe_overwrite(
ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
sqlite3 *db;
int rc;
int b;
char z[100];
if( objc!=3 ){
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetStringFromObj(objv[0], 0), " DB FLAG", 0);
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
if( Tcl_GetIntFromObj(interp, objv[2], &b) ) return TCL_ERROR;
rc = sqlite3_file_control(db,NULL,SQLITE_FCNTL_POWERSAFE_OVERWRITE,(void*)&b);
sqlite3_snprintf(sizeof(z), z, "%d %d", rc, b);
Tcl_AppendResult(interp, z, (char*)0);
return TCL_OK;
}
/*
** tclcmd: file_control_vfsname DB ?AUXDB?
@ -6093,6 +6125,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
{ "file_control_persist_wal", file_control_persist_wal, 0 },
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
{ "file_control_vfsname", file_control_vfsname, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
{ "sqlite3_create_function_v2", test_create_function_v2, 0 },

View File

@ -705,17 +705,18 @@ static int processDevSymArgs(
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 },
{ "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 },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ 0, 0 }
};

View File

@ -1162,18 +1162,19 @@ static int testvfs_obj_cmd(
int iValue;
} aFlag[] = {
{ "default", -1 },
{ "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 },
{ "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 },
{ "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
{ "powersafe_overwrite", SQLITE_IOCAP_POWERSAFE_OVERWRITE },
{ 0, 0 }
};
Tcl_Obj *pRet;
@ -1207,7 +1208,7 @@ static int testvfs_obj_cmd(
iNew |= aFlag[idx].iValue;
}
p->iDevchar = iNew;
p->iDevchar = iNew| 0x10000000;
}
pRet = Tcl_NewObj();

226
src/wal.c
View File

@ -424,7 +424,8 @@ struct Wal {
u8 ckptLock; /* True if holding a checkpoint lock */
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
u8 truncateOnCommit; /* True to truncate WAL file on commit */
u8 noSyncHeader; /* Avoid WAL header fsyncs if true */
u8 syncHeader; /* Fsync the WAL header if true */
u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
@ -1153,7 +1154,6 @@ static int walIndexRecover(Wal *pWal){
/* Read all frames from the log file. */
iFrame = 0;
isValid = 1;
for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
u32 pgno; /* Database page number for frame */
u32 nTruncate; /* dbsize field from frame header */
@ -1162,15 +1162,8 @@ static int walIndexRecover(Wal *pWal){
iFrame++;
rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
if( rc!=SQLITE_OK ) break;
if( sqlite3Get4byte(&aFrame[8]) ==
1+sqlite3Get4byte((u8*)&pWal->hdr.aSalt[0]) ){
pWal->hdr.mxFrame = 0;
pWal->hdr.nPage = 0;
break;
}
if( !isValid ) continue;
isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
if( !isValid ) continue;
if( !isValid ) break;
rc = walIndexAppend(pWal, iFrame, pgno);
if( rc!=SQLITE_OK ) break;
@ -1294,6 +1287,8 @@ int sqlite3WalOpen(
pRet->readLock = -1;
pRet->mxWalSize = mxWalSize;
pRet->zWalName = zWalName;
pRet->syncHeader = 1;
pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
/* Open file handle on the write-ahead log file. */
@ -1309,7 +1304,10 @@ int sqlite3WalOpen(
sqlite3_free(pRet);
}else{
int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd);
if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->noSyncHeader = 1; }
if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
pRet->padToSectorBoundary = 0;
}
*ppWal = pRet;
WALTRACE(("WAL%d: opened\n", pRet));
}
@ -2631,41 +2629,71 @@ static int walRestartLog(Wal *pWal){
return rc;
}
/*
** Information about the current state of the WAL file and where
** the next fsync should occur - passed from sqlite3WalFrames() into
** walWriteToLog().
*/
typedef struct WalWriter {
Wal *pWal; /* The complete WAL information */
sqlite3_file *pFd; /* The WAL file to which we write */
sqlite3_int64 iSyncPoint; /* Fsync at this offset */
int syncFlags; /* Flags for the fsync */
int szPage; /* Size of one page */
} WalWriter;
/*
** Write iAmt bytes of content into the WAL file beginning at iOffset.
** Do a sync when crossing the p->iSyncPoint boundary.
**
** When crossing the boundary between the first and second sectors of the
** file, first write all of the first sector content, then fsync(), then
** continue writing content for the second sector. This ensures that
** the WAL header is overwritten before the first commit mark.
** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
** first write the part before iSyncPoint, then sync, then write the
** rest.
*/
static int walWriteToLog(
Wal *pWal, /* WAL to write to */
WalWriter *p, /* WAL to write to */
void *pContent, /* Content to be written */
int iAmt, /* Number of bytes to write */
sqlite3_int64 iOffset /* Start writing at this offset */
){
int rc;
if( iOffset>=pWal->szFirstBlock
|| iOffset+iAmt<pWal->szFirstBlock
|| pWal->syncFlags==0
){
/* The common and fast case. Just write the data. */
rc = sqlite3OsWrite(pWal->pWalFd, pContent, iAmt, iOffset);
}else{
/* If this write will cross the first sector boundary, it has to
** be split it two with a sync in between. */
int iFirstAmt = pWal->szFirstBlock - iOffset;
assert( iFirstAmt>0 && iFirstAmt<iAmt );
rc = sqlite3OsWrite(pWal->pWalFd, pContent, iFirstAmt, iOffset);
if( rc ) return rc;
assert( pWal->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
rc = sqlite3OsSync(pWal->pWalFd, pWal->syncFlags);
if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
int iFirstAmt = (int)(p->iSyncPoint - iOffset);
rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
if( rc ) return rc;
iOffset += iFirstAmt;
iAmt -= iFirstAmt;
pContent = (void*)(iFirstAmt + (char*)pContent);
rc = sqlite3OsWrite(pWal->pWalFd, pContent,
iAmt-iFirstAmt, iOffset+iFirstAmt);
assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
rc = sqlite3OsSync(p->pFd, p->syncFlags);
if( iAmt==0 || rc ) return rc;
}
rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
return rc;
}
/*
** Write out a single frame of the WAL
*/
static int walWriteOneFrame(
WalWriter *p, /* Where to write the frame */
PgHdr *pPage, /* The page of the frame to be written */
int nTruncate, /* The commit flag. Usually 0. >0 for commit */
sqlite3_int64 iOffset /* Byte offset at which to write */
){
int rc; /* Result code from subfunctions */
void *pData; /* Data actually written */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM;
#else
pData = pPage->pData;
#endif
walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
if( rc ) return rc;
/* Write the page data */
rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
return rc;
}
@ -2683,10 +2711,12 @@ int sqlite3WalFrames(
){
int rc; /* Used to catch return codes */
u32 iFrame; /* Next frame address */
u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
PgHdr *p; /* Iterator to run through pList with. */
PgHdr *pLast = 0; /* Last frame in list */
int nLast = 0; /* Number of extra copies of last page */
int nExtra = 0; /* Number of extra copies of last page */
int szFrame; /* The size of a single frame */
i64 iOffset; /* Next byte to write in WAL file */
WalWriter w; /* The writer */
assert( pList );
assert( pWal->writeLock );
@ -2739,86 +2769,78 @@ int sqlite3WalFrames(
if( rc!=SQLITE_OK ){
return rc;
}
/* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
** an out-of-order write following a WAL restart could result in
** database corruption. See the ticket:
**
** http://localhost:591/sqlite/info/ff5be73dee
*/
if( pWal->syncHeader && sync_flags ){
rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
if( rc ) return rc;
}
}
assert( (int)pWal->szPage==szPage );
/* Setup information needed to do the WAL header sync */
if( pWal->noSyncHeader ){
assert( pWal->szFirstBlock==0 );
assert( pWal->syncFlags==0 );
}else{
pWal->szFirstBlock = sqlite3OsSectorSize(pWal->pWalFd);
if( szPage>pWal->szFirstBlock ) pWal->szFirstBlock = szPage;
pWal->syncFlags = sync_flags & SQLITE_SYNC_MASK;
}
/* Setup information needed to write frames into the WAL */
w.pWal = pWal;
w.pFd = pWal->pWalFd;
w.iSyncPoint = 0;
w.syncFlags = sync_flags;
w.szPage = szPage;
iOffset = walFrameOffset(iFrame+1, szPage);
szFrame = szPage + WAL_FRAME_HDRSIZE;
/* Write the log file. */
/* Write all frames into the log file exactly once */
for(p=pList; p; p=p->pDirty){
u32 nDbsize; /* Db-size field for frame header */
i64 iOffset; /* Write offset in log file */
void *pData;
iOffset = walFrameOffset(++iFrame, szPage);
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
/* Populate and write the frame header */
nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0;
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
#else
pData = p->pData;
#endif
walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame);
rc = walWriteToLog(pWal, aFrame, sizeof(aFrame), iOffset);
if( rc!=SQLITE_OK ){
return rc;
}
/* Write the page data */
rc = walWriteToLog(pWal, pData, szPage, iOffset+sizeof(aFrame));
if( rc!=SQLITE_OK ){
return rc;
}
int nDbSize; /* 0 normally. Positive == commit flag */
iFrame++;
assert( iOffset==walFrameOffset(iFrame, szPage) );
nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
if( rc ) return rc;
pLast = p;
iOffset += szFrame;
}
/* Sync the log file if the 'isSync' flag was specified. */
/* If this is the end of a transaction, then we might need to pad
** the transaction and/or sync the WAL file.
**
** Padding and syncing only occur if this set of frames complete a
** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
** or synchonous==OFF, then no padding or syncing are needed.
**
** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
** needed and only the sync is done. If padding is needed, then the
** final frame is repeated (with its commit mark) until the next sector
** boundary is crossed. Only the part of the WAL prior to the last
** sector boundary is synced; the part of the last frame that extends
** past the sector boundary is written after the sync.
*/
if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd);
i64 iOffset = walFrameOffset(iFrame+1, szPage);
assert( iSegment>0 );
iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment);
while( iOffset<iSegment ){
void *pData;
#if defined(SQLITE_HAS_CODEC)
if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
#else
pData = pLast->pData;
#endif
walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
/* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
rc = walWriteToLog(pWal, aFrame, sizeof(aFrame), iOffset);
if( rc!=SQLITE_OK ){
return rc;
if( pWal->padToSectorBoundary ){
int sectorSize = sqlite3OsSectorSize(pWal->pWalFd);
w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
while( iOffset<w.iSyncPoint ){
rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
if( rc ) return rc;
iOffset += szFrame;
nExtra++;
}
iOffset += WAL_FRAME_HDRSIZE;
rc = walWriteToLog(pWal, pData, szPage, iOffset);
if( rc!=SQLITE_OK ){
return rc;
}
nLast++;
iOffset += szPage;
}
rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
}
/* If this frame set completes the first transaction in the WAL and
** if PRAGMA journal_size_limit is set, then truncate the WAL to the
** journal size limit, if possible.
*/
if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
i64 sz = pWal->mxWalSize;
if( walFrameOffset(iFrame+nLast+1, szPage)>pWal->mxWalSize ){
sz = walFrameOffset(iFrame+nLast+1, szPage);
if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
sz = walFrameOffset(iFrame+nExtra+1, szPage);
}
walLimitSize(pWal, sz);
pWal->truncateOnCommit = 0;
@ -2834,9 +2856,9 @@ int sqlite3WalFrames(
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
while( nLast>0 && rc==SQLITE_OK ){
while( nExtra>0 && rc==SQLITE_OK ){
iFrame++;
nLast--;
nExtra--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}

View File

@ -191,7 +191,7 @@ ifcapable wal {
PRAGMA wal_checkpoint;
}
file size test.db-wal
} {1640}
} [expr {32+2*(512+24)}]
do_test 4.3 {
db close
@ -205,7 +205,7 @@ ifcapable wal {
if {$newsz>$maxsz} {set maxsz $newsz}
}
set maxsz
} {2176}
} [expr {32+3*(512+24)}]
}
finish_test

View File

@ -34,7 +34,7 @@ proc a_string {n} {
# characteristics flags to "SAFE_DELETE".
#
testvfs tvfs -default 1
tvfs devchar undeletable_when_open
tvfs devchar {undeletable_when_open powersafe_overwrite}
# Set up a hook so that each time a journal file is opened, closed or
# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
@ -231,4 +231,3 @@ ifcapable wal {
tvfs delete
finish_test

View File

@ -1331,6 +1331,7 @@ foreach sectorsize {
4096 8192 16384 32768 65536 131072 262144
} {
tv sectorsize $sectorsize
tv devchar {}
set eff $sectorsize
if {$sectorsize < 512} { set eff 512 }
if {$sectorsize > 65536} { set eff 65536 }

View File

@ -76,7 +76,10 @@ do_catchsql_test 3.4 { INSERT INTO t1 VALUES(5, 6)} {1 {database is locked}}
do_catchsql_test 3.5 { PRAGMA wal_checkpoint } {0 {1 -1 -1}}
do_test 3.6 { unlock } {}
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 2 2}
# At this point the WAL file consists of a single frame only - written
# by test case 3.1. If the ZERO_DAMAGE flag were not set, it would consist
# of two frames - the frame written by 3.1 and a padding frame.
do_execsql_test 4.1 { PRAGMA wal_checkpoint } {0 1 1}
do_test 4.2 { sqlite3demo_superlock unlock test.db } {unlock}
do_catchsql_test 4.3 { SELECT * FROM t1 } {1 {database is locked}}

View File

@ -59,7 +59,8 @@ do_test 2.1.2 { test_syscall exists nosuchcall } 0
foreach s {
open close access getcwd stat fstat ftruncate
fcntl read pread write pwrite fchmod fallocate
pread64 pwrite64 unlink openDirectory mkdir rmdir
pread64 pwrite64 unlink openDirectory mkdir rmdir
statvfs
} {
if {[test_syscall exists $s]} {lappend syscall_list $s}
}

View File

@ -82,8 +82,8 @@ do_multiclient_test tn {
do_multiclient_test tn {
do_test unixexcl-3.$tn.1 {
code1 { db close; sqlite3 db test.db -vfs unix-excl }
code2 { db2 close; sqlite3 db2 test.db -vfs unix-excl }
code1 { db close; sqlite3 db file:test.db?psow=0 -vfs unix-excl -uri 1 }
code2 { db2 close; sqlite3 db2 file:test.db?psow=0 -vfs unix-excl -uri 1 }
sql1 {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a, b);
@ -108,7 +108,7 @@ do_multiclient_test tn {
} {1 2}
do_test unixexcl-3.$tn.3 {
sql1 { PRAGMA wal_checkpoint; INSERT INTO t1 VALUES(3, 4); }
} {0 5 5}
} {0 3 3}
do_test unixexcl-3.$tn.4 {
sql2 { SELECT * FROM t1; }
} {1 2}
@ -120,7 +120,7 @@ do_multiclient_test tn {
} {1 2 3 4}
do_test unixexcl-3.$tn.7 {
sql1 { PRAGMA wal_checkpoint; }
} {0 7 7}
} {0 4 4}
}
}

View File

@ -546,7 +546,7 @@ do_multiclient_test tn {
} {1 2 3 4 5 6 7 8 9 10}
do_test wal-10.$tn.12 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 13 13}} ;# Reader no longer block checkpoints
} {0 {0 7 7}} ;# Reader no longer block checkpoints
do_test wal-10.$tn.13 {
execsql { INSERT INTO t1 VALUES(11, 12) }
sql2 {SELECT * FROM t1}
@ -556,7 +556,7 @@ do_multiclient_test tn {
#
do_test wal-10.$tn.14 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 15 13}}
} {0 {0 8 7}}
# The following series of test cases used to verify another blocking
# case in WAL - a case which no longer blocks.
@ -566,10 +566,10 @@ do_multiclient_test tn {
} {1 2 3 4 5 6 7 8 9 10 11 12}
do_test wal-10.$tn.16 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 15 15}}
} {0 {0 8 8}}
do_test wal-10.$tn.17 {
execsql { PRAGMA wal_checkpoint }
} {0 15 15}
} {0 8 8}
do_test wal-10.$tn.18 {
sql3 { BEGIN; SELECT * FROM t1 }
} {1 2 3 4 5 6 7 8 9 10 11 12}
@ -592,13 +592,13 @@ do_multiclient_test tn {
#
do_test wal-10.$tn.23 {
execsql { PRAGMA wal_checkpoint }
} {0 17 17}
} {0 9 9}
do_test wal-10.$tn.24 {
sql2 { BEGIN; SELECT * FROM t1; }
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14}
do_test wal-10.$tn.25 {
execsql { PRAGMA wal_checkpoint }
} {0 17 17}
} {0 9 9}
do_test wal-10.$tn.26 {
catchsql { INSERT INTO t1 VALUES(15, 16) }
} {0 {}}
@ -615,11 +615,11 @@ do_multiclient_test tn {
do_test wal-10.$tn.29 {
execsql { INSERT INTO t1 VALUES(19, 20) }
catchsql { PRAGMA wal_checkpoint }
} {0 {0 6 0}}
} {0 {0 3 0}}
do_test wal-10.$tn.30 {
code3 { sqlite3_finalize $::STMT }
execsql { PRAGMA wal_checkpoint }
} {0 6 0}
} {0 3 0}
# At one point, if a reader failed to upgrade to a writer because it
# was reading an old snapshot, the write-locks were not being released.
@ -658,7 +658,7 @@ do_multiclient_test tn {
} {a b c d}
do_test wal-10.$tn.36 {
catchsql { PRAGMA wal_checkpoint }
} {0 {0 16 16}}
} {0 {0 8 8}}
do_test wal-10.$tn.36 {
sql3 { INSERT INTO t1 VALUES('e', 'f') }
sql2 { SELECT * FROM t1 }
@ -666,7 +666,7 @@ do_multiclient_test tn {
do_test wal-10.$tn.37 {
sql2 COMMIT
execsql { PRAGMA wal_checkpoint }
} {0 18 18}
} {0 9 9}
}
#-------------------------------------------------------------------------
@ -1040,7 +1040,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
5 {sqlite3_wal_checkpoint db aux} SQLITE_OK 0 1
6 {sqlite3_wal_checkpoint db temp} SQLITE_OK 0 0
7 {db eval "PRAGMA main.wal_checkpoint"} {0 10 10} 1 0
8 {db eval "PRAGMA aux.wal_checkpoint"} {0 16 16} 0 1
8 {db eval "PRAGMA aux.wal_checkpoint"} {0 13 13} 0 1
9 {db eval "PRAGMA temp.wal_checkpoint"} {0 -1 -1} 0 0
} {
do_test wal-16.$tn.1 {
@ -1054,7 +1054,8 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
PRAGMA aux.auto_vacuum = 0;
PRAGMA main.journal_mode = WAL;
PRAGMA aux.journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA main.synchronous = NORMAL;
PRAGMA aux.synchronous = NORMAL;
}
} {wal wal}
@ -1072,7 +1073,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
} [list [expr 1*1024] [wal_file_size 10 1024]]
do_test wal-16.$tn.3 {
list [file size test2.db] [file size test2.db-wal]
} [list [expr 1*1024] [wal_file_size 16 1024]]
} [list [expr 1*1024] [wal_file_size 13 1024]]
do_test wal-16.$tn.4 [list eval $ckpt_cmd] $ckpt_res
@ -1082,7 +1083,7 @@ foreach {tn ckpt_cmd ckpt_res ckpt_main ckpt_aux} {
do_test wal-16.$tn.6 {
list [file size test2.db] [file size test2.db-wal]
} [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 16 1024]]
} [list [expr ($ckpt_aux ? 7 : 1)*1024] [wal_file_size 13 1024]]
catch { db close }
}
@ -1552,9 +1553,13 @@ ifcapable autovacuum {
}
file size test.db
} [expr 3 * 1024]
# WAL file now contains a single frame - the new root page for table t1.
# It would be two frames (the new root page and a padding frame) if the
# ZERO_DAMAGE flag were not set.
do_test 24.5 {
file size test.db-wal
} 2128
} [wal_file_size 1 1024]
}
db close

View File

@ -361,7 +361,9 @@ do_test wal2-4.1 {
INSERT INTO data VALUES('need xShmOpen to see this');
PRAGMA wal_checkpoint;
}
} {wal 0 5 5}
# Three pages in the WAL file at this point: One copy of page 1 and two
# of the root page for table "data".
} {wal 0 3 3}
do_test wal2-4.2 {
db close
testvfs tvfs -noshm 1
@ -730,7 +732,7 @@ do_test wal2-6.5.1 {
INSERT INTO t2 VALUES('I', 'II');
PRAGMA journal_mode;
}
} {wal exclusive 0 3 3 wal}
} {wal exclusive 0 2 2 wal}
do_test wal2-6.5.2 {
execsql {
PRAGMA locking_mode = normal;
@ -741,7 +743,7 @@ do_test wal2-6.5.2 {
} {normal exclusive I II III IV}
do_test wal2-6.5.3 {
execsql { PRAGMA wal_checkpoint }
} {0 4 4}
} {0 2 2}
db close
proc lock_control {method filename handle spec} {
@ -1184,6 +1186,7 @@ foreach {tn sql reslist} {
execsql {PRAGMA auto_vacuum = 0}
execsql $sql
do_execsql_test wal2-14.$tn.0 { PRAGMA page_size = 4096 } {}
do_execsql_test wal2-14.$tn.1 { PRAGMA journal_mode = WAL } {wal}
set sqlite_sync_count 0
@ -1199,7 +1202,7 @@ foreach {tn sql reslist} {
INSERT INTO t1 VALUES(5, 6);
COMMIT; -- 2 wal sync
PRAGMA wal_checkpoint; -- 1 wal sync, 1 db sync
} {10 0 5 5 0 2 2}
} {10 0 3 3 0 1 1}
do_test wal2-14.$tn.3 {
cond_incr_sync_count 1
@ -1261,6 +1264,7 @@ foreach {tn settings restart_sync commit_sync ckpt_sync} {
sqlite3 db test.db
do_execsql_test 15.$tn.1 "
PRAGMA page_size = 4096;
CREATE TABLE t1(x);
PRAGMA wal_autocheckpoint = OFF;
PRAGMA journal_mode = WAL;
@ -1269,6 +1273,7 @@ foreach {tn settings restart_sync commit_sync ckpt_sync} {
PRAGMA synchronous = [lindex $settings 2];
" {0 wal}
if { $tn==2} breakpoint
do_test 15.$tn.2 {
set sync(normal) 0
set sync(full) 0

View File

@ -429,7 +429,7 @@ do_test wal3-6.1.2 {
} {o t t f}
do_test wal3-6.1.3 {
execsql { PRAGMA wal_checkpoint } db2
} {0 7 7}
} {0 4 4}
# At this point the log file has been fully checkpointed. However,
# connection [db3] holds a lock that prevents the log from being wrapped.
@ -518,7 +518,7 @@ proc lock_callback {method file handle spec} {
}
do_test wal3-6.2.2 {
execsql { PRAGMA wal_checkpoint }
} {0 7 7}
} {0 4 4}
do_test wal3-6.2.3 {
set ::R
} {h h l b}
@ -628,7 +628,7 @@ do_test wal3-8.1 {
INSERT INTO b VALUES('Markazi');
PRAGMA wal_checkpoint;
}
} {wal 0 9 9}
} {wal 0 5 5}
do_test wal3-8.2 {
execsql { SELECT * FROM b }
} {Tehran Qom Markazi}

View File

@ -197,9 +197,9 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 5 5}
do_test 2.1.$tn.4 { file_page_counts } {2 5 2 5}
do_test 2.2.$tn.2 { file_page_counts } {1 3 1 3}
do_test 2.1.$tn.3 { code1 { do_wal_checkpoint db } } {0 3 3}
do_test 2.1.$tn.4 { file_page_counts } {2 3 2 3}
}
do_multiclient_test tn {
@ -213,10 +213,10 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(3, 4);
}
} {}
do_test 2.2.$tn.2 { file_page_counts } {1 5 1 7}
do_test 2.2.$tn.2 { file_page_counts } {1 3 1 4}
do_test 2.2.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 5 5}
do_test 2.2.$tn.5 { file_page_counts } {2 5 2 7}
do_test 2.2.$tn.4 { code1 { do_wal_checkpoint db -mode restart } } {1 3 3}
do_test 2.2.$tn.5 { file_page_counts } {2 3 2 4}
}
do_multiclient_test tn {
@ -229,13 +229,13 @@ foreach {testprefix do_wal_checkpoint} {
INSERT INTO t2 VALUES(1, 2);
}
} {}
do_test 2.3.$tn.2 { file_page_counts } {1 5 1 5}
do_test 2.3.$tn.2 { file_page_counts } {1 3 1 3}
do_test 2.3.$tn.3 { sql2 { BEGIN; SELECT * FROM t1 } } {1 2}
do_test 2.3.$tn.4 { sql1 { INSERT INTO t1 VALUES(3, 4) } } {}
do_test 2.3.$tn.5 { sql1 { INSERT INTO t2 VALUES(3, 4) } } {}
do_test 2.3.$tn.6 { file_page_counts } {1 7 1 7}
do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 7 5}
do_test 2.3.$tn.8 { file_page_counts } {1 7 2 7}
do_test 2.3.$tn.6 { file_page_counts } {1 4 1 4}
do_test 2.3.$tn.7 { code1 { do_wal_checkpoint db -mode full } } {1 4 3}
do_test 2.3.$tn.8 { file_page_counts } {1 4 2 4}
}
# Check that checkpoints block on the correct locks. And respond correctly
@ -256,18 +256,18 @@ foreach {testprefix do_wal_checkpoint} {
# processes holding all three types of locks.
#
foreach {tn1 checkpoint busy_on ckpt_expected expected} {
1 PASSIVE - {0 5 5} -
2 TYPO - {0 5 5} -
1 PASSIVE - {0 3 3} -
2 TYPO - {0 3 3} -
3 FULL - {0 7 7} 2
4 FULL 1 {1 5 5} 1
5 FULL 2 {1 7 5} 2
6 FULL 3 {0 7 7} 2
3 FULL - {0 4 4} 2
4 FULL 1 {1 3 3} 1
5 FULL 2 {1 4 3} 2
6 FULL 3 {0 4 4} 2
7 RESTART - {0 7 7} 3
8 RESTART 1 {1 5 5} 1
9 RESTART 2 {1 7 5} 2
10 RESTART 3 {1 7 7} 3
7 RESTART - {0 4 4} 3
8 RESTART 1 {1 3 3} 1
9 RESTART 2 {1 4 3} 2
10 RESTART 3 {1 4 4} 3
} {
do_multiclient_test tn {

112
test/zerodamage.test Normal file
View File

@ -0,0 +1,112 @@
# 2011 December 21
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
#
# This file implements tests of the SQLITE_IOCAP_POWERSAFE_OVERWRITE property
# and the SQLITE_FCNTL_POWERSAFE_OVERWRITE file-control for manipulating it.
#
# The name of this file comes from the fact that we used to call the
# POWERSAFE_OVERWRITE property ZERO_DAMAGE.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix wal5
# POWERSAFE_OVERWRITE defaults to true
#
do_test zerodamage-1.0 {
file_control_powersafe_overwrite db -1
} {0 1}
# Check the ability to turn zero-damage on and off.
#
do_test zerodamage-1.1 {
file_control_powersafe_overwrite db 0
file_control_powersafe_overwrite db -1
} {0 0}
do_test zerodamage-1.2 {
file_control_powersafe_overwrite db 1
file_control_powersafe_overwrite db -1
} {0 1}
# Run a transaction with zero-damage on, a small page size and a much larger
# sectorsize. Verify that the maximum journal size is small - that the
# rollback journal is not being padded.
#
do_test zerodamage-2.0 {
db close
testvfs tv -default 1
tv sectorsize 8192
sqlite3 db file:test.db?psow=TRUE -uri 1
unset -nocomplain ::max_journal_size
set ::max_journal_size 0
proc xDeleteCallback {method file args} {
set sz [file size $file]
if {$sz>$::max_journal_size} {set ::max_journal_size $sz}
}
tv filter xDelete
tv script xDeleteCallback
register_wholenumber_module db
db eval {
PRAGMA page_size=1024;
PRAGMA journal_mode=DELETE;
PRAGMA cache_size=5;
CREATE VIRTUAL TABLE nums USING wholenumber;
CREATE TABLE t1(x, y);
INSERT INTO t1 SELECT value, randomblob(100) FROM nums
WHERE value BETWEEN 1 AND 400;
}
set ::max_journal_size 0
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=123;
}
concat [file_control_powersafe_overwrite db -1] [set ::max_journal_size]
} {0 1 2576}
# Repeat the previous step with zero-damage turned off. This time the
# maximum rollback journal size should be much larger.
#
do_test zerodamage-2.1 {
set ::max_journal_size 0
db close
sqlite3 db file:test.db?psow=FALSE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
concat [file_control_powersafe_overwrite db -1] [set ::max_journal_size]
} {0 0 24704}
# Run a WAL-mode transaction with POWERSAFE_OVERWRITE on to verify that the
# WAL file does not get too big.
#
do_test zerodamage-3.0 {
db eval {
PRAGMA journal_mode=WAL;
}
db close
sqlite3 db file:test.db?psow=TRUE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
file size test.db-wal
} {1080}
# Repeat the previous with POWERSAFE_OVERWRITE off. Verify that the WAL file
# is padded.
#
do_test zerodamage-3.1 {
db close
sqlite3 db file:test.db?psow=FALSE -uri 1
db eval {
UPDATE t1 SET y=randomblob(50) WHERE x=124;
}
file size test.db-wal
} {8416}