]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a vfs backend that detects problems like the one addressed by (6043) and (6047...
authordanielk1977 <danielk1977@noemail.net>
Sat, 20 Dec 2008 18:33:59 +0000 (18:33 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Sat, 20 Dec 2008 18:33:59 +0000 (18:33 +0000)
FossilOrigin-Name: 49172e487610268662c39fc4038032779a41c47f

main.mk
manifest
manifest.uuid
src/test6.c
src/test_journal.c [new file with mode: 0644]
test/savepoint2.test

diff --git a/main.mk b/main.mk
index b693a6839a8d4a5baa80cfa6d9bdd1cb9ba5e3f4..c9f654cac0b082b364a97caee17477ac79639138 100644 (file)
--- a/main.mk
+++ b/main.mk
@@ -225,6 +225,7 @@ TESTSRC = \
   $(TOP)/src/test_devsym.c \
   $(TOP)/src/test_func.c \
   $(TOP)/src/test_hexio.c \
+  $(TOP)/src/test_journal.c \
   $(TOP)/src/test_malloc.c \
   $(TOP)/src/test_md5.c \
   $(TOP)/src/test_mutex.c \
index c1b1d3dfb06883cc61c972ca27f8bfe36236561f..04d5272b2ee7956a1f83d560f3847c4bf631d2a7 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Do\snot\suse\slong\slong\sconstants\sin\scode.\s\sTicket\s#3547.\s(CVS\s6048)
-D 2008-12-20T13:18:50
+C Add\sa\svfs\sbackend\sthat\sdetects\sproblems\slike\sthe\sone\saddressed\sby\s(6043)\sand\s(6047).\s(CVS\s6049)
+D 2008-12-20T18:33:59
 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
 F Makefile.in f7e4c81c347b04f7b0f1c1b081a168645d7b8af7
 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -83,7 +83,7 @@ F ext/rtree/tkt3363.test 6662237ea75bb431cd5d262dfc9535e1023315fc
 F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869
 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
 F ltmain.sh 09fe5815427dc7d0abb188bbcdf0e34896577210
-F main.mk f6eb58a66f942bf672ab58e74e30e72cad39b93f
+F main.mk 189d17c22bc35a9223f2de0eb9ac6e818439cef7
 F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
 F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
 F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -168,7 +168,7 @@ F src/test2.c 4e0ea288e1cf237f8ff26c8817f177f45486f4a6
 F src/test3.c 88a246b56b824275300e6c899634fbac1dc94b14
 F src/test4.c f79ab52d27ff49b784b631a42e2ccd52cfd5c84c
 F src/test5.c 162a1cea2105a2c460a3f39fa6919617b562a288
-F src/test6.c 11fc775cced479a169ff1e4be515d61f5ef4869a
+F src/test6.c 10025acfb5d978abc08b3ddbba8b2ce4dd6ca0c1
 F src/test7.c b94e68c2236de76889d82b8d7d8e00ad6a4d80b1
 F src/test8.c 3637439424d0d21ff2dcf9b015c30fcc1e7bcb24
 F src/test9.c 904ebe0ed1472d6bad17a81e2ecbfc20017dc237
@@ -179,6 +179,7 @@ F src/test_config.c 4f85387a52f3c7966c3ffab913e988a3830fe1af
 F src/test_devsym.c 9f4bc2551e267ce7aeda195f3897d0f30c5228f4
 F src/test_func.c a55c4d5479ff2eb5c0a22d4d88e9528ab59c953b
 F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f
+F src/test_journal.c 74b97d631841b0ebd55e54ba059f61299f537667
 F src/test_loadext.c 97dc8800e46a46ed002c2968572656f37e9c0dd9
 F src/test_malloc.c 5127337c9fb4c851a7f604c0170e0e5ca1fbfe33
 F src/test_md5.c 28209a4e2068711b5443c33104fe41f21d160071
@@ -492,7 +493,7 @@ F test/rowid.test 1c8fc43c60d273e6ea44dfb992db587f3164312c
 F test/rtree.test b85fd4f0861a40ca366ac195e363be2528dcfadf
 F test/safety.test b69e2b2dd5d52a3f78e216967086884bbc1a09c6
 F test/savepoint.test 24b7d67971c0b7a8d22ba1cabbfd846e72f21594
-F test/savepoint2.test 65fed3f179cff053e0a75864b1afc13e100fce1f
+F test/savepoint2.test e1c29bd8341a0214903455971833603852254279
 F test/savepoint3.test b3c9aa5af3f777ccb8b9e15597c75c93eb5bc369
 F test/savepoint4.test fd8850063e3c40565545f5c291e7f79a30591670
 F test/schema.test a8b000723375fd42c68d310091bdbd744fde647c
@@ -683,7 +684,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81
 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
-P 688336266f0aa5630f4f550ae3787a64f39f9cfa
-R c6607ded9f1ca70aef2682145ec9f3ad
-U drh
-Z 6d73f04848023c003aea708646f655a9
+P 51b3bfc3b9628ca4ec754fa7f23aef7302f890ff
+R 1cd5d078a6ea4c15c2f815a2234d69ba
+U danielk1977
+Z 18495ec6b55a95f7beb0c7b663238125
index 356a54992d9888c3549dcbf93cd6f244b6b1efde..b3d6bfa7ef9cdc0f07346a9ba1961eddde7cd1e1 100644 (file)
@@ -1 +1 @@
-51b3bfc3b9628ca4ec754fa7f23aef7302f890ff
\ No newline at end of file
+49172e487610268662c39fc4038032779a41c47f
\ No newline at end of file
index f95c2043eac0d3f4c1671809edd3bb0b290e9d62..6fc55b7ea63cae6db14f2e0d6349dbbe45516a15 100644 (file)
@@ -14,7 +14,7 @@
 ** the effect on the database file of an OS crash or power failure.  This
 ** is used to test the ability of SQLite to recover from those situations.
 **
-** $Id: test6.c,v 1.40 2008/12/09 01:32:03 drh Exp $
+** $Id: test6.c,v 1.41 2008/12/20 18:33:59 danielk1977 Exp $
 */
 #if SQLITE_TEST          /* This file is used for testing only */
 #include "sqliteInt.h"
@@ -864,6 +864,64 @@ static int devSymObjCmd(
   return TCL_OK;
 }
 
+/*
+** tclcmd: register_jt_vfs ?-default? PARENT-VFS
+*/
+static int jtObjCmd(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  int jt_register(char *, int);
+  char *zParent = 0;
+
+  if( objc!=2 && objc!=3 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");
+    return TCL_ERROR;
+  }
+  zParent = Tcl_GetString(objv[1]);
+  if( objc==3 ){
+    if( strcmp(zParent, "-default") ){
+      Tcl_AppendResult(interp, 
+          "bad option \"", zParent, "\": must be -default", 0
+      );
+      return TCL_ERROR;
+    }
+    zParent = Tcl_GetString(objv[2]);
+  }
+
+  if( !(*zParent) ){
+    zParent = 0;
+  }
+  if( jt_register(zParent, objc==3) ){
+    Tcl_AppendResult(interp, "Error in jt_register", 0);
+    return TCL_ERROR;
+  }
+
+  return TCL_OK;
+}
+
+/*
+** tclcmd: unregister_jt_vfs
+*/
+static int jtUnregisterObjCmd(
+  void * clientData,
+  Tcl_Interp *interp,
+  int objc,
+  Tcl_Obj *CONST objv[]
+){
+  void jt_unregister(void);
+
+  if( objc!=1 ){
+    Tcl_WrongNumArgs(interp, 1, objv, "");
+    return TCL_ERROR;
+  }
+
+  jt_unregister();
+  return TCL_OK;
+}
+
 #endif /* SQLITE_OMIT_DISKIO */
 
 /*
@@ -874,6 +932,8 @@ int Sqlitetest6_Init(Tcl_Interp *interp){
   Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
   Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
   Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
+  Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
+  Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
 #endif
   return TCL_OK;
 }
diff --git a/src/test_journal.c b/src/test_journal.c
new file mode 100644 (file)
index 0000000..73fc8b9
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+** 2008 Jan 22
+**
+** 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 contains code for a VFS layer that acts as a wrapper around
+** an existing VFS. The code in this file attempts to detect a specific
+** bug in SQLite - writing data to a database file page when:
+**
+**   a) the original page data is not stored in a synced portion of the
+**      journal file, and
+**   b) the page was not a free-list leaf page when the transaction was
+**      first opened.
+**
+** $Id: test_journal.c,v 1.1 2008/12/20 18:33:59 danielk1977 Exp $
+*/
+#if SQLITE_TEST          /* This file is used for testing only */
+
+#include "sqlite3.h"
+#include "sqliteInt.h"
+
+/*
+** Maximum pathname length supported by the jt backend.
+*/
+#define JT_MAX_PATHNAME 512
+
+/*
+** Name used to identify this VFS.
+*/
+#define JT_VFS_NAME "jt"
+
+typedef struct jt_file jt_file;
+struct jt_file {
+  sqlite3_file base;
+  const char *zName;       /* Name of open file */
+  int flags;               /* Flags the file was opened with */
+
+  /* The following are only used by database file file handles */
+  int eLock;               /* Current lock held on the file */
+  u32 nPage;               /* Size of file in pages when transaction started */
+  u32 nPagesize;           /* Page size when transaction started */
+  Bitvec *pWritable;       /* Bitvec of pages that may be written to the file */
+
+  jt_file *pNext;          /* All files are stored in a linked list */
+  sqlite3_file *pReal;     /* The file handle for the underlying vfs */
+};
+
+/*
+** Method declarations for jt_file.
+*/
+static int jtClose(sqlite3_file*);
+static int jtRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
+static int jtWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
+static int jtTruncate(sqlite3_file*, sqlite3_int64 size);
+static int jtSync(sqlite3_file*, int flags);
+static int jtFileSize(sqlite3_file*, sqlite3_int64 *pSize);
+static int jtLock(sqlite3_file*, int);
+static int jtUnlock(sqlite3_file*, int);
+static int jtCheckReservedLock(sqlite3_file*, int *);
+static int jtFileControl(sqlite3_file*, int op, void *pArg);
+static int jtSectorSize(sqlite3_file*);
+static int jtDeviceCharacteristics(sqlite3_file*);
+
+/*
+** Method declarations for jt_vfs.
+*/
+static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
+static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
+static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
+static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
+static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
+static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
+static void jtDlClose(sqlite3_vfs*, void*);
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
+static int jtSleep(sqlite3_vfs*, int microseconds);
+static int jtCurrentTime(sqlite3_vfs*, double*);
+
+static sqlite3_vfs jt_vfs = {
+  1,                             /* iVersion */
+  sizeof(jt_file),               /* szOsFile */
+  JT_MAX_PATHNAME,               /* mxPathname */
+  0,                             /* pNext */
+  JT_VFS_NAME,                   /* zName */
+  0,                             /* pAppData */
+  jtOpen,                        /* xOpen */
+  jtDelete,                      /* xDelete */
+  jtAccess,                      /* xAccess */
+  jtFullPathname,                /* xFullPathname */
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+  jtDlOpen,                      /* xDlOpen */
+  jtDlError,                     /* xDlError */
+  jtDlSym,                       /* xDlSym */
+  jtDlClose,                     /* xDlClose */
+#else
+  0,                             /* xDlOpen */
+  0,                             /* xDlError */
+  0,                             /* xDlSym */
+  0,                             /* xDlClose */
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+  jtRandomness,                  /* xRandomness */
+  jtSleep,                       /* xSleep */
+  jtCurrentTime                  /* xCurrentTime */
+};
+
+static sqlite3_io_methods jt_io_methods = {
+  1,                             /* iVersion */
+  jtClose,                       /* xClose */
+  jtRead,                        /* xRead */
+  jtWrite,                       /* xWrite */
+  jtTruncate,                    /* xTruncate */
+  jtSync,                        /* xSync */
+  jtFileSize,                    /* xFileSize */
+  jtLock,                        /* xLock */
+  jtUnlock,                      /* xUnlock */
+  jtCheckReservedLock,           /* xCheckReservedLock */
+  jtFileControl,                 /* xFileControl */
+  jtSectorSize,                  /* xSectorSize */
+  jtDeviceCharacteristics        /* xDeviceCharacteristics */
+};
+
+struct JtGlobal {
+  sqlite3_vfs *pVfs;
+  jt_file *pList;
+};
+static struct JtGlobal g = {0, 0};
+
+/*
+** Close an jt-file.
+*/
+static int jtClose(sqlite3_file *pFile){
+  jt_file **pp;
+  jt_file *p = (jt_file *)pFile;
+
+  if( p->zName ){
+    for(pp=&g.pList; *pp!=p; *pp=(*pp)->pNext);
+    *pp = p->pNext;
+  }
+
+  return sqlite3OsClose(p->pReal);
+}
+
+/*
+** Read data from an jt-file.
+*/
+static int jtRead(
+  sqlite3_file *pFile, 
+  void *zBuf, 
+  int iAmt, 
+  sqlite_int64 iOfst
+){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
+}
+
+
+static jt_file *locateDatabaseHandle(const char *zJournal){
+  jt_file *pMain;
+  for(pMain=g.pList; pMain; pMain=pMain->pNext){
+    int nName = strlen(zJournal) - strlen("-journal");
+    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
+     && (strlen(pMain->zName)==nName)
+     && 0==memcmp(pMain->zName, zJournal, nName)
+     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
+    ){
+      break;
+    }
+  }
+  return pMain;
+}
+
+
+static u32 decodeUint32(const unsigned char *z){
+  return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
+}
+
+static void readFreelist(jt_file *pMain){
+  sqlite3_file *p = pMain->pReal;
+  sqlite3_int64 iSize;
+
+  sqlite3OsFileSize(p, &iSize);
+  if( iSize>=pMain->nPagesize ){
+    unsigned char *zBuf = (unsigned char *)malloc(pMain->nPagesize);
+    u32 iTrunk;
+
+    sqlite3OsRead(p, zBuf, pMain->nPagesize, 0);
+    iTrunk = decodeUint32(&zBuf[32]);
+    while( iTrunk>0 ){
+      u32 nLeaf;
+      u32 iLeaf;
+      sqlite3OsRead(p, zBuf, pMain->nPagesize, (iTrunk-1)*pMain->nPagesize);
+      nLeaf = decodeUint32(&zBuf[4]);
+      for(iLeaf=0; iLeaf<nLeaf; iLeaf++){
+        u32 pgno = decodeUint32(&zBuf[8+4*iLeaf]);
+        sqlite3BitvecSet(pMain->pWritable, pgno);
+      }
+      iTrunk = decodeUint32(zBuf);
+    }
+
+    free(zBuf);
+  }
+}
+
+/*
+** The first argument, zBuf, points to a buffer containing a 28 byte
+** serialized journal header. This function deserializes four of the
+** integer fields contained in the journal header and writes their
+** values to the output variables.
+*/
+static int decodeJournalHdr(
+  const unsigned char *zBuf,         /* Input: 28 byte journal header */
+  u32 *pnRec,                        /* Out: Number of journalled records */
+  u32 *pnPage,                       /* Out: Original database page count */
+  u32 *pnSector,                     /* Out: Sector size in bytes */
+  u32 *pnPagesize                    /* Out: Page size in bytes */
+){
+  unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
+  if( memcmp(aMagic, zBuf, 8) ) return 1;
+  if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
+  if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
+  if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
+  if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);
+  return 0;
+}
+
+/*
+** Write data to an jt-file.
+*/
+static int jtWrite(
+  sqlite3_file *pFile, 
+  const void *zBuf, 
+  int iAmt, 
+  sqlite_int64 iOfst
+){
+  jt_file *p = (jt_file *)pFile;
+  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && iOfst==0 ){
+    jt_file *pMain = locateDatabaseHandle(p->zName);
+    assert( pMain );
+
+    if( decodeJournalHdr(zBuf, 0, &pMain->nPage, 0, &pMain->nPagesize) ){
+      /* Zeroing the first journal-file header. This is the end of a
+      ** transaction. */
+      sqlite3BitvecDestroy(pMain->pWritable);
+      pMain->pWritable = 0;
+    }else{
+      /* Writing the first journal header to a journal file. This happens
+      ** when a transaction is first started.  */
+      pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
+      readFreelist(pMain);
+    }
+  }
+
+  if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
+    u32 pgno;
+    assert( iAmt==p->nPagesize );
+    pgno = iOfst/p->nPagesize + 1;
+    assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
+  }
+
+  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+}
+
+/*
+** Truncate an jt-file.
+*/
+static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
+  jt_file *p = (jt_file *)pFile;
+  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){
+    /* Truncating a journal file. This is the end of a transaction. */
+    jt_file *pMain = locateDatabaseHandle(p->zName);
+    sqlite3BitvecDestroy(pMain->pWritable);
+    pMain->pWritable = 0;
+  }
+  return sqlite3OsTruncate(p->pReal, size);
+}
+
+/*
+** The first argument to this function is a handle open on a journal file.
+** This function reads the journal file and adds the page number for each
+** page in the journal to the Bitvec object passed as the second argument.
+*/
+static void readJournalFile(jt_file *p, jt_file *pMain){
+  unsigned char zBuf[28];
+  sqlite3_file *pReal = p->pReal;
+  sqlite3_int64 iOff = 0;
+  sqlite3_int64 iSize = 0;
+
+  sqlite3OsFileSize(p->pReal, &iSize);
+  while( iOff<iSize ){
+    u32 nRec, nPage, nSector, nPagesize;
+    u32 ii;
+    sqlite3OsRead(pReal, zBuf, 28, iOff);
+    if( decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize) ){
+      return;
+    }
+    iOff += nSector;
+    for(ii=0; ii<nRec && iOff<iSize; ii++){
+      u32 pgno;
+      sqlite3OsRead(pReal, zBuf, 4, iOff);
+      pgno = decodeUint32(zBuf);
+      iOff += (8 + pMain->nPagesize);
+      sqlite3BitvecSet(pMain->pWritable, pgno);
+    }
+
+    iOff = ((iOff + (nSector-1)) / nSector) * nSector;
+  }
+}
+
+/*
+** Sync an jt-file.
+*/
+static int jtSync(sqlite3_file *pFile, int flags){
+  jt_file *p = (jt_file *)pFile;
+
+  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
+    jt_file *pMain;                   /* The associated database file */
+
+    /* The journal file is being synced. At this point, we inspect the 
+    ** contents of the file up to this point and set each bit in the 
+    ** jt_file.pWritable bitvec of the main database file associated with
+    ** this journal file.
+    */
+    pMain = locateDatabaseHandle(p->zName);
+    assert(pMain);
+    assert(pMain->pWritable);
+
+    /* Set the bitvec values */
+    readJournalFile(p, pMain);
+  }
+
+  return sqlite3OsSync(p->pReal, flags);
+}
+
+/*
+** Return the current file-size of an jt-file.
+*/
+static int jtFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsFileSize(p->pReal, pSize);
+}
+
+/*
+** Lock an jt-file.
+*/
+static int jtLock(sqlite3_file *pFile, int eLock){
+  int rc;
+  jt_file *p = (jt_file *)pFile;
+  rc = sqlite3OsLock(p->pReal, eLock);
+  if( rc==SQLITE_OK && eLock>p->eLock ){
+    p->eLock = eLock;
+  }
+  return rc;
+}
+
+/*
+** Unlock an jt-file.
+*/
+static int jtUnlock(sqlite3_file *pFile, int eLock){
+  int rc;
+  jt_file *p = (jt_file *)pFile;
+  rc = sqlite3OsUnlock(p->pReal, eLock);
+  if( rc==SQLITE_OK && eLock<p->eLock ){
+    p->eLock = eLock;
+  }
+  return rc;
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an jt-file.
+*/
+static int jtCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsCheckReservedLock(p->pReal, pResOut);
+}
+
+/*
+** File control method. For custom operations on an jt-file.
+*/
+static int jtFileControl(sqlite3_file *pFile, int op, void *pArg){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsFileControl(p->pReal, op, pArg);
+}
+
+/*
+** Return the sector-size in bytes for an jt-file.
+*/
+static int jtSectorSize(sqlite3_file *pFile){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsSectorSize(p->pReal);
+}
+
+/*
+** Return the device characteristic flags supported by an jt-file.
+*/
+static int jtDeviceCharacteristics(sqlite3_file *pFile){
+  jt_file *p = (jt_file *)pFile;
+  return sqlite3OsDeviceCharacteristics(p->pReal);
+}
+
+/*
+** Open an jt file handle.
+*/
+static int jtOpen(
+  sqlite3_vfs *pVfs,
+  const char *zName,
+  sqlite3_file *pFile,
+  int flags,
+  int *pOutFlags
+){
+  int rc;
+  jt_file *p = (jt_file *)pFile;
+  p->pReal = (sqlite3_file *)&p[1];
+  rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
+  if( p->pReal->pMethods ){
+    pFile->pMethods = &jt_io_methods;
+    p->eLock = 0;
+    p->zName = zName;
+    p->flags = flags;
+    p->pNext = 0;
+    if( zName ){
+      p->pNext = g.pList;
+      g.pList = p;
+    }
+  }
+  return rc;
+}
+
+/*
+** Delete the file located at zPath. If the dirSync argument is true,
+** ensure the file-system modifications are synced to disk before
+** returning.
+*/
+static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+  int nPath = strlen(zPath);
+  if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
+    /* Deleting a journal file. The end of a transaction. */
+    jt_file *pMain = locateDatabaseHandle(zPath);
+    sqlite3BitvecDestroy(pMain->pWritable);
+    pMain->pWritable = 0;
+  }
+
+  return sqlite3OsDelete(g.pVfs, zPath, dirSync);
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int jtAccess(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int flags, 
+  int *pResOut
+){
+  return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (JT_MAX_PATHNAME+1) bytes.
+*/
+static int jtFullPathname(
+  sqlite3_vfs *pVfs, 
+  const char *zPath, 
+  int nOut, 
+  char *zOut
+){
+  return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
+}
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+  return sqlite3OsDlOpen(g.pVfs, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated 
+** with dynamic libraries.
+*/
+static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+  sqlite3OsDlError(g.pVfs, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
+  return sqlite3OsDlSym(g.pVfs, p, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
+  sqlite3OsDlClose(g.pVfs, pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of 
+** random data.
+*/
+static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+  return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds 
+** actually slept.
+*/
+static int jtSleep(sqlite3_vfs *pVfs, int nMicro){
+  return sqlite3OsSleep(g.pVfs, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+  return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
+}
+
+int jt_register(char *zWrap, int isDefault){
+  g.pVfs = sqlite3_vfs_find(zWrap);
+  if( g.pVfs==0 ){
+    return SQLITE_ERROR;
+  }
+  jt_vfs.szOsFile += g.pVfs->szOsFile;
+  sqlite3_vfs_register(&jt_vfs, isDefault);
+  return SQLITE_OK;
+}
+
+void jt_unregister(){
+  sqlite3_vfs_unregister(&jt_vfs);
+}
+
+#endif
index dff8a8e85f5091eb44c3a0e13994bcd7ad86c0d3..e7d4f8b2024d0475d6cee70397def4d64f8a33eb 100644 (file)
@@ -9,7 +9,7 @@
 #
 #***********************************************************************
 #
-# $Id: savepoint2.test,v 1.2 2008/12/18 18:31:39 danielk1977 Exp $
+# $Id: savepoint2.test,v 1.3 2008/12/20 18:33:59 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -18,6 +18,10 @@ source $testdir/tester.tcl
 # avtrans.test.
 #
 
+db close
+register_jt_vfs -default ""
+sqlite3 db test.db -vfs jt
+
 proc signature {} {
   return [db eval {SELECT count(*), md5sum(x) FROM t3}]
 }
@@ -145,5 +149,7 @@ for {set ii 2} {$ii < ($iterations+2)} {incr ii} {
 unset -nocomplain ::sig
 unset -nocomplain SQL
 
+unregister_jt_vfs
+
 finish_test