Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add some fault-injection tests to improve coverage. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | experimental |
Files: | files | file ages | folders |
SHA1: |
37b26d125f4b1d8e75bb38800fefd145 |
User & Date: | dan 2010-06-14 07:53:26.000 |
Context
2010-06-14
| ||
10:30 | Add the xShmPage method to the "crash" vfs in test6.c. (check-in: 1008f53644 user: dan tags: experimental) | |
07:53 | Add some fault-injection tests to improve coverage. (check-in: 37b26d125f user: dan tags: experimental) | |
2010-06-12
| ||
12:02 | Fix some problems with handling IO errors on the experimental branch. (check-in: eade8bc238 user: dan tags: experimental) | |
Changes
Changes to src/test_vfs.c.
︙ | ︙ | |||
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 | ){ int rc = SQLITE_OK; TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( p->pScript && p->mask&TESTVFS_SHMPAGE_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); tvfsExecTcl(p, "xShmPage", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMPAGE_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ tvfsAllocPage(pFd->pShm, iPage, pgsz); | > > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | ){ int rc = SQLITE_OK; TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); if( p->pScript && p->mask&TESTVFS_SHMPAGE_MASK ){ Tcl_Obj *pArg = Tcl_NewObj(); Tcl_IncrRefCount(pArg); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(iPage)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); tvfsExecTcl(p, "xShmPage", Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg ); tvfsResultCode(p, &rc); Tcl_DecrRefCount(pArg); } if( rc==SQLITE_OK && p->mask&TESTVFS_SHMPAGE_MASK && tvfsInjectIoerr(p) ){ rc = SQLITE_IOERR; } if( rc==SQLITE_OK && isWrite && !pFd->pShm->aPage[iPage] ){ tvfsAllocPage(pFd->pShm, iPage, pgsz); |
︙ | ︙ | |||
824 825 826 827 828 829 830 | int i; TestvfsBuffer *pBuffer; char *zName; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); return TCL_ERROR; } | > > | > > > | | 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 | int i; TestvfsBuffer *pBuffer; char *zName; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); return TCL_ERROR; } zName = ckalloc(p->pParent->mxPathname); p->pParent->xFullPathname( p->pParent, Tcl_GetString(objv[2]), p->pParent->mxPathname, zName ); for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pBuffer->zFile, zName) ) break; } ckfree(zName); if( !pBuffer ){ Tcl_AppendResult(interp, "no such file: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } if( objc==4 ){ int n; u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n); assert( pBuffer->pgsz==0 || pBuffer->pgsz==32768 ); for(i=0; i*32768<n; i++){ |
︙ | ︙ | |||
866 867 868 869 870 871 872 873 874 875 876 877 878 879 | { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmSize", TESTVFS_SHMSIZE_MASK }, { "xShmGet", TESTVFS_SHMGET_MASK }, { "xShmRelease", TESTVFS_SHMRELEASE_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; | > | 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 | { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmSize", TESTVFS_SHMSIZE_MASK }, { "xShmGet", TESTVFS_SHMGET_MASK }, { "xShmRelease", TESTVFS_SHMRELEASE_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xShmPage", TESTVFS_SHMPAGE_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; |
︙ | ︙ | |||
907 908 909 910 911 912 913 914 915 916 917 918 919 920 | if( objc==3 ){ int nByte; if( p->pScript ){ Tcl_DecrRefCount(p->pScript); ckfree((char *)p->apScript); p->apScript = 0; p->nScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ p->pScript = Tcl_DuplicateObj(objv[2]); Tcl_IncrRefCount(p->pScript); } }else if( objc!=2 ){ | > | 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 | if( objc==3 ){ int nByte; if( p->pScript ){ Tcl_DecrRefCount(p->pScript); ckfree((char *)p->apScript); p->apScript = 0; p->nScript = 0; p->pScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ p->pScript = Tcl_DuplicateObj(objv[2]); Tcl_IncrRefCount(p->pScript); } }else if( objc!=2 ){ |
︙ | ︙ | |||
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 | } zVfs = Tcl_GetString(objv[1]); nByte = sizeof(Testvfs) + strlen(zVfs)+1; p = (Testvfs *)ckalloc(nByte); memset(p, 0, nByte); p->pParent = sqlite3_vfs_find(0); p->interp = interp; p->zName = (char *)&p[1]; memcpy(p->zName, zVfs, strlen(zVfs)+1); 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; p->isNoshm = isNoshm; p->mask = TESTVFS_ALL_MASK; | > > > > > > > < | 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 | } zVfs = Tcl_GetString(objv[1]); nByte = sizeof(Testvfs) + strlen(zVfs)+1; p = (Testvfs *)ckalloc(nByte); memset(p, 0, nByte); /* Create the new object command before querying SQLite for a default VFS ** to use for 'real' IO operations. This is because creating the new VFS ** may delete an existing [testvfs] VFS of the same name. If such a VFS ** is currently the default, the new [testvfs] may end up calling the ** methods of a deleted object. */ Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); p->pParent = sqlite3_vfs_find(0); p->interp = interp; p->zName = (char *)&p[1]; memcpy(p->zName, zVfs, strlen(zVfs)+1); 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; p->isNoshm = isNoshm; p->mask = TESTVFS_ALL_MASK; sqlite3_vfs_register(pVfs, isDefault); return TCL_OK; bad_args: Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?"); return TCL_ERROR; |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 | int i; for(i=1; i<*pnList; i++){ assert( aContent[aList[i]] > aContent[aList[i-1]] ); } } #endif } /* ** Map the wal-index into memory owned by this thread, if it is not ** mapped already. Then construct a WalInterator object that can be ** used to loop over all pages in the WAL in ascending order. ** ** On success, make *pp point to the newly allocated WalInterator object | > > > > > > > | 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 | int i; for(i=1; i<*pnList; i++){ assert( aContent[aList[i]] > aContent[aList[i-1]] ); } } #endif } /* ** Free an iterator allocated by walIteratorInit(). */ static void walIteratorFree(WalIterator *p){ sqlite3_free(p); } /* ** Map the wal-index into memory owned by this thread, if it is not ** mapped already. Then construct a WalInterator object that can be ** used to loop over all pages in the WAL in ascending order. ** ** On success, make *pp point to the newly allocated WalInterator object |
︙ | ︙ | |||
1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 | u32 iZero; int nEntry; volatile u32 *aPgno; int rc; rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero); if( rc!=SQLITE_OK ){ return rc; } nEntry = ((i+1)==nSegment)?iLast-iZero:(u32 *)aHash-(u32 *)&aPgno[iZero+1]; iZero++; aPgno += iZero; | > | 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 | u32 iZero; int nEntry; volatile u32 *aPgno; int rc; rc = walHashGet(pWal, i, &aHash, &aPgno, &iZero); if( rc!=SQLITE_OK ){ walIteratorFree(p); return rc; } nEntry = ((i+1)==nSegment)?iLast-iZero:(u32 *)aHash-(u32 *)&aPgno[iZero+1]; iZero++; aPgno += iZero; |
︙ | ︙ | |||
1357 1358 1359 1360 1361 1362 1363 | assert( aSpace==aTmp ); /* Return the fully initialized WalIterator object */ *pp = p; return SQLITE_OK ; } | < < < < < < < | 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 | assert( aSpace==aTmp ); /* Return the fully initialized WalIterator object */ *pp = p; return SQLITE_OK ; } /* ** Copy as much content as we can from the WAL back into the database file ** in response to an sqlite3_wal_checkpoint() request or the equivalent. ** ** The amount of information copies from WAL to database might be limited ** by active readers. This routine will never overwrite a database page ** that a concurrent reader might be using. |
︙ | ︙ | |||
2072 2073 2074 2075 2076 2077 2078 | int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; if( pWal->writeLock ){ int unused; Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; | > > > | | | | | | | | | | | | | | | | | | | | | | | < > | 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 | int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){ int rc = SQLITE_OK; if( pWal->writeLock ){ int unused; Pgno iMax = pWal->hdr.mxFrame; Pgno iFrame; /* Restore the clients cache of the wal-index header to the state it ** was in before the client began writing to the database. */ memcpy(&pWal->hdr, walIndexHdr(pWal), sizeof(WalIndexHdr)); for(iFrame=pWal->hdr.mxFrame+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++ ){ /* This call cannot fail. Unless the page for which the page number ** is passed as the second argument is (a) in the cache and ** (b) has an outstanding reference, then xUndo is either a no-op ** (if (a) is false) or simply expels the page from the cache (if (b) ** is false). ** ** If the upper layer is doing a rollback, it is guaranteed that there ** are no outstanding references to any page other than page 1. And ** page 1 is never written to the log until the transaction is ** committed. As a result, the call to xUndo may not fail. */ assert( pWal->writeLock ); assert( walFramePgno(pWal, iFrame)!=1 ); rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame)); } walCleanupHash(pWal); } assert( rc==SQLITE_OK ); return rc; } /* ** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 ** values. This function populates the array with values required to ** "rollback" the write position of the WAL handle back to the current |
︙ | ︙ | |||
2138 2139 2140 2141 2142 2143 2144 | aWalData[3] = pWal->nCkpt; } if( aWalData[0]<pWal->hdr.mxFrame ){ pWal->hdr.mxFrame = aWalData[0]; pWal->hdr.aFrameCksum[0] = aWalData[1]; pWal->hdr.aFrameCksum[1] = aWalData[2]; | < | < | 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 | aWalData[3] = pWal->nCkpt; } if( aWalData[0]<pWal->hdr.mxFrame ){ pWal->hdr.mxFrame = aWalData[0]; pWal->hdr.aFrameCksum[0] = aWalData[1]; pWal->hdr.aFrameCksum[1] = aWalData[2]; walCleanupHash(pWal); } return rc; } /* ** This function is called just before writing a set of frames to the log |
︙ | ︙ |
Changes to test/walfault.test.
︙ | ︙ | |||
115 116 117 118 119 120 121 122 123 124 125 126 127 128 | db eval { DELETE FROM abc; PRAGMA wal_checkpoint; } } -test { faultsim_test_result {0 {}} } #-------------------------------------------------------------------------- # faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | db eval { DELETE FROM abc; PRAGMA wal_checkpoint; } } -test { faultsim_test_result {0 {}} } #-------------------------------------------------------------------------- # faultsim_delete_and_reopen faultsim_save_and_close do_faultsim_test walfault-4 -prep { faultsim_restore_and_reopen |
︙ | ︙ | |||
148 149 150 151 152 153 154 | PRAGMA journal_mode = WAL; } faultsim_save_and_close } {} do_faultsim_test walfault-5 -faults shmerr* -prep { faultsim_restore_and_reopen execsql { PRAGMA wal_autocheckpoint = 0 } | | | 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | PRAGMA journal_mode = WAL; } faultsim_save_and_close } {} do_faultsim_test walfault-5 -faults shmerr* -prep { faultsim_restore_and_reopen execsql { PRAGMA wal_autocheckpoint = 0 } shmfault filter xShmPage } -body { execsql { CREATE TABLE t1(x); BEGIN; INSERT INTO t1 VALUES(randomblob(400)); /* 1 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 2 */ INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 4 */ |
︙ | ︙ | |||
207 208 209 210 211 212 213 | INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-6 -faults shmerr* -prep { faultsim_restore_and_reopen | | | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | INSERT INTO t1 SELECT randomblob(400) FROM t1; /* 16384 */ COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-6 -faults shmerr* -prep { faultsim_restore_and_reopen shmfault filter xShmPage } -body { execsql { SELECT count(*) FROM t1 } } -test { faultsim_test_result {0 16384} faultsim_integrity_check set n [db one {SELECT count(*) FROM t1}] if {$n != 16384 && $n != 0} { error "Incorrect number of rows: $n" } |
︙ | ︙ | |||
322 323 324 325 326 327 328 | if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" } } do_test walfault-10-pre1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; | | | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | if {$n != 1 && $n != 2} { error "Incorrect number of rows: $n" } } do_test walfault-10-pre1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; CREATE TABLE z(zz INTEGER PRIMARY KEY, zzz BLOB); CREATE INDEX zzzz ON z(zzz); INSERT INTO z VALUES(NULL, randomblob(800)); INSERT INTO z VALUES(NULL, randomblob(800)); INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; INSERT INTO z SELECT NULL, randomblob(800) FROM z; |
︙ | ︙ | |||
358 359 360 361 362 363 364 365 366 367 | faultsim_test_result {0 {}} catch { db eval { ROLLBACK } } faultsim_integrity_check set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}] if {$n != "64 51200"} { error "Incorrect data: $n" } } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 | faultsim_test_result {0 {}} catch { db eval { ROLLBACK } } faultsim_integrity_check set n [db eval {SELECT count(*), sum(length(zzz)) FROM z}] if {$n != "64 51200"} { error "Incorrect data: $n" } } #-------------------------------------------------------------------------- # Test fault injection while checkpointing a large WAL file, if the # checkpoint is the first operation run after opening the database. # This means that some of the required wal-index pages are mapped as part of # the checkpoint process, which means there are a few more opportunities # for IO errors. # # To speed this up, IO errors are only simulated within xShmPage() calls. # do_test walfault-11-pre-1 { sqlite3 db test.db execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 8 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 16 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 32 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 64 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 128 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 256 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 512 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 1024 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 2048 INSERT INTO abc SELECT randomblob(1500) FROM abc; -- 4096 COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-11 -faults shmerr* -prep { catch { db2 close } faultsim_restore_and_reopen shmfault filter xShmPage } -body { db eval { SELECT count(*) FROM abc } sqlite3 db2 test.db -vfs shmfault db2 eval { PRAGMA wal_checkpoint } } -test { faultsim_test_result {0 {}} } #------------------------------------------------------------------------- # Test the handling of the various IO/OOM/SHM errors that may occur during # a log recovery operation undertaken as part of a call to # sqlite3_wal_checkpoint(). # do_test walfault-12-pre-1 { faultsim_delete_and_reopen execsql { PRAGMA journal_mode = WAL; PRAGMA wal_autocheckpoint = 0; BEGIN; CREATE TABLE abc(a PRIMARY KEY); INSERT INTO abc VALUES(randomblob(1500)); INSERT INTO abc VALUES(randomblob(1500)); COMMIT; } faultsim_save_and_close } {} do_faultsim_test walfault-12 -prep { if {[info commands shmfault] == ""} { testvfs shmfault -default true } faultsim_restore_and_reopen db eval { SELECT * FROM sqlite_master } shmfault shm test.db [string repeat "\000" 40] } -body { set rc [sqlite3_wal_checkpoint db] if {$rc != "SQLITE_OK"} { error [sqlite3_errmsg db] } } -test { db close faultsim_test_result {0 {}} } finish_test |