]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Support FCNTL_CHUNK_SIZE on windows too.
authordan <dan@noemail.net>
Wed, 28 Jul 2010 14:26:17 +0000 (14:26 +0000)
committerdan <dan@noemail.net>
Wed, 28 Jul 2010 14:26:17 +0000 (14:26 +0000)
FossilOrigin-Name: a038688c991435967b935946c2283707820bb5da

manifest
manifest.uuid
src/os_unix.c
src/os_win.c
src/pager.c
src/sqlite.h.in
src/wal.c
test/fallocate.test

index 35fb9775327e2ebc3c75cd2b7b91757e9785dc64..ab3b4c342df45433a085ebdfde2bf41af1827df1 100644 (file)
--- 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
index 9d45064befd84d74332f4fcb29f36e11487da0fe..1ec6093ac1cf97f404db8ac250ed4eae0dd144fe 100644 (file)
@@ -1 +1 @@
-621824092d443425c420ba9010bbe1202fe99ea2
\ No newline at end of file
+a038688c991435967b935946c2283707820bb5da
\ No newline at end of file
index 467409b198ec51416294dc5a58e6c6e686a40fb8..5c648e3f9ce685e4d7261f7bbe677fd2851399c5 100644 (file)
@@ -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 && iWrite<nSize );
-      if( wrote!=1 ) amt = 1;
-#endif
-    }
-  }
-
   if( amt>0 ){
     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 && iWrite<nSize );
+      if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
+#endif
+    }
+  }
+
+  return SQLITE_OK;
+}
 
 /*
 ** Information and control of an open file handle.
@@ -3099,15 +3111,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
     }
     case SQLITE_FCNTL_CHUNK_SIZE: {
       ((unixFile*)id)->szChunk = *(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
index 095131232734c287290645fe5b3efcfc0f8bfb35..562282f20be0ae42eda9f0acd69f9dee31f45358 100644 (file)
@@ -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);
index dfb29e3df70308123f2fcad5477fe746d9d6531c..603e7b50260497736086f8d3882411115b0f5d8a 100644 (file)
@@ -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);
   }
index eab2b3a84e8d8a85194dd247102124ed6236ee1b..209abd4823c48601c78daf2846452aa2636930cf 100644 (file)
@@ -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
index 54274640ed6f4092481c92fe77d17fc33227606b..3ac8071548700ad1509cd7d536b81f6fa97c6211 100644 (file)
--- 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; i<WAL_NREADER; i++){
     u32 y = pInfo->aReadMark[i];
@@ -1565,6 +1567,8 @@ static int walCheckpoint(
   if( pInfo->nBackfill<mxSafeFrame
    && (rc = walLockExclusive(pWal, WAL_READ_LOCK(0), 1))==SQLITE_OK
   ){
+    i64 nReq;                     /* File size hint passed to VFS */
+    i64 nSize;                    /* Current size of database file */
     u32 nBackfill = pInfo->nBackfill;
 
     /* 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 && nSize<nReq ){
+      rc = sqlite3OsFileControl(pWal->pDbFd, 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);
index b9c4f460d48e200071c4ac603940bf7c8a09d227..82ceb1d8fa4e2643a28e01be68306fb322926841 100644 (file)
 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
+