diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/os_unix.c | 37 | ||||
-rw-r--r-- | src/os_win.c | 3 | ||||
-rw-r--r-- | src/pager.c | 32 | ||||
-rw-r--r-- | src/sqlite.h.in | 9 | ||||
-rw-r--r-- | src/test6.c | 1 | ||||
-rw-r--r-- | src/test_vfs.c | 3 | ||||
-rw-r--r-- | src/wal.c | 51 |
7 files changed, 95 insertions, 41 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index ee5971f10..a9d7de832 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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> @@ -217,6 +221,7 @@ struct unixFile { const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ + int szSector; /* Sector size */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif @@ -414,6 +419,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 */ /* @@ -3572,9 +3585,23 @@ 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; +#else + struct statvfs x; + int sz; + memset(&x, 0, sizeof(x)); + osStatvfs(p->zPath, &x); + p->szSector = sz = (int)x.f_frsize; + if( sz<512 || sz>65536 || (sz&(sz-1))!=0 ){ + p->szSector = SQLITE_DEFAULT_SECTOR_SIZE; + } +#endif + } + return p->szSector; } /* @@ -3582,7 +3609,7 @@ static int unixSectorSize(sqlite3_file *NotUsed){ */ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ UNUSED_PARAMETER(NotUsed); - return 0; + return SQLITE_IOCAP_ZERO_DAMAGE; } #ifndef SQLITE_OMIT_WAL @@ -6777,7 +6804,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++){ diff --git a/src/os_win.c b/src/os_win.c index ab70eebbf..5e0667d18 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -2213,7 +2213,8 @@ static int winSectorSize(sqlite3_file *id){ */ static int winDeviceCharacteristics(sqlite3_file *id){ UNUSED_PARAMETER(id); - return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; + return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | + SQLITE_IOCAP_ZERO_DAMAGE; } #ifndef SQLITE_OMIT_WAL diff --git a/src/pager.c b/src/pager.c index 9db6ebd3f..ab944aa4f 100644 --- a/src/pager.c +++ b/src/pager.c @@ -2515,23 +2515,35 @@ 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_ZERO_DAMAGE 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 ZERO_DAMAGE, the blast radius is zero, 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_ZERO_DAMAGE)!=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; + } } } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 0e0e3af6b..f7a2616ba 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -504,7 +504,13 @@ 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_ZERO_DAMAGE 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 introduce garbage or randomness into a file, and 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 +524,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_ZERO_DAMAGE 0x00001000 /* ** CAPI3REF: File Locking Levels diff --git a/src/test6.c b/src/test6.c index 23fb14c5b..1bb9c65f0 100644 --- a/src/test6.c +++ b/src/test6.c @@ -716,6 +716,7 @@ static int processDevSymArgs( { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, + { "zero_damage", SQLITE_IOCAP_ZERO_DAMAGE }, { 0, 0 } }; diff --git a/src/test_vfs.c b/src/test_vfs.c index a59aa40a4..2c985a7db 100644 --- a/src/test_vfs.c +++ b/src/test_vfs.c @@ -1174,6 +1174,7 @@ static int testvfs_obj_cmd( { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN }, + { "zero_damage", SQLITE_IOCAP_ZERO_DAMAGE }, { 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(); @@ -425,6 +425,7 @@ struct Wal { 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 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 */ @@ -1294,6 +1295,7 @@ int sqlite3WalOpen( pRet->readLock = -1; pRet->mxWalSize = mxWalSize; pRet->zWalName = zWalName; + pRet->padToSectorBoundary = 1; pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE); /* Open file handle on the write-ahead log file. */ @@ -1310,6 +1312,7 @@ int sqlite3WalOpen( }else{ int iDC = sqlite3OsDeviceCharacteristics(pRet->pWalFd); if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->noSyncHeader = 1; } + if( iDC & SQLITE_IOCAP_ZERO_DAMAGE ){ pRet->padToSectorBoundary = 0; } *ppWal = pRet; WALTRACE(("WAL%d: opened\n", pRet)); } @@ -2780,34 +2783,36 @@ int sqlite3WalFrames( /* Sync the log file if the 'isSync' flag was specified. */ 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( pWal->padToSectorBoundary ){ + 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; + if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM; #else - pData = pLast->pData; + 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; - } - iOffset += WAL_FRAME_HDRSIZE; - rc = walWriteToLog(pWal, pData, szPage, iOffset); - if( rc!=SQLITE_OK ){ - return rc; + 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; + } + iOffset += WAL_FRAME_HDRSIZE; + rc = walWriteToLog(pWal, pData, szPage, iOffset); + if( rc!=SQLITE_OK ){ + return rc; + } + nLast++; + iOffset += szPage; } - nLast++; - iOffset += szPage; } - + rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK); } |