Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add further tests for deferred page allocation. And fixes for the same. |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | begin-concurrent |
Files: | files | file ages | folders |
SHA1: |
ed0a31be726e60115a5dd73d4ed58020 |
User & Date: | dan 2015-08-22 17:28:55.533 |
Context
2015-08-22
| ||
20:32 | Fix a problem to do with detecting unlocked transaction conflicts if another client restarts the wal while the transaction is running. (check-in: e3968b2562 user: dan tags: begin-concurrent) | |
17:28 | Add further tests for deferred page allocation. And fixes for the same. (check-in: ed0a31be72 user: dan tags: begin-concurrent) | |
07:56 | Merge further trunk changes. (check-in: c2327a3b8e user: dan tags: begin-concurrent) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
435 436 437 438 439 440 441 442 | pLock->eLock = READ_LOCK; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ /* | > > | | | | < < < < < < < < > > > > | | > | | | 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 489 490 491 492 493 494 495 496 497 498 | pLock->eLock = READ_LOCK; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ #ifdef SQLITE_ENABLE_UNLOCKED /* ** The following structure - BtreePtrmap - stores the in-memory pointer map ** used for newly allocated pages in UNLOCKED transactions. Such pages are ** always allocated in a contiguous block (from the end of the file) starting ** with page BtreePtrmap.iFirst. */ typedef struct RollbackEntry RollbackEntry; typedef struct PtrmapEntry PtrmapEntry; struct PtrmapEntry { Pgno parent; u8 eType; }; struct RollbackEntry { Pgno pgno; Pgno parent; u8 eType; }; struct BtreePtrmap { Pgno iFirst; /* First new page number aPtr[0] */ int nPtrAlloc; /* Allocated size of aPtr[] array */ PtrmapEntry *aPtr; /* Array of parent page numbers */ int nSvpt; /* Used size of aSvpt[] array */ int nSvptAlloc; /* Allocated size of aSvpt[] */ int *aSvpt; /* First aRollback[] entry for savepoint i */ int nRollback; /* Used size of aRollback[] array */ int nRollbackAlloc; /* Allocated size of aRollback[] array */ RollbackEntry *aRollback; /* Array of rollback entries */ }; /* ** If page number pgno is greater than or equal to BtreePtrmap.iFirst, ** store an entry for it in the pointer-map structure. */ static int btreePtrmapStore( BtShared *pBt, Pgno pgno, u8 eType, Pgno parent ){ BtreePtrmap *pMap = pBt->pMap; if( pgno>=pMap->iFirst ){ int iEntry = pgno - pMap->iFirst; /* Grow the aPtr[] array as required */ while( iEntry>=pMap->nPtrAlloc ){ int nNew = pMap->nPtrAlloc ? pMap->nPtrAlloc*2 : 16; PtrmapEntry *aNew = (PtrmapEntry*)sqlite3_realloc( pMap->aPtr, nNew*sizeof(PtrmapEntry) ); if( aNew==0 ){ return SQLITE_NOMEM; }else{ |
︙ | ︙ | |||
531 532 533 534 535 536 537 | return SQLITE_OK; } /* ** Open savepoint iSavepoint, if it is not already open. */ | | > | | 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | return SQLITE_OK; } /* ** Open savepoint iSavepoint, if it is not already open. */ static int btreePtrmapBegin(BtShared *pBt, int nSvpt){ BtreePtrmap *pMap = pBt->pMap; if( pMap && nSvpt<pMap->nSvpt ){ int i; if( nSvpt>=pMap->nSvptAlloc ){ int nNew = pMap->nSvptAlloc ? pMap->nSvptAlloc*2 : 16; int *aNew = sqlite3_realloc(pMap->aSvpt, sizeof(int) * nNew); if( aNew==0 ){ return SQLITE_NOMEM; }else{ |
︙ | ︙ | |||
558 559 560 561 562 563 564 | return SQLITE_OK; } /* ** Rollback (if op==SAVEPOINT_ROLLBACK) or release (if op==SAVEPOINT_RELEASE) ** savepoint iSvpt. */ | | > > | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | return SQLITE_OK; } /* ** Rollback (if op==SAVEPOINT_ROLLBACK) or release (if op==SAVEPOINT_RELEASE) ** savepoint iSvpt. */ static void btreePtrmapEnd(BtShared *pBt, int op, int iSvpt){ BtreePtrmap *pMap = pBt->pMap; if( pMap ){ assert( op==SAVEPOINT_ROLLBACK || op==SAVEPOINT_RELEASE ); assert( iSvpt>=0 || (iSvpt==-1 && op==SAVEPOINT_ROLLBACK) ); if( iSvpt<0 ){ pMap->nSvpt = 0; pMap->nRollback = 0; memset(pMap->aPtr, 0, sizeof(Pgno) * pMap->nPtrAlloc); }else if( iSvpt<pMap->nSvpt ){ if( op==SAVEPOINT_ROLLBACK ){ int ii; for(ii=pMap->nRollback-1; ii>=pMap->aSvpt[iSvpt]; ii--){ RollbackEntry *p = &pMap->aRollback[ii]; PtrmapEntry *pEntry = &pMap->aPtr[p->pgno - pMap->iFirst]; pEntry->parent = p->parent; pEntry->eType = p->eType; } } pMap->nSvpt = iSvpt + (op==SAVEPOINT_ROLLBACK); pMap->nRollback = pMap->aSvpt[iSvpt]; } } } /* ** This function is called after an UNLOCKED transaction is opened on the ** database. It allocates the BtreePtrmap structure used to track pointers ** to allocated pages and zeroes the nFree/iTrunk fields in the database ** header on page 1. */ static int btreePtrmapAllocate(BtShared *pBt){ int rc = SQLITE_OK; BtreePtrmap *pMap = sqlite3_malloc(sizeof(BtreePtrmap)); assert( pBt->pMap==0 && sqlite3PagerIsUnlocked(pBt->pPager) ); if( pMap==0 ){ rc = SQLITE_NOMEM; }else{ memset(&pBt->pPage1->aData[32], 0, sizeof(u32)*2); memset(pMap, 0, sizeof(BtreePtrmap)); pMap->iFirst = pBt->nPage + 1; pBt->pMap = pMap; } return rc; } /* ** Free any BtreePtrmap structure allocated by an earlier call to ** btreePtrmapAllocate(). */ static void btreePtrmapDelete(BtShared *pBt){ BtreePtrmap *pMap = pBt->pMap; if( pMap ){ sqlite3_free(pMap->aRollback); sqlite3_free(pMap->aPtr); sqlite3_free(pMap->aSvpt); sqlite3_free(pMap); pBt->pMap = 0; } } #else # define btreePtrmapAllocate(x) SQLITE_OK # define btreePtrmapDelete(x) # define btreePtrmapBegin(x,y) SQLITE_OK # define btreePtrmapEnd(x,y,z) #endif static void releasePage(MemPage *pPage); /* Forward reference */ /* ***** This routine is used inside of assert() only **** ** ** Verify that the cursor holds the mutex on its BtShared |
︙ | ︙ | |||
1020 1021 1022 1023 1024 1025 1026 | static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ DbPage *pDbPage; /* The pointer map page */ u8 *pPtrmap; /* The pointer map data */ Pgno iPtrmap; /* The pointer map page number */ int offset; /* Offset in pointer map page */ int rc; /* Return code from subfunctions */ | < > > > > > | | < > | | | | | | | | | | | | | | | | > | | | | | | | | | | | > | < | 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 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 | static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ DbPage *pDbPage; /* The pointer map page */ u8 *pPtrmap; /* The pointer map data */ Pgno iPtrmap; /* The pointer map page number */ int offset; /* Offset in pointer map page */ int rc; /* Return code from subfunctions */ if( *pRC ) return; assert( sqlite3_mutex_held(pBt->mutex) ); /* The master-journal page number is never added to a pointer-map page */ assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); #ifdef SQLITE_ENABLE_UNLOCKED if( pBt->pMap ){ *pRC = btreePtrmapStore(pBt, key, eType, parent); return; } #endif assert( pBt->autoVacuum ); if( key==0 ){ *pRC = SQLITE_CORRUPT_BKPT; return; } iPtrmap = PTRMAP_PAGENO(pBt, key); rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); if( rc!=SQLITE_OK ){ *pRC = rc; return; } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ *pRC = SQLITE_CORRUPT_BKPT; goto ptrmap_exit; } assert( offset <= (int)pBt->usableSize-5 ); pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); *pRC= rc = sqlite3PagerWrite(pDbPage); if( rc==SQLITE_OK ){ pPtrmap[offset] = eType; put4byte(&pPtrmap[offset+1], parent); } } ptrmap_exit: sqlite3PagerUnref(pDbPage); } /* ** Read an entry from the pointer map. ** ** This routine retrieves the pointer map entry for page 'key', writing ** the type and parent page number to *pEType and *pPgno respectively. |
︙ | ︙ | |||
2068 2069 2070 2071 2072 2073 2074 | } u32 sqlite3BtreeLastPage(Btree *p){ assert( sqlite3BtreeHoldsMutex(p) ); assert( ((p->pBt->nPage)&0x8000000)==0 ); return btreePagecount(p->pBt); } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 | } u32 sqlite3BtreeLastPage(Btree *p){ assert( sqlite3BtreeHoldsMutex(p) ); assert( ((p->pBt->nPage)&0x8000000)==0 ); return btreePagecount(p->pBt); } /* ** Get a page from the pager and initialize it. ** ** If pCur!=0 then the page is being fetched as part of a moveToChild() ** call. Do additional sanity checking on the page in this case. ** And if the fetch fails, this routine must decrement pCur->iPage. ** |
︙ | ︙ | |||
3337 3338 3339 3340 3341 3342 3343 | int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1); int bSubjInMem = sqlite3TempInMemory(p->db); assert( p->db->bUnlocked==0 || wrflag==1 ); rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } | | | | 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 | int exFlag = (p->db->bUnlocked && !ISAUTOVACUUM) ? -1 : (wrflag>1); int bSubjInMem = sqlite3TempInMemory(p->db); assert( p->db->bUnlocked==0 || wrflag==1 ); rc = sqlite3PagerBegin(pBt->pPager, exFlag, bSubjInMem); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } if( rc==SQLITE_OK && sqlite3PagerIsUnlocked(pBt->pPager) ){ rc = btreePtrmapAllocate(pBt); } } } if( rc!=SQLITE_OK ){ unlockBtreeIfUnused(pBt); } |
︙ | ︙ | |||
3398 3399 3400 3401 3402 3403 3404 | if( rc==SQLITE_OK && wrflag ){ /* This call makes sure that the pager has the correct number of ** open savepoints. If the second parameter is greater than 0 and ** the sub-journal is not already open, then it will be opened here. */ int nSavepoint = p->db->nSavepoint; rc = sqlite3PagerOpenSavepoint(pBt->pPager, nSavepoint); | | | | 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 | if( rc==SQLITE_OK && wrflag ){ /* This call makes sure that the pager has the correct number of ** open savepoints. If the second parameter is greater than 0 and ** the sub-journal is not already open, then it will be opened here. */ int nSavepoint = p->db->nSavepoint; rc = sqlite3PagerOpenSavepoint(pBt->pPager, nSavepoint); if( rc==SQLITE_OK && nSavepoint ){ rc = btreePtrmapBegin(pBt, nSavepoint); } } btreeIntegrity(p); sqlite3BtreeLeave(p); return rc; } |
︙ | ︙ | |||
3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 | return rc; } #else /* ifndef SQLITE_OMIT_AUTOVACUUM */ # define setChildPtrmaps(x) SQLITE_OK #endif /* ** The b-tree handle passed as the only argument is about to commit an ** UNLOCKED transaction. At this point it is guaranteed that this is ** possible - the wal WRITER lock is held and it is known that there are ** no conflicts with committed transactions. */ static int btreeFixUnlocked(Btree *p){ | > | 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 | return rc; } #else /* ifndef SQLITE_OMIT_AUTOVACUUM */ # define setChildPtrmaps(x) SQLITE_OK #endif #ifdef SQLITE_ENABLE_UNLOCKED /* ** The b-tree handle passed as the only argument is about to commit an ** UNLOCKED transaction. At this point it is guaranteed that this is ** possible - the wal WRITER lock is held and it is known that there are ** no conflicts with committed transactions. */ static int btreeFixUnlocked(Btree *p){ |
︙ | ︙ | |||
3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 3935 | nCurrent = MAX(nPage, nHPage); for(iPg=pMap->iFirst; iPg<=iLast && rc==SQLITE_OK; iPg++){ MemPage *pPg = 0; Pgno iNew; /* New page number for pPg */ PtrmapEntry *pEntry; /* Pointer map entry for page iPg */ pEntry = &pMap->aPtr[iPg - pMap->iFirst]; if( pEntry->eType==PTRMAP_FREEPAGE ){ MemPage *pFree = 0; Pgno dummy; rc = allocateBtreePage(pBt, &pFree, &dummy, iPg, BTALLOC_EXACT); releasePage(pFree); assert( rc!=SQLITE_OK || dummy==iPg ); }else{ btreeGetPage(pBt, iPg, &pPg, 0); assert( sqlite3PagerIswriteable(pPg->pDbPage) ); assert( sqlite3PagerPageRefcount(pPg->pDbPage)==1 ); iNew = ++nCurrent; rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent, iNew, 1); releasePageNotNull(pPg); } } sqlite3PagerSetDbsize(pPager, nCurrent); nFree = get4byte(&p1[36]); nFinal = MAX(nCurrent-nFree, nHPage); | > > > > > | > | > > > > | 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 3942 3943 3944 3945 3946 3947 3948 3949 3950 3951 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 | nCurrent = MAX(nPage, nHPage); for(iPg=pMap->iFirst; iPg<=iLast && rc==SQLITE_OK; iPg++){ MemPage *pPg = 0; Pgno iNew; /* New page number for pPg */ PtrmapEntry *pEntry; /* Pointer map entry for page iPg */ if( iPg==PENDING_BYTE_PAGE(pBt) ) continue; pEntry = &pMap->aPtr[iPg - pMap->iFirst]; if( pEntry->eType==PTRMAP_FREEPAGE ){ MemPage *pFree = 0; Pgno dummy; rc = allocateBtreePage(pBt, &pFree, &dummy, iPg, BTALLOC_EXACT); releasePage(pFree); assert( rc!=SQLITE_OK || dummy==iPg ); }else{ btreeGetPage(pBt, iPg, &pPg, 0); assert( sqlite3PagerIswriteable(pPg->pDbPage) ); assert( sqlite3PagerPageRefcount(pPg->pDbPage)==1 ); iNew = ++nCurrent; if( iNew==PENDING_BYTE_PAGE(pBt) ) iNew = ++nCurrent; rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent, iNew, 1); releasePageNotNull(pPg); } } sqlite3PagerSetDbsize(pPager, nCurrent); assert( nCurrent!=PENDING_BYTE_PAGE(pBt) ); nFree = get4byte(&p1[36]); nFinal = MAX(nCurrent-nFree, nHPage); if( nCurrent>PENDING_BYTE_PAGE(pBt) && nFinal<=PENDING_BYTE_PAGE(pBt) ){ nFinal--; } for(iPg=nFinal+1; rc==SQLITE_OK && iPg<=nCurrent; iPg++){ Pgno iNew; /* New page number for pPg */ MemPage *pFree; PtrmapEntry *pEntry; /* Pointer map entry for page iPg */ if( iPg==PENDING_BYTE_PAGE(pBt) ) continue; pEntry = &pMap->aPtr[iPg - pMap->iFirst]; if( pEntry->eType==PTRMAP_FREEPAGE ){ rc = allocateBtreePage(pBt, &pFree, &iNew, iPg, BTALLOC_EXACT); releasePage(pFree); }else{ rc = allocateBtreePage(pBt, &pFree, &iNew, nFinal, BTALLOC_LE); assert( rc!=SQLITE_OK || iNew<=nFinal ); releasePage(pFree); if( rc==SQLITE_OK ){ MemPage *pPg = 0; btreeGetPage(pBt, iPg, &pPg, 0); rc = relocatePage(pBt, pPg, pEntry->eType, pEntry->parent,iNew,1); releasePage(pPg); } } } put4byte(&p1[28], nFinal); } } sqlite3PagerSetDbsize(pPager, nFinal); } return rc; } #endif /* ** This routine does the first phase of a two-phase commit. This routine ** causes a rollback journal to be created (if it does not already exist) ** and populated with enough information so that if a power loss occurs ** the database can be restored to its original state by playing back ** the journal. Then the contents of the journal are flushed out to |
︙ | ︙ | |||
3991 3992 3993 3994 3995 3996 3997 | int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM | < < < | | | 4007 4008 4009 4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021 4022 4023 4024 4025 4026 4027 4028 4029 4030 4031 4032 4033 | int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){ int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); #ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ assert( ISUNLOCKED==0 ); rc = autoVacuumCommit(pBt); if( rc!=SQLITE_OK ){ sqlite3BtreeLeave(p); return rc; } } if( pBt->bDoTruncate ){ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage); } #endif if( rc==SQLITE_OK && ISUNLOCKED ){ rc = btreeFixUnlocked(p); } if( rc==SQLITE_OK ){ rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); } sqlite3BtreeLeave(p); } |
︙ | ︙ | |||
4055 4056 4057 4058 4059 4060 4061 | /* Set the current transaction state to TRANS_NONE and unlock the ** pager if this call closed the only read or write transaction. */ p->inTrans = TRANS_NONE; unlockBtreeIfUnused(pBt); } /* If this was an UNLOCKED transaction, delete the pBt->pMap object */ | | | 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 | /* Set the current transaction state to TRANS_NONE and unlock the ** pager if this call closed the only read or write transaction. */ p->inTrans = TRANS_NONE; unlockBtreeIfUnused(pBt); } /* If this was an UNLOCKED transaction, delete the pBt->pMap object */ btreePtrmapDelete(pBt); btreeIntegrity(p); } /* ** Commit the transaction currently in progress. ** ** This routine implements the second phase of a 2-phase commit. The |
︙ | ︙ | |||
4281 4282 4283 4284 4285 4286 4287 | assert( pBt->inTransaction==TRANS_WRITE ); /* At the pager level, a statement transaction is a savepoint with ** an index greater than all savepoints created explicitly using ** SQL statements. It is illegal to open, release or rollback any ** such savepoints while the statement transaction savepoint is active. */ rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); | | | | 4294 4295 4296 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 | assert( pBt->inTransaction==TRANS_WRITE ); /* At the pager level, a statement transaction is a savepoint with ** an index greater than all savepoints created explicitly using ** SQL statements. It is illegal to open, release or rollback any ** such savepoints while the statement transaction savepoint is active. */ rc = sqlite3PagerOpenSavepoint(pBt->pPager, iStatement); if( rc==SQLITE_OK ){ rc = btreePtrmapBegin(pBt, iStatement); } sqlite3BtreeLeave(p); return rc; } /* ** The second argument to this function, op, is always SAVEPOINT_ROLLBACK |
︙ | ︙ | |||
4307 4308 4309 4310 4311 4312 4313 | int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); | | | 4320 4321 4322 4323 4324 4325 4326 4327 4328 4329 4330 4331 4332 4333 4334 | int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){ int rc = SQLITE_OK; if( p && p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK ); assert( iSavepoint>=0 || (iSavepoint==-1 && op==SAVEPOINT_ROLLBACK) ); sqlite3BtreeEnter(p); btreePtrmapEnd(pBt, op, iSavepoint); rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint); if( rc==SQLITE_OK ){ if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){ pBt->nPage = 0; } rc = newDatabase(pBt); pBt->nPage = get4byte(28 + pBt->pPage1->aData); |
︙ | ︙ | |||
5813 5814 5815 5816 5817 5818 5819 | assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 ** stores stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); | | | 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 | assert( eMode==BTALLOC_ANY || (nearby>0 && REQUIRE_PTRMAP ) ); pPage1 = pBt->pPage1; mxPage = btreePagecount(pBt); /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36 ** stores stores the total number of pages on the freelist. */ n = get4byte(&pPage1->aData[36]); testcase( n==mxPage-1 ); if( ISUNLOCKED==0 && n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } /* Ensure page 1 is writable. This function will either change the number ** of pages in the free-list or the size of the database file. Since both ** of these operations involve modifying page 1 header fields, page 1 ** will definitely be written by this transaction. If this is an UNLOCKED |
︙ | ︙ | |||
5836 5837 5838 5839 5840 5841 5842 | u32 nSearch = 0; /* Count of the number of search attempts */ /* If eMode==BTALLOC_EXACT and a query of the pointer-map ** shows that the page 'nearby' is somewhere on the free-list, then ** the entire-list will be searched for that page. */ if( eMode==BTALLOC_EXACT ){ | | | 5849 5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860 5861 5862 5863 | u32 nSearch = 0; /* Count of the number of search attempts */ /* If eMode==BTALLOC_EXACT and a query of the pointer-map ** shows that the page 'nearby' is somewhere on the free-list, then ** the entire-list will be searched for that page. */ if( eMode==BTALLOC_EXACT ){ assert( ISAUTOVACUUM!=ISUNLOCKED ); if( ISAUTOVACUUM ){ if( nearby<=mxPage ){ u8 eType; assert( nearby>0 ); assert( pBt->autoVacuum ); rc = ptrmapGet(pBt, nearby, &eType, 0); if( rc ) return rc; |
︙ | ︙ | |||
9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 | } /* ** Return the size of the header added to each page by this module. */ int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } int sqlite3BtreeExclusiveLock(Btree *p){ int rc; BtShared *pBt = p->pBt; assert( p->inTrans==TRANS_WRITE && pBt->pPage1 ); sqlite3BtreeEnter(p); rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage); sqlite3BtreeLeave(p); return rc; } | > > > > > > > > > > > > > > > > > | 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9954 9955 9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 | } /* ** Return the size of the header added to each page by this module. */ int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); } /* ** This function is called to ensure that all locks required to commit the ** current write-transaction to the database file are held. If the db is ** in rollback mode, this means the EXCLUSIVE lock on the database file. ** ** Or, if this is an UNLOCKED transaction on a wal-mode database, the WRITER ** lock on the wal file. In this case this function also checks that the ** UNLOCKED transaction can be safely committed (does not commit with any ** other transaction committed since it was opened). ** ** SQLITE_OK is returned if successful. SQLITE_BUSY if the required locks ** cannot be obtained due to a conflicting lock. If the locks cannot be ** obtained for an UNLOCKED transaction due to a conflict with an already ** committed transaction, SQLITE_BUSY_SNAPSHOT is returned. Otherwise, if ** some other error (OOM, IO, etc.) occurs, the relevant SQLite error code ** is returned. */ int sqlite3BtreeExclusiveLock(Btree *p){ int rc; BtShared *pBt = p->pBt; assert( p->inTrans==TRANS_WRITE && pBt->pPage1 ); sqlite3BtreeEnter(p); rc = sqlite3PagerExclusiveLock(pBt->pPager, pBt->pPage1->pDbPage); sqlite3BtreeLeave(p); return rc; } |
Changes to src/btreeInt.h.
︙ | ︙ | |||
660 661 662 663 664 665 666 | #ifndef SQLITE_OMIT_AUTOVACUUM #define ISAUTOVACUUM (pBt->autoVacuum) #else #define ISAUTOVACUUM 0 #endif #ifdef SQLITE_ENABLE_UNLOCKED | | | > | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 | #ifndef SQLITE_OMIT_AUTOVACUUM #define ISAUTOVACUUM (pBt->autoVacuum) #else #define ISAUTOVACUUM 0 #endif #ifdef SQLITE_ENABLE_UNLOCKED # define ISUNLOCKED (pBt->pMap!=0) #else # define ISUNLOCKED 0 #endif #define REQUIRE_PTRMAP (ISAUTOVACUUM || ISUNLOCKED) /* ** This structure is passed around through all the sanity checking routines ** in order to keep track of some global state information. ** ** The aRef[] array is allocated so that there is 1 bit for each page in ** the database. As the integrity-check proceeds, for each page used in |
︙ | ︙ |
Changes to test/unlocked2.test.
︙ | ︙ | |||
64 65 66 67 68 69 70 | ROLLBACK; BEGIN UNLOCKED; INSERT INTO t1 VALUES(randomblob(1500)); } sql2 { INSERT INTO t2 VALUES(8); } | < | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | ROLLBACK; BEGIN UNLOCKED; INSERT INTO t1 VALUES(randomblob(1500)); } sql2 { INSERT INTO t2 VALUES(8); } sql1 { COMMIT; } } {} do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} } |
︙ | ︙ | |||
220 221 222 223 224 225 226 227 228 229 | INSERT INTO t2 VALUES(randomblob(1500)); DELETE FROM t2 WHERE rowid IN (1, 2); } sql1 COMMIT } {} } finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | INSERT INTO t2 VALUES(randomblob(1500)); DELETE FROM t2 WHERE rowid IN (1, 2); } sql1 COMMIT } {} } #------------------------------------------------------------------------- # do_multiclient_test tn { do_test 5.$tn.1 { sql1 { PRAGMA journal_mode = wal; CREATE TABLE t1(x); CREATE TABLE t2(x); INSERT INTO t1 VALUES(randomblob(1500)); PRAGMA page_count; } } {wal 4} do_test 5.$tn.2 { sql1 { BEGIN UNLOCKED; INSERT INTO t2 VALUES(randomblob(1500)); PRAGMA page_count; } } {5} do_test 5.$tn.3 { sql2 { DELETE FROM t1; PRAGMA freelist_count; PRAGMA page_count; } } {1 4} do_test 5.$tn.4 { sql1 COMMIT } {} do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} } finish_test |
Added test/unlocked3.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 | # 2015 July 26 # # 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. # #*********************************************************************** # # Tests for transactions started with BEGIN UNLOCKED. The tests in this # file focus on testing that deferred page allocation works properly. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set ::testprefix unlocked3 if {$AUTOVACUUM} { finish_test ; return } proc create_schema {} { db eval { PRAGMA journal_mode = wal; CREATE TABLE t1(x, y); CREATE TABLE t2(x, y); CREATE TABLE t3(x, y); CREATE TABLE t4(x, y); CREATE INDEX i1 ON t1(y, x); CREATE INDEX i2 ON t2(y, x); CREATE INDEX i3 ON t3(y, x); CREATE INDEX i4 ON t4(y, x); } } proc do_sql_op {iTbl iOp} { set db "db$iTbl" switch $iOp { "i" { set sql " WITH cnt(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM cnt WHERE i<10) INSERT INTO t$iTbl SELECT randomblob(800), randomblob(800) FROM cnt; " } "d" { set sql " DELETE FROM t$iTbl WHERE rowid IN ( SELECT rowid FROM t$iTbl ORDER BY 1 ASC LIMIT 10 ) " } default { error "bad iOp parameter: $iOp" } } $db eval $sql } set DBLIST {db1 db2 db3 db4} create_schema foreach {tn oplist} { 1 {1i 2i 3i 4i} 2 {1iii 2iii 3iii 4iii} 3 {1d 2d 3d 4d} . ----------------------- 4 {1i} 5 {1d 2i} . ----------------------- 6 {1iii 2iii 3iii 4iii} 7 {1di 2id 3iii 4ddd} 8 {1iii 2iii 3iii 4iii} } { if {[string range $oplist 0 0]=="-"} { reset_db create_schema continue } foreach db $DBLIST { sqlite3 $db test.db } do_test 1.$tn { foreach db $DBLIST { $db eval "BEGIN UNLOCKED" } foreach op $oplist { set iTbl [string range $op 0 0] foreach char [split [string range $op 1 end] {}] { do_sql_op $iTbl $char } } foreach db $DBLIST { $db eval "COMMIT" } db eval {PRAGMA integrity_check} } {ok} foreach db $DBLIST { if {$db=="db2"} breakpoint $db close } } finish_test |