From 502019c8bbf382e97720c711af03b24147107eff Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 28 Jul 2010 14:26:17 +0000 Subject: [PATCH] Support FCNTL_CHUNK_SIZE on windows too. FossilOrigin-Name: a038688c991435967b935946c2283707820bb5da --- manifest | 22 +++--- manifest.uuid | 2 +- src/os_unix.c | 93 +++++++++++++------------ src/os_win.c | 160 ++++++++++++++++++++++++++------------------ src/pager.c | 2 +- src/sqlite.h.in | 8 +++ src/wal.c | 15 ++++- test/fallocate.test | 89 ++++++++++++++++++++++-- 8 files changed, 264 insertions(+), 127 deletions(-) diff --git a/manifest b/manifest index 35fb977532..ab3b4c342d 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\strunk\schanges\sinto\sexperimental\sbranch. -D 2010-07-27T18:36:38 +C Support\sFCNTL_CHUNK_SIZE\son\swindows\stoo. +D 2010-07-28T14:26:18 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -154,9 +154,9 @@ F src/os.c 60178f518c4d6c0dcb59f7292232281d7bea2dcf F src/os.h 9dbed8c2b9c1f2f2ebabc09e49829d4777c26bf9 F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_os2.c 72d0b2e562952a2464308c4ce5f7913ac10bef3e -F src/os_unix.c 77e963fbbed6a2ca9b7c9115ae06f05181729490 -F src/os_win.c 1f8b0a1a5bcf6289e7754d0d3c16cec16d4c93ab -F src/pager.c 78ca1e1f3315c8227431c403c04d791dccf242fb +F src/os_unix.c ae5ca8a6031380708f3fec7be325233d49944914 +F src/os_win.c 51cb62f76262d961ea4249489383d714501315a7 +F src/pager.c 27ace2e07c8bfe2e04428eba03b2ddb5b03abd7d F src/pager.h 879fdde5a102d2f21a3135d6f647530b21c2796c F src/parse.y 12b7ebd61ea54f0e1b1083ff69cc2c8ce9353d58 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07 @@ -170,7 +170,7 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c 74fef1334bec27e606ef0b19e5c41cd0a639e69c F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714 -F src/sqlite.h.in 2dfa3db44fd123ea5f0e20e87fa3d9db189feb98 +F src/sqlite.h.in 10bd4aed244fc0eadb10ec01cedb60bafc2d7bfe F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 F src/sqliteInt.h a9be6badc6cd6a3c1ae54475a98661cf351ecad5 F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3 @@ -227,7 +227,7 @@ F src/vdbeblob.c 258a6010ba7a82b72b327fb24c55790655689256 F src/vdbemem.c 5e579abf6532001dfbee0e640dc34eae897a9807 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vtab.c 82200af3881fa4e1c9cf07cf31d98c09d437e3ab -F src/wal.c 0925601f3299c2941a67c9cfff41ee710f70ca82 +F src/wal.c 72cb5df7f4c26f83cb661d5a607b9918da99f758 F src/wal.h 906c85760598b18584921fe08008435aa4eeeeb2 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f F src/where.c 79202ca81e740eeb1f54512147e29b6c518d84ca @@ -351,7 +351,7 @@ F test/exclusive.test 5fe18e10a159342dd52ca14b1554e33f98734267 F test/exclusive2.test fcbb1c9ca9739292a0a22a3763243ad6d868086b F test/exec.test e949714dc127eaa5ecc7d723efec1ec27118fdd7 F test/expr.test 9f521ae22f00e074959f72ce2e55d46b9ed23f68 -F test/fallocate.test 2aa524a237893aca5b2a7ad5d450ee4801b4abdd +F test/fallocate.test 0594314eb04268f7d0779d054fa850a36a5ae8bc F test/filectrl.test 97003734290887566e01dded09dc9e99cb937e9e F test/filefmt.test 5d271bf467e6557fe7499dcc8203069c9dc5825e F test/fkey1.test 01c7de578e11747e720c2d9aeef27f239853c4da @@ -839,7 +839,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 7cf0e851d4c5e826ea22ed08291b7c91d7b1abc7 8118de2af33557f75b4f2f1b1194a21d46ccf7fe -R db6f0268ed3b851f8967be0e4ae70758 +P 621824092d443425c420ba9010bbe1202fe99ea2 +R d7ede0d4642b161610b7d818427c7a8c U dan -Z d9d588012d8704dd90455874cd937598 +Z 95d00fbeff8589e69f2af3194811a965 diff --git a/manifest.uuid b/manifest.uuid index 9d45064bef..1ec6093ac1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -621824092d443425c420ba9010bbe1202fe99ea2 \ No newline at end of file +a038688c991435967b935946c2283707820bb5da \ No newline at end of file diff --git a/src/os_unix.c b/src/os_unix.c index 467409b198..5c648e3f9c 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -2765,42 +2765,6 @@ static int unixWrite( SimulateIOError(( wrote=(-1), amt=1 )); SimulateDiskfullError(( wrote=0, amt=1 )); - /* If the user has configured a chunk-size for this file, it could be - ** that the file needs to be extended at this point. - */ - if( pFile->szChunk && amt==0 ){ - i64 nSize; /* Required file size */ - struct stat buf; /* Used to hold return values of fstat() */ - int rc = fstat(pFile->h, &buf); - if( rc!=0 ) return SQLITE_IOERR_FSTAT; - nSize = ((offset+amt+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; - if( nSize>(i64)buf.st_size ){ -#ifdef HAVE_POSIX_FALLOCATE - if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){ - return SQLITE_IOERR_WRITE; - } -#else - /* If the OS does not have posix_fallocate(), fake it. First use - ** ftruncate() to set the file size, then write a single byte to - ** the last byte in each block within the extended region. - */ - int nBlk = buf.st_blksize; /* File-system block size */ - i64 iWrite; /* Next offset to write to */ - - if( ftruncate(pFile->h, nSize) ){ - pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; - } - iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - do { - wrote = seekAndWrite(pFile, iWrite, "", 1); - iWrite += nBlk; - } while( wrote==1 && iWrite0 ){ if( wrote<0 ){ /* lastErrno set by seekAndWrite */ @@ -3083,6 +3047,54 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){ static int proxyFileControl(sqlite3_file*,int,void*); #endif +/* +** This function is called to handle the SQLITE_FCNTL_SIZE_HINT +** file-control operation. +** +** If the user has configured a chunk-size for this file, it could be +** that the file needs to be extended at this point. Otherwise, the +** SQLITE_FCNTL_SIZE_HINT operation is a no-op for Unix. +*/ +static int fcntlSizeHint(unixFile *pFile, i64 nByte){ + if( pFile->szChunk ){ + i64 nSize; /* Required file size */ + struct stat buf; /* Used to hold return values of fstat() */ + + if( fstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT; + + nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; + if( nSize>(i64)buf.st_size ){ +#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE + if( posix_fallocate(pFile->h, buf.st_size, nSize-buf.st_size) ){ + return SQLITE_IOERR_WRITE; + } +#else + /* If the OS does not have posix_fallocate(), fake it. First use + ** ftruncate() to set the file size, then write a single byte to + ** the last byte in each block within the extended region. This + ** is the same technique used by glibc to implement posix_fallocate() + ** on systems that do not have a real fallocate() system call. + */ + int nBlk = buf.st_blksize; /* File-system block size */ + i64 iWrite; /* Next offset to write to */ + int nWrite; /* Return value from seekAndWrite() */ + + if( ftruncate(pFile->h, nSize) ){ + pFile->lastErrno = errno; + return SQLITE_IOERR_TRUNCATE; + } + iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; + do { + nWrite = seekAndWrite(pFile, iWrite, "", 1); + iWrite += nBlk; + } while( nWrite==1 && iWriteszChunk = *(int *)pArg; - return SQLITE_OK; + return SQLITE_OK; } case SQLITE_FCNTL_SIZE_HINT: { -#if 0 /* No performance advantage seen on Linux */ - sqlite3_int64 szFile = *(sqlite3_int64*)pArg; - unixFile *pFile = (unixFile*)id; - ftruncate(pFile->h, szFile); -#endif - return SQLITE_OK; + return fcntlSizeHint((unixFile *)id, *(i64 *)pArg); } #ifndef NDEBUG /* The pager calls this method to signal that it has done diff --git a/src/os_win.c b/src/os_win.c index 0951312327..562282f20b 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -108,6 +108,7 @@ struct winFile { DWORD sectorSize; /* Sector size of the device file is on */ winShm *pShm; /* Instance of shared memory on this file */ const char *zPath; /* Full pathname of this file */ + int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ @@ -620,6 +621,42 @@ static BOOL winceLockFileEx( ** by the sqlite3_io_methods object. ******************************************************************************/ +/* +** Some microsoft compilers lack this definition. +*/ +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +/* +** Move the current position of the file handle passed as the first +** argument to offset iOffset within the file. If successful, return 0. +** Otherwise, set pFile->lastErrno and return non-zero. +*/ +static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ + LONG upperBits; /* Most sig. 32 bits of new offset */ + LONG lowerBits; /* Least sig. 32 bits of new offset */ + DWORD dwRet; /* Value returned by SetFilePointer() */ + + upperBits = (LONG)((iOffset>>32) & 0x7fffffff); + lowerBits = (LONG)(iOffset & 0xffffffff); + + /* API oddity: If successful, SetFilePointer() returns a dword + ** containing the lower 32-bits of the new file-offset. Or, if it fails, + ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, + ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine + ** whether an error has actually occured, it is also necessary to call + ** GetLastError(). + */ + dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); + if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ + pFile->lastErrno = GetLastError(); + return 1; + } + + return 0; +} + /* ** Close a file. ** @@ -662,13 +699,6 @@ static int winClose(sqlite3_file *id){ return rc ? SQLITE_OK : SQLITE_IOERR; } -/* -** Some microsoft compilers lack this definition. -*/ -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER ((DWORD)-1) -#endif - /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes @@ -680,32 +710,27 @@ static int winRead( int amt, /* Number of bytes to read */ sqlite3_int64 offset /* Begin reading at this offset */ ){ - LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); - LONG lowerBits = (LONG)(offset & 0xffffffff); - DWORD rc; - winFile *pFile = (winFile*)id; - DWORD error; - DWORD got; + winFile *pFile = (winFile*)id; /* file handle */ + DWORD nRead; /* Number of bytes actually read from file */ assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); - rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ - pFile->lastErrno = error; + + if( seekWinFile(pFile, offset) ){ return SQLITE_FULL; } - if( !ReadFile(pFile->h, pBuf, amt, &got, 0) ){ + if( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ pFile->lastErrno = GetLastError(); return SQLITE_IOERR_READ; } - if( got==(DWORD)amt ){ - return SQLITE_OK; - }else{ + if( nRead<(DWORD)amt ){ /* Unread parts of the buffer must be zero-filled */ - memset(&((char*)pBuf)[got], 0, amt-got); + memset(&((char*)pBuf)[nRead], 0, amt-nRead); return SQLITE_IOERR_SHORT_READ; } + + return SQLITE_OK; } /* @@ -713,47 +738,42 @@ static int winRead( ** or some other error code on failure. */ static int winWrite( - sqlite3_file *id, /* File to write into */ - const void *pBuf, /* The bytes to be written */ - int amt, /* Number of bytes to write */ - sqlite3_int64 offset /* Offset into the file to begin writing at */ + sqlite3_file *id, /* File to write into */ + const void *pBuf, /* The bytes to be written */ + int amt, /* Number of bytes to write */ + sqlite3_int64 offset /* Offset into the file to begin writing at */ ){ - LONG upperBits = (LONG)((offset>>32) & 0x7fffffff); - LONG lowerBits = (LONG)(offset & 0xffffffff); - DWORD rc; - winFile *pFile = (winFile*)id; - DWORD error; - DWORD wrote = 0; + int rc; /* True if error has occured, else false */ + winFile *pFile = (winFile*)id; /* File handle */ - assert( id!=0 ); + assert( amt>0 ); + assert( pFile ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); + OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); - rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ - pFile->lastErrno = error; - if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ - return SQLITE_FULL; - }else{ - return SQLITE_IOERR_WRITE; + + rc = seekWinFile(pFile, offset); + if( rc==0 ){ + u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ + int nRem = amt; /* Number of bytes yet to be written */ + DWORD nWrite; /* Bytes written by each WriteFile() call */ + + while( nRem>0 && WriteFile(pFile->h, aRem, nRem, &nWrite, 0) && nWrite>0 ){ + aRem += nWrite; + nRem -= nWrite; + } + if( nRem>0 ){ + pFile->lastErrno = GetLastError(); + rc = 1; } } - assert( amt>0 ); - while( - amt>0 - && (rc = WriteFile(pFile->h, pBuf, amt, &wrote, 0))!=0 - && wrote>0 - ){ - amt -= wrote; - pBuf = &((char*)pBuf)[wrote]; - } - if( !rc || amt>(int)wrote ){ - pFile->lastErrno = GetLastError(); + + if( rc ){ if( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ){ return SQLITE_FULL; - }else{ - return SQLITE_IOERR_WRITE; } + return SQLITE_IOERR_WRITE; } return SQLITE_OK; } @@ -762,26 +782,32 @@ static int winWrite( ** Truncate an open file to a specified size */ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ - LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff); - LONG lowerBits = (LONG)(nByte & 0xffffffff); - DWORD dwRet; - winFile *pFile = (winFile*)id; - DWORD error; - int rc = SQLITE_OK; + winFile *pFile = (winFile*)id; /* File handle object */ + int rc = SQLITE_OK; /* Return code for this function */ + + assert( pFile ); - assert( id!=0 ); OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); SimulateIOError(return SQLITE_IOERR_TRUNCATE); - dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ - pFile->lastErrno = error; + + /* If the user has configured a chunk-size for this file, truncate the + ** file so that it consists of an integer number of chunks (i.e. the + ** actual file size after the operation may be larger than the requested + ** size). + */ + if( pFile->szChunk ){ + nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; + } + + /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ + if( seekWinFile(pFile, nByte) ){ rc = SQLITE_IOERR_TRUNCATE; - /* SetEndOfFile will fail if nByte is negative */ - }else if( !SetEndOfFile(pFile->h) ){ + }else if( 0==SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); rc = SQLITE_IOERR_TRUNCATE; } - OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed")); + + OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); return rc; } @@ -1146,6 +1172,10 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){ *(int*)pArg = (int)((winFile*)id)->lastErrno; return SQLITE_OK; } + case SQLITE_FCNTL_CHUNK_SIZE: { + ((winFile*)id)->szChunk = *(int *)pArg; + return SQLITE_OK; + } case SQLITE_FCNTL_SIZE_HINT: { sqlite3_int64 sz = *(sqlite3_int64*)pArg; SimulateIOErrorBenign(1); diff --git a/src/pager.c b/src/pager.c index dfb29e3df7..603e7b5026 100644 --- a/src/pager.c +++ b/src/pager.c @@ -3365,7 +3365,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){ ** file size will be. */ assert( rc!=SQLITE_OK || isOpen(pPager->fd) ); - if( rc==SQLITE_OK && pPager->dbSize>(pPager->dbOrigSize+1) ){ + if( rc==SQLITE_OK && pPager->dbSize>pPager->dbOrigSize ){ sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize; sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile); } diff --git a/src/sqlite.h.in b/src/sqlite.h.in index eab2b3a84e..209abd4823 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -690,6 +690,14 @@ struct sqlite3_io_methods { ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. +** +** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS +** extends and truncates the database file in chunks of a size specified +** by the user. The fourth argument to [sqlite3_file_control()] should +** point to an integer (type int) containing the new chunk-size to use +** for the nominated database. Allocating database file space in large +** chunks (say 1MB at a time), may reduce file-system fragmentation and +** improve performance on some systems. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 diff --git a/src/wal.c b/src/wal.c index 54274640ed..3ac8071548 100644 --- a/src/wal.c +++ b/src/wal.c @@ -1521,6 +1521,7 @@ static int walCheckpoint( u32 iDbpage = 0; /* Next database page to write */ u32 iFrame = 0; /* Wal frame containing data for iDbpage */ u32 mxSafeFrame; /* Max frame that can be backfilled */ + u32 mxPage; /* Max database page to write */ int i; /* Loop counter */ volatile WalCkptInfo *pInfo; /* The checkpoint status information */ @@ -1545,6 +1546,7 @@ static int walCheckpoint( ** cannot be backfilled from the WAL. */ mxSafeFrame = pWal->hdr.mxFrame; + mxPage = pWal->hdr.nPage; pInfo = walCkptInfo(pWal); for(i=1; iaReadMark[i]; @@ -1565,6 +1567,8 @@ static int walCheckpoint( if( pInfo->nBackfillnBackfill; /* Sync the WAL to disk */ @@ -1572,11 +1576,20 @@ static int walCheckpoint( rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } + /* If the database file may grow as a result of this checkpoint, hint + ** about the eventual size of the db file to the VFS layer. + */ + nReq = ((i64)mxPage * szPage); + rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); + if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + } + /* Iterate through the contents of the WAL, copying data to the db file. */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); - if( iFrame<=nBackfill || iFrame>mxSafeFrame ) continue; + if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue; iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE; /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */ rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset); diff --git a/test/fallocate.test b/test/fallocate.test index b9c4f460d4..82ceb1d8fa 100644 --- a/test/fallocate.test +++ b/test/fallocate.test @@ -13,11 +13,6 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -if {$::tcl_platform(platform)!="unix"} { - finish_test - return -} - file_control_chunksize_test db main [expr 1024*1024] do_test fallocate-1.1 { @@ -53,4 +48,88 @@ do_test fallocate-1.6 { execsql { PRAGMA freelist_count } } {0} +# Start a write-transaction and read the "database file size" field from +# the journal file. This field should be set to the number of pages in +# the database file based on the size of the file on disk, not the actual +# logical size of the database within the file. +# +# We need to check this to verify that if in the unlikely event a rollback +# causes a database file to grow, the database grows to its previous size +# on disk, not to the minimum size required to hold the database image. +# +do_test fallocate-1.7 { + execsql { BEGIN; INSERT INTO t1 VALUES(1, 2); } + hexio_get_int [hexio_read test.db-journal 16 4] +} {1024} +do_test fallocate-1.8 { execsql { COMMIT } } {} + + +#------------------------------------------------------------------------- +# The following tests - fallocate-2.* - test that things work in WAL +# mode as well. +# +db close +file delete -force test.db +sqlite3 db test.db +file_control_chunksize_test db main [expr 32*1024] + +do_test fallocate-2.1 { + execsql { + PRAGMA page_size = 1024; + PRAGMA journal_mode = WAL; + CREATE TABLE t1(a, b); + } + file size test.db +} [expr 32*1024] + +do_test fallocate-2.2 { + execsql { INSERT INTO t1 VALUES(1, zeroblob(35*1024)) } + execsql { PRAGMA wal_checkpoint } + file size test.db +} [expr 64*1024] + +do_test fallocate-2.3 { + execsql { DELETE FROM t1 } + execsql { VACUUM } + file size test.db +} [expr 64*1024] + +do_test fallocate-2.4 { + execsql { PRAGMA wal_checkpoint } + file size test.db +} [expr 32*1024] + +do_test fallocate-2.5 { + execsql { + INSERT INTO t1 VALUES(2, randomblob(35*1024)); + PRAGMA wal_checkpoint; + INSERT INTO t1 VALUES(3, randomblob(128)); + DELETE FROM t1 WHERE a = 2; + VACUUM; + } + file size test.db +} [expr 64*1024] + +do_test fallocate-2.6 { + sqlite3 db2 test.db + execsql { BEGIN ; SELECT count(a) FROM t1 } db2 + execsql { + INSERT INTO t1 VALUES(4, randomblob(128)); + PRAGMA wal_checkpoint; + } + file size test.db +} [expr 64*1024] + +do_test fallocate-2.7 { + execsql { SELECT count(b) FROM t1 } db2 +} {1} + +do_test fallocate-2.8 { + execsql { COMMIT } db2 + execsql { PRAGMA wal_checkpoint } + file size test.db +} [expr 32*1024] + + finish_test +