/ Check-in [637f51dd]
Login

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

Overview
Comment:Only pass -1 as the size parameter to xShmGet when the caller does not care what size mapping is returned. Only call xShmSize when the underlying allocation (not the mapping) should be resized.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 637f51ddf9e4a57e1f171b6d2c6c93152074267e
User & Date: dan 2010-05-06 18:04:51
Context
2010-05-06
18:27
If recovery is run before a checkpoint, change back to a CHECKPOINT lock before performing the actual checkpoint. check-in: dc98ee16 user: dan tags: trunk
18:04
Only pass -1 as the size parameter to xShmGet when the caller does not care what size mapping is returned. Only call xShmSize when the underlying allocation (not the mapping) should be resized. check-in: 637f51dd user: dan tags: trunk
17:28
Fix a bug whereby an old snapshot could be checkpointed (and subsequent transactions discarded) if the last connection to disconnect from a WAL database happended to be holding an out-of-date wal-index header. check-in: d0c0034b user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_vfs.c.

644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
...
728
729
730
731
732
733
734

735
736
737
738

739

740
741
742
743
744
745
746
747
748
...
764
765
766
767
768
769
770








771
772
773
774
775




776
777
778
779
780
781
782
783
  ckfree((char *)p->pVfs);
  ckfree((char *)p);
}

#define TESTVFS_MAX_ARGS 12

/*
** Usage:  testvfs VFSNAME SCRIPT
**
** This command creates two things when it is invoked: an SQLite VFS, and
** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
** installed as the default VFS.
**
** The VFS passes all file I/O calls through to the underlying VFS.
**
................................................................................
  sqlite3_vfs *pVfs;              /* New VFS */
  char *zVfs;
  Tcl_Obj *pScript;
  int nScript;                    /* Number of elements in list pScript */
  Tcl_Obj **apScript;             /* Array of pScript elements */
  int nByte;                      /* Bytes of space to allocate at p */
  int i;                          /* Counter variable */


  if( objc!=3 ){
    Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME SCRIPT");
    return TCL_ERROR;

  }

  zVfs = Tcl_GetString(objv[1]);
  pScript = objv[2];

  if( TCL_OK!=Tcl_ListObjGetElements(interp, pScript, &nScript, &apScript) ){
    return TCL_ERROR;
  }

  nByte = sizeof(Testvfs)
        + (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *) 
................................................................................
  pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
  memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
  pVfs->pAppData = (void *)p;
  pVfs->zName = p->zName;
  pVfs->mxPathname = p->pParent->mxPathname;
  pVfs->szOsFile += p->pParent->szOsFile;
  p->pVfs = pVfs;









  Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
  sqlite3_vfs_register(pVfs, 0);

  return TCL_OK;




}

int Sqlitetestvfs_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
  return TCL_OK;
}

#endif







|







 







>

|
<
|
>

>
|
|







 







>
>
>
>
>
>
>
>





>
>
>
>








644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
...
728
729
730
731
732
733
734
735
736
737

738
739
740
741
742
743
744
745
746
747
748
749
750
...
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
  ckfree((char *)p->pVfs);
  ckfree((char *)p);
}

#define TESTVFS_MAX_ARGS 12

/*
** Usage:  testvfs ?-noshm? VFSNAME SCRIPT
**
** This command creates two things when it is invoked: an SQLite VFS, and
** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
** installed as the default VFS.
**
** The VFS passes all file I/O calls through to the underlying VFS.
**
................................................................................
  sqlite3_vfs *pVfs;              /* New VFS */
  char *zVfs;
  Tcl_Obj *pScript;
  int nScript;                    /* Number of elements in list pScript */
  Tcl_Obj **apScript;             /* Array of pScript elements */
  int nByte;                      /* Bytes of space to allocate at p */
  int i;                          /* Counter variable */
  int isNoshm = 0;                /* True if -noshm is passed */

  if( objc<3 ) goto bad_args;

  if( strcmp(Tcl_GetString(objv[1]), "-noshm")==0 ){
    isNoshm = 1;
  }
  if( objc!=3+isNoshm ) goto bad_args;
  zVfs = Tcl_GetString(objv[isNoshm+1]);
  pScript = objv[isNoshm+2];

  if( TCL_OK!=Tcl_ListObjGetElements(interp, pScript, &nScript, &apScript) ){
    return TCL_ERROR;
  }

  nByte = sizeof(Testvfs)
        + (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *) 
................................................................................
  pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
  memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
  pVfs->pAppData = (void *)p;
  pVfs->zName = p->zName;
  pVfs->mxPathname = p->pParent->mxPathname;
  pVfs->szOsFile += p->pParent->szOsFile;
  p->pVfs = pVfs;
  if( isNoshm ){
    pVfs->xShmOpen = 0;
    pVfs->xShmGet = 0;
    pVfs->xShmSize = 0;
    pVfs->xShmRelease = 0;
    pVfs->xShmClose = 0;
    pVfs->xShmLock = 0;
  }

  Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
  sqlite3_vfs_register(pVfs, 0);

  return TCL_OK;

 bad_args:
  Tcl_WrongNumArgs(interp, 1, objv, "?-noshm? VFSNAME SCRIPT");
  return TCL_ERROR;
}

int Sqlitetestvfs_Init(Tcl_Interp *interp){
  Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
  return TCL_OK;
}

#endif

Changes to src/wal.c.

573
574
575
576
577
578
579




580
581

582
583
584
585
586
587
588
...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
...
874
875
876
877
878
879
880

881
882
883
884
885
886
887
888
889
890
891
...
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
...
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
....
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
....
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
    sqlite3_free(aFrame);
  }else{
    hdr.iCheck1 = 2;
    hdr.iCheck2 = 3;
  }

finished:




  walIndexWriteHdr(pWal, &hdr);
  memcpy(&pWal->hdr, &hdr, sizeof(hdr));

  return rc;
}

/*
** Close an open wal-index.
*/
static void walIndexClose(Wal *pWal, int isDelete){
................................................................................
  int nByte;                      /* Number of bytes to allocate */
  int i;                          /* Iterator variable */
  int nFinal;                     /* Number of unindexed entries */
  struct WalSegment *pFinal;      /* Final (unindexed) segment */
  u8 *aTmp;                       /* Temp space used by merge-sort */
  int rc;                         /* Return code of walIndexMap() */

  rc = walIndexMap(pWal, -1);
  if( rc!=SQLITE_OK ){
    return rc;
  }
  aData = pWal->pWiData;
  iLast = pWal->hdr.iLastPg;
  nSegment = (iLast >> 8) + 1;
  nFinal = (iLast & 0x000000FF);
................................................................................
**
** If the checksum cannot be verified return SQLITE_ERROR.
*/
int walIndexTryHdr(Wal *pWal, int *pisValid, int *pChanged){
  u32 aCksum[2] = {1, 1};
  u32 aHdr[WALINDEX_HDR_NFIELD+2];


  if( pWal->szWIndex==0 ){
    int rc;
    rc = walIndexRemap(pWal, WALINDEX_MMAP_INCREMENT);
    if( rc ) return rc;
  }

  /* Read the header. The caller may or may not have locked the wal-index
  ** file, meaning it is possible that an inconsistent snapshot is read
  ** from the file. If this happens, return SQLITE_ERROR.
  */
  memcpy(aHdr, pWal->pWiData, sizeof(aHdr));
................................................................................
  ** file and try again. If the header checksum verification fails this
  ** time as well, run log recovery.
  */
  if( SQLITE_OK==(rc = walSetLock(pWal, SQLITE_SHM_RECOVER)) ){
    /* This call to walIndexTryHdr() may not return an error code, as the
    ** wal-index is already mapped. It may find that the header is invalid,
    ** but there is no chance of hitting an actual error.  */
    assert( pWal->szWIndex );
    rc = walIndexTryHdr(pWal, &isValid, pChanged);
    assert( rc==SQLITE_OK );
    if( isValid==0 ){
      *pChanged = 1;
      rc = walIndexRecover(pWal);
    }
    walSetLock(pWal, SQLITE_SHM_READ);
................................................................................
  assert( rc!=SQLITE_OK || pWal->lockState==SQLITE_SHM_READ );

  if( rc==SQLITE_OK ){
    rc = walIndexReadHdr(pWal, pChanged);
    if( rc!=SQLITE_OK ){
      /* An error occured while attempting log recovery. */
      sqlite3WalCloseSnapshot(pWal);
    }else{
      /* Check if the mapping needs to grow. */
      if( pWal->hdr.iLastPg 
       && walIndexEntry(pWal->hdr.iLastPg)*sizeof(u32)>=pWal->szWIndex
      ){
         walIndexRemap(pWal, -1);
      }
    }
  }

  walIndexUnmap(pWal);
  return rc;
}

................................................................................
){
  int rc;                         /* Return code */
  u32 iRead = 0;
  u32 *aData; 
  int iFrame = (pWal->hdr.iLastPg & 0xFFFFFF00);

  assert( pWal->lockState==SQLITE_SHM_READ||pWal->lockState==SQLITE_SHM_WRITE );
  rc = walIndexMap(pWal, -1);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* Do a linear search of the unindexed block of page-numbers (if any) 
  ** at the end of the wal-index. An alternative to this would be to
  ** build an index in private memory each time a read transaction is
................................................................................
    rc = walSetLock(pWal, SQLITE_SHM_WRITE);

    /* If this connection is not reading the most recent database snapshot,
    ** it is not possible to write to the database. In this case release
    ** the write locks and return SQLITE_BUSY.
    */
    if( rc==SQLITE_OK ){
      rc = walIndexMap(pWal, -1);
      if( rc==SQLITE_OK
       && memcmp(&pWal->hdr, pWal->pWiData, sizeof(WalIndexHdr))
      ){
        rc = SQLITE_BUSY;
      }
      walIndexUnmap(pWal);
      if( rc!=SQLITE_OK ){







>
>
>
>
|
|
>







 







|







 







>

<
<
|







 







|







 







<
<
<
<
<
<
<







 







|







 







|







573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
...
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
...
879
880
881
882
883
884
885
886
887


888
889
890
891
892
893
894
895
...
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
...
972
973
974
975
976
977
978







979
980
981
982
983
984
985
....
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
....
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
    sqlite3_free(aFrame);
  }else{
    hdr.iCheck1 = 2;
    hdr.iCheck2 = 3;
  }

finished:
  if( rc==SQLITE_OK && hdr.iLastPg==0 ){
    rc = walIndexRemap(pWal, WALINDEX_MMAP_INCREMENT);
  }
  if( rc==SQLITE_OK ){
    walIndexWriteHdr(pWal, &hdr);
    memcpy(&pWal->hdr, &hdr, sizeof(hdr));
  }
  return rc;
}

/*
** Close an open wal-index.
*/
static void walIndexClose(Wal *pWal, int isDelete){
................................................................................
  int nByte;                      /* Number of bytes to allocate */
  int i;                          /* Iterator variable */
  int nFinal;                     /* Number of unindexed entries */
  struct WalSegment *pFinal;      /* Final (unindexed) segment */
  u8 *aTmp;                       /* Temp space used by merge-sort */
  int rc;                         /* Return code of walIndexMap() */

  rc = walIndexMap(pWal, walMappingSize(pWal->hdr.iLastPg));
  if( rc!=SQLITE_OK ){
    return rc;
  }
  aData = pWal->pWiData;
  iLast = pWal->hdr.iLastPg;
  nSegment = (iLast >> 8) + 1;
  nFinal = (iLast & 0x000000FF);
................................................................................
**
** If the checksum cannot be verified return SQLITE_ERROR.
*/
int walIndexTryHdr(Wal *pWal, int *pisValid, int *pChanged){
  u32 aCksum[2] = {1, 1};
  u32 aHdr[WALINDEX_HDR_NFIELD+2];

  assert( *pisValid==0 );
  if( pWal->szWIndex==0 ){


    return SQLITE_OK;
  }

  /* Read the header. The caller may or may not have locked the wal-index
  ** file, meaning it is possible that an inconsistent snapshot is read
  ** from the file. If this happens, return SQLITE_ERROR.
  */
  memcpy(aHdr, pWal->pWiData, sizeof(aHdr));
................................................................................
  ** file and try again. If the header checksum verification fails this
  ** time as well, run log recovery.
  */
  if( SQLITE_OK==(rc = walSetLock(pWal, SQLITE_SHM_RECOVER)) ){
    /* This call to walIndexTryHdr() may not return an error code, as the
    ** wal-index is already mapped. It may find that the header is invalid,
    ** but there is no chance of hitting an actual error.  */
    assert( pWal->pWiData );
    rc = walIndexTryHdr(pWal, &isValid, pChanged);
    assert( rc==SQLITE_OK );
    if( isValid==0 ){
      *pChanged = 1;
      rc = walIndexRecover(pWal);
    }
    walSetLock(pWal, SQLITE_SHM_READ);
................................................................................
  assert( rc!=SQLITE_OK || pWal->lockState==SQLITE_SHM_READ );

  if( rc==SQLITE_OK ){
    rc = walIndexReadHdr(pWal, pChanged);
    if( rc!=SQLITE_OK ){
      /* An error occured while attempting log recovery. */
      sqlite3WalCloseSnapshot(pWal);







    }
  }

  walIndexUnmap(pWal);
  return rc;
}

................................................................................
){
  int rc;                         /* Return code */
  u32 iRead = 0;
  u32 *aData; 
  int iFrame = (pWal->hdr.iLastPg & 0xFFFFFF00);

  assert( pWal->lockState==SQLITE_SHM_READ||pWal->lockState==SQLITE_SHM_WRITE );
  rc = walIndexMap(pWal, walMappingSize(pWal->hdr.iLastPg));
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* Do a linear search of the unindexed block of page-numbers (if any) 
  ** at the end of the wal-index. An alternative to this would be to
  ** build an index in private memory each time a read transaction is
................................................................................
    rc = walSetLock(pWal, SQLITE_SHM_WRITE);

    /* If this connection is not reading the most recent database snapshot,
    ** it is not possible to write to the database. In this case release
    ** the write locks and return SQLITE_BUSY.
    */
    if( rc==SQLITE_OK ){
      rc = walIndexMap(pWal, sizeof(WalIndexHdr));
      if( rc==SQLITE_OK
       && memcmp(&pWal->hdr, pWal->pWiData, sizeof(WalIndexHdr))
      ){
        rc = SQLITE_BUSY;
      }
      walIndexUnmap(pWal);
      if( rc!=SQLITE_OK ){

Changes to test/wal2.test.

301
302
303
304
305
306
307
308






























309
} {4 10}
do_test wal2-3.5 {
  list [info exists ::sabotage] [info exists ::locked]
} {0 0}
db close
tvfs delete
file delete -force test.db test.db-wal test.db-journal































finish_test








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
} {4 10}
do_test wal2-3.5 {
  list [info exists ::sabotage] [info exists ::locked]
} {0 0}
db close
tvfs delete
file delete -force test.db test.db-wal test.db-journal

#-------------------------------------------------------------------------
# Test that a database connection using a VFS that does not support the
# xShmXXX interfaces cannot open a WAL database.
#
do_test wal2-4.1 {
  sqlite3 db test.db
  execsql {
    PRAGMA journal_mode = WAL;
    CREATE TABLE data(x);
    INSERT INTO data VALUES('need xShmOpen to see this');
    PRAGMA wal_checkpoint;
  }
} {wal}
do_test wal2-4.2 {
  db close
  proc ok {args} {return SQLITE_OK}
  testvfs -noshm tvfs ok
  sqlite3 db test.db -vfs tvfs
  catchsql { SELECT * FROM data }
} {1 {unable to open database file}}
do_test wal2-4.3 {
  db close
  proc ok {args} {return SQLITE_OK}
  testvfs tvfs ok
  sqlite3 db test.db -vfs tvfs
  catchsql { SELECT * FROM data }
} {0 {{need xShmOpen to see this}}}
db close
tvfs delete

finish_test