Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Experimental change: On systems where it is not possible to unlink a file while one or more processes has it open (i.e. not unix), avoid closing the journal file each time the database is unlocked and reopening it at the start of each transaction. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | experimental |
Files: | files | file ages | folders |
SHA1: |
bede8c8a148fb9be5ffbf38df7fa733e |
User & Date: | dan 2010-06-16 19:04:23.000 |
Context
2010-06-17
| ||
06:19 | Merge fix [f80c3f922a] with experimental changes. (check-in: 20133e9ca9 user: dan tags: experimental) | |
2010-06-16
| ||
19:04 | Experimental change: On systems where it is not possible to unlink a file while one or more processes has it open (i.e. not unix), avoid closing the journal file each time the database is unlocked and reopening it at the start of each transaction. (check-in: bede8c8a14 user: dan tags: experimental) | |
12:30 | Add extra test cases to pager1.test. (check-in: ad3209572d user: dan tags: trunk) | |
Changes
Changes to src/pager.c.
︙ | ︙ | |||
332 333 334 335 336 337 338 339 340 341 342 343 344 345 | u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ /* The following block contains those class members that are dynamically ** modified during normal operations. The other variables in this structure ** are either constant throughout the lifetime of the pager, or else ** used to store configuration parameters that affect the way the pager ** operates. ** | > | 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | u8 noReadlock; /* Do not bother to obtain readlocks */ u8 noSync; /* Do not sync the journal if true */ u8 fullSync; /* Do extra syncs of the journal for robustness */ u8 sync_flags; /* One of SYNC_NORMAL or SYNC_FULL */ u8 tempFile; /* zFilename is a temporary file */ u8 readOnly; /* True for a read-only database */ u8 memDb; /* True to inhibit all file I/O */ u8 safeJrnlHandle; /* True if jrnl may be held open with no lock */ /* The following block contains those class members that are dynamically ** modified during normal operations. The other variables in this structure ** are either constant throughout the lifetime of the pager, or else ** used to store configuration parameters that affect the way the pager ** operates. ** |
︙ | ︙ | |||
1220 1221 1222 1223 1224 1225 1226 | if( !pPager->exclusiveMode ){ int rc = SQLITE_OK; /* Return code */ /* Always close the journal file when dropping the database lock. ** Otherwise, another connection with journal_mode=delete might ** delete the file out from under us. */ | > > > > | > > | 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 | if( !pPager->exclusiveMode ){ int rc = SQLITE_OK; /* Return code */ /* Always close the journal file when dropping the database lock. ** Otherwise, another connection with journal_mode=delete might ** delete the file out from under us. */ if( pPager->safeJrnlHandle==0 || (pPager->journalMode!=PAGER_JOURNALMODE_TRUNCATE && pPager->journalMode!=PAGER_JOURNALMODE_PERSIST) ){ sqlite3OsClose(pPager->jfd); } sqlite3BitvecDestroy(pPager->pInJournal); pPager->pInJournal = 0; releaseAllSavepoints(pPager); /* If the file is unlocked, somebody else might change it. The ** values stored in Pager.dbSize etc. might become invalid if ** this happens. One can argue that this doesn't need to be cleared |
︙ | ︙ | |||
3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 | int sqlite3PagerClose(Pager *pPager){ u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif | > | 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 | int sqlite3PagerClose(Pager *pPager){ u8 *pTmp = (u8 *)pPager->pTmpSpace; disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; pPager->safeJrnlHandle = 0; #ifndef SQLITE_OMIT_WAL sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif |
︙ | ︙ | |||
3904 3905 3906 3907 3908 3909 3910 | ** SQLITE_OK returned. If no hot-journal file is present, *pExists is ** set to 0 and SQLITE_OK returned. If an IO error occurs while trying ** to determine whether or not a hot-journal file exists, the IO error ** code is returned and the value of *pExists is undefined. */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; | | | > < > > > > | > | 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 3936 3937 3938 3939 3940 3941 | ** SQLITE_OK returned. If no hot-journal file is present, *pExists is ** set to 0 and SQLITE_OK returned. If an IO error occurs while trying ** to determine whether or not a hot-journal file exists, the IO error ** code is returned and the value of *pExists is undefined. */ static int hasHotJournal(Pager *pPager, int *pExists){ sqlite3_vfs * const pVfs = pPager->pVfs; int rc = SQLITE_OK; /* Return code */ int exists = 1; /* True if a journal file is present */ int jrnlOpen = !!isOpen(pPager->jfd); assert( pPager!=0 ); assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); assert( pPager->state <= PAGER_SHARED ); assert( jrnlOpen==0 || sqlite3OsDeviceCharacteristics(pPager->jfd)&SQLITE_IOCAP_SAFE_DELETE ); *pExists = 0; if( !jrnlOpen ){ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); } if( rc==SQLITE_OK && exists ){ int locked; /* True if some process holds a RESERVED lock */ /* Race condition here: Another process might have been holding the ** the RESERVED lock and have a journal open at the sqlite3OsAccess() ** call above, but then delete the journal and drop the lock before ** we get to the following sqlite3OsCheckReservedLock() call. If that |
︙ | ︙ | |||
3952 3953 3954 3955 3956 3957 3958 | }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, ** it can be ignored. */ | > | | > > | > | 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 | }else{ /* The journal file exists and no other connection has a reserved ** or greater lock on the database file. Now check that there is ** at least one non-zero bytes at the start of the journal file. ** If there is, then we consider this journal to be hot. If not, ** it can be ignored. */ if( !jrnlOpen ){ int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); } if( rc==SQLITE_OK ){ u8 first = 0; rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } if( !jrnlOpen ){ sqlite3OsClose(pPager->jfd); } *pExists = (first!=0); }else if( rc==SQLITE_CANTOPEN ){ /* If we cannot open the rollback journal file in order to see if ** its has a zero header, that might be due to an I/O error, or ** it might be due to the race condition described above and in ** ticket #3883. Either way, assume that the journal is hot. ** This might be a false positive. But if it is, then the |
︙ | ︙ | |||
4486 4487 4488 4489 4490 4491 4492 4493 4494 4495 4496 4497 4498 4499 | ); #ifdef SQLITE_ENABLE_ATOMIC_WRITE rc = sqlite3JournalOpen( pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) ); #else rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); #endif } assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } /* Write the first journal header to the journal file and open | > > > > | 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 | ); #ifdef SQLITE_ENABLE_ATOMIC_WRITE rc = sqlite3JournalOpen( pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager) ); #else rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0); if( rc==SQLITE_OK ){ int iDc = sqlite3OsDeviceCharacteristics(pPager->jfd); pPager->safeJrnlHandle = (iDc&SQLITE_IOCAP_SAFE_DELETE)!=0; } #endif } assert( rc!=SQLITE_OK || isOpen(pPager->jfd) ); } /* Write the first journal header to the journal file and open |
︙ | ︙ |
Changes to src/sqlite.h.in.
︙ | ︙ | |||
504 505 506 507 508 509 510 511 512 513 514 515 516 517 | #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. | > | 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 | #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_SAFE_DELETE 0x00000800 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. |
︙ | ︙ |
Changes to src/test_vfs.c.
︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | TestvfsBuffer *pBuffer; /* List of shared buffers */ int isNoshm; int mask; int iIoerrCnt; int ioerr; int nIoerrFail; }; /* ** The Testvfs.mask variable is set to a combination of the following. ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the ** corresponding VFS method is ignored for purposes of: ** ** + Simulating IO errors, and ** + Invoking the Tcl callback script. */ #define TESTVFS_SHMOPEN_MASK 0x00000001 #define TESTVFS_SHMLOCK_MASK 0x00000010 #define TESTVFS_SHMMAP_MASK 0x00000020 #define TESTVFS_SHMBARRIER_MASK 0x00000040 #define TESTVFS_SHMCLOSE_MASK 0x00000080 #define TESTVFS_OPEN_MASK 0x00000100 #define TESTVFS_SYNC_MASK 0x00000200 #define TESTVFS_DELETE_MASK 0x00000400 | > > > > > > | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | TestvfsBuffer *pBuffer; /* List of shared buffers */ int isNoshm; int mask; int iIoerrCnt; int ioerr; int nIoerrFail; int iDevchar; int iSectorsize; }; /* ** The Testvfs.mask variable is set to a combination of the following. ** If a bit is clear in Testvfs.mask, then calls made by SQLite to the ** corresponding VFS method is ignored for purposes of: ** ** + Simulating IO errors, and ** + Invoking the Tcl callback script. */ #define TESTVFS_SHMOPEN_MASK 0x00000001 #define TESTVFS_SHMLOCK_MASK 0x00000010 #define TESTVFS_SHMMAP_MASK 0x00000020 #define TESTVFS_SHMBARRIER_MASK 0x00000040 #define TESTVFS_SHMCLOSE_MASK 0x00000080 #define TESTVFS_OPEN_MASK 0x00000100 #define TESTVFS_SYNC_MASK 0x00000200 #define TESTVFS_DELETE_MASK 0x00000400 #define TESTVFS_CLOSE_MASK 0x00000800 #define TESTVFS_WRITE_MASK 0x00001000 #define TESTVFS_TRUNCATE_MASK 0x00002000 #define TESTVFS_ALL_MASK 0x00003FFF #define TESTVFS_MAX_PAGES 256 /* ** A shared-memory buffer. There is one of these objects for each shared ** memory region opened by clients. If two clients open the same file, |
︙ | ︙ | |||
241 242 243 244 245 246 247 | } /* ** Close an tvfs-file. */ static int tvfsClose(sqlite3_file *pFile){ | | > > > > > > > > | | | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 | } /* ** Close an tvfs-file. */ static int tvfsClose(sqlite3_file *pFile){ TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){ tvfsExecTcl(p, "xClose", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); } if( pFd->pShmId ){ Tcl_DecrRefCount(pFd->pShmId); pFd->pShmId = 0; } if( pFile->pMethods ){ ckfree((char *)pFile->pMethods); } return sqlite3OsClose(pFd->pReal); } /* ** Read data from an tvfs-file. */ static int tvfsRead( sqlite3_file *pFile, |
︙ | ︙ | |||
274 275 276 277 278 279 280 | */ static int tvfsWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ | > | > > > > > > > > > > | > > > | > > > > > > > > > > | > > | 288 289 290 291 292 293 294 295 296 297 298 299 300 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 | */ static int tvfsWrite( sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ int rc = SQLITE_OK; TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){ tvfsExecTcl(p, "xWrite", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst); } return rc; } /* ** Truncate an tvfs-file. */ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ int rc = SQLITE_OK; TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ tvfsExecTcl(p, "xTruncate", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); } if( rc==SQLITE_OK ){ rc = sqlite3OsTruncate(pFd->pReal, size); } return rc; } /* ** Sync an tvfs-file. */ static int tvfsSync(sqlite3_file *pFile, int flags){ int rc = SQLITE_OK; |
︙ | ︙ | |||
372 373 374 375 376 377 378 | return sqlite3OsFileControl(p->pReal, op, pArg); } /* ** Return the sector-size in bytes for an tvfs-file. */ static int tvfsSectorSize(sqlite3_file *pFile){ | | > > > > | | > > > > | | 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 | return sqlite3OsFileControl(p->pReal, op, pArg); } /* ** Return the sector-size in bytes for an tvfs-file. */ static int tvfsSectorSize(sqlite3_file *pFile){ TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->iSectorsize>=0 ){ return p->iSectorsize; } return sqlite3OsSectorSize(pFd->pReal); } /* ** Return the device characteristic flags supported by an tvfs-file. */ static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ TestvfsFile *pFd = (TestvfsFile *)pFile; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; if( p->iDevchar>=0 ){ return p->iDevchar; } return sqlite3OsDeviceCharacteristics(pFd->pReal); } /* ** Open an tvfs file handle. */ static int tvfsOpen( sqlite3_vfs *pVfs, |
︙ | ︙ | |||
778 779 780 781 782 783 784 | ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Testvfs *p = (Testvfs *)cd; | < < < | > | > > > > > > > > > > > > | > > | | 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 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Testvfs *p = (Testvfs *)cd; enum DB_enum { CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT, CMD_DEVCHAR, CMD_SECTORSIZE }; struct TestvfsSubcmd { char *zName; enum DB_enum eCmd; } aSubcmd[] = { { "shm", CMD_SHM }, { "delete", CMD_DELETE }, { "filter", CMD_FILTER }, { "ioerr", CMD_IOERR }, { "script", CMD_SCRIPT }, { "devchar", CMD_DEVCHAR }, { "sectorsize", CMD_SECTORSIZE }, { 0, 0 } }; int i; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); return TCL_ERROR; } if( Tcl_GetIndexFromObjStruct( interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i) ){ return TCL_ERROR; } Tcl_ResetResult(interp); switch( aSubcmd[i].eCmd ){ case CMD_SHM: { Tcl_Obj *pObj; int i; TestvfsBuffer *pBuffer; char *zName; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); |
︙ | ︙ | |||
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 | { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xDelete", TESTVFS_DELETE_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "LIST"); | > > > | 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 | { "xShmOpen", TESTVFS_SHMOPEN_MASK }, { "xShmLock", TESTVFS_SHMLOCK_MASK }, { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, { "xShmClose", TESTVFS_SHMCLOSE_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK }, { "xSync", TESTVFS_SYNC_MASK }, { "xDelete", TESTVFS_DELETE_MASK }, { "xWrite", TESTVFS_WRITE_MASK }, { "xTruncate", TESTVFS_TRUNCATE_MASK }, { "xOpen", TESTVFS_OPEN_MASK }, { "xClose", TESTVFS_CLOSE_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "LIST"); |
︙ | ︙ | |||
944 945 946 947 948 949 950 951 952 953 954 955 956 957 | break; } case CMD_DELETE: { Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; } } return TCL_OK; } static void testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 | break; } case CMD_DELETE: { Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); break; } case CMD_DEVCHAR: { struct DeviceFlag { char *zName; int iValue; } aFlag[] = { { "default", -1 }, { "atomic", SQLITE_IOCAP_ATOMIC }, { "atomic512", SQLITE_IOCAP_ATOMIC512 }, { "atomic1k", SQLITE_IOCAP_ATOMIC1K }, { "atomic2k", SQLITE_IOCAP_ATOMIC2K }, { "atomic4k", SQLITE_IOCAP_ATOMIC4K }, { "atomic8k", SQLITE_IOCAP_ATOMIC8K }, { "atomic16k", SQLITE_IOCAP_ATOMIC16K }, { "atomic32k", SQLITE_IOCAP_ATOMIC32K }, { "atomic64k", SQLITE_IOCAP_ATOMIC64K }, { "sequential", SQLITE_IOCAP_SEQUENTIAL }, { "safe_append", SQLITE_IOCAP_SAFE_APPEND }, { "safe_delete", SQLITE_IOCAP_SAFE_DELETE }, { 0, 0 } }; Tcl_Obj *pRet; int iFlag; if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?"); return TCL_ERROR; } if( objc==3 ){ int j; int iNew = 0; Tcl_Obj **flags = 0; int nFlags = 0; if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){ return TCL_ERROR; } for(j=0; j<nFlags; j++){ int idx = 0; if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag, sizeof(aFlag[0]), "flag", 0, &idx) ){ return TCL_ERROR; } if( aFlag[idx].iValue<0 && nFlags>1 ){ Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0); return TCL_ERROR; } iNew |= aFlag[idx].iValue; } p->iDevchar = iNew; } pRet = Tcl_NewObj(); for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){ if( p->iDevchar & aFlag[iFlag].iValue ){ Tcl_ListObjAppendElement( interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1) ); } } Tcl_SetObjResult(interp, pRet); break; } case CMD_SECTORSIZE: { if( objc>3 ){ Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?"); return TCL_ERROR; } if( objc==3 ){ int iNew = 0; if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){ return TCL_ERROR; } p->iSectorsize = iNew; } Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize)); break; } } return TCL_OK; } static void testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; |
︙ | ︙ | |||
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 | } } 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. */ | > > | 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 | } } zVfs = Tcl_GetString(objv[1]); nByte = sizeof(Testvfs) + strlen(zVfs)+1; p = (Testvfs *)ckalloc(nByte); memset(p, 0, nByte); p->iDevchar = -1; p->iSectorsize = -1; /* 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. */ |
︙ | ︙ |
Added test/journal2.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | # 2010 June 16 # # 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 regression tests for SQLite library. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/malloc_common.tcl db close set a_string_counter 1 proc a_string {n} { global a_string_counter incr a_string_counter string range [string repeat "${a_string_counter}." $n] 1 $n } # Create a [testvfs] and install it as the default VFS. Set the device # characteristics flags to "SAFE_DELETE". # testvfs tvfs -default 1 tvfs devchar safe_delete # Set up a hook so that each time a journal file is opened, closed or # deleted, the method name ("xOpen", "xClose" or "xDelete") and the final # segment of the journal file-name (i.e. "test.db-journal") are appended to # global list variable $::oplog. # tvfs filter {xOpen xClose xDelete} tvfs script journal_op_catcher proc journal_op_catcher {method filename args} { # If global variable ::tvfs_error_on_write is defined, then return an # IO error to every attempt to modify the file-system. Otherwise, return # SQLITE_OK. # if {[info exists ::tvfs_error_on_write]} { if {$method == "xDelete" || $method == "xWrite" || $method == "xTruncate"} { return SQLITE_IOERR } return SQLITE_OK } if {[string match *journal* $filename]==0} return set f [file tail $filename] lappend ::oplog $method $f if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 } switch -- $method { xOpen { incr ::open_journals($f) +1 } xClose { incr ::open_journals($f) -1 } xDelete { if {$::open_journals($f)>0} { return SQLITE_IOERR } } } return } do_test journal2-1.1 { set ::oplog [list] sqlite3 db test.db execsql { CREATE TABLE t1(a, b) } set ::oplog } {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal} do_test journal2-1.2 { set ::oplog [list] execsql { PRAGMA journal_mode = truncate; INSERT INTO t1 VALUES(1, 2); } set ::oplog } {xOpen test.db-journal} do_test journal2-1.3 { set ::oplog [list] execsql { INSERT INTO t1 VALUES(3, 4) } set ::oplog } {} do_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4} # Add a second connection. This connection attempts to commit data in # journal_mode=DELETE mode. When it tries to delete the journal file, # the VFS layer returns an IO error. # do_test journal2-1.5 { set ::oplog [list] sqlite3 db2 test.db execsql { PRAGMA journal_mode = delete } db2 catchsql { INSERT INTO t1 VALUES(5, 6) } db2 } {1 {disk I/O error}} do_test journal2-1.6 { file exists test.db-journal } 1 do_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4} do_test journal2-1.8 { execsql { PRAGMA journal_mode = truncate } db2 execsql { INSERT INTO t1 VALUES(5, 6) } db2 } {} do_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6} # Grow the database until it is reasonably large. Then, from a # journal_mode=DELETE connection, attempt to commit a large transaction (one # that involves upgrading to an exclusive lock and writing the database # before the transaction is committed). # do_test journal2-1.10 { db2 close db func a_string a_string execsql { CREATE TABLE t2(a UNIQUE, b UNIQUE); INSERT INTO t2 VALUES(a_string(200), a_string(300)); INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 2 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 4 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 8 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 16 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 32 INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 64 } file size test.db-journal } {0} do_test journal2-1.11 { set sz [expr [file size test.db] / 1024] expr {$sz>120 && $sz<200} } 1 do_test journal2-1.12 { sqlite3 db2 test.db execsql { PRAGMA cache_size = 10; BEGIN; INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2; -- 128 } db2 } {} do_test journal2-1.13 { tvfs filter {xOpen xClose xDelete xWrite xTruncate} set ::tvfs_error_on_write 1 catchsql { COMMIT } db2 } {1 {disk I/O error}} db2 close unset ::tvfs_error_on_write file copy -force test.db testX.db do_test journal2-1.14 { file exists test.db-journal } 1 do_test journal2-1.15 { execsql { SELECT count(*) FROM t2; PRAGMA integrity_check; } } {64 ok} # This block checks that in the test case above, connection [db2] really # did begin writing to the database file before it hit IO errors. If # this is true, then the copy of the database file made before [db] # rolled back the hot journal should fail the integrity-check. # do_test journal2-1.16 { set sz [expr [file size testX.db] / 1024] expr {$sz>240 && $sz<400} } 1 do_test journal2-1.17 { expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"} } {1} do_test journal2-1.20 { sqlite3 db2 testX.db expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"} } {0} do_test journal2-1.21 { db2 close } {} db close tvfs delete finish_test |