Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Allows UPDATE, INSERT, and DELETEs to occur while a SELECT is pending on the same table. (CVS 3355) |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
8c52d2ad468615e50a727adab2977a0b |
User & Date: | drh 2006-08-16 16:42:48 |
Context
2006-08-16
| ||
22:58 | Remove obsolete clause in the documentation. Ticket #1923. (CVS 3356) check-in: d4f182e5 user: drh tags: trunk | |
16:42 | Allows UPDATE, INSERT, and DELETEs to occur while a SELECT is pending on the same table. (CVS 3355) check-in: 8c52d2ad user: drh tags: trunk | |
2006-08-15
| ||
14:21 | Tighten an assert (ticket #1920). Change to "sqlite3.h" from <sqlite3.h> on the sqlite3ext.h header (ticket #1916). Fix a bug in the test scripts. (CVS 3354) check-in: 3ebedbb6 user: drh tags: trunk | |
Changes
Changes to src/btree.c.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... 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 ... 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 ... 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 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 ... 743 744 745 746 747 748 749 750 751 752 753 754 755 756 .... 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 .... 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 .... 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 5245 .... 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 5282 .... 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 .... 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653 5654 5655 5656 5657 5658 5659 |
** 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. ** ************************************************************************* ** $Id: btree.c,v 1.327 2006/08/13 18:39:26 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley ** Publishing Company, Reading, Massachusetts. ................................................................................ void *pArg; /* First arg to xCompare() */ Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->aCell[] */ CellInfo info; /* A parse of the cell we are pointing at */ u8 wrFlag; /* True if writable */ u8 eState; /* One of the CURSOR_XXX constants (see below) */ #ifndef SQLITE_OMIT_SHARED_CACHE void *pKey; /* Saved key that was cursor's last known position */ i64 nKey; /* Size of pKey, or last integer key */ int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ #endif }; /* ** Potential values for BtCursor.eState. The first two values (VALID and ** INVALID) may occur in any build. The third (REQUIRESEEK) may only occur ** if sqlite was compiled without the OMIT_SHARED_CACHE symbol defined. ** ** CURSOR_VALID: ** Cursor points to a valid entry. getPayload() etc. may be called. ** ** CURSOR_INVALID: ** Cursor does not point to a valid entry. This can happen (for example) ** because the table is empty or because BtreeCursorFirst() has not been ................................................................................ #else # define TRACE(X) #endif /* ** Forward declaration */ static int checkReadLocks(BtShared*,Pgno,BtCursor*); /* ** Read or write a two- and four-byte big-endian integer values. */ static u32 get2byte(unsigned char *p){ return (p[0]<<8) | p[1]; } ................................................................................ ** shared-cache feature disabled, then there is only ever one user ** of each BtShared structure and so this locking is not necessary. ** So define the lock related functions as no-ops. */ #define queryTableLock(a,b,c) SQLITE_OK #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a) #define restoreOrClearCursorPosition(a,b) SQLITE_OK #define saveAllCursors(a,b,c) SQLITE_OK #else static void releasePage(MemPage *pPage); /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. */ static int saveCursorPosition(BtCursor *pCur){ int rc; assert( CURSOR_VALID==pCur->eState ); assert( 0==pCur->pKey ); rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); /* If this is an intKey table, then the above call to BtreeKeySize() ** stores the integer key in pCur->nKey. In this case this value is ** all that is required. Otherwise, if pCur is not open on an intKey ** table, then malloc space for and store the pCur->nKey bytes of key ** data. */ if( rc==SQLITE_OK && 0==pCur->pPage->intKey){ void *pKey = sqliteMalloc(pCur->nKey); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey); if( rc==SQLITE_OK ){ pCur->pKey = pKey; }else{ sqliteFree(pKey); } }else{ rc = SQLITE_NOMEM; } } assert( !pCur->pPage->intKey || !pCur->pKey ); if( rc==SQLITE_OK ){ releasePage(pCur->pPage); pCur->pPage = 0; pCur->eState = CURSOR_REQUIRESEEK; } return rc; } /* ** Save the positions of all cursors except pExcept open on the table ** with root-page iRoot. Usually, this is called just before cursor ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). */ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ BtCursor *p; if( sqlite3ThreadDataReadOnly()->useSharedData ){ for(p=pBt->pCursor; p; p=p->pNext){ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && p->eState==CURSOR_VALID ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; } } } } return SQLITE_OK; } /* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the ** saved position info stored by saveCursorPosition(), so there can be ** at most one effective restoreOrClearCursorPosition() call after each ** saveCursorPosition(). ** ** If the second argument argument - doSeek - is false, then instead of ** returning the cursor to it's saved position, any saved position is deleted ** and the cursor state set to CURSOR_INVALID. */ static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){ int rc = SQLITE_OK; assert( sqlite3ThreadDataReadOnly()->useSharedData ); assert( pCur->eState==CURSOR_REQUIRESEEK ); pCur->eState = CURSOR_INVALID; if( doSeek ){ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip); } if( rc==SQLITE_OK ){ sqliteFree(pCur->pKey); pCur->pKey = 0; assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState ); } return rc; } #define restoreOrClearCursorPosition(p,x) \ (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK) /* ** Query to see if btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return ** SQLITE_OK if the lock may be obtained (by calling lockTable()), or ** SQLITE_LOCKED if not. */ ................................................................................ }else{ ppIter = &pLock->pNext; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ #ifndef SQLITE_OMIT_AUTOVACUUM /* ** These macros define the location of the pointer-map entry for a ** database page. The first argument to each is the number of usable ** bytes on each page of the database (often 1024). The second is the ** page number to look up in the pointer map. ** ................................................................................ /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if ** either of the shared-data or autovacuum features are compiled ** into the library. */ #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) #ifdef SQLITE_OMIT_MEMORYDB const int isMemdb = !zFilename; #else const int isMemdb = !zFilename || (strcmp(zFilename, ":memory:")?0:1); #endif #endif p = sqliteMalloc(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; } ................................................................................ BtShared *pBt = p->pBt; *ppCur = 0; if( wrFlag ){ if( pBt->readOnly ){ return SQLITE_READONLY; } if( checkReadLocks(pBt, iTable, 0) ){ return SQLITE_LOCKED; } } if( pBt->pPage1==0 ){ rc = lockBtreeWithRetry(p); if( rc!=SQLITE_OK ){ ................................................................................ } } return rc; } /* ** This routine checks all cursors that point to table pgnoRoot. ** If any of those cursors other than pExclude were opened with ** wrFlag==0 then this routine returns SQLITE_LOCKED. If all ** cursors that point to pgnoRoot were opened with wrFlag==1 ** then this routine returns SQLITE_OK. ** ** In addition to checking for read-locks (where a read-lock ** means a cursor opened with wrFlag==0) this routine also moves ** all cursors other than pExclude so that they are pointing to the ** first Cell on root page. This is necessary because an insert ** or delete might change the number of cells on a page or delete ** a page entirely and we do not want to leave any cursors ** pointing to non-existant pages or cells. */ static int checkReadLocks(BtShared *pBt, Pgno pgnoRoot, BtCursor *pExclude){ BtCursor *p; for(p=pBt->pCursor; p; p=p->pNext){ u32 flags = (p->pBtree->pSqlite ? p->pBtree->pSqlite->flags : 0); if( p->pgnoRoot!=pgnoRoot || p==pExclude ) continue; if( p->wrFlag==0 && flags&SQLITE_ReadUncommitted ) continue; if( p->wrFlag==0 ) return SQLITE_LOCKED; if( p->pPage->pgno!=p->pgnoRoot ){ moveToRoot(p); } } return SQLITE_OK; } /* ................................................................................ /* Must start a transaction before doing an insert */ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } assert( !pBt->readOnly ); if( !pCur->wrFlag ){ return SQLITE_PERM; /* Cursor not open for writing */ } if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } /* Save the positions of any other cursors open on this table */ restoreOrClearCursorPosition(pCur, 0); if( SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || ................................................................................ assert( !pBt->readOnly ); if( pCur->idx >= pPage->nCell ){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } if( !pCur->wrFlag ){ return SQLITE_PERM; /* Did not open this cursor for writing */ } if( checkReadLocks(pBt, pCur->pgnoRoot, pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } /* Restore the current cursor position (a no-op if the cursor is not in ** CURSOR_REQUIRESEEK state) and save the positions of any other cursors ** open on the same table. Then call sqlite3pager_write() on the page ** that the entry will be deleted from. ................................................................................ ** ** This routine will fail with SQLITE_LOCKED if there are any open ** read cursors on the table. Open write cursors are moved to the ** root of the table. */ int sqlite3BtreeClearTable(Btree *p, int iTable){ int rc; BtCursor *pCur; BtShared *pBt = p->pBt; sqlite3 *db = p->pSqlite; if( p->inTrans!=TRANS_WRITE ){ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } /* If this connection is not in read-uncommitted mode and currently has ** a read-cursor open on the table being cleared, return SQLITE_LOCKED. */ if( 0==db || 0==(db->flags&SQLITE_ReadUncommitted) ){ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pBtree==p && pCur->pgnoRoot==(Pgno)iTable ){ if( 0==pCur->wrFlag ){ return SQLITE_LOCKED; } moveToRoot(pCur); } } } /* Save the position of all cursors open on this table */ if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ return rc; } |
| < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > | | | > > < | > > | > > > | > | | | < < < > | < < < < < < | < < < < |
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ... 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 ... 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 ... 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 ... 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 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 .... 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 .... 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 .... 5202 5203 5204 5205 5206 5207 5208 5209 5210 5211 5212 5213 5214 5215 5216 5217 5218 5219 5220 5221 5222 5223 5224 5225 5226 5227 5228 5229 5230 5231 5232 5233 5234 5235 5236 5237 5238 5239 5240 5241 5242 5243 5244 .... 5267 5268 5269 5270 5271 5272 5273 5274 5275 5276 5277 5278 5279 5280 5281 .... 5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 .... 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 |
** 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. ** ************************************************************************* ** $Id: btree.c,v 1.328 2006/08/16 16:42:48 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: ** "Sorting And Searching", pages 473-480. Addison-Wesley ** Publishing Company, Reading, Massachusetts. ................................................................................ void *pArg; /* First arg to xCompare() */ Pgno pgnoRoot; /* The root page of this tree */ MemPage *pPage; /* Page that contains the entry */ int idx; /* Index of the entry in pPage->aCell[] */ CellInfo info; /* A parse of the cell we are pointing at */ u8 wrFlag; /* True if writable */ u8 eState; /* One of the CURSOR_XXX constants (see below) */ void *pKey; /* Saved key that was cursor's last known position */ i64 nKey; /* Size of pKey, or last integer key */ int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ }; /* ** Potential values for BtCursor.eState. ** ** CURSOR_VALID: ** Cursor points to a valid entry. getPayload() etc. may be called. ** ** CURSOR_INVALID: ** Cursor does not point to a valid entry. This can happen (for example) ** because the table is empty or because BtreeCursorFirst() has not been ................................................................................ #else # define TRACE(X) #endif /* ** Forward declaration */ static int checkReadLocks(Btree*,Pgno,BtCursor*); /* ** Read or write a two- and four-byte big-endian integer values. */ static u32 get2byte(unsigned char *p){ return (p[0]<<8) | p[1]; } ................................................................................ ** shared-cache feature disabled, then there is only ever one user ** of each BtShared structure and so this locking is not necessary. ** So define the lock related functions as no-ops. */ #define queryTableLock(a,b,c) SQLITE_OK #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a) #else /* ** Query to see if btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return ** SQLITE_OK if the lock may be obtained (by calling lockTable()), or ** SQLITE_LOCKED if not. */ ................................................................................ }else{ ppIter = &pLock->pNext; } } } #endif /* SQLITE_OMIT_SHARED_CACHE */ static void releasePage(MemPage *pPage); /* Forward reference */ /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. */ static int saveCursorPosition(BtCursor *pCur){ int rc; assert( CURSOR_VALID==pCur->eState ); assert( 0==pCur->pKey ); rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); /* If this is an intKey table, then the above call to BtreeKeySize() ** stores the integer key in pCur->nKey. In this case this value is ** all that is required. Otherwise, if pCur is not open on an intKey ** table, then malloc space for and store the pCur->nKey bytes of key ** data. */ if( rc==SQLITE_OK && 0==pCur->pPage->intKey){ void *pKey = sqliteMalloc(pCur->nKey); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, pCur->nKey, pKey); if( rc==SQLITE_OK ){ pCur->pKey = pKey; }else{ sqliteFree(pKey); } }else{ rc = SQLITE_NOMEM; } } assert( !pCur->pPage->intKey || !pCur->pKey ); if( rc==SQLITE_OK ){ releasePage(pCur->pPage); pCur->pPage = 0; pCur->eState = CURSOR_REQUIRESEEK; } return rc; } /* ** Save the positions of all cursors except pExcept open on the table ** with root-page iRoot. Usually, this is called just before cursor ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). */ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ BtCursor *p; for(p=pBt->pCursor; p; p=p->pNext){ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) && p->eState==CURSOR_VALID ){ int rc = saveCursorPosition(p); if( SQLITE_OK!=rc ){ return rc; } } } return SQLITE_OK; } /* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the ** saved position info stored by saveCursorPosition(), so there can be ** at most one effective restoreOrClearCursorPosition() call after each ** saveCursorPosition(). ** ** If the second argument argument - doSeek - is false, then instead of ** returning the cursor to it's saved position, any saved position is deleted ** and the cursor state set to CURSOR_INVALID. */ static int restoreOrClearCursorPositionX(BtCursor *pCur, int doSeek){ int rc = SQLITE_OK; assert( pCur->eState==CURSOR_REQUIRESEEK ); pCur->eState = CURSOR_INVALID; if( doSeek ){ rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, &pCur->skip); } if( rc==SQLITE_OK ){ sqliteFree(pCur->pKey); pCur->pKey = 0; assert( CURSOR_VALID==pCur->eState || CURSOR_INVALID==pCur->eState ); } return rc; } #define restoreOrClearCursorPosition(p,x) \ (p->eState==CURSOR_REQUIRESEEK?restoreOrClearCursorPositionX(p,x):SQLITE_OK) #ifndef SQLITE_OMIT_AUTOVACUUM /* ** These macros define the location of the pointer-map entry for a ** database page. The first argument to each is the number of usable ** bytes on each page of the database (often 1024). The second is the ** page number to look up in the pointer map. ** ................................................................................ /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if ** either of the shared-data or autovacuum features are compiled ** into the library. */ #if !defined(SQLITE_OMIT_SHARED_CACHE) || !defined(SQLITE_OMIT_AUTOVACUUM) #ifdef SQLITE_OMIT_MEMORYDB const int isMemdb = 0; #else const int isMemdb = zFilename && !strcmp(zFilename, ":memory:"); #endif #endif p = sqliteMalloc(sizeof(Btree)); if( !p ){ return SQLITE_NOMEM; } ................................................................................ BtShared *pBt = p->pBt; *ppCur = 0; if( wrFlag ){ if( pBt->readOnly ){ return SQLITE_READONLY; } if( checkReadLocks(p, iTable, 0) ){ return SQLITE_LOCKED; } } if( pBt->pPage1==0 ){ rc = lockBtreeWithRetry(p); if( rc!=SQLITE_OK ){ ................................................................................ } } return rc; } /* ** This routine checks all cursors that point to table pgnoRoot. ** If any of those cursors were opened with wrFlag==0 in a different ** database connection (a database connection that shares the pager ** cache with the current connection) and that other connection ** is not in the ReadUncommmitted state, then this routine returns ** SQLITE_LOCKED. ** ** In addition to checking for read-locks (where a read-lock ** means a cursor opened with wrFlag==0) this routine also moves ** all cursors write cursors so that they are pointing to the ** first Cell on the root page. This is necessary because an insert ** or delete might change the number of cells on a page or delete ** a page entirely and we do not want to leave any cursors ** pointing to non-existant pages or cells. */ static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){ BtCursor *p; BtShared *pBt = pBtree->pBt; sqlite3 *db = pBtree->pSqlite; for(p=pBt->pCursor; p; p=p->pNext){ if( p==pExclude ) continue; if( p->eState!=CURSOR_VALID ) continue; if( p->pgnoRoot!=pgnoRoot ) continue; if( p->wrFlag==0 ){ sqlite3 *dbOther = p->pBtree->pSqlite; if( dbOther==0 || (dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0) ){ return SQLITE_LOCKED; } }else if( p->pPage->pgno!=p->pgnoRoot ){ moveToRoot(p); } } return SQLITE_OK; } /* ................................................................................ /* Must start a transaction before doing an insert */ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } assert( !pBt->readOnly ); if( !pCur->wrFlag ){ return SQLITE_PERM; /* Cursor not open for writing */ } if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } /* Save the positions of any other cursors open on this table */ restoreOrClearCursorPosition(pCur, 0); if( SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || ................................................................................ assert( !pBt->readOnly ); if( pCur->idx >= pPage->nCell ){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } if( !pCur->wrFlag ){ return SQLITE_PERM; /* Did not open this cursor for writing */ } if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } /* Restore the current cursor position (a no-op if the cursor is not in ** CURSOR_REQUIRESEEK state) and save the positions of any other cursors ** open on the same table. Then call sqlite3pager_write() on the page ** that the entry will be deleted from. ................................................................................ ** ** This routine will fail with SQLITE_LOCKED if there are any open ** read cursors on the table. Open write cursors are moved to the ** root of the table. */ int sqlite3BtreeClearTable(Btree *p, int iTable){ int rc; BtShared *pBt = p->pBt; if( p->inTrans!=TRANS_WRITE ){ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } rc = checkReadLocks(p, iTable, 0); if( rc ){ return rc; } /* Save the position of all cursors open on this table */ if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ return rc; } |
Changes to test/btree.test.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
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
|
# 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 btree database backend # # $Id: btree.test,v 1.36 2006/03/19 13:00:25 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable default_autovacuum { finish_test ................................................................................ catch {btree_insert $::c1 101 helloworld} msg set msg } {SQLITE_PERM} do_test btree-16.10 { catch {btree_delete $::c1} msg set msg } {SQLITE_PERM} do_test btree-16.11 { btree_close_cursor $::c1 set ::c2 [btree_cursor $::b1 2 1] set ::c1 [btree_cursor $::b1 2 0] catch {btree_insert $::c2 101 helloworld} msg set msg } {SQLITE_LOCKED} do_test btree-16.12 { btree_first $::c2 catch {btree_delete $::c2} msg set msg } {SQLITE_LOCKED} do_test btree-16.13 { catch {btree_clear_table $::b1 2} msg set msg } {SQLITE_LOCKED} do_test btree-16.14 { btree_close_cursor $::c1 btree_close_cursor $::c2 btree_commit $::b1 catch {btree_clear_table $::b1 2} msg set msg } {SQLITE_ERROR} |
|
>
>
>
>
>
>
|
|
|
>
>
|
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
....
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
|
# 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 btree database backend # # $Id: btree.test,v 1.37 2006/08/16 16:42:48 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable default_autovacuum { finish_test ................................................................................ catch {btree_insert $::c1 101 helloworld} msg set msg } {SQLITE_PERM} do_test btree-16.10 { catch {btree_delete $::c1} msg set msg } {SQLITE_PERM} # As of 2006-08-16 (version 3.3.7+) a read cursor will no # longer block a write cursor from the same database # connectiin. The following three tests uses to return # the SQLITE_LOCK error, but no more. # do_test btree-16.11 { btree_close_cursor $::c1 set ::c2 [btree_cursor $::b1 2 1] set ::c1 [btree_cursor $::b1 2 0] catch {btree_insert $::c2 101 helloworld} msg set msg } {} do_test btree-16.12 { btree_first $::c2 catch {btree_delete $::c2} msg set msg } {} do_test btree-16.13 { catch {btree_clear_table $::b1 2} msg set msg } {} do_test btree-16.14 { btree_close_cursor $::c1 btree_close_cursor $::c2 btree_commit $::b1 catch {btree_clear_table $::b1 2} msg set msg } {SQLITE_ERROR} |
Changes to test/capi2.test.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
|
# 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 testing the callback-free C/C++ API. # # $Id: capi2.test,v 1.31 2006/02/10 13:33:31 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the text values from the current row pointed at by STMT as a list. proc get_row_values {STMT} { ................................................................................ do_test capi2-6.12 { list [sqlite3_step $VM1] \ [sqlite3_column_count $VM1] \ [get_row_values $VM1] \ [get_column_names $VM1] } {SQLITE_ROW 1 5 {x counter}} do_test capi2-6.13 { catchsql {UPDATE t3 SET x=x+1} } {1 {database table is locked}} do_test capi2-6.14 { list [sqlite3_step $VM1] \ [sqlite3_column_count $VM1] \ [get_row_values $VM1] \ [get_column_names $VM1] } {SQLITE_ROW 1 6 {x counter}} do_test capi2-6.15 { |
|
>
|
|
|
>
|
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
|
# 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 testing the callback-free C/C++ API. # # $Id: capi2.test,v 1.32 2006/08/16 16:42:48 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the text values from the current row pointed at by STMT as a list. proc get_row_values {STMT} { ................................................................................ do_test capi2-6.12 { list [sqlite3_step $VM1] \ [sqlite3_column_count $VM1] \ [get_row_values $VM1] \ [get_column_names $VM1] } {SQLITE_ROW 1 5 {x counter}} # A read no longer blocks a write in the same connection. #do_test capi2-6.13 { # catchsql {UPDATE t3 SET x=x+1} #} {1 {database table is locked}} do_test capi2-6.14 { list [sqlite3_step $VM1] \ [sqlite3_column_count $VM1] \ [get_row_values $VM1] \ [get_column_names $VM1] } {SQLITE_ROW 1 6 {x counter}} do_test capi2-6.15 { |
Changes to test/capi3.test.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
|
# 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 testing the callback-free C/C++ API. # # $Id: capi3.test,v 1.45 2006/04/07 13:50:38 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. ................................................................................ } {0 {}} do_test capi3-11.21 { sqlite3_finalize $STMT } {SQLITE_OK} # The following tests - capi3-12.* - check that it's Ok to start a # transaction while other VMs are active, and that it's Ok to execute # atomic updates in the same situation (so long as they are on a different # table). do_test capi3-12.1 { set STMT [sqlite3_prepare $DB "SELECT a FROM t2" -1 TAIL] sqlite3_step $STMT } {SQLITE_ROW} do_test capi3-12.2 { catchsql { INSERT INTO t1 VALUES(3, NULL); } } {0 {}} do_test capi3-12.3 { catchsql { INSERT INTO t2 VALUES(4); } } {1 {database table is locked}} do_test capi3-12.4 { catchsql { BEGIN; INSERT INTO t1 VALUES(4, NULL); } } {0 {}} do_test capi3-12.5 { sqlite3_step $STMT } {SQLITE_ROW} do_test capi3-12.6 { sqlite3_step $STMT } {SQLITE_DONE} do_test capi3-12.7 { sqlite3_finalize $STMT } {SQLITE_OK} |
|
|
|
<
|
>
>
>
|
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
|
# 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 testing the callback-free C/C++ API. # # $Id: capi3.test,v 1.46 2006/08/16 16:42:48 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Return the UTF-16 representation of the supplied UTF-8 string $str. # If $nt is true, append two 0x00 bytes as a nul terminator. ................................................................................ } {0 {}} do_test capi3-11.21 { sqlite3_finalize $STMT } {SQLITE_OK} # The following tests - capi3-12.* - check that it's Ok to start a # transaction while other VMs are active, and that it's Ok to execute # atomic updates in the same situation # do_test capi3-12.1 { set STMT [sqlite3_prepare $DB "SELECT a FROM t2" -1 TAIL] sqlite3_step $STMT } {SQLITE_ROW} do_test capi3-12.2 { catchsql { INSERT INTO t1 VALUES(3, NULL); } } {0 {}} do_test capi3-12.3 { catchsql { INSERT INTO t2 VALUES(4); } } {0 {}} do_test capi3-12.4 { catchsql { BEGIN; INSERT INTO t1 VALUES(4, NULL); } } {0 {}} do_test capi3-12.5 { sqlite3_step $STMT } {SQLITE_ROW} do_test capi3-12.5.1 { sqlite3_step $STMT } {SQLITE_ROW} do_test capi3-12.6 { sqlite3_step $STMT } {SQLITE_DONE} do_test capi3-12.7 { sqlite3_finalize $STMT } {SQLITE_OK} |
Changes to test/delete2.test.
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
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
|
# index entry was deleted first, before the table entry. And the index # delete worked. Thus an entry was deleted from the index but not from # the table. # # The solution to the problem was to detect that the table is locked # before the index entry is deleted. # # $Id: delete2.test,v 1.6 2006/01/05 11:34:34 danielk1977 Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a table that has an index. # ................................................................................ # do_test delete2-1.4 { set STMT [sqlite3_prepare $DB {SELECT * FROM q} -1 TAIL] sqlite3_step $STMT } SQLITE_ROW integrity_check delete2-1.5 # Try to delete a row from the table. The delete should fail. # breakpoint do_test delete2-1.6 { catchsql { DELETE FROM q WHERE rowid=1 } } {1 {database table is locked}} integrity_check delete2-1.7 do_test delete2-1.8 { execsql { SELECT * FROM q; } } {hello id.1 goodbye id.2 again id.3} # Finalize the query, thus clearing the lock on the table. Then # retry the delete. The delete should work this time. # do_test delete2-1.9 { sqlite3_finalize $STMT catchsql { |
|
|
|
|
|
|
|
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
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
|
# index entry was deleted first, before the table entry. And the index # delete worked. Thus an entry was deleted from the index but not from # the table. # # The solution to the problem was to detect that the table is locked # before the index entry is deleted. # # $Id: delete2.test,v 1.7 2006/08/16 16:42:48 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl # Create a table that has an index. # ................................................................................ # do_test delete2-1.4 { set STMT [sqlite3_prepare $DB {SELECT * FROM q} -1 TAIL] sqlite3_step $STMT } SQLITE_ROW integrity_check delete2-1.5 # Try to delete a row from the table while a read is in process. # As of 2006-08-16, this is allowed. (It used to fail with SQLITE_LOCKED.) # do_test delete2-1.6 { catchsql { DELETE FROM q WHERE rowid=1 } } {0 {}} integrity_check delete2-1.7 do_test delete2-1.8 { execsql { SELECT * FROM q; } } {goodbye id.2 again id.3} # Finalize the query, thus clearing the lock on the table. Then # retry the delete. The delete should work this time. # do_test delete2-1.9 { sqlite3_finalize $STMT catchsql { |
Changes to test/lock.test.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
# 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 database locks. # # $Id: lock.test,v 1.32 2005/03/29 03:11:00 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create an alternative connection to the database # ................................................................................ set x [db eval {SELECT * FROM t2}] } set x } {8 9} # You cannot UPDATE a table from within the callback of a SELECT # on that same table because the SELECT has the table locked. do_test lock-1.18 { db eval {SELECT * FROM t1} qv { set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg] lappend r $msg } set r } {1 {database table is locked}} # But you can UPDATE a different table from the one that is used in # the SELECT. # do_test lock-1.19 { db eval {SELECT * FROM t1} qv { set r [catch {db eval {UPDATE t2 SET x=y, y=x}} msg] |
|
>
>
>
>
|
|
|
|
<
>
|
|
|
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
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
|
# 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 database locks. # # $Id: lock.test,v 1.33 2006/08/16 16:42:48 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create an alternative connection to the database # ................................................................................ set x [db eval {SELECT * FROM t2}] } set x } {8 9} # You cannot UPDATE a table from within the callback of a SELECT # on that same table because the SELECT has the table locked. # # 2006-08-16: Reads no longer block writes within the same # database connection. # #do_test lock-1.18 { # db eval {SELECT * FROM t1} qv { # set r [catch {db eval {UPDATE t1 SET a=b, b=a}} msg] # lappend r $msg # } # set r #} {1 {database table is locked}} # But you can UPDATE a different table from the one that is used in # the SELECT. # do_test lock-1.19 { db eval {SELECT * FROM t1} qv { set r [catch {db eval {UPDATE t2 SET x=y, y=x}} msg] |
Changes to test/misc2.test.
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
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
|
# #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for miscellanous features that were # left out of other test files. # # $Id: misc2.test,v 1.24 2006/01/17 09:35:02 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {trigger} { # Test for ticket #360 # ................................................................................ } } {1 2} # Make sure we get an error message (not a segfault) on an attempt to # update a table from within the callback of a select on that same # table. # do_test misc2-7.1 { db close file delete -force test.db sqlite3 db test.db execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); } set rc [catch { db eval {SELECT rowid FROM t1} {} { db eval "DELETE FROM t1 WHERE rowid=$rowid" } } msg] lappend rc $msg } {1 {database table is locked}} do_test misc2-7.2 { set rc [catch { db eval {SELECT rowid FROM t1} {} { db eval "INSERT INTO t1 VALUES(3)" } } msg] lappend rc $msg } {1 {database table is locked}} ifcapable memorydb { do_test misc2-7.3 { sqlite3 db :memory: execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); } set rc [catch { db eval {SELECT rowid FROM t1} {} { db eval "DELETE FROM t1 WHERE rowid=$rowid" } } msg] lappend rc $msg } {1 {database table is locked}} do_test misc2-7.4 { set rc [catch { db eval {SELECT rowid FROM t1} {} { db eval "INSERT INTO t1 VALUES(3)" } } msg] lappend rc $msg } {1 {database table is locked}} } db close file delete -force test.db sqlite3 db test.db # Ticket #453. If the SQL ended with "-", the tokenizer was calling that # an incomplete token, which caused problem. The solution was to just call |
|
>
>
>
>
>
>
>
>
<
>
|
<
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
|
>
|
<
<
<
<
>
>
>
>
>
>
>
|
<
|
<
>
|
>
>
>
|
<
|
>
|
|
>
>
|
<
<
|
<
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
|
|
<
<
<
>
>
>
|
>
>
>
|
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
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
|
# #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for miscellanous features that were # left out of other test files. # # $Id: misc2.test,v 1.25 2006/08/16 16:42:48 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable {trigger} { # Test for ticket #360 # ................................................................................ } } {1 2} # Make sure we get an error message (not a segfault) on an attempt to # update a table from within the callback of a select on that same # table. # # 2006-08-16: This has changed. It is now permitted to update # the table being SELECTed from within the callback of the query. # do_test misc2-7.1 { db close file delete -force test.db sqlite3 db test.db execsql { CREATE TABLE t1(x); INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); SELECT * FROM t1; } } {1 2 3} do_test misc2-7.2 { set rc [catch { db eval {SELECT rowid FROM t1} {} { db eval "DELETE FROM t1 WHERE rowid=$rowid" } } msg] lappend rc $msg } {0 {}} do_test misc2-7.3 { execsql {SELECT * FROM t1} } {} do_test misc2-7.4 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); } db eval {SELECT rowid, x FROM t1} { if {$x & 1} { db eval {DELETE FROM t1 WHERE rowid=$rowid} } } execsql {SELECT * FROM t1} } {2 4} do_test misc2-7.5 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); } db eval {SELECT rowid, x FROM t1} { if {$x & 1} { db eval {DELETE FROM t1 WHERE rowid=$rowid+1} } } execsql {SELECT * FROM t1} } {1 3} do_test misc2-7.6 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); } db eval {SELECT rowid, x FROM t1} { if {$x & 1} { db eval {DELETE FROM t1} } } execsql {SELECT * FROM t1} } {} do_test misc2-7.7 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1); INSERT INTO t1 VALUES(2); INSERT INTO t1 VALUES(3); INSERT INTO t1 VALUES(4); } db eval {SELECT rowid, x FROM t1} { if {$x & 1} { db eval {UPDATE t1 SET x=x+100 WHERE rowid=$rowid} } } execsql {SELECT * FROM t1} } {101 2 103 4} do_test misc2-7.8 { execsql { DELETE FROM t1; INSERT INTO t1 VALUES(1); } db eval {SELECT rowid, x FROM t1} { if {$x<10} { db eval {INSERT INTO t1 VALUES($x+1)} } } execsql {SELECT * FROM t1} } {1 2 3 4 5 6 7 8 9 10} db close file delete -force test.db sqlite3 db test.db # Ticket #453. If the SQL ended with "-", the tokenizer was calling that # an incomplete token, which caused problem. The solution was to just call |