/ Check-in [49172e48]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add a vfs backend that detects problems like the one addressed by (6043) and (6047). (CVS 6049)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 49172e487610268662c39fc4038032779a41c47f
User & Date: danielk1977 2008-12-20 18:33:59
Context
2008-12-21
03:51
Continue refactoring where.c in preparation for installing OR-clause optimizations. (CVS 6050) check-in: 778e91dd user: drh tags: trunk
2008-12-20
18:33
Add a vfs backend that detects problems like the one addressed by (6043) and (6047). (CVS 6049) check-in: 49172e48 user: danielk1977 tags: trunk
13:18
Do not use long long constants in code. Ticket #3547. (CVS 6048) check-in: 51b3bfc3 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to main.mk.

   221    221     $(TOP)/src/test_autoext.c \
   222    222     $(TOP)/src/test_async.c \
   223    223     $(TOP)/src/test_btree.c \
   224    224     $(TOP)/src/test_config.c \
   225    225     $(TOP)/src/test_devsym.c \
   226    226     $(TOP)/src/test_func.c \
   227    227     $(TOP)/src/test_hexio.c \
          228  +  $(TOP)/src/test_journal.c \
   228    229     $(TOP)/src/test_malloc.c \
   229    230     $(TOP)/src/test_md5.c \
   230    231     $(TOP)/src/test_mutex.c \
   231    232     $(TOP)/src/test_onefile.c \
   232    233     $(TOP)/src/test_osinst.c \
   233    234     $(TOP)/src/test_pcache.c \
   234    235     $(TOP)/src/test_schema.c \

Changes to src/test6.c.

    10     10   **
    11     11   ******************************************************************************
    12     12   **
    13     13   ** This file contains code that modified the OS layer in order to simulate
    14     14   ** the effect on the database file of an OS crash or power failure.  This
    15     15   ** is used to test the ability of SQLite to recover from those situations.
    16     16   **
    17         -** $Id: test6.c,v 1.40 2008/12/09 01:32:03 drh Exp $
           17  +** $Id: test6.c,v 1.41 2008/12/20 18:33:59 danielk1977 Exp $
    18     18   */
    19     19   #if SQLITE_TEST          /* This file is used for testing only */
    20     20   #include "sqliteInt.h"
    21     21   #include "tcl.h"
    22     22   
    23     23   #ifndef SQLITE_OMIT_DISKIO  /* This file is a no-op if disk I/O is disabled */
    24     24   
................................................................................
   859    859     if( processDevSymArgs(interp, objc-1, &objv[1], &iDc, &iSectorSize) ){
   860    860       return TCL_ERROR;
   861    861     }
   862    862     devsym_register(iDc, iSectorSize);
   863    863   
   864    864     return TCL_OK;
   865    865   }
          866  +
          867  +/*
          868  +** tclcmd: register_jt_vfs ?-default? PARENT-VFS
          869  +*/
          870  +static int jtObjCmd(
          871  +  void * clientData,
          872  +  Tcl_Interp *interp,
          873  +  int objc,
          874  +  Tcl_Obj *CONST objv[]
          875  +){
          876  +  int jt_register(char *, int);
          877  +  char *zParent = 0;
          878  +
          879  +  if( objc!=2 && objc!=3 ){
          880  +    Tcl_WrongNumArgs(interp, 1, objv, "?-default? PARENT-VFS");
          881  +    return TCL_ERROR;
          882  +  }
          883  +  zParent = Tcl_GetString(objv[1]);
          884  +  if( objc==3 ){
          885  +    if( strcmp(zParent, "-default") ){
          886  +      Tcl_AppendResult(interp, 
          887  +          "bad option \"", zParent, "\": must be -default", 0
          888  +      );
          889  +      return TCL_ERROR;
          890  +    }
          891  +    zParent = Tcl_GetString(objv[2]);
          892  +  }
          893  +
          894  +  if( !(*zParent) ){
          895  +    zParent = 0;
          896  +  }
          897  +  if( jt_register(zParent, objc==3) ){
          898  +    Tcl_AppendResult(interp, "Error in jt_register", 0);
          899  +    return TCL_ERROR;
          900  +  }
          901  +
          902  +  return TCL_OK;
          903  +}
          904  +
          905  +/*
          906  +** tclcmd: unregister_jt_vfs
          907  +*/
          908  +static int jtUnregisterObjCmd(
          909  +  void * clientData,
          910  +  Tcl_Interp *interp,
          911  +  int objc,
          912  +  Tcl_Obj *CONST objv[]
          913  +){
          914  +  void jt_unregister(void);
          915  +
          916  +  if( objc!=1 ){
          917  +    Tcl_WrongNumArgs(interp, 1, objv, "");
          918  +    return TCL_ERROR;
          919  +  }
          920  +
          921  +  jt_unregister();
          922  +  return TCL_OK;
          923  +}
   866    924   
   867    925   #endif /* SQLITE_OMIT_DISKIO */
   868    926   
   869    927   /*
   870    928   ** This procedure registers the TCL procedures defined in this file.
   871    929   */
   872    930   int Sqlitetest6_Init(Tcl_Interp *interp){
   873    931   #ifndef SQLITE_OMIT_DISKIO
   874    932     Tcl_CreateObjCommand(interp, "sqlite3_crash_enable", crashEnableCmd, 0, 0);
   875    933     Tcl_CreateObjCommand(interp, "sqlite3_crashparams", crashParamsObjCmd, 0, 0);
   876    934     Tcl_CreateObjCommand(interp, "sqlite3_simulate_device", devSymObjCmd, 0, 0);
          935  +  Tcl_CreateObjCommand(interp, "register_jt_vfs", jtObjCmd, 0, 0);
          936  +  Tcl_CreateObjCommand(interp, "unregister_jt_vfs", jtUnregisterObjCmd, 0, 0);
   877    937   #endif
   878    938     return TCL_OK;
   879    939   }
   880    940   
   881    941   #endif /* SQLITE_TEST */

Added src/test_journal.c.

            1  +/*
            2  +** 2008 Jan 22
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +******************************************************************************
           12  +**
           13  +** This file contains code for a VFS layer that acts as a wrapper around
           14  +** an existing VFS. The code in this file attempts to detect a specific
           15  +** bug in SQLite - writing data to a database file page when:
           16  +**
           17  +**   a) the original page data is not stored in a synced portion of the
           18  +**      journal file, and
           19  +**   b) the page was not a free-list leaf page when the transaction was
           20  +**      first opened.
           21  +**
           22  +** $Id: test_journal.c,v 1.1 2008/12/20 18:33:59 danielk1977 Exp $
           23  +*/
           24  +#if SQLITE_TEST          /* This file is used for testing only */
           25  +
           26  +#include "sqlite3.h"
           27  +#include "sqliteInt.h"
           28  +
           29  +/*
           30  +** Maximum pathname length supported by the jt backend.
           31  +*/
           32  +#define JT_MAX_PATHNAME 512
           33  +
           34  +/*
           35  +** Name used to identify this VFS.
           36  +*/
           37  +#define JT_VFS_NAME "jt"
           38  +
           39  +typedef struct jt_file jt_file;
           40  +struct jt_file {
           41  +  sqlite3_file base;
           42  +  const char *zName;       /* Name of open file */
           43  +  int flags;               /* Flags the file was opened with */
           44  +
           45  +  /* The following are only used by database file file handles */
           46  +  int eLock;               /* Current lock held on the file */
           47  +  u32 nPage;               /* Size of file in pages when transaction started */
           48  +  u32 nPagesize;           /* Page size when transaction started */
           49  +  Bitvec *pWritable;       /* Bitvec of pages that may be written to the file */
           50  +
           51  +  jt_file *pNext;          /* All files are stored in a linked list */
           52  +  sqlite3_file *pReal;     /* The file handle for the underlying vfs */
           53  +};
           54  +
           55  +/*
           56  +** Method declarations for jt_file.
           57  +*/
           58  +static int jtClose(sqlite3_file*);
           59  +static int jtRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
           60  +static int jtWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
           61  +static int jtTruncate(sqlite3_file*, sqlite3_int64 size);
           62  +static int jtSync(sqlite3_file*, int flags);
           63  +static int jtFileSize(sqlite3_file*, sqlite3_int64 *pSize);
           64  +static int jtLock(sqlite3_file*, int);
           65  +static int jtUnlock(sqlite3_file*, int);
           66  +static int jtCheckReservedLock(sqlite3_file*, int *);
           67  +static int jtFileControl(sqlite3_file*, int op, void *pArg);
           68  +static int jtSectorSize(sqlite3_file*);
           69  +static int jtDeviceCharacteristics(sqlite3_file*);
           70  +
           71  +/*
           72  +** Method declarations for jt_vfs.
           73  +*/
           74  +static int jtOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
           75  +static int jtDelete(sqlite3_vfs*, const char *zName, int syncDir);
           76  +static int jtAccess(sqlite3_vfs*, const char *zName, int flags, int *);
           77  +static int jtFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
           78  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
           79  +static void *jtDlOpen(sqlite3_vfs*, const char *zFilename);
           80  +static void jtDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
           81  +static void (*jtDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
           82  +static void jtDlClose(sqlite3_vfs*, void*);
           83  +#endif /* SQLITE_OMIT_LOAD_EXTENSION */
           84  +static int jtRandomness(sqlite3_vfs*, int nByte, char *zOut);
           85  +static int jtSleep(sqlite3_vfs*, int microseconds);
           86  +static int jtCurrentTime(sqlite3_vfs*, double*);
           87  +
           88  +static sqlite3_vfs jt_vfs = {
           89  +  1,                             /* iVersion */
           90  +  sizeof(jt_file),               /* szOsFile */
           91  +  JT_MAX_PATHNAME,               /* mxPathname */
           92  +  0,                             /* pNext */
           93  +  JT_VFS_NAME,                   /* zName */
           94  +  0,                             /* pAppData */
           95  +  jtOpen,                        /* xOpen */
           96  +  jtDelete,                      /* xDelete */
           97  +  jtAccess,                      /* xAccess */
           98  +  jtFullPathname,                /* xFullPathname */
           99  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
          100  +  jtDlOpen,                      /* xDlOpen */
          101  +  jtDlError,                     /* xDlError */
          102  +  jtDlSym,                       /* xDlSym */
          103  +  jtDlClose,                     /* xDlClose */
          104  +#else
          105  +  0,                             /* xDlOpen */
          106  +  0,                             /* xDlError */
          107  +  0,                             /* xDlSym */
          108  +  0,                             /* xDlClose */
          109  +#endif /* SQLITE_OMIT_LOAD_EXTENSION */
          110  +  jtRandomness,                  /* xRandomness */
          111  +  jtSleep,                       /* xSleep */
          112  +  jtCurrentTime                  /* xCurrentTime */
          113  +};
          114  +
          115  +static sqlite3_io_methods jt_io_methods = {
          116  +  1,                             /* iVersion */
          117  +  jtClose,                       /* xClose */
          118  +  jtRead,                        /* xRead */
          119  +  jtWrite,                       /* xWrite */
          120  +  jtTruncate,                    /* xTruncate */
          121  +  jtSync,                        /* xSync */
          122  +  jtFileSize,                    /* xFileSize */
          123  +  jtLock,                        /* xLock */
          124  +  jtUnlock,                      /* xUnlock */
          125  +  jtCheckReservedLock,           /* xCheckReservedLock */
          126  +  jtFileControl,                 /* xFileControl */
          127  +  jtSectorSize,                  /* xSectorSize */
          128  +  jtDeviceCharacteristics        /* xDeviceCharacteristics */
          129  +};
          130  +
          131  +struct JtGlobal {
          132  +  sqlite3_vfs *pVfs;
          133  +  jt_file *pList;
          134  +};
          135  +static struct JtGlobal g = {0, 0};
          136  +
          137  +/*
          138  +** Close an jt-file.
          139  +*/
          140  +static int jtClose(sqlite3_file *pFile){
          141  +  jt_file **pp;
          142  +  jt_file *p = (jt_file *)pFile;
          143  +
          144  +  if( p->zName ){
          145  +    for(pp=&g.pList; *pp!=p; *pp=(*pp)->pNext);
          146  +    *pp = p->pNext;
          147  +  }
          148  +
          149  +  return sqlite3OsClose(p->pReal);
          150  +}
          151  +
          152  +/*
          153  +** Read data from an jt-file.
          154  +*/
          155  +static int jtRead(
          156  +  sqlite3_file *pFile, 
          157  +  void *zBuf, 
          158  +  int iAmt, 
          159  +  sqlite_int64 iOfst
          160  +){
          161  +  jt_file *p = (jt_file *)pFile;
          162  +  return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
          163  +}
          164  +
          165  +
          166  +static jt_file *locateDatabaseHandle(const char *zJournal){
          167  +  jt_file *pMain;
          168  +  for(pMain=g.pList; pMain; pMain=pMain->pNext){
          169  +    int nName = strlen(zJournal) - strlen("-journal");
          170  +    if( (pMain->flags&SQLITE_OPEN_MAIN_DB)
          171  +     && (strlen(pMain->zName)==nName)
          172  +     && 0==memcmp(pMain->zName, zJournal, nName)
          173  +     && (pMain->eLock>=SQLITE_LOCK_RESERVED)
          174  +    ){
          175  +      break;
          176  +    }
          177  +  }
          178  +  return pMain;
          179  +}
          180  +
          181  +
          182  +static u32 decodeUint32(const unsigned char *z){
          183  +  return (z[0]<<24) + (z[1]<<16) + (z[2]<<8) + z[3];
          184  +}
          185  +
          186  +static void readFreelist(jt_file *pMain){
          187  +  sqlite3_file *p = pMain->pReal;
          188  +  sqlite3_int64 iSize;
          189  +
          190  +  sqlite3OsFileSize(p, &iSize);
          191  +  if( iSize>=pMain->nPagesize ){
          192  +    unsigned char *zBuf = (unsigned char *)malloc(pMain->nPagesize);
          193  +    u32 iTrunk;
          194  +
          195  +    sqlite3OsRead(p, zBuf, pMain->nPagesize, 0);
          196  +    iTrunk = decodeUint32(&zBuf[32]);
          197  +    while( iTrunk>0 ){
          198  +      u32 nLeaf;
          199  +      u32 iLeaf;
          200  +      sqlite3OsRead(p, zBuf, pMain->nPagesize, (iTrunk-1)*pMain->nPagesize);
          201  +      nLeaf = decodeUint32(&zBuf[4]);
          202  +      for(iLeaf=0; iLeaf<nLeaf; iLeaf++){
          203  +        u32 pgno = decodeUint32(&zBuf[8+4*iLeaf]);
          204  +        sqlite3BitvecSet(pMain->pWritable, pgno);
          205  +      }
          206  +      iTrunk = decodeUint32(zBuf);
          207  +    }
          208  +
          209  +    free(zBuf);
          210  +  }
          211  +}
          212  +
          213  +/*
          214  +** The first argument, zBuf, points to a buffer containing a 28 byte
          215  +** serialized journal header. This function deserializes four of the
          216  +** integer fields contained in the journal header and writes their
          217  +** values to the output variables.
          218  +*/
          219  +static int decodeJournalHdr(
          220  +  const unsigned char *zBuf,         /* Input: 28 byte journal header */
          221  +  u32 *pnRec,                        /* Out: Number of journalled records */
          222  +  u32 *pnPage,                       /* Out: Original database page count */
          223  +  u32 *pnSector,                     /* Out: Sector size in bytes */
          224  +  u32 *pnPagesize                    /* Out: Page size in bytes */
          225  +){
          226  +  unsigned char aMagic[] = { 0xd9, 0xd5, 0x05, 0xf9, 0x20, 0xa1, 0x63, 0xd7 };
          227  +  if( memcmp(aMagic, zBuf, 8) ) return 1;
          228  +  if( pnRec ) *pnRec = decodeUint32(&zBuf[8]);
          229  +  if( pnPage ) *pnPage = decodeUint32(&zBuf[16]);
          230  +  if( pnSector ) *pnSector = decodeUint32(&zBuf[20]);
          231  +  if( pnPagesize ) *pnPagesize = decodeUint32(&zBuf[24]);
          232  +  return 0;
          233  +}
          234  +
          235  +/*
          236  +** Write data to an jt-file.
          237  +*/
          238  +static int jtWrite(
          239  +  sqlite3_file *pFile, 
          240  +  const void *zBuf, 
          241  +  int iAmt, 
          242  +  sqlite_int64 iOfst
          243  +){
          244  +  jt_file *p = (jt_file *)pFile;
          245  +  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && iOfst==0 ){
          246  +    jt_file *pMain = locateDatabaseHandle(p->zName);
          247  +    assert( pMain );
          248  +
          249  +    if( decodeJournalHdr(zBuf, 0, &pMain->nPage, 0, &pMain->nPagesize) ){
          250  +      /* Zeroing the first journal-file header. This is the end of a
          251  +      ** transaction. */
          252  +      sqlite3BitvecDestroy(pMain->pWritable);
          253  +      pMain->pWritable = 0;
          254  +    }else{
          255  +      /* Writing the first journal header to a journal file. This happens
          256  +      ** when a transaction is first started.  */
          257  +      pMain->pWritable = sqlite3BitvecCreate(pMain->nPage);
          258  +      readFreelist(pMain);
          259  +    }
          260  +  }
          261  +
          262  +  if( p->flags&SQLITE_OPEN_MAIN_DB && p->pWritable ){
          263  +    u32 pgno;
          264  +    assert( iAmt==p->nPagesize );
          265  +    pgno = iOfst/p->nPagesize + 1;
          266  +    assert( pgno>p->nPage || sqlite3BitvecTest(p->pWritable, pgno) );
          267  +  }
          268  +
          269  +  return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
          270  +}
          271  +
          272  +/*
          273  +** Truncate an jt-file.
          274  +*/
          275  +static int jtTruncate(sqlite3_file *pFile, sqlite_int64 size){
          276  +  jt_file *p = (jt_file *)pFile;
          277  +  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL && size==0 ){
          278  +    /* Truncating a journal file. This is the end of a transaction. */
          279  +    jt_file *pMain = locateDatabaseHandle(p->zName);
          280  +    sqlite3BitvecDestroy(pMain->pWritable);
          281  +    pMain->pWritable = 0;
          282  +  }
          283  +  return sqlite3OsTruncate(p->pReal, size);
          284  +}
          285  +
          286  +/*
          287  +** The first argument to this function is a handle open on a journal file.
          288  +** This function reads the journal file and adds the page number for each
          289  +** page in the journal to the Bitvec object passed as the second argument.
          290  +*/
          291  +static void readJournalFile(jt_file *p, jt_file *pMain){
          292  +  unsigned char zBuf[28];
          293  +  sqlite3_file *pReal = p->pReal;
          294  +  sqlite3_int64 iOff = 0;
          295  +  sqlite3_int64 iSize = 0;
          296  +
          297  +  sqlite3OsFileSize(p->pReal, &iSize);
          298  +  while( iOff<iSize ){
          299  +    u32 nRec, nPage, nSector, nPagesize;
          300  +    u32 ii;
          301  +    sqlite3OsRead(pReal, zBuf, 28, iOff);
          302  +    if( decodeJournalHdr(zBuf, &nRec, &nPage, &nSector, &nPagesize) ){
          303  +      return;
          304  +    }
          305  +    iOff += nSector;
          306  +    for(ii=0; ii<nRec && iOff<iSize; ii++){
          307  +      u32 pgno;
          308  +      sqlite3OsRead(pReal, zBuf, 4, iOff);
          309  +      pgno = decodeUint32(zBuf);
          310  +      iOff += (8 + pMain->nPagesize);
          311  +      sqlite3BitvecSet(pMain->pWritable, pgno);
          312  +    }
          313  +
          314  +    iOff = ((iOff + (nSector-1)) / nSector) * nSector;
          315  +  }
          316  +}
          317  +
          318  +/*
          319  +** Sync an jt-file.
          320  +*/
          321  +static int jtSync(sqlite3_file *pFile, int flags){
          322  +  jt_file *p = (jt_file *)pFile;
          323  +
          324  +  if( p->flags&SQLITE_OPEN_MAIN_JOURNAL ){
          325  +    jt_file *pMain;                   /* The associated database file */
          326  +
          327  +    /* The journal file is being synced. At this point, we inspect the 
          328  +    ** contents of the file up to this point and set each bit in the 
          329  +    ** jt_file.pWritable bitvec of the main database file associated with
          330  +    ** this journal file.
          331  +    */
          332  +    pMain = locateDatabaseHandle(p->zName);
          333  +    assert(pMain);
          334  +    assert(pMain->pWritable);
          335  +
          336  +    /* Set the bitvec values */
          337  +    readJournalFile(p, pMain);
          338  +  }
          339  +
          340  +  return sqlite3OsSync(p->pReal, flags);
          341  +}
          342  +
          343  +/*
          344  +** Return the current file-size of an jt-file.
          345  +*/
          346  +static int jtFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
          347  +  jt_file *p = (jt_file *)pFile;
          348  +  return sqlite3OsFileSize(p->pReal, pSize);
          349  +}
          350  +
          351  +/*
          352  +** Lock an jt-file.
          353  +*/
          354  +static int jtLock(sqlite3_file *pFile, int eLock){
          355  +  int rc;
          356  +  jt_file *p = (jt_file *)pFile;
          357  +  rc = sqlite3OsLock(p->pReal, eLock);
          358  +  if( rc==SQLITE_OK && eLock>p->eLock ){
          359  +    p->eLock = eLock;
          360  +  }
          361  +  return rc;
          362  +}
          363  +
          364  +/*
          365  +** Unlock an jt-file.
          366  +*/
          367  +static int jtUnlock(sqlite3_file *pFile, int eLock){
          368  +  int rc;
          369  +  jt_file *p = (jt_file *)pFile;
          370  +  rc = sqlite3OsUnlock(p->pReal, eLock);
          371  +  if( rc==SQLITE_OK && eLock<p->eLock ){
          372  +    p->eLock = eLock;
          373  +  }
          374  +  return rc;
          375  +}
          376  +
          377  +/*
          378  +** Check if another file-handle holds a RESERVED lock on an jt-file.
          379  +*/
          380  +static int jtCheckReservedLock(sqlite3_file *pFile, int *pResOut){
          381  +  jt_file *p = (jt_file *)pFile;
          382  +  return sqlite3OsCheckReservedLock(p->pReal, pResOut);
          383  +}
          384  +
          385  +/*
          386  +** File control method. For custom operations on an jt-file.
          387  +*/
          388  +static int jtFileControl(sqlite3_file *pFile, int op, void *pArg){
          389  +  jt_file *p = (jt_file *)pFile;
          390  +  return sqlite3OsFileControl(p->pReal, op, pArg);
          391  +}
          392  +
          393  +/*
          394  +** Return the sector-size in bytes for an jt-file.
          395  +*/
          396  +static int jtSectorSize(sqlite3_file *pFile){
          397  +  jt_file *p = (jt_file *)pFile;
          398  +  return sqlite3OsSectorSize(p->pReal);
          399  +}
          400  +
          401  +/*
          402  +** Return the device characteristic flags supported by an jt-file.
          403  +*/
          404  +static int jtDeviceCharacteristics(sqlite3_file *pFile){
          405  +  jt_file *p = (jt_file *)pFile;
          406  +  return sqlite3OsDeviceCharacteristics(p->pReal);
          407  +}
          408  +
          409  +/*
          410  +** Open an jt file handle.
          411  +*/
          412  +static int jtOpen(
          413  +  sqlite3_vfs *pVfs,
          414  +  const char *zName,
          415  +  sqlite3_file *pFile,
          416  +  int flags,
          417  +  int *pOutFlags
          418  +){
          419  +  int rc;
          420  +  jt_file *p = (jt_file *)pFile;
          421  +  p->pReal = (sqlite3_file *)&p[1];
          422  +  rc = sqlite3OsOpen(g.pVfs, zName, p->pReal, flags, pOutFlags);
          423  +  if( p->pReal->pMethods ){
          424  +    pFile->pMethods = &jt_io_methods;
          425  +    p->eLock = 0;
          426  +    p->zName = zName;
          427  +    p->flags = flags;
          428  +    p->pNext = 0;
          429  +    if( zName ){
          430  +      p->pNext = g.pList;
          431  +      g.pList = p;
          432  +    }
          433  +  }
          434  +  return rc;
          435  +}
          436  +
          437  +/*
          438  +** Delete the file located at zPath. If the dirSync argument is true,
          439  +** ensure the file-system modifications are synced to disk before
          440  +** returning.
          441  +*/
          442  +static int jtDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
          443  +  int nPath = strlen(zPath);
          444  +  if( nPath>8 && 0==strcmp("-journal", &zPath[nPath-8]) ){
          445  +    /* Deleting a journal file. The end of a transaction. */
          446  +    jt_file *pMain = locateDatabaseHandle(zPath);
          447  +    sqlite3BitvecDestroy(pMain->pWritable);
          448  +    pMain->pWritable = 0;
          449  +  }
          450  +
          451  +  return sqlite3OsDelete(g.pVfs, zPath, dirSync);
          452  +}
          453  +
          454  +/*
          455  +** Test for access permissions. Return true if the requested permission
          456  +** is available, or false otherwise.
          457  +*/
          458  +static int jtAccess(
          459  +  sqlite3_vfs *pVfs, 
          460  +  const char *zPath, 
          461  +  int flags, 
          462  +  int *pResOut
          463  +){
          464  +  return sqlite3OsAccess(g.pVfs, zPath, flags, pResOut);
          465  +}
          466  +
          467  +/*
          468  +** Populate buffer zOut with the full canonical pathname corresponding
          469  +** to the pathname in zPath. zOut is guaranteed to point to a buffer
          470  +** of at least (JT_MAX_PATHNAME+1) bytes.
          471  +*/
          472  +static int jtFullPathname(
          473  +  sqlite3_vfs *pVfs, 
          474  +  const char *zPath, 
          475  +  int nOut, 
          476  +  char *zOut
          477  +){
          478  +  return sqlite3OsFullPathname(g.pVfs, zPath, nOut, zOut);
          479  +}
          480  +
          481  +#ifndef SQLITE_OMIT_LOAD_EXTENSION
          482  +/*
          483  +** Open the dynamic library located at zPath and return a handle.
          484  +*/
          485  +static void *jtDlOpen(sqlite3_vfs *pVfs, const char *zPath){
          486  +  return sqlite3OsDlOpen(g.pVfs, zPath);
          487  +}
          488  +
          489  +/*
          490  +** Populate the buffer zErrMsg (size nByte bytes) with a human readable
          491  +** utf-8 string describing the most recent error encountered associated 
          492  +** with dynamic libraries.
          493  +*/
          494  +static void jtDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
          495  +  sqlite3OsDlError(g.pVfs, nByte, zErrMsg);
          496  +}
          497  +
          498  +/*
          499  +** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
          500  +*/
          501  +static void (*jtDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
          502  +  return sqlite3OsDlSym(g.pVfs, p, zSym);
          503  +}
          504  +
          505  +/*
          506  +** Close the dynamic library handle pHandle.
          507  +*/
          508  +static void jtDlClose(sqlite3_vfs *pVfs, void *pHandle){
          509  +  sqlite3OsDlClose(g.pVfs, pHandle);
          510  +}
          511  +#endif /* SQLITE_OMIT_LOAD_EXTENSION */
          512  +
          513  +/*
          514  +** Populate the buffer pointed to by zBufOut with nByte bytes of 
          515  +** random data.
          516  +*/
          517  +static int jtRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
          518  +  return sqlite3OsRandomness(g.pVfs, nByte, zBufOut);
          519  +}
          520  +
          521  +/*
          522  +** Sleep for nMicro microseconds. Return the number of microseconds 
          523  +** actually slept.
          524  +*/
          525  +static int jtSleep(sqlite3_vfs *pVfs, int nMicro){
          526  +  return sqlite3OsSleep(g.pVfs, nMicro);
          527  +}
          528  +
          529  +/*
          530  +** Return the current time as a Julian Day number in *pTimeOut.
          531  +*/
          532  +static int jtCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
          533  +  return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
          534  +}
          535  +
          536  +int jt_register(char *zWrap, int isDefault){
          537  +  g.pVfs = sqlite3_vfs_find(zWrap);
          538  +  if( g.pVfs==0 ){
          539  +    return SQLITE_ERROR;
          540  +  }
          541  +  jt_vfs.szOsFile += g.pVfs->szOsFile;
          542  +  sqlite3_vfs_register(&jt_vfs, isDefault);
          543  +  return SQLITE_OK;
          544  +}
          545  +
          546  +void jt_unregister(){
          547  +  sqlite3_vfs_unregister(&jt_vfs);
          548  +}
          549  +
          550  +#endif

Changes to test/savepoint2.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: savepoint2.test,v 1.2 2008/12/18 18:31:39 danielk1977 Exp $
           12  +# $Id: savepoint2.test,v 1.3 2008/12/20 18:33:59 danielk1977 Exp $
    13     13   
    14     14   set testdir [file dirname $argv0]
    15     15   source $testdir/tester.tcl
    16     16   
    17     17   # Tests in this file are quite similar to those run by trans.test and
    18     18   # avtrans.test.
    19     19   #
    20     20   
           21  +db close
           22  +register_jt_vfs -default ""
           23  +sqlite3 db test.db -vfs jt
           24  +
    21     25   proc signature {} {
    22     26     return [db eval {SELECT count(*), md5sum(x) FROM t3}]
    23     27   }
    24     28   
    25     29   do_test savepoint2-1 {
    26     30     execsql {
    27     31       PRAGMA cache_size=10;
................................................................................
   140    144       sqlite3_get_autocommit db
   141    145     } {1}
   142    146     integrity_check savepoint2-$ii.6.1
   143    147   }
   144    148   
   145    149   unset -nocomplain ::sig
   146    150   unset -nocomplain SQL
          151  +
          152  +unregister_jt_vfs
   147    153   
   148    154   finish_test
   149    155