Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Merge the KeyInfo cache mutex fix from trunk. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | sessions |
Files: | files | file ages | folders |
SHA1: |
f0940c73bd13cb49a3d229ce25177363 |
User & Date: | drh 2014-12-09 14:54:26.273 |
Context
2014-12-16
| ||
01:05 | Merge threading fixes from trunk into the sessions branch. (check-in: 9817a2864e user: drh tags: sessions) | |
2014-12-09
| ||
14:54 | Merge the KeyInfo cache mutex fix from trunk. (check-in: f0940c73bd user: drh tags: sessions) | |
14:42 | Increase the default minimum PMA size for multi-threaded sorting from 10x the page size to 250x the page size. Provide the SQLITE_SORTER_PMASZ compile-time option to change this default. (check-in: b05340fe3c user: drh tags: trunk) | |
2014-12-05
| ||
20:05 | Merge the fix for the shared-cache KeyInfo cache bug from trunk. (check-in: 5a73da6a30 user: drh tags: sessions) | |
Changes
Changes to src/analyze.c.
︙ | ︙ | |||
1592 1593 1594 1595 1596 1597 1598 | int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ tRowcnt avgEq = 0; tRowcnt nRow; /* Number of rows in index */ i64 nSum100 = 0; /* Number of terms contributing to sumEq */ i64 nDist100; /* Number of distinct values in index */ | | | 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 | int i; /* Used to iterate through samples */ tRowcnt sumEq = 0; /* Sum of the nEq values */ tRowcnt avgEq = 0; tRowcnt nRow; /* Number of rows in index */ i64 nSum100 = 0; /* Number of terms contributing to sumEq */ i64 nDist100; /* Number of distinct values in index */ if( !pIdx->aiRowEst || iCol>=pIdx->nKeyCol || pIdx->aiRowEst[iCol+1]==0 ){ nRow = pFinal->anLt[iCol]; nDist100 = (i64)100 * pFinal->anDLt[iCol]; nSample--; }else{ nRow = pIdx->aiRowEst[0]; nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1]; } |
︙ | ︙ |
Changes to src/btree.c.
︙ | ︙ | |||
6781 6782 6783 6784 6785 6786 6787 | szScratch = nMaxCells*sizeof(u8*) /* apCell */ + nMaxCells*sizeof(u16) /* szCell */ + pBt->pageSize; /* aSpace1 */ /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer ** that is more than 6 times the database page size. */ | | | 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 6791 6792 6793 6794 6795 | szScratch = nMaxCells*sizeof(u8*) /* apCell */ + nMaxCells*sizeof(u16) /* szCell */ + pBt->pageSize; /* aSpace1 */ /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer ** that is more than 6 times the database page size. */ assert( szScratch<=6*(int)pBt->pageSize ); apCell = sqlite3ScratchMalloc( szScratch ); if( apCell==0 ){ rc = SQLITE_NOMEM; goto balance_cleanup; } szCell = (u16*)&apCell[nMaxCells]; aSpace1 = (u8*)&szCell[nMaxCells]; |
︙ | ︙ | |||
6855 6856 6857 6858 6859 6860 6861 | assert( pOld->hdrOffset==0 ); /* The right pointer of the child page pOld becomes the left ** pointer of the divider cell */ memcpy(apCell[nCell], &pOld->aData[8], 4); }else{ assert( leafCorrection==4 ); if( szCell[nCell]<4 ){ | | > > > > | 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 | assert( pOld->hdrOffset==0 ); /* The right pointer of the child page pOld becomes the left ** pointer of the divider cell */ memcpy(apCell[nCell], &pOld->aData[8], 4); }else{ assert( leafCorrection==4 ); if( szCell[nCell]<4 ){ /* Do not allow any cells smaller than 4 bytes. If a smaller cell ** does exist, pad it with 0x00 bytes. */ assert( szCell[nCell]==3 ); assert( apCell[nCell]==&pTemp[iSpace1-3] ); pTemp[iSpace1++] = 0x00; szCell[nCell] = 4; } } nCell++; } } |
︙ | ︙ | |||
7090 7091 7092 7093 7094 7095 7096 | /* Cell pCell is destined for new sibling page pNew. Originally, it ** was either part of sibling page iOld (possibly an overflow cell), ** or else the divider cell to the left of sibling page iOld. So, ** if sibling page iOld had the same page number as pNew, and if ** pCell really was a part of sibling page iOld (not a divider or ** overflow cell), we can skip updating the pointer map entries. */ | > > > | > | 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 | /* Cell pCell is destined for new sibling page pNew. Originally, it ** was either part of sibling page iOld (possibly an overflow cell), ** or else the divider cell to the left of sibling page iOld. So, ** if sibling page iOld had the same page number as pNew, and if ** pCell really was a part of sibling page iOld (not a divider or ** overflow cell), we can skip updating the pointer map entries. */ if( iOld>=nNew || pNew->pgno!=aPgno[iOld] || pCell<aOld || pCell>=&aOld[usableSize] ){ if( !leafCorrection ){ ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc); } if( szCell[i]>pNew->minLocal ){ ptrmapPutOvflPtr(pNew, pCell, &rc); } } |
︙ | ︙ |
Changes to src/main.c.
︙ | ︙ | |||
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 | /* Close all database connections */ for(j=0; j<db->nDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ if( pDb->pSchema ){ /* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */ for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3KeyInfoUnref(pIdx->pKeyInfo); pIdx->pKeyInfo = 0; } } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; } } | > > | 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 | /* Close all database connections */ for(j=0; j<db->nDb; j++){ struct Db *pDb = &db->aDb[j]; if( pDb->pBt ){ if( pDb->pSchema ){ /* Must clear the KeyInfo cache. See ticket [e4a18565a36884b00edf] */ sqlite3BtreeEnter(pDb->pBt); for(i=sqliteHashFirst(&pDb->pSchema->idxHash); i; i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3KeyInfoUnref(pIdx->pKeyInfo); pIdx->pKeyInfo = 0; } sqlite3BtreeLeave(pDb->pBt); } sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; if( j!=1 ){ pDb->pSchema = 0; } } |
︙ | ︙ | |||
2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 | if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite3Error(db, rc); goto opendb_out; } db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); /* The default safety_level for the main database is 'full'; for the temp ** database it is 'NONE'. This matches the pager layer defaults. */ db->aDb[0].zName = "main"; db->aDb[0].safety_level = 3; | > > | 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 | if( rc!=SQLITE_OK ){ if( rc==SQLITE_IOERR_NOMEM ){ rc = SQLITE_NOMEM; } sqlite3Error(db, rc); goto opendb_out; } sqlite3BtreeEnter(db->aDb[0].pBt); db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt); sqlite3BtreeLeave(db->aDb[0].pBt); db->aDb[1].pSchema = sqlite3SchemaGet(db, 0); /* The default safety_level for the main database is 'full'; for the temp ** database it is 'NONE'. This matches the pager layer defaults. */ db->aDb[0].zName = "main"; db->aDb[0].safety_level = 3; |
︙ | ︙ |
Changes to src/test1.c.
︙ | ︙ | |||
5700 5701 5702 5703 5704 5705 5706 | Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?"); return TCL_ERROR; } if( objc==4 ){ zDb = Tcl_GetString(objv[3]); } | | > | | > | | 5700 5701 5702 5703 5704 5705 5706 5707 5708 5709 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 | Tcl_WrongNumArgs(interp, 1, objv, "DB MODE ?NAME?"); return TCL_ERROR; } if( objc==4 ){ zDb = Tcl_GetString(objv[3]); } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) || ( TCL_OK!=Tcl_GetIntFromObj(0, objv[2], &eMode) && TCL_OK!=Tcl_GetIndexFromObj(interp, objv[2], aMode, "mode", 0, &eMode) )){ return TCL_ERROR; } rc = sqlite3_wal_checkpoint_v2(db, zDb, eMode, &nLog, &nCkpt); if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ const char *zErrCode = sqlite3ErrName(rc); Tcl_AppendResult(interp, zErrCode, " - ", (char *)sqlite3_errmsg(db), 0); return TCL_ERROR; } pRet = Tcl_NewObj(); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(rc==SQLITE_BUSY?1:0)); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nLog)); Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(nCkpt)); |
︙ | ︙ |
Changes to src/vdbesort.c.
︙ | ︙ | |||
446 447 448 449 450 451 452 | ** ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } */ #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) /* The minimum PMA size is set to this value multiplied by the database ** page size in bytes. */ | > | > | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | ** ** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; } */ #define SRVAL(p) ((void*)((SorterRecord*)(p) + 1)) /* The minimum PMA size is set to this value multiplied by the database ** page size in bytes. */ #ifndef SQLITE_SORTER_PMASZ # define SQLITE_SORTER_PMASZ 250 #endif /* Maximum number of PMAs that a single MergeEngine can merge */ #define SORTER_MAX_MERGE_COUNT 16 static int vdbeIncrSwap(IncrMerger*); static void vdbeIncrFree(IncrMerger *); |
︙ | ︙ | |||
845 846 847 848 849 850 851 | pSorter->db = db; for(i=0; i<pSorter->nTask; i++){ SortSubtask *pTask = &pSorter->aTask[i]; pTask->pSorter = pSorter; } if( !sqlite3TempInMemory(db) ){ | | | | 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 | pSorter->db = db; for(i=0; i<pSorter->nTask; i++){ SortSubtask *pTask = &pSorter->aTask[i]; pTask->pSorter = pSorter; } if( !sqlite3TempInMemory(db) ){ pSorter->mnPmaSize = SQLITE_SORTER_PMASZ * pgsz; mxCache = db->aDb[0].pSchema->cache_size; if( mxCache<SQLITE_SORTER_PMASZ ) mxCache = SQLITE_SORTER_PMASZ; pSorter->mxPmaSize = MIN((i64)mxCache*pgsz, SQLITE_MAX_MXPMASIZE); /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary ** large heap allocations. */ if( sqlite3GlobalConfig.pScratch==0 ){ |
︙ | ︙ |
Changes to src/where.c.
︙ | ︙ | |||
2944 2945 2946 2947 2948 2949 2950 | Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ ){ const char *zObj = 0; WhereLoop *pLoop = pLvl->pWLoop; | | | 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 | Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ ){ const char *zObj = 0; WhereLoop *pLoop = pLvl->pWLoop; if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){ zObj = pLoop->u.btree.pIndex->zName; }else{ zObj = pSrclist->a[pLvl->iFrom].zName; } sqlite3VdbeScanStatus( v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj ); |
︙ | ︙ |
Added test/e_walckpt.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 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 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 278 279 280 281 282 283 284 285 286 287 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 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 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 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | # 2014 December 04 # # 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. # #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl source $testdir/wal_common.tcl set testprefix e_walckpt # The following two commands are used to determine if any of the files # "test.db", "test.db2" and "test.db3" are modified by a test case. # # The [save_db_hashes] command saves a hash of the current contents of # all three files in global variables. The [compare_db_hashes] compares # the current contents with the saved hashes and returns a list of the # files that have changed. # proc save_db_hashes {} { global H foreach f {test.db test.db2 test.db3} { set H($f) 0 catch { set H($f) [md5file $f] } } } proc compare_db_hashes {} { global H set ret [list] foreach f {test.db test.db2 test.db3} { set expect 0 catch { set expect [md5file $f] } if {$H($f) != $expect} { lappend ret $f } } set ret } # The following tests are run 3 times, each using a different method of # invoking a checkpoint: # # 1) Using sqlite3_wal_checkpoint_v2() # 2) Using "PRAGMA wal_checkpoint" # 3) Using sqlite3_wal_checkpoint() in place of checkpoint_v2(PASSIVE) # # Cases (2) and (3) are to show that the following statements are # correct, respectively: # # EVIDENCE-OF: R-36706-10507 The PRAGMA wal_checkpoint command can be # used to invoke this interface from SQL. # # EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is # equivalent to # sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). # foreach {tn script} { 1 { proc checkpoint {db mode args} { eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args } } 2 { proc checkpoint {db mode args} { set sql "PRAGMA wal_checkpoint = $mode" if {[llength $args] && [lindex $args 0]!=""} { set sql "PRAGMA [lindex $args 0].wal_checkpoint = $mode" } set rc [catch { $db eval $sql } msg] if {$rc} { regsub {database} $msg {database:} msg error "[sqlite3_errcode $db] - $msg" } set msg } } 3 { proc checkpoint {db mode args} { if {$mode == "passive"} { set rc [eval sqlite3_wal_checkpoint [list $db] $args] if {$rc != "SQLITE_OK"} { error "$rc - [sqlite3_errmsg $db]" } } else { eval sqlite3_wal_checkpoint_v2 [list $db] [list $mode] $args } } } } { eval $script reset_db forcedelete test.db2 test.db3 test.db4 execsql { ATTACH 'test.db2' AS aux; ATTACH 'test.db3' AS aux2; ATTACH 'test.db4' AS aux3; CREATE TABLE t1(x); CREATE TABLE aux.t2(x); CREATE TABLE aux2.t3(x); CREATE TABLE aux3.t4(x); PRAGMA main.journal_mode = WAL; PRAGMA aux.journal_mode = WAL; PRAGMA aux2.journal_mode = WAL; /* Leave aux4 in rollback mode */ } # EVIDENCE-OF: R-49787-09095 The sqlite3_wal_checkpoint_v2(D,X,M,L,C) # interface runs a checkpoint operation on database X of database # connection D in mode M. Status information is written back into # integers pointed to by L and C. # # Tests 1, 2 and 3 below verify the "on database X" part of the # above. Other parts of this requirement are tested below. # # EVIDENCE-OF: R-00653-06026 If parameter zDb is NULL or points to a # zero length string, then the specified operation is attempted on all # WAL databases attached to database connection db. # # Tests 4 and 5 below test this. # foreach {tn2 zDb dblist} { 1 main test.db 2 aux test.db2 3 aux2 test.db3 4 "" {test.db test.db2 test.db3} 5 - {test.db test.db2 test.db3} 6 temp {} } { do_test $tn.1.$tn2 { execsql { INSERT INTO t1 VALUES(1); INSERT INTO t2 VALUES(2); INSERT INTO t3 VALUES(3); } save_db_hashes if {$zDb == "-"} { checkpoint db passive } else { checkpoint db passive $zDb } compare_db_hashes } $dblist } # EVIDENCE-OF: R-38207-48996 If zDb is not NULL (or a zero length # string) and is not the name of any attached database, SQLITE_ERROR is # returned to the caller. do_test $tn.2.1 { list [catch { checkpoint db passive notadb } msg] $msg } {1 {SQLITE_ERROR - unknown database: notadb}} # EVIDENCE-OF: R-14303-42483 If database zDb is the name of an attached # database that is not in WAL mode, SQLITE_OK is returned and both # *pnLog and *pnCkpt set to -1. # if {$tn==3} { # With sqlite3_wal_checkpoint() the two output variables cannot be # tested. So just test that no error is returned when attempting to # checkpoint a db in rollback mode. do_test $tn.2.2.a { checkpoint db passive aux3 } {} } else { do_test $tn.2.2.b { checkpoint db passive aux3 } {0 -1 -1} } # EVIDENCE-OF: R-62028-47212 All calls obtain an exclusive "checkpoint" # lock on the database file. db close testvfs tvfs tvfs filter xShmLock tvfs script filelock proc filelock {method file handle details} { # Test for an exclusive checkpoint lock. A checkpoint lock locks a # single byte starting at offset 1. if {$details == "1 1 lock exclusive"} { set ::seen_checkpoint_lock 1 } } sqlite3 db test.db -vfs tvfs do_test $tn.3.1 { execsql { INSERT INTO t1 VALUES('xyz') } unset -nocomplain ::seen_checkpoint_lock checkpoint db passive set ::seen_checkpoint_lock } {1} db close tvfs delete reset_db #----------------------------------------------------------------------- # EVIDENCE-OF: R-10421-19736 If any other process is running a # checkpoint operation at the same time, the lock cannot be obtained and # SQLITE_BUSY is returned. # # EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured, # it will not be invoked in this case. # testvfs tvfs tvfs filter xWrite sqlite3 db test.db -vfs tvfs sqlite3 db2 test.db -vfs tvfs do_test $tn.3.2.1 { db2 eval { PRAGMA journal_mode = WAL; CREATE TABLE t1(x, y); INSERT INTO t1 VALUES(1,2); INSERT INTO t1 VALUES(3,4); INSERT INTO t1 VALUES(5,6); } file size test.db-wal } [wal_file_size 5 1024] # Connection [db] runs a checkpoint. During this checkpoint, each # time it calls xWrite() to write a page into the database file, we # attempt to start a checkpoint using [db2]. According to the # first requirement being tested, this should return SQLITE_BUSY. According # to the second, the busy-handler belonging to [db2] should not be # invoked. # set ::write_count 0 set ::write_errors [list] proc busy_callback {args} { lappend ::write_errors "busy handler called!" } proc write_callback {args} { set rc [catch {checkpoint db2 passive} msg] if {0==[regexp "database is locked" $msg] && $msg!="1 -1 -1"} { lappend ::write_errors "$rc $msg" } incr ::write_count } db2 busy busy_callback tvfs script write_callback do_test $tn.3.2.2 { db eval {SELECT * FROM sqlite_master} checkpoint db full set ::write_count } {2} do_test $tn.3.2.3 { set ::write_errors } {} db close db2 close tvfs delete proc busy_handler {mode busy_handler_mode n} { incr ::busy_handler_counter switch -- $busy_handler_mode { 1 { # Do nothing. Do not block. return 1 } 2 { # Close first the reader, then later the writer. if {$n==5} { catch {db2 eval commit} } if {$n==10} { catch {db3 eval commit} } return 0 } 3 { # Close first the writer, then later the reader. if {$n==5} { catch {db2 eval commit} } if {$n==10} { catch {db3 eval commit} } return 0 } } } foreach {mode busy_handler_mode} { passive 1 full 1 full 2 full 3 } { set ::sync_counter 0 proc tvfs_callback {method args} { set tail [file tail [lindex $args 0]] if {$method == "xSync" && $tail == "test.db"} { incr ::sync_counter } if {$method == "xWrite" && $tail=="test.db"} { if {$::write_ok < 0} { set ::write_ok [expr ![catch {db5 eval { BEGIN IMMEDIATE }}]] catch { db5 eval ROLLBACK } } if {$::read_ok < 0} { set ::read_ok [expr ![catch {db5 eval { SELECT * FROM t1 }}]] } } } catch { db close } forcedelete test.db testvfs tvfs sqlite3 db test.db -vfs tvfs #tvfs filter xSync tvfs script tvfs_callback do_execsql_test $tn.4.$mode.0 { CREATE TABLE t1(a, b); CREATE TABLE t2(a, b); PRAGMA journal_mode = wal; INSERT INTO t1 VALUES(1, 2); INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(5, 6); } {wal} # Open a reader on the current database snapshot. do_test $tn.4.$mode.1 { sqlite3 db2 test.db -vfs tvfs execsql { BEGIN; SELECT * FROM t1 UNION ALL SELECT * FROM t2; } db2 } {1 2 3 4 5 6} # Open a writer. Write a transaction. Then begin, but do not commit, # a second transaction. do_test $tn.4.$mode.2 { sqlite3 db3 test.db -vfs tvfs execsql { INSERT INTO t2 VALUES(7, 8); BEGIN; INSERT INTO t2 VALUES(9, 10); SELECT * FROM t1 UNION ALL SELECT * FROM t2; } db3 } {1 2 3 4 5 6 7 8 9 10} sqlite3 db5 test.db -vfs tvfs # Register a busy-handler with connection [db]. # db busy [list busy_handler $mode $busy_handler_mode] set ::sync_counter 0 set ::busy_handler_counter 0 set ::read_ok -1 set ::write_ok -1 do_test $tn.4.$mode.3 { checkpoint db $mode main set {} {} } {} if { $mode=="passive" } { # EVIDENCE-OF: R-16333-64433 Checkpoint as many frames as possible # without waiting for any database readers or writers to finish, then # sync the database file if all frames in the log were checkpointed. # # "As many frames as possible" means all but the last two transactions # (the two that write to table t2, of which the scond is unfinished). # So copying the db file only we see the t1 change, but not the t2 # modifications. # # The busy handler is not invoked (see below) and the db reader and # writer are still active - so the checkpointer did not wait for either # readers or writers. As a result the checkpoint was not finished and # so the db file is not synced. # # EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked # in the SQLITE_CHECKPOINT_PASSIVE mode. # # It's not. Test case "$tn.4.$mode.6". # do_test $tn.4.$mode.4 { forcecopy test.db abc.db sqlite3 db4 abc.db db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } } {1 2 3 4 5 6} do_test $tn.4.$mode.5 { set ::sync_counter } 0 do_test $tn.4.$mode.6 { set ::busy_handler_counter } 0 db4 close db2 eval COMMIT db3 eval COMMIT # EVIDENCE-OF: R-65499-53765 On the other hand, passive mode might leave # the checkpoint unfinished if there are concurrent readers or writers. # # The reader and writer have now dropped their locks. And so a # checkpoint now is able to checkpoint more frames. Showing that the # attempt above was left "unfinished". # # Also, because the checkpoint finishes this time, the db is synced. # Which is part of R-16333-64433 above. # do_test $tn.4.$mode.7 { checkpoint db $mode main forcecopy test.db abc.db sqlite3 db4 abc.db db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } } {1 2 3 4 5 6 7 8 9 10} do_test $tn.4.$mode.6 { set ::sync_counter } 1 do_test $tn.4.$mode.7 { set ::busy_handler_counter } 0 db4 close } if { $mode=="full" } { if {$busy_handler_mode==2 || $busy_handler_mode==3} { # EVIDENCE-OF: R-59171-47567 This mode blocks (it invokes the # busy-handler callback) until there is no database writer and all # readers are reading from the most recent database snapshot. # # Show that both the reader and writer have finished: # do_test $tn.4.$mode.7 { list [catchsql COMMIT db2] [catchsql COMMIT db3] } [list \ {1 {cannot commit - no transaction is active}} \ {1 {cannot commit - no transaction is active}} \ ] # EVIDENCE-OF: R-29177-48281 It then checkpoints all frames in the log # file and syncs the database file. # do_test $tn.4.$mode.8 { forcecopy test.db abc.db sqlite3 db4 abc.db db4 eval { SELECT * FROM t1 UNION ALL SELECT * FROM t2 } } {1 2 3 4 5 6 7 8 9 10} do_test $tn.4.$mode.9 { set ::sync_counter } 1 db4 close # EVIDENCE-OF: R-51867-44713 This mode blocks new database writers # while it is pending, but new database readers are allowed to continue # unimpeded. do_test $tn.4.$mode.10 { list $::write_ok $::read_ok } {0 1} } } db2 close db3 close db5 close } db close tvfs delete } #----------------------------------------------------------------------- # EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint # mode: # # Valid checkpoint modes are 0, 1, 2 and 3. # sqlite3 db test.db foreach {tn mode res} { 0 -1001 {1 {SQLITE_MISUSE - not an error}} 1 -1 {1 {SQLITE_MISUSE - not an error}} 2 0 {0 {0 -1 -1}} 3 1 {0 {0 -1 -1}} 4 2 {0 {0 -1 -1}} 5 3 {0 {0 -1 -1}} 6 4 {1 {SQLITE_MISUSE - not an error}} 7 114 {1 {SQLITE_MISUSE - not an error}} 8 1000000 {1 {SQLITE_MISUSE - not an error}} } { do_test 4.$tn { list [catch "sqlite3_wal_checkpoint_v2 db $mode" msg] $msg } $res } finish_test |
Changes to test/threadtest3.c.
︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 | launch_thread(&err, &threads, dynamic_triggers_2, 0); launch_thread(&err, &threads, dynamic_triggers_1, 0); join_all_threads(&err, &threads); print_and_free_err(&err); } #include "tt3_checkpoint.c" int main(int argc, char **argv){ struct ThreadTest { void (*xTest)(int); const char *zTest; int nMs; } aTest[] = { { walthread1, "walthread1", 20000 }, { walthread2, "walthread2", 20000 }, { walthread3, "walthread3", 20000 }, { walthread4, "walthread4", 20000 }, { walthread5, "walthread5", 1000 }, { walthread5, "walthread5", 1000 }, { cgt_pager_1, "cgt_pager_1", 0 }, { dynamic_triggers, "dynamic_triggers", 20000 }, { checkpoint_starvation_1, "checkpoint_starvation_1", 10000 }, { checkpoint_starvation_2, "checkpoint_starvation_2", 10000 }, }; int i; char *zTest = 0; int nTest = 0; int bTestfound = 0; int bPrefix = 0; | > > > > > | 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 | launch_thread(&err, &threads, dynamic_triggers_2, 0); launch_thread(&err, &threads, dynamic_triggers_1, 0); join_all_threads(&err, &threads); print_and_free_err(&err); } #include "tt3_checkpoint.c" #include "tt3_index.c" int main(int argc, char **argv){ struct ThreadTest { void (*xTest)(int); const char *zTest; int nMs; } aTest[] = { { walthread1, "walthread1", 20000 }, { walthread2, "walthread2", 20000 }, { walthread3, "walthread3", 20000 }, { walthread4, "walthread4", 20000 }, { walthread5, "walthread5", 1000 }, { walthread5, "walthread5", 1000 }, { cgt_pager_1, "cgt_pager_1", 0 }, { dynamic_triggers, "dynamic_triggers", 20000 }, { checkpoint_starvation_1, "checkpoint_starvation_1", 10000 }, { checkpoint_starvation_2, "checkpoint_starvation_2", 10000 }, { create_drop_index_1, "create_drop_index_1", 10000 }, }; int i; char *zTest = 0; int nTest = 0; int bTestfound = 0; int bPrefix = 0; |
︙ | ︙ |
Added test/tt3_index.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | /* ** 2014 December 9 ** ** 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. ** ************************************************************************* ** ** create_drop_index_1 */ static char *create_drop_index_thread(int iTid, int iArg){ Error err = {0}; /* Error code and message */ Sqlite db = {0}; /* SQLite database connection */ while( !timetostop(&err) ){ opendb(&err, &db, "test.db", 0); sql_script(&err, &db, "DROP INDEX IF EXISTS i1;" "DROP INDEX IF EXISTS i2;" "DROP INDEX IF EXISTS i3;" "DROP INDEX IF EXISTS i4;" "CREATE INDEX IF NOT EXISTS i1 ON t1(a);" "CREATE INDEX IF NOT EXISTS i2 ON t1(b);" "CREATE INDEX IF NOT EXISTS i3 ON t1(c);" "CREATE INDEX IF NOT EXISTS i4 ON t1(d);" "SELECT * FROM t1 ORDER BY a;" "SELECT * FROM t1 ORDER BY b;" "SELECT * FROM t1 ORDER BY c;" "SELECT * FROM t1 ORDER BY d;" ); closedb(&err, &db); } print_and_free_err(&err); return sqlite3_mprintf("ok"); } static void create_drop_index_1(int nMs){ Error err = {0}; Sqlite db = {0}; Threadset threads = {0}; opendb(&err, &db, "test.db", 1); sql_script(&err, &db, "CREATE TABLE t1(a, b, c, d);" "WITH data(x) AS (SELECT 1 UNION ALL SELECT x+1 FROM data WHERE x<100) " "INSERT INTO t1 SELECT x,x,x,x FROM data;" ); closedb(&err, &db); setstoptime(&err, nMs); sqlite3_enable_shared_cache(1); launch_thread(&err, &threads, create_drop_index_thread, 0); launch_thread(&err, &threads, create_drop_index_thread, 0); launch_thread(&err, &threads, create_drop_index_thread, 0); launch_thread(&err, &threads, create_drop_index_thread, 0); launch_thread(&err, &threads, create_drop_index_thread, 0); sqlite3_enable_shared_cache(0); join_all_threads(&err, &threads); print_and_free_err(&err); } |