Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Support read-only MVCC transactions in server-mode. Started using "BEGIN READONLY". |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | server-process-edition |
Files: | files | file ages | folders |
SHA3-256: |
5a043aa8dd0751e644c495a59deea5fe |
User & Date: | dan 2017-07-08 20:46:17.624 |
Context
2017-07-11
| ||
16:47 | Fix a bug causing readonly mvcc connections to read the wrong cache entry in some cases. (check-in: b6157267f9 user: dan tags: server-process-edition) | |
2017-07-08
| ||
20:46 | Support read-only MVCC transactions in server-mode. Started using "BEGIN READONLY". (check-in: 5a043aa8dd user: dan tags: server-process-edition) | |
2017-07-07
| ||
16:40 | Merge latest trunk changes with this branch. (check-in: 216c757f92 user: dan tags: server-process-edition) | |
Changes
Changes to src/btree.c.
︙ | ︙ | |||
2906 2907 2908 2909 2910 2911 2912 | MemPage *pPage1; /* Page 1 of the database file */ int nPage; /* Number of pages in the database */ int nPageFile = 0; /* Number of pages in the database file */ int nPageHeader; /* Number of pages in the database according to hdr */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); | | | 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 | MemPage *pPage1; /* Page 1 of the database file */ int nPage; /* Number of pages in the database */ int nPageFile = 0; /* Number of pages in the database file */ int nPageHeader; /* Number of pages in the database according to hdr */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); rc = sqlite3PagerSharedLock(pBt->pPager, pBt->db->readonlyTrans); if( rc!=SQLITE_OK ) return rc; rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is ** a valid database file. */ |
︙ | ︙ |
Changes to src/build.c.
︙ | ︙ | |||
3952 3953 3954 3955 3956 3957 3958 | db = pParse->db; assert( db!=0 ); if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){ return; } v = sqlite3GetVdbe(pParse); if( !v ) return; | | | | 3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 | db = pParse->db; assert( db!=0 ); if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){ return; } v = sqlite3GetVdbe(pParse); if( !v ) return; if( type!=TK_DEFERRED && type!=TK_READONLY ){ for(i=0; i<db->nDb; i++){ sqlite3VdbeAddOp2(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); sqlite3VdbeUsesBtree(v, i); } } sqlite3VdbeAddOp3(v, OP_AutoCommit, 0, 0, type); } /* ** Generate VDBE code for a COMMIT or ROLLBACK statement. ** Code for ROLLBACK is generated if eType==TK_ROLLBACK. Otherwise ** code is generated for a COMMIT. */ |
︙ | ︙ |
Changes to src/pager.c.
︙ | ︙ | |||
3024 3025 3026 3027 3028 3029 3030 | #ifndef SQLITE_OMIT_WAL if( iFrame ){ /* Try to pull the page from the write-ahead log. */ rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData); }else #endif { | > > > > > > > > > > | | | | | > > > > > > > | 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 | #ifndef SQLITE_OMIT_WAL if( iFrame ){ /* Try to pull the page from the write-ahead log. */ rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData); }else #endif { #ifdef SQLITE_SERVER_EDITION u8 *pData = 0; if( pagerIsServer(pPager) ){ sqlite3ServerReadPage(pPager->pServer, pgno, &pData); if( pData ){ memcpy(pPg->pData, pData, pgsz); } } if( pData==0 ){ #endif i64 iOffset = (pgno-1)*(i64)pPager->pageSize; rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset); if( rc==SQLITE_IOERR_SHORT_READ ){ rc = SQLITE_OK; } #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ sqlite3ServerEndReadPage(pPager->pServer, pgno); } } #endif } if( pgno==1 ){ if( rc ){ /* If the read is unsuccessful, set the dbFileVers[] to something ** that will never be a valid file version. dbFileVers[] is a copy ** of bytes 24..39 of the database. Bytes 28..31 should always be |
︙ | ︙ | |||
5215 5216 5217 5218 5219 5220 5221 | ** the contents of the page cache and rolling back any open journal ** file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs while locking the database, checking for a hot-journal file or ** rolling back a journal file, the IO error code is returned. */ | | | 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 5246 | ** the contents of the page cache and rolling back any open journal ** file. ** ** If everything is successful, SQLITE_OK is returned. If an IO error ** occurs while locking the database, checking for a hot-journal file or ** rolling back a journal file, the IO error code is returned. */ int sqlite3PagerSharedLock(Pager *pPager, int bReadonly){ int rc = SQLITE_OK; /* Return code */ /* This routine is only called from b-tree and only when there are no ** outstanding pages. This implies that the pager state should either ** be OPEN or READER. READER is only possible if the pager is or was in ** exclusive access mode. */ assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); |
︙ | ︙ | |||
5414 5415 5416 5417 5418 5419 5420 | } #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ assert( rc==SQLITE_OK ); assert( sqlite3PagerRefcount(pPager)==0 ); pager_reset(pPager); | | | | 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 5445 5446 | } #ifdef SQLITE_SERVER_EDITION if( pagerIsServer(pPager) ){ assert( rc==SQLITE_OK ); assert( sqlite3PagerRefcount(pPager)==0 ); pager_reset(pPager); rc = sqlite3ServerBegin(pPager->pServer, bReadonly); if( rc==SQLITE_OK ){ rc = sqlite3ServerLock(pPager->pServer, 1, 0, 0); } } #endif if( rc==SQLITE_OK && pagerUseWal(pPager) ){ assert( rc==SQLITE_OK ); rc = pagerBeginReadTransaction(pPager); |
︙ | ︙ | |||
5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 | if( !p ) return SQLITE_NOMEM_BKPT; memset(p, 0, sizeof(ServerPage)); p->aData = (u8*)&p[1]; p->nData = pPager->pageSize; p->pgno = pPg->pgno; p->pNext = pPager->pServerPage; pPager->pServerPage = p; } #endif /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); | > | 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 | if( !p ) return SQLITE_NOMEM_BKPT; memset(p, 0, sizeof(ServerPage)); p->aData = (u8*)&p[1]; p->nData = pPager->pageSize; p->pgno = pPg->pgno; p->pNext = pPager->pServerPage; pPager->pServerPage = p; memcpy(p->aData, pPg->pData, pPager->pageSize); } #endif /* We should never write to the journal file the page that ** contains the database locks. The following assert verifies ** that we do not. */ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) ); |
︙ | ︙ | |||
7380 7381 7382 7383 7384 7385 7386 | if( pPager->eLock>=RESERVED_LOCK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); }else{ int rc = SQLITE_OK; int state = pPager->eState; assert( state==PAGER_OPEN || state==PAGER_READER ); if( state==PAGER_OPEN ){ | | | 7398 7399 7400 7401 7402 7403 7404 7405 7406 7407 7408 7409 7410 7411 7412 | if( pPager->eLock>=RESERVED_LOCK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); }else{ int rc = SQLITE_OK; int state = pPager->eState; assert( state==PAGER_OPEN || state==PAGER_READER ); if( state==PAGER_OPEN ){ rc = sqlite3PagerSharedLock(pPager, 0); } if( pPager->eState==PAGER_READER ){ assert( rc==SQLITE_OK ); rc = pagerLockDb(pPager, RESERVED_LOCK); } if( rc==SQLITE_OK ){ sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); |
︙ | ︙ |
Changes to src/pager.h.
︙ | ︙ | |||
166 167 168 169 170 171 172 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerExclusiveLock(Pager*); int sqlite3PagerSync(Pager *pPager, const char *zMaster); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); | | | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int); int sqlite3PagerExclusiveLock(Pager*); int sqlite3PagerSync(Pager *pPager, const char *zMaster); int sqlite3PagerCommitPhaseTwo(Pager*); int sqlite3PagerRollback(Pager*); int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager, int bReadonly); #ifndef SQLITE_OMIT_WAL int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*); int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager, sqlite3*); |
︙ | ︙ |
Changes to src/parse.y.
︙ | ︙ | |||
133 134 135 136 137 138 139 140 141 142 143 144 145 146 | cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);} trans_opt ::= . trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/} cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} savepoint_opt ::= SAVEPOINT. | > | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | cmd ::= BEGIN transtype(Y) trans_opt. {sqlite3BeginTransaction(pParse, Y);} trans_opt ::= . trans_opt ::= TRANSACTION. trans_opt ::= TRANSACTION nm. %type transtype {int} transtype(A) ::= . {A = TK_DEFERRED;} transtype(A) ::= READONLY(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= DEFERRED(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= IMMEDIATE(X). {A = @X; /*A-overwrites-X*/} transtype(A) ::= EXCLUSIVE(X). {A = @X; /*A-overwrites-X*/} cmd ::= COMMIT|END(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} cmd ::= ROLLBACK(X) trans_opt. {sqlite3EndTransaction(pParse,@X);} savepoint_opt ::= SAVEPOINT. |
︙ | ︙ |
Changes to src/server.c.
︙ | ︙ | |||
114 115 116 117 118 119 120 121 | /* ** Once instance for each client connection open on a server mode database ** in this process. */ struct Server { ServerDb *pDb; /* Database object */ Pager *pPager; /* Associated pager object */ int iTransId; /* Current transaction id (or -1) */ | > | > > > > > > > | 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 | /* ** Once instance for each client connection open on a server mode database ** in this process. */ struct Server { ServerDb *pDb; /* Database object */ Pager *pPager; /* Associated pager object */ int eTrans; /* One of the SERVER_TRANS_xxx values */ int iTransId; /* Current transaction id (or -1) */ int iCommitId; /* Current commit id (or 0) */ int nAlloc; /* Allocated size of aLock[] array */ int nLock; /* Number of entries in aLock[] */ u32 *aLock; /* Mapped lock file */ Server *pNext; /* Next in pCommit or pReader list */ }; /* ** Possible values for Server.eTrans. */ #define SERVER_TRANS_NONE 0 #define SERVER_TRANS_READONLY 1 #define SERVER_TRANS_READWRITE 2 #define SERVER_WRITE_LOCK 3 #define SERVER_READ_LOCK 2 #define SERVER_NO_LOCK 1 /* ** Global mutex functions used by code in this file. */ |
︙ | ︙ | |||
323 324 325 326 327 328 329 | *ppOut = pNew; return rc; } /* ** Begin a transaction. */ | | | > > > > > > > > > > > > > > | | | | | | | | | | | > | > | | 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 | *ppOut = pNew; return rc; } /* ** Begin a transaction. */ int sqlite3ServerBegin(Server *p, int bReadonly){ int rc = SQLITE_OK; if( p->eTrans==SERVER_TRANS_NONE ){ int id; ServerDb *pDb = p->pDb; u32 t; assert( p->iTransId<0 ); assert( p->pNext==0 ); sqlite3_mutex_enter(pDb->mutex); if( bReadonly ){ Server *pIter; p->iCommitId = pDb->iNextCommit; for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ if( pIter->iCommitId<p->iCommitId ){ p->iCommitId = pIter->iCommitId; } } p->pNext = pDb->pReader; pDb->pReader = p; p->eTrans = SERVER_TRANS_READONLY; }else{ /* Find a transaction id to use */ rc = SQLITE_BUSY; t = pDb->transmask; for(id=0; id<HMA_MAX_TRANSACTIONID; id++){ if( (t & (1 << id))==0 ){ t = t | (1 << id); rc = SQLITE_OK; break; } } pDb->transmask = t; p->eTrans = SERVER_TRANS_READWRITE; } sqlite3_mutex_leave(pDb->mutex); if( rc==SQLITE_OK && bReadonly==0 ){ ServerJournal *pJrnl = &pDb->aJrnl[id]; sqlite3PagerServerJournal(p->pPager, pJrnl->jfd, pJrnl->zJournal); p->iTransId = id; } } return rc; |
︙ | ︙ | |||
378 379 380 381 382 383 384 | } /* ** End a transaction (and release all locks). */ int sqlite3ServerEnd(Server *p){ int rc = SQLITE_OK; | > | | | | > | > > > > > | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > | | > < > > > | 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 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 | } /* ** End a transaction (and release all locks). */ int sqlite3ServerEnd(Server *p){ int rc = SQLITE_OK; if( p->eTrans!=SERVER_TRANS_NONE ){ Server **pp; ServerDb *pDb = p->pDb; ServerPage *pFree = 0; ServerPage *pPg = 0; sqlite3_mutex_enter(pDb->mutex); if( p->eTrans==SERVER_TRANS_READONLY ){ /* Remove the connection from the readers list */ for(pp=&pDb->pReader; *pp!=p; pp = &((*pp)->pNext)); *pp = p->pNext; }else{ serverReleaseLocks(p); /* Clear the bit in the transaction mask. */ pDb->transmask &= ~((u32)1 << p->iTransId); /* If this connection is in the committers list, remove it. */ for(pp=&pDb->pCommit; *pp; pp = &((*pp)->pNext)){ if( *pp==p ){ *pp = p->pNext; break; } } } /* See if it is possible to free any ServerPage records. If so, remove ** them from the linked list and hash table, but do not call sqlite3_free() ** on them until the mutex has been released. */ if( pDb->pPgFirst ){ Server *pIter; int iOldest = 0x7FFFFFFF; for(pIter=pDb->pReader; pIter; pIter=pIter->pNext){ iOldest = MIN(iOldest, pIter->iCommitId); } for(pIter=pDb->pCommit; pIter; pIter=pIter->pNext){ iOldest = MIN(iOldest, pIter->iCommitId); } pFree = pDb->pPgFirst; for(pPg=pDb->pPgFirst; pPg && pPg->iCommitId<iOldest; pPg=pPg->pNext){ if( pPg->pHashPrev ){ pPg->pHashPrev->pHashNext = pPg->pHashNext; }else{ int iHash = pPg->pgno % HMA_HASH_SIZE; assert( pDb->apPg[iHash]==pPg ); pDb->apPg[iHash] = pPg->pHashNext; } if( pPg->pHashNext ){ pPg->pHashNext->pHashPrev = pPg->pHashPrev; } } if( pPg==0 ){ pDb->pPgFirst = pDb->pPgLast = 0; }else{ pDb->pPgFirst = pPg; } } sqlite3_mutex_leave(pDb->mutex); /* Call sqlite3_free() on any pages that were unlinked from the hash ** table above. */ while( pFree && pFree!=pPg ){ ServerPage *pNext = pFree->pNext; sqlite3_free(pFree); pFree = pNext; } p->pNext = 0; p->eTrans = SERVER_TRANS_NONE; p->iTransId = -1; p->iCommitId = 0; } return rc; } int sqlite3ServerPreCommit(Server *p, ServerPage *pPg){ ServerDb *pDb = p->pDb; int rc = SQLITE_OK; ServerPage *pIter; if( pPg==0 ) return SQLITE_OK; sqlite3_mutex_enter(pDb->mutex); /* Assign a commit id to this transaction */ assert( p->iCommitId==0 ); assert( p->eTrans==SERVER_TRANS_READWRITE ); assert( p->iTransId>=0 ); p->iCommitId = pDb->iNextCommit++; /* Iterate through all pages. For each: ** ** 1. Set the iCommitId field. ** 2. Add the page to the hash table. ** 3. Wait until all slow-reader locks have cleared. |
︙ | ︙ | |||
520 521 522 523 524 525 526 | ** Lock page pgno for reading (bWrite==0) or writing (bWrite==1). ** ** If parameter bBlock is non-zero, then make this a blocking lock if ** possible. */ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ int rc = SQLITE_OK; | > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 557 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 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | ** Lock page pgno for reading (bWrite==0) or writing (bWrite==1). ** ** If parameter bBlock is non-zero, then make this a blocking lock if ** possible. */ int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock){ int rc = SQLITE_OK; assert( p->eTrans==SERVER_TRANS_READWRITE || p->eTrans==SERVER_TRANS_READONLY ); if( p->eTrans==SERVER_TRANS_READWRITE ){ ServerDb *pDb = p->pDb; int iWriter; int bSkip = 0; u32 *pSlot; assert( p->iTransId>=0 ); assert( p->nLock<=p->nAlloc ); if( p->nLock==p->nAlloc ){ int nNew = p->nLock ? p->nLock*2 : 256; u32 *aNew = sqlite3_realloc(p->aLock, nNew*sizeof(u32)); if( aNew==0 ) return SQLITE_NOMEM_BKPT; memset(&aNew[p->nLock], 0, sizeof(u32) * (nNew - p->nLock)); p->nAlloc = nNew; p->aLock = aNew; } sqlite3_mutex_enter(pDb->mutex); pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; assert( slotGetWriter(*pSlot)<0 || slotReaderMask(*pSlot)==0 || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot)) ); iWriter = slotGetWriter(*pSlot); if( iWriter==p->iTransId || (bWrite==0 && (*pSlot & (1<<p->iTransId))) ){ bSkip = 1; }else if( iWriter>=0 ){ rc = SQLITE_BUSY_DEADLOCK; }else if( bWrite ){ if( (slotReaderMask(*pSlot) & ~(1 << p->iTransId))==0 ){ *pSlot += ((p->iTransId + 1) << HMA_MAX_TRANSACTIONID); }else{ rc = SQLITE_BUSY_DEADLOCK; } }else{ *pSlot |= (1 << p->iTransId); } assert( slotGetWriter(*pSlot)<0 || slotReaderMask(*pSlot)==0 || slotReaderMask(*pSlot)==(1 << slotGetWriter(*pSlot)) ); sqlite3_mutex_leave(pDb->mutex); if( bSkip==0 ){ p->aLock[p->nLock++] = pgno; } } return rc; } int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite){ assert( 0 ); return 0; } static void serverIncrSlowReader(u32 *pSlot, int n){ assert( n==1 || n==-1 ); *pSlot += (n * (1 << HMA_SLOT_RLWL_BITS)); } void sqlite3ServerReadPage(Server *p, Pgno pgno, u8 **ppData){ if( p->eTrans==SERVER_TRANS_READONLY ){ ServerDb *pDb = p->pDb; ServerPage *pIter; ServerPage *pBest = 0; int iHash = pgno % HMA_HASH_SIZE; sqlite3_mutex_enter(pDb->mutex); /* Search the hash table for the oldest version of page pgno with ** a commit-id greater than or equal to Server.iCommitId. */ for(pIter=pDb->apPg[iHash]; pIter; pIter=pIter->pHashNext){ if( pIter->iCommitId>=p->iCommitId && (pBest==0 || pIter->iCommitId<pBest->iCommitId) ){ pBest = pIter; } } if( pBest ){ *ppData = pBest->aData; }else{ u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; serverIncrSlowReader(pSlot, 1); } sqlite3_mutex_leave(pDb->mutex); } } void sqlite3ServerEndReadPage(Server *p, Pgno pgno){ if( p->eTrans==SERVER_TRANS_READONLY ){ ServerDb *pDb = p->pDb; u32 *pSlot = &pDb->aSlot[pgno % HMA_PAGELOCK_SLOTS]; sqlite3_mutex_enter(pDb->mutex); serverIncrSlowReader(pSlot, -1); assert( slotGetSlowReaders(*pSlot)>=0 ); sqlite3_mutex_leave(pDb->mutex); } } #endif /* ifdef SQLITE_SERVER_EDITION */ |
Changes to src/server.h.
︙ | ︙ | |||
30 31 32 33 34 35 36 | ServerPage *pHashNext; ServerPage *pHashPrev; }; int sqlite3ServerConnect(Pager *pPager, Server **ppOut); void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd); | | > > > | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | ServerPage *pHashNext; ServerPage *pHashPrev; }; int sqlite3ServerConnect(Pager *pPager, Server **ppOut); void sqlite3ServerDisconnect(Server *p, sqlite3_file *dbfd); int sqlite3ServerBegin(Server *p, int bReadonly); int sqlite3ServerPreCommit(Server*, ServerPage*); int sqlite3ServerEnd(Server *p); int sqlite3ServerReleaseWriteLocks(Server *p); int sqlite3ServerLock(Server *p, Pgno pgno, int bWrite, int bBlock); int sqlite3ServerHasLock(Server *p, Pgno pgno, int bWrite); void sqlite3ServerReadPage(Server*, Pgno, u8**); void sqlite3ServerEndReadPage(Server*, Pgno); #endif /* SQLITE_SERVER_H */ #endif /* SQLITE_SERVER_EDITION */ |
Changes to src/sqliteInt.h.
︙ | ︙ | |||
1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 | unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ int iSysErrno; /* Errno value from last system error */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 bBenignMalloc; /* Do not require OOMs if true */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ | > | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 | unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */ int errCode; /* Most recent error code (SQLITE_*) */ int errMask; /* & result codes with this before returning */ int iSysErrno; /* Errno value from last system error */ u16 dbOptFlags; /* Flags to enable/disable optimizations */ u8 enc; /* Text encoding */ u8 autoCommit; /* The auto-commit flag. */ u8 readonlyTrans; /* Transaction opened with BEGIN READONLY */ u8 temp_store; /* 1: file 2: memory 0: default */ u8 mallocFailed; /* True if we have seen a malloc failure */ u8 bBenignMalloc; /* Do not require OOMs if true */ u8 dfltLockMode; /* Default locking-mode for attached dbs */ signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */ u8 suppressErr; /* Do not issue error messages if true */ u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */ |
︙ | ︙ |
Changes to src/test2.c.
︙ | ︙ | |||
320 321 322 323 324 325 326 | if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID PGNO\"", 0); return TCL_ERROR; } pPager = sqlite3TestTextToPtr(argv[1]); if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; | | | 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ID PGNO\"", 0); return TCL_ERROR; } pPager = sqlite3TestTextToPtr(argv[1]); if( Tcl_GetInt(interp, argv[2], &pgno) ) return TCL_ERROR; rc = sqlite3PagerSharedLock(pPager, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerGet(pPager, pgno, &pPage, 0); } if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, sqlite3ErrName(rc), 0); return TCL_ERROR; } |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
3116 3117 3118 3119 3120 3121 3122 | } } if( rc ) goto abort_due_to_error; break; } | | > | 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 | } } if( rc ) goto abort_due_to_error; break; } /* Opcode: AutoCommit P1 P2 P3 * * ** ** Set the database auto-commit flag to P1 (1 or 0). If P2 is true, roll ** back any currently active btree transactions. If there are any active ** VMs (apart from this one), then a ROLLBACK fails. A COMMIT fails if ** there are active writing VMs or active VMs that use shared cache. ** ** This instruction causes the VM to halt. */ case OP_AutoCommit: { int desiredAutoCommit; int iRollback; desiredAutoCommit = pOp->p1; iRollback = pOp->p2; assert( desiredAutoCommit==1 || desiredAutoCommit==0 ); assert( desiredAutoCommit==1 || iRollback==0 ); assert( desiredAutoCommit==0 || pOp->p3==0 ); assert( db->nVdbeActive>0 ); /* At least this one VM is active */ assert( p->bIsReader ); if( desiredAutoCommit!=db->autoCommit ){ if( iRollback ){ assert( desiredAutoCommit==1 ); sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK); |
︙ | ︙ | |||
3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 | p->rc = rc = SQLITE_BUSY; goto vdbe_return; } assert( db->nStatement==0 ); sqlite3CloseSavepoints(db); if( p->rc==SQLITE_OK ){ rc = SQLITE_DONE; }else{ rc = SQLITE_ERROR; } goto vdbe_return; }else{ sqlite3VdbeError(p, (!desiredAutoCommit)?"cannot start a transaction within a transaction":( | > | 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 | p->rc = rc = SQLITE_BUSY; goto vdbe_return; } assert( db->nStatement==0 ); sqlite3CloseSavepoints(db); if( p->rc==SQLITE_OK ){ rc = SQLITE_DONE; db->readonlyTrans = (pOp->p3==TK_READONLY); }else{ rc = SQLITE_ERROR; } goto vdbe_return; }else{ sqlite3VdbeError(p, (!desiredAutoCommit)?"cannot start a transaction within a transaction":( |
︙ | ︙ |
Changes to src/wal.c.
︙ | ︙ | |||
3394 3395 3396 3397 3398 3399 3400 | ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained ** immediately, and a busy-handler is configured, it is invoked and the ** writer lock retried until either the busy-handler returns 0 or the ** lock is successfully obtained. */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ if( walIsServer(pWal) ){ | | | 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 | ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained ** immediately, and a busy-handler is configured, it is invoked and the ** writer lock retried until either the busy-handler returns 0 or the ** lock is successfully obtained. */ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){ if( walIsServer(pWal) ){ rc = sqlite3ServerBegin(pWal->pServer, 0); if( rc!=SQLITE_OK ) goto ckpt_out; if( eMode>=SQLITE_CHECKPOINT_RESTART ){ /* Exclusive lock on page 1. This is exclusive access to the db. */ rc = sqlite3ServerLock(pWal->pServer, 1, 1, 1); }else{ /* Take the server write-lock ("page" 0) */ rc = sqlite3ServerLock(pWal->pServer, 0, 1, 1); |
︙ | ︙ |
Changes to test/permutations.test.
︙ | ︙ | |||
273 274 275 276 277 278 279 | All FTS5 tests. } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] test_suite "server" -prefix "" -description { All server-edition tests. } -files [ test_set \ | | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | All FTS5 tests. } -files [glob -nocomplain $::testdir/../ext/fts5/test/*.test] test_suite "server" -prefix "" -description { All server-edition tests. } -files [ test_set \ select1.test server2.test server3.test server4.test ] test_suite "fts5-light" -prefix "" -description { All FTS5 tests. } -files [ test_set \ [glob -nocomplain $::testdir/../ext/fts5/test/*.test] \ |
︙ | ︙ |
Added test/server4.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 | # 2017 July 09 # # 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. # # The focus of this script is testing the server mode of SQLite. # Specifically, that "BEGIN READONLY" starts a read-only MVCC # transaction. # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/lock_common.tcl set testprefix server3 sqlite3 db2 test.db do_execsql_test 1.0 { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); CREATE TABLE t2(x); INSERT INTO t2 VALUES(1); BEGIN; INSERT INTO t1 VALUES(2); INSERT INTO t2 VALUES(2); } do_execsql_test -db db2 1.1 { BEGIN READONLY; SELECT * FROM t1; } {1} do_execsql_test 1.2 { COMMIT; INSERT INTO t1 VALUES(3); SELECT * FROM t1; } {1 2 3} do_execsql_test 1.2a { INSERT INTO t2 VALUES(3); } {} do_execsql_test -db db2 1.3 { SELECT * FROM t2; } {1} do_execsql_test -db db2 1.4 { ROLLBACK; SELECT * FROM t1; } {1 2 3} finish_test |
Changes to tool/mkkeywordhash.c.
︙ | ︙ | |||
235 236 237 238 239 240 241 242 243 244 245 246 247 248 | { "ORDER", "TK_ORDER", ALWAYS }, { "OUTER", "TK_JOIN_KW", ALWAYS }, { "PLAN", "TK_PLAN", EXPLAIN }, { "PRAGMA", "TK_PRAGMA", PRAGMA }, { "PRIMARY", "TK_PRIMARY", ALWAYS }, { "QUERY", "TK_QUERY", EXPLAIN }, { "RAISE", "TK_RAISE", TRIGGER }, { "RECURSIVE", "TK_RECURSIVE", CTE }, { "REFERENCES", "TK_REFERENCES", FKEY }, { "REGEXP", "TK_LIKE_KW", ALWAYS }, { "REINDEX", "TK_REINDEX", REINDEX }, { "RELEASE", "TK_RELEASE", ALWAYS }, { "RENAME", "TK_RENAME", ALTER }, { "REPLACE", "TK_REPLACE", CONFLICT }, | > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | { "ORDER", "TK_ORDER", ALWAYS }, { "OUTER", "TK_JOIN_KW", ALWAYS }, { "PLAN", "TK_PLAN", EXPLAIN }, { "PRAGMA", "TK_PRAGMA", PRAGMA }, { "PRIMARY", "TK_PRIMARY", ALWAYS }, { "QUERY", "TK_QUERY", EXPLAIN }, { "RAISE", "TK_RAISE", TRIGGER }, { "READONLY", "TK_READONLY", ALWAYS }, { "RECURSIVE", "TK_RECURSIVE", CTE }, { "REFERENCES", "TK_REFERENCES", FKEY }, { "REGEXP", "TK_LIKE_KW", ALWAYS }, { "REINDEX", "TK_REINDEX", REINDEX }, { "RELEASE", "TK_RELEASE", ALWAYS }, { "RENAME", "TK_RENAME", ALTER }, { "REPLACE", "TK_REPLACE", CONFLICT }, |
︙ | ︙ |