]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the ability to enable or disable SQLITE_IOCAP_ZERO_DAMAGE using a URI
authordrh <drh@noemail.net>
Wed, 21 Dec 2011 14:42:29 +0000 (14:42 +0000)
committerdrh <drh@noemail.net>
Wed, 21 Dec 2011 14:42:29 +0000 (14:42 +0000)
parameter for both unix and windows.  Add a file-control to query or disable
the ZERO_DAMAGE setting.  Add the -uri option to the "sqlite3" TCL command
in tclsqlite3.c.  Allow the sqlite3_uri_parameter() interface to accept a
NULL pointer for its first parameter.

FossilOrigin-Name: c83627b73285f883719845c1b9fe85f378f28dd2

manifest
manifest.uuid
src/main.c
src/os_unix.c
src/os_win.c
src/sqlite.h.in
src/tclsqlite.c
src/test1.c
test/zerodamage.test [new file with mode: 0644]

index f43cdd027220a5e9479914308ce33738be7c450e..9b902fbbaf24be713f66a8d38fba2becbcb43529 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Remove\sthe\scode\sthat\stries\sto\sdetect\sOOO\sheader\swrites\son\sa\sWAL\srecovery.\nThe\scode\sis\smade\sobsolete\sby\ssyncing\sthe\sWAL\sheader.
-D 2011-12-20T22:18:51.258
+C Add\sthe\sability\sto\senable\sor\sdisable\sSQLITE_IOCAP_ZERO_DAMAGE\susing\sa\sURI\nparameter\sfor\sboth\sunix\sand\swindows.\s\sAdd\sa\sfile-control\sto\squery\sor\sdisable\nthe\sZERO_DAMAGE\ssetting.\s\sAdd\sthe\s-uri\soption\sto\sthe\s"sqlite3"\sTCL\scommand\nin\stclsqlite3.c.\s\sAllow\sthe\ssqlite3_uri_parameter()\sinterface\sto\saccept\sa\nNULL\spointer\sfor\sits\sfirst\sparameter.
+D 2011-12-21T14:42:29.520
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -147,7 +147,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
 F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
 F src/loadext.c f20382fbaeec832438a1ba7797bee3d3c8a6d51d
-F src/main.c c837e24182e8ec7121c61e44f547b59cbe413e7f
+F src/main.c 7f960366043b80c764de07e745d12b0705f56b30
 F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
@@ -166,8 +166,8 @@ 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 e2b96f85eb91c962765005892014b014db3e6478
-F src/os_win.c 3cf34661f5ef47be81bb7be541582d6e14852159
+F src/os_unix.c 3e630701efd54ecbdba237c5a3cb3c55d83d8a2e
+F src/os_win.c 4fc4aa8aecb334c64a250a4b36cff6a798704da4
 F src/pager.c c9bd2f7183edba480bea1dd49361a02d0c3d8f43
 F src/pager.h 5cd760857707529b403837d813d86b68938d6183
 F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
@@ -182,14 +182,14 @@ 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 d0e81fd4c72fbfdc786d3067e17a16a32f249428
+F src/sqlite.h.in 369e7597b7673e8bc490e2012bc00fb15cd55674
 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
 F src/sqliteInt.h 165409fa8adc8701148830804febeded3f2e4448
 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 4cbabeb8b2c487f67d724e2ea03841b69fa640c4
 F src/test2.c 80d323d11e909cf0eb1b6fbb4ac22276483bcf31
 F src/test3.c 124ff9735fb6bb7d41de180d6bac90e7b1509432
 F src/test4.c d1e5a5e904d4b444cf572391fdcb017638e36ff7
@@ -940,6 +940,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 0e12b2d7343f1fbb015e56a4a2a6074336ff46cf
 F tool/build-shell.sh 12aa4391073a777fcb6dcc490b219a018ae98bac
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
 F tool/extract.c 054069d81b095fbdc189a6f5d4466e40380505e2
@@ -984,7 +985,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 92c73b421b6242b09247dfb759777a531a107523
-R 0e9b80c28c07203b08d85530f30aefdc
+P 7ac713a14e24c48651c2a97acc9839850fa7f3c3
+R 8d88cdfbb2218ba883425a8ad72e347f
 U drh
-Z 960abf57a2870882cf541fd88a471911
+Z 6c0db6cbcd8026c224cda9667bf4cff6
index e405f4958a5fec79ed3aa9d4194e51edff380ce9..074245c6aeb6ee7beec67830db37b0b39a755c5c 100644 (file)
@@ -1 +1 @@
-7ac713a14e24c48651c2a97acc9839850fa7f3c3
\ No newline at end of file
+c83627b73285f883719845c1b9fe85f378f28dd2
\ No newline at end of file
index 690b73c2e7b0d574f723263a03792881c7778533..ea64d8be9a9d2b85eaf9e8632da923a272525641 100644 (file)
@@ -2977,6 +2977,7 @@ int sqlite3_test_control(int op, ...){
 ** returns a NULL pointer.
 */
 const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
+  if( zFilename==0 ) return 0;
   zFilename += sqlite3Strlen30(zFilename) + 1;
   while( zFilename[0] ){
     int x = strcmp(zFilename, zParam);
index a9d7de832d826ee3d818a5d0bd0ed9450194c072..4a31c8cdeac295d3492a976b3cd9652d0b2ba12e 100644 (file)
@@ -215,13 +215,13 @@ 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 */
   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
@@ -263,6 +263,7 @@ struct unixFile {
 #else
 # define UNIXFILE_DIRSYNC    0x00
 #endif
+#define UNIXFILE_ZERO_DAMAGE 0x10     /* True if SQLITE_IOCAP_ZERO_DAMAGE */
 
 /*
 ** Include code that is common to all os_*.c files
@@ -3511,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.
 */
@@ -3537,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_ZERO_DAMAGE: {
+      unixModeBit(pFile, UNIXFILE_ZERO_DAMAGE, (int*)pArg);
       return SQLITE_OK;
     }
     case SQLITE_FCNTL_VFSNAME: {
@@ -3589,27 +3603,37 @@ static int unixSectorSize(sqlite3_file *pFile){
   unixFile *p = (unixFile*)pFile;
   if( p->szSector==0 ){
 #ifdef MISSING_STATVFS
-    p->szSector = SQLITE_DEFAULT_SECTOR_SIZE;
+    p->szSector = SQLITE_DEFAULT_SECTOR_SIZE/512;
 #else
     struct statvfs x;
     int sz;
     memset(&x, 0, sizeof(x));
     osStatvfs(p->zPath, &x);
-    p->szSector = sz = (int)x.f_frsize;
+    sz = (int)x.f_frsize;
     if( sz<512 || sz>65536 || (sz&(sz-1))!=0 ){
-      p->szSector = SQLITE_DEFAULT_SECTOR_SIZE;
+      sz = SQLITE_DEFAULT_SECTOR_SIZE;
     }
+    p->szSector = sz/512;
 #endif
   }
-  return p->szSector;
+  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_ZERO_DAMAGE by default.
+** However, that choice is contraversial sicne technically the underlying
+** file system does not always provide ZERO_DAMAGE.  (In other words, after
+** a power-loss event, parts of the file that were never written might end
+** up being altered.)  However, non-ZERO-DAMAGE behavior is very, very rare.
+** And asserting ZERO_DAMAGE makes a large reduction in the amount of required
+** I/O.  Hence, while ZERO_DAMAGE is on by default, there is a file-control
+** available to turn it off.
 */
-static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
-  UNUSED_PARAMETER(NotUsed);
-  return SQLITE_IOCAP_ZERO_DAMAGE;
+static int unixDeviceCharacteristics(sqlite3_file *id){
+  unixFile *p = (unixFile*)id;
+  return (p->ctrlFlags & UNIXFILE_ZERO_DAMAGE) ? SQLITE_IOCAP_ZERO_DAMAGE : 0;
 }
 
 #ifndef SQLITE_OMIT_WAL
@@ -4568,6 +4592,7 @@ static int fillInUnixFile(
   const sqlite3_io_methods *pLockingStyle;
   unixFile *pNew = (unixFile *)pId;
   int rc = SQLITE_OK;
+  const char *zZeroDam;   /* Value of the zero_damage query parameter */
 
   assert( pNew->pInode==NULL );
 
@@ -4594,10 +4619,11 @@ static int fillInUnixFile(
   pNew->h = h;
   pNew->pVfs = pVfs;
   pNew->zPath = zFilename;
+  zZeroDam = sqlite3_uri_parameter(zFilename, "zero_damage");
+  if( zZeroDam==0 ) zZeroDam = "1";
+  pNew->ctrlFlags = atoi(zZeroDam) ? UNIXFILE_ZERO_DAMAGE : 1;
   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;
index 5e0667d188dcade58c5734fe06224a67fa3b4d91..ec4d062f9b8d4d4946a0bff64b4926597a18c0a2 100644 (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_ZERO_DAMAGE     0x10   /* True if SQLITE_IOCAP_ZERO_DAMAGE */
+
 /*
  * 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_ZERO_DAMAGE: {
+      winModeBit(pFile, WINFILE_ZERO_DAMAGE, (int*)pArg);
       return SQLITE_OK;
     }
     case SQLITE_FCNTL_VFSNAME: {
@@ -2212,9 +2233,9 @@ static int winSectorSize(sqlite3_file *id){
 ** Return a vector of device characteristics.
 */
 static int winDeviceCharacteristics(sqlite3_file *id){
-  UNUSED_PARAMETER(id);
+  winFile *p = (winFile*)id;
   return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
-         SQLITE_IOCAP_ZERO_DAMAGE;
+         ((p->ctrlFlags & WINFILE_ZERO_DAMAGE)?SQLITE_IOCAP_ZERO_DAMAGE:0);
 }
 
 #ifndef SQLITE_OMIT_WAL
@@ -3004,6 +3025,7 @@ static int winOpen(
   void *zConverted;              /* Filename in OS encoding */
   const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
   int cnt = 0;
+  const char *zZeroDam;          /* Value of zero_damage query parameter */
 
   /* If argument zPath is a NULL pointer, this function is required to open
   ** a temporary file. Use this buffer to store the file name in.
@@ -3179,6 +3201,9 @@ static int winOpen(
   pFile->pVfs = pVfs;
   pFile->pShm = 0;
   pFile->zPath = zName;
+  zZeroDam = sqlite3_uri_parameter(zName, "zero_damage");
+  if( zZeroDam==0 ) zZeroDam = "1";
+  pFile->ctrlFlags = atoi(zZeroDam) ? WINFILE_ZERO_DAMAGE : 1;
   pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
 
 #if SQLITE_OS_WINCE
index f7a2616baa9e8ac38834603593d6376de684c463..e036449d02de2011e3a53de1f29886c0de55921f 100644 (file)
@@ -774,6 +774,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_ZERO_DAMAGE] opcode is used to set or query the
+** persistent zero-damage setting.  The zero-damage setting determines
+** the [SQLITE_IOCAP_ZERO_DAMAGE] 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 
@@ -802,6 +811,7 @@ struct sqlite3_io_methods {
 #define SQLITE_FCNTL_PERSIST_WAL     10
 #define SQLITE_FCNTL_OVERWRITE       11
 #define SQLITE_FCNTL_VFSNAME         12
+#define SQLITE_FCNTL_ZERO_DAMAGE     13
 
 /*
 ** CAPI3REF: Mutex Handle
index c8f0fbd31b6c4838033957aebbb573176f06b008..3692bef9ca1199e8e054a0748d2f0b0b1e11030c 100644 (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;
index 798366a8c72ff0b423c955ba6b892ff3df4f0f65..b96b99ce8d4cd1eeadbef69fd8a476d2c6a88730 100644 (file)
@@ -5235,6 +5235,38 @@ static int file_control_persist_wal(
   return TCL_OK;  
 }
 
+/*
+** tclcmd:   file_control_zero_damage DB ZERO-DAMAGE-FLAG
+**
+** This TCL command runs the sqlite3_file_control interface with
+** the SQLITE_FCNTL_ZERO_DAMAGE opcode.
+*/
+static int file_control_zero_damage(
+  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 bDamage;
+  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], &bDamage) ) return TCL_ERROR;
+  rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_ZERO_DAMAGE,(void*)&bDamage);
+  sqlite3_snprintf(sizeof(z), z, "%d %d", rc, bDamage);
+  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_zero_damage",    file_control_zero_damage,     0   },
      { "file_control_vfsname",        file_control_vfsname,         0   },
      { "sqlite3_vfs_list",           vfs_list,     0   },
      { "sqlite3_create_function_v2", test_create_function_v2, 0 },
diff --git a/test/zerodamage.test b/test/zerodamage.test
new file mode 100644 (file)
index 0000000..bef82b2
--- /dev/null
@@ -0,0 +1,109 @@
+# 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_ZERO_DAMAGE property
+# and the SQLITE_FCNTL_ZERO_DAMAGE file-control for manipulating it.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix wal5
+
+# ZERO_DAMAGE defaults to true
+#
+do_test zerodamage-1.0 {
+  file_control_zero_damage db -1
+} {0 1}
+
+# Check the ability to turn zero-damage on and off.
+#
+do_test zerodamage-1.1 {
+  file_control_zero_damage db 0
+  file_control_zero_damage db -1
+} {0 0}
+do_test zerodamage-1.2 {
+  file_control_zero_damage db 1
+  file_control_zero_damage 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?zero_damage=1 -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_zero_damage 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?zero_damage=0 -uri 1
+  db eval {
+    UPDATE t1 SET y=randomblob(50) WHERE x=124;
+  }
+  concat [file_control_zero_damage db -1] [set ::max_journal_size]
+} {0 0 24704}
+
+# Run a WAL-mode transaction with ZERO_DAMAGE 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?zero_damage=1 -uri 1
+  db eval {
+     UPDATE t1 SET y=randomblob(50) WHERE x=124;
+  }
+  file size test.db-wal
+} {1080}
+
+# Repeat the previous with ZERO_DAMAGE off.  Verify that the WAL file
+# is padded.
+#
+do_test zerodamage-3.1 {
+  db close
+  sqlite3 db file:test.db?zero_damage=0 -uri 1
+  db eval {
+     UPDATE t1 SET y=randomblob(50) WHERE x=124;
+  }
+  file size test.db-wal
+} {8416}