Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix for ticket #62: Do not report an SQLITE_READONLY error until the application actually tries to write data into a readonly file. It is OK to start a transaction on a read-only file, and doing so will get you a read lock. This change allows TEMP tables to be read/write even though the main database is readonly. (CVS 607) |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
9ef795d1d756a404d2123ebc19df1985 |
User & Date: | drh 2002-06-06 23:16:05.000 |
Context
2002-06-06
| ||
23:30 | Fix for ticket #59: Add documentation for the || operator. Also added documentation for the new SQL92 join syntax. (CVS 608) (check-in: a0abef62bf user: drh tags: trunk) | |
23:16 | Fix for ticket #62: Do not report an SQLITE_READONLY error until the application actually tries to write data into a readonly file. It is OK to start a transaction on a read-only file, and doing so will get you a read lock. This change allows TEMP tables to be read/write even though the main database is readonly. (CVS 607) (check-in: 9ef795d1d7 user: drh tags: trunk) | |
19:04 | Additional grammar cleanup resulting from the %fallback directive. (CVS 606) (check-in: c0cb3a012e user: drh tags: trunk) | |
Changes
Changes to src/btree.c.
1 2 3 4 5 6 7 8 9 10 11 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /* ** 2001 September 15 ** ** 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. ** ************************************************************************* ** $Id: btree.c,v 1.62 2002/06/06 23:16:05 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. |
︙ | ︙ | |||
313 314 315 316 317 318 319 320 321 322 323 324 325 326 | */ struct Btree { Pager *pPager; /* The page cache */ BtCursor *pCursor; /* A list of all open cursors */ PageOne *page1; /* First page of the database */ u8 inTrans; /* True if a transaction is in progress */ u8 inCkpt; /* True if there is a checkpoint on the transaction */ Hash locks; /* Key: root page number. Data: lock count */ }; typedef Btree Bt; /* ** A cursor is a pointer to a particular entry in the BTree. ** The entry is identified by its MemPage and the index in | > | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 | */ struct Btree { Pager *pPager; /* The page cache */ BtCursor *pCursor; /* A list of all open cursors */ PageOne *page1; /* First page of the database */ u8 inTrans; /* True if a transaction is in progress */ u8 inCkpt; /* True if there is a checkpoint on the transaction */ u8 readOnly; /* True if the underlying file is readonly */ Hash locks; /* Key: root page number. Data: lock count */ }; typedef Btree Bt; /* ** A cursor is a pointer to a particular entry in the BTree. ** The entry is identified by its MemPage and the index in |
︙ | ︙ | |||
626 627 628 629 630 631 632 633 634 635 636 637 638 639 | sqliteFree(pBt); *ppBtree = 0; return rc; } sqlitepager_set_destructor(pBt->pPager, pageDestructor); pBt->pCursor = 0; pBt->page1 = 0; sqliteHashInit(&pBt->locks, SQLITE_HASH_INT, 0); *ppBtree = pBt; return SQLITE_OK; } /* ** Close an open database and invalidate all cursors. | > | 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | sqliteFree(pBt); *ppBtree = 0; return rc; } sqlitepager_set_destructor(pBt->pPager, pageDestructor); pBt->pCursor = 0; pBt->page1 = 0; pBt->readOnly = sqlitepager_isreadonly(pBt->pPager); sqliteHashInit(&pBt->locks, SQLITE_HASH_INT, 0); *ppBtree = pBt; return SQLITE_OK; } /* ** Close an open database and invalidate all cursors. |
︙ | ︙ | |||
767 768 769 770 771 772 773 | if( pBt->inTrans ) return SQLITE_ERROR; if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ return rc; } } | | | | | | | > | | 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 | if( pBt->inTrans ) return SQLITE_ERROR; if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ return rc; } } if( pBt->readOnly ){ rc = SQLITE_OK; }else{ rc = sqlitepager_begin(pBt->page1); if( rc==SQLITE_OK ){ rc = newDatabase(pBt); } } if( rc==SQLITE_OK ){ pBt->inTrans = 1; pBt->inCkpt = 0; }else{ unlockBtreeIfUnused(pBt); } return rc; } /* ** Commit the transaction currently in progress. ** ** This will release the write lock on the database file. If there ** are no active cursors, it also releases the read lock. */ int sqliteBtreeCommit(Btree *pBt){ int rc; if( pBt->inTrans==0 ) return SQLITE_ERROR; rc = pBt->readOnly ? SQLITE_OK : sqlitepager_commit(pBt->pPager); pBt->inTrans = 0; pBt->inCkpt = 0; unlockBtreeIfUnused(pBt); return rc; } /* |
︙ | ︙ | |||
820 821 822 823 824 825 826 | pBt->inCkpt = 0; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pPage ){ sqlitepager_unref(pCur->pPage); pCur->pPage = 0; } } | | | | | | 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 | pBt->inCkpt = 0; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pPage ){ sqlitepager_unref(pCur->pPage); pCur->pPage = 0; } } rc = pBt->readOnly ? SQLITE_OK : sqlitepager_rollback(pBt->pPager); unlockBtreeIfUnused(pBt); return rc; } /* ** Set the checkpoint for the current transaction. The checkpoint serves ** as a sub-transaction that can be rolled back independently of the ** main transaction. You must start a transaction before starting a ** checkpoint. The checkpoint is ended automatically if the transaction ** commits or rolls back. ** ** Only one checkpoint may be active at a time. It is an error to try ** to start a new checkpoint if another checkpoint is already active. */ int sqliteBtreeBeginCkpt(Btree *pBt){ int rc; if( !pBt->inTrans || pBt->inCkpt ){ return SQLITE_ERROR; } rc = pBt->readOnly ? SQLITE_OK : sqlitepager_ckpt_begin(pBt->pPager); pBt->inCkpt = 1; return rc; } /* ** Commit a checkpoint to transaction currently in progress. If no ** checkpoint is active, this is a no-op. */ int sqliteBtreeCommitCkpt(Btree *pBt){ int rc; if( pBt->inCkpt && !pBt->readOnly ){ rc = sqlitepager_ckpt_commit(pBt->pPager); }else{ rc = SQLITE_OK; } pBt->inCkpt = 0; return rc; } /* ** Rollback the checkpoint to the current transaction. If there ** is no active checkpoint or transaction, this routine is a no-op. ** ** All cursors will be invalided by this operation. Any attempt ** to use a cursor that was open at the beginning of this operation ** will result in an error. */ int sqliteBtreeRollbackCkpt(Btree *pBt){ int rc; BtCursor *pCur; if( pBt->inCkpt==0 || pBt->readOnly ) return SQLITE_OK; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pPage ){ sqlitepager_unref(pCur->pPage); pCur->pPage = 0; } } rc = sqlitepager_ckpt_rollback(pBt->pPager); |
︙ | ︙ | |||
911 912 913 914 915 916 917 918 919 920 921 922 923 924 | if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ *ppCur = 0; return rc; } } pCur = sqliteMalloc( sizeof(*pCur) ); if( pCur==0 ){ rc = SQLITE_NOMEM; goto create_cursor_exception; } pCur->pgnoRoot = (Pgno)iTable; | > > > > | 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | if( pBt->page1==0 ){ rc = lockBtree(pBt); if( rc!=SQLITE_OK ){ *ppCur = 0; return rc; } } if( wrFlag && pBt->readOnly ){ *ppCur = 0; return SQLITE_READONLY; } pCur = sqliteMalloc( sizeof(*pCur) ); if( pCur==0 ){ rc = SQLITE_NOMEM; goto create_cursor_exception; } pCur->pgnoRoot = (Pgno)iTable; |
︙ | ︙ | |||
2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 | int sqliteBtreeCreateTable(Btree *pBt, int *piTable){ MemPage *pRoot; Pgno pgnoRoot; int rc; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } rc = allocatePage(pBt, &pRoot, &pgnoRoot); if( rc ) return rc; assert( sqlitepager_iswriteable(pRoot) ); zeroPage(pRoot); sqlitepager_unref(pRoot); *piTable = (int)pgnoRoot; return SQLITE_OK; } /* ** Create a new BTree index. Write into *piTable the page ** number for the root page of the new index. ** ** In the current implementation, BTree tables and BTree indices are the ** the same. But in the future, we may change this so that BTree tables ** are restricted to having a 4-byte integer key and arbitrary data and ** BTree indices are restricted to having an arbitrary key and no data. */ int sqliteBtreeCreateIndex(Btree *pBt, int *piIndex){ | > > > < < < < < < < < < < < < | | 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 | int sqliteBtreeCreateTable(Btree *pBt, int *piTable){ MemPage *pRoot; Pgno pgnoRoot; int rc; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pBt->readOnly ){ return SQLITE_READONLY; } rc = allocatePage(pBt, &pRoot, &pgnoRoot); if( rc ) return rc; assert( sqlitepager_iswriteable(pRoot) ); zeroPage(pRoot); sqlitepager_unref(pRoot); *piTable = (int)pgnoRoot; return SQLITE_OK; } /* ** Create a new BTree index. Write into *piTable the page ** number for the root page of the new index. ** ** In the current implementation, BTree tables and BTree indices are the ** the same. But in the future, we may change this so that BTree tables ** are restricted to having a 4-byte integer key and arbitrary data and ** BTree indices are restricted to having an arbitrary key and no data. */ int sqliteBtreeCreateIndex(Btree *pBt, int *piIndex){ return sqliteBtreeCreateTable(pBt, piIndex); } /* ** Erase the given database page and all its children. Return ** the page to the freelist. */ static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){ |
︙ | ︙ | |||
2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 | */ int sqliteBtreeClearTable(Btree *pBt, int iTable){ int rc; ptr nLock; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } nLock = (ptr)sqliteHashFind(&pBt->locks, 0, iTable); if( nLock ){ return SQLITE_LOCKED; } rc = clearDatabasePage(pBt, (Pgno)iTable, 0); if( rc ){ sqliteBtreeRollback(pBt); | > > > | 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 | */ int sqliteBtreeClearTable(Btree *pBt, int iTable){ int rc; ptr nLock; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pBt->readOnly ){ return SQLITE_READONLY; } nLock = (ptr)sqliteHashFind(&pBt->locks, 0, iTable); if( nLock ){ return SQLITE_LOCKED; } rc = clearDatabasePage(pBt, (Pgno)iTable, 0); if( rc ){ sqliteBtreeRollback(pBt); |
︙ | ︙ | |||
2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 | */ int sqliteBtreeDropTable(Btree *pBt, int iTable){ int rc; MemPage *pPage; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage); if( rc ) return rc; rc = sqliteBtreeClearTable(pBt, iTable); if( rc ) return rc; if( iTable>2 ){ rc = freePage(pBt, pPage, iTable); }else{ | > > > | 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 | */ int sqliteBtreeDropTable(Btree *pBt, int iTable){ int rc; MemPage *pPage; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pBt->readOnly ){ return SQLITE_READONLY; } rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage); if( rc ) return rc; rc = sqliteBtreeClearTable(pBt, iTable); if( rc ) return rc; if( iTable>2 ){ rc = freePage(pBt, pPage, iTable); }else{ |
︙ | ︙ | |||
2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 | */ int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){ PageOne *pP1; int rc; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } pP1 = pBt->page1; rc = sqlitepager_write(pP1); if( rc ) return rc; memcpy(pP1->aMeta, &aMeta[1], sizeof(pP1->aMeta)); return SQLITE_OK; } | > > > | 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 | */ int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){ PageOne *pP1; int rc; if( !pBt->inTrans ){ return SQLITE_ERROR; /* Must start a transaction first */ } if( pBt->readOnly ){ return SQLITE_READONLY; } pP1 = pBt->page1; rc = sqlitepager_write(pP1); if( rc ) return rc; memcpy(pP1->aMeta, &aMeta[1], sizeof(pP1->aMeta)); return SQLITE_OK; } |
︙ | ︙ |
Changes to src/vdbe.c.
︙ | ︙ | |||
26 27 28 29 30 31 32 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | ** type to the other occurs as necessary. ** ** Most of the code in this file is taken up by the sqliteVdbeExec() ** function which does the work of interpreting a VDBE program. ** But other routines are also provided to help in building up ** a program instruction by instruction. ** ** $Id: vdbe.c,v 1.153 2002/06/06 23:16:06 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> /* ** The following global variable is incremented every time a cursor ** moves, either by the OP_MoveTo or the OP_Next opcode. The test |
︙ | ︙ | |||
4860 4861 4862 4863 4864 4865 4866 | break; } /* Fall through to ROLLBACK */ } case OE_Rollback: { sqliteBtreeRollback(pBt); if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); | < < > | 4860 4861 4862 4863 4864 4865 4866 4867 4868 4869 4870 4871 4872 4873 4874 4875 4876 4877 4878 4879 4880 4881 4882 4883 4884 4885 4886 4887 4888 | break; } /* Fall through to ROLLBACK */ } case OE_Rollback: { sqliteBtreeRollback(pBt); if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; break; } default: { if( undoTransOnError ){ sqliteBtreeCommit(pBt); if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp); db->flags &= ~SQLITE_InTrans; db->onError = OE_Default; } break; } } sqliteRollbackInternalChanges(db); } sqliteBtreeCommitCkpt(pBt); if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp); assert( p->tos<pc ); return rc; /* Jump to here if a malloc() fails. It's hard to get a malloc() |
︙ | ︙ |
Changes to test/temptable.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for temporary tables and indices. # | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # May you share freely, never taking more than you give. # #*********************************************************************** # This file implements regression tests for SQLite library. # # This file implements tests for temporary tables and indices. # # $Id: temptable.test,v 1.6 2002/06/06 23:16:06 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl # Create an alternative connection to the database # do_test temptable-1.0 { |
︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 | do_test temptable-5.9 { execsql { SELECT y FROM t2 WHERE x=3 } } {4} db2 close finish_test | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | do_test temptable-5.9 { execsql { SELECT y FROM t2 WHERE x=3 } } {4} db2 close # Test for correct operation of read-only databases # do_test temptable-6.1 { execsql { CREATE TABLE t8(x); INSERT INTO t8 VALUES('xyzzy'); SELECT * FROM t8; } } {xyzzy} do_test temptable-6.2 { db close catch {file attributes test.db -permissions 0444} catch {file attributes test.db -readonly 1} sqlite db test.db execsql { SELECT * FROM t8; } } {xyzzy} do_test temptable-6.3 { catchsql { CREATE TABLE t9(x,y); } } {1 {attempt to write a readonly database}} do_test temptable-6.4 { catchsql { CREATE TEMP TABLE t9(x,y); } } {0 {}} do_test temptable-6.5 { catchsql { INSERT INTO t9 VALUES(1,2); SELECT * FROM t9; } } {0 {1 2}} do_test temptable-6.6 { catchsql { INSERT INTO t8 VALUES('hello'); SELECT * FROM t8; } } {1 {attempt to write a readonly database}} do_test temptable-6.7 { catchsql { SELECT * FROM t8,t9; } } {0 {xyzzy 1 2}} do_test temptable-6.8 { db close sqlite db test.db catchsql { SELECT * FROM t8,t9; } } {1 {no such table: t9}} finish_test |