aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/os_unix.c37
-rw-r--r--src/os_win.c3
-rw-r--r--src/pager.c32
-rw-r--r--src/sqlite.h.in9
-rw-r--r--src/test6.c1
-rw-r--r--src/test_vfs.c3
-rw-r--r--src/wal.c51
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();
diff --git a/src/wal.c b/src/wal.c
index 03c482554..b601d42ff 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -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);
}