/ Check-in [8c2a0836]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Relax the locking requirements on BTree cursors. Any number of read and write cursors can be open at the same time now, but a write cannot occur as long as one or more read cursors are open.

Before this change, one or more read cursors could be open on a table, or a single write cursor, but not both. Both policies have the same desirable effect: they prevent writes to a table while a sequential scan of that table is underway. But the new policy is a little less restrictive. Both policies prevent an UPDATE from occurring inside a SELECT (which is what we want) but the new policy allows a SELECT to occur inside an UPDATE. (CVS 739)

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 8c2a0836980341faa479cfe6c716409e6057367d
User & Date: drh 2002-09-01 23:20:45
Context
2002-09-02
12:14
Detect when the test scripts are being run as root and issue an appropriate error message. (CVS 740) check-in: 9ca2c507 user: drh tags: trunk
2002-09-01
23:20
Relax the locking requirements on BTree cursors. Any number of read and write cursors can be open at the same time now, but a write cannot occur as long as one or more read cursors are open.

Before this change, one or more read cursors could be open on a table, or a single write cursor, but not both. Both policies have the same desirable effect: they prevent writes to a table while a sequential scan of that table is underway. But the new policy is a little less restrictive. Both policies prevent an UPDATE from occurring inside a SELECT (which is what we want) but the new policy allows a SELECT to occur inside an UPDATE. (CVS 739) check-in: 8c2a0836 user: drh tags: trunk

2002-08-31
18:53
Parse foreign key constraints and populate internal data structures appropriately. Constraints are still not enforced. (CVS 738) check-in: 170711ca user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/btree.c.

     5      5   ** a legal notice, here is a blessing:
     6      6   **
     7      7   **    May you do good and not evil.
     8      8   **    May you find forgiveness for yourself and forgive others.
     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12         -** $Id: btree.c,v 1.71 2002/08/15 13:50:49 drh Exp $
           12  +** $Id: btree.c,v 1.72 2002/09/01 23:20:45 drh Exp $
    13     13   **
    14     14   ** This file implements a external (disk-based) database using BTrees.
    15     15   ** For a detailed discussion of BTrees, refer to
    16     16   **
    17     17   **     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
    18     18   **     "Sorting And Searching", pages 473-480. Addison-Wesley
    19     19   **     Publishing Company, Reading, Massachusetts.
................................................................................
   341    341     Pager *pPager;        /* The page cache */
   342    342     BtCursor *pCursor;    /* A list of all open cursors */
   343    343     PageOne *page1;       /* First page of the database */
   344    344     u8 inTrans;           /* True if a transaction is in progress */
   345    345     u8 inCkpt;            /* True if there is a checkpoint on the transaction */
   346    346     u8 readOnly;          /* True if the underlying file is readonly */
   347    347     u8 needSwab;          /* Need to byte-swapping */
   348         -  Hash locks;           /* Key: root page number.  Data: lock count */
   349    348   };
   350    349   typedef Btree Bt;
   351    350   
   352    351   /*
   353    352   ** A cursor is a pointer to a particular entry in the BTree.
   354    353   ** The entry is identified by its MemPage and the index in
   355    354   ** MemPage.apCell[] of the entry.
   356    355   */
   357    356   struct BtCursor {
   358    357     Btree *pBt;               /* The Btree to which this cursor belongs */
   359    358     BtCursor *pNext, *pPrev;  /* Forms a linked list of all cursors */
          359  +  BtCursor *pShared;        /* Loop of cursors with the same root page */
   360    360     Pgno pgnoRoot;            /* The root page of this tree */
   361    361     MemPage *pPage;           /* Page that contains the entry */
   362    362     int idx;                  /* Index of the entry in pPage->apCell[] */
   363    363     u8 wrFlag;                /* True if writable */
   364    364     u8 bSkipNext;             /* sqliteBtreeNext() is no-op if true */
   365    365     u8 iMatch;                /* compare result from last sqliteBtreeMoveto() */
   366    366   };
................................................................................
   678    678       *ppBtree = 0;
   679    679       return rc;
   680    680     }
   681    681     sqlitepager_set_destructor(pBt->pPager, pageDestructor);
   682    682     pBt->pCursor = 0;
   683    683     pBt->page1 = 0;
   684    684     pBt->readOnly = sqlitepager_isreadonly(pBt->pPager);
   685         -  sqliteHashInit(&pBt->locks, SQLITE_HASH_INT, 0);
   686    685     *ppBtree = pBt;
   687    686     return SQLITE_OK;
   688    687   }
   689    688   
   690    689   /*
   691    690   ** Close an open database and invalidate all cursors.
   692    691   */
   693    692   int sqliteBtreeClose(Btree *pBt){
   694    693     while( pBt->pCursor ){
   695    694       sqliteBtreeCloseCursor(pBt->pCursor);
   696    695     }
   697    696     sqlitepager_close(pBt->pPager);
   698         -  sqliteHashClear(&pBt->locks);
   699    697     sqliteFree(pBt);
   700    698     return SQLITE_OK;
   701    699   }
   702    700   
   703    701   /*
   704    702   ** Change the limit on the number of pages allowed the cache.
   705    703   **
................................................................................
   821    819   **      sqliteBtreeInsert()
   822    820   **      sqliteBtreeDelete()
   823    821   **      sqliteBtreeUpdateMeta()
   824    822   */
   825    823   int sqliteBtreeBeginTrans(Btree *pBt){
   826    824     int rc;
   827    825     if( pBt->inTrans ) return SQLITE_ERROR;
          826  +  if( pBt->readOnly ) return SQLITE_READONLY;
   828    827     if( pBt->page1==0 ){
   829    828       rc = lockBtree(pBt);
   830    829       if( rc!=SQLITE_OK ){
   831    830         return rc;
   832    831       }
   833    832     }
   834         -  if( pBt->readOnly ){
   835         -    rc = SQLITE_OK;
   836         -  }else{
   837         -    rc = sqlitepager_begin(pBt->page1);
   838         -    if( rc==SQLITE_OK ){
   839         -      rc = newDatabase(pBt);
   840         -    }
          833  +  rc = sqlitepager_begin(pBt->page1);
          834  +  if( rc==SQLITE_OK ){
          835  +    rc = newDatabase(pBt);
   841    836     }
   842    837     if( rc==SQLITE_OK ){
   843    838       pBt->inTrans = 1;
   844    839       pBt->inCkpt = 0;
   845    840     }else{
   846    841       unlockBtreeIfUnused(pBt);
   847    842     }
................................................................................
   852    847   ** Commit the transaction currently in progress.
   853    848   **
   854    849   ** This will release the write lock on the database file.  If there
   855    850   ** are no active cursors, it also releases the read lock.
   856    851   */
   857    852   int sqliteBtreeCommit(Btree *pBt){
   858    853     int rc;
   859         -  if( pBt->inTrans==0 ) return SQLITE_ERROR;
   860    854     rc = pBt->readOnly ? SQLITE_OK : sqlitepager_commit(pBt->pPager);
   861    855     pBt->inTrans = 0;
   862    856     pBt->inCkpt = 0;
   863    857     unlockBtreeIfUnused(pBt);
   864    858     return rc;
   865    859   }
   866    860   
................................................................................
   899    893   **
   900    894   ** Only one checkpoint may be active at a time.  It is an error to try
   901    895   ** to start a new checkpoint if another checkpoint is already active.
   902    896   */
   903    897   int sqliteBtreeBeginCkpt(Btree *pBt){
   904    898     int rc;
   905    899     if( !pBt->inTrans || pBt->inCkpt ){
   906         -    return SQLITE_ERROR;
          900  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
   907    901     }
   908    902     rc = pBt->readOnly ? SQLITE_OK : sqlitepager_ckpt_begin(pBt->pPager);
   909    903     pBt->inCkpt = 1;
   910    904     return rc;
   911    905   }
   912    906   
   913    907   
................................................................................
   951    945   
   952    946   /*
   953    947   ** Create a new cursor for the BTree whose root is on the page
   954    948   ** iTable.  The act of acquiring a cursor gets a read lock on 
   955    949   ** the database file.
   956    950   **
   957    951   ** If wrFlag==0, then the cursor can only be used for reading.
   958         -** If wrFlag==1, then the cursor can be used for reading or writing.
   959         -** A read/write cursor requires exclusive access to its table.  There
   960         -** cannot be two or more cursors open on the same table if any one of
   961         -** cursors is a read/write cursor.  But there can be two or more
   962         -** read-only cursors open on the same table.
          952  +** If wrFlag==1, then the cursor can be used for reading or for
          953  +** writing if other conditions for writing are also met.  These
          954  +** are the conditions that must be met in order for writing to
          955  +** be allowed:
   963    956   **
          957  +** 1:  The cursor must have been opened with wrFlag==1
          958  +**
          959  +** 2:  No other cursors may be open with wrFlag==0 on the same table
          960  +**
          961  +** 3:  The database must be writable (not on read-only media)
          962  +**
          963  +** 4:  There must be an active transaction.
          964  +**
          965  +** Condition 2 warrants further discussion.  If any cursor is opened
          966  +** on a table with wrFlag==0, that prevents all other cursors from
          967  +** writing to that table.  This is a kind of "read-lock".  When a cursor
          968  +** is opened with wrFlag==0 it is guaranteed that the table will not
          969  +** change as long as the cursor is open.  This allows the cursor to
          970  +** do a sequential scan of the table without having to worry about
          971  +** entries being inserted or deleted during the scan.  Cursors should
          972  +** be opened with wrFlag==0 only if this read-lock property is needed.
          973  +** That is to say, cursors should be opened with wrFlag==0 only if they
          974  +** intend to use the sqliteBtreeNext() system call.  All other cursors
          975  +** should be opened with wrFlag==1 even if they never really intend
          976  +** to write.
          977  +** 
   964    978   ** No checking is done to make sure that page iTable really is the
   965    979   ** root page of a b-tree.  If it is not, then the cursor acquired
   966    980   ** will not work correctly.
   967    981   */
   968    982   int sqliteBtreeCursor(Btree *pBt, int iTable, int wrFlag, BtCursor **ppCur){
   969    983     int rc;
   970         -  BtCursor *pCur;
   971         -  ptr nLock;
          984  +  BtCursor *pCur, *pRing;
   972    985   
   973    986     if( pBt->page1==0 ){
   974    987       rc = lockBtree(pBt);
   975    988       if( rc!=SQLITE_OK ){
   976    989         *ppCur = 0;
   977    990         return rc;
   978    991       }
   979    992     }
   980         -  if( wrFlag && pBt->readOnly ){
   981         -    *ppCur = 0;
   982         -    return SQLITE_READONLY;
   983         -  }
   984    993     pCur = sqliteMalloc( sizeof(*pCur) );
   985    994     if( pCur==0 ){
   986    995       rc = SQLITE_NOMEM;
   987    996       goto create_cursor_exception;
   988    997     }
   989    998     pCur->pgnoRoot = (Pgno)iTable;
   990    999     rc = sqlitepager_get(pBt->pPager, pCur->pgnoRoot, (void**)&pCur->pPage);
................................................................................
   991   1000     if( rc!=SQLITE_OK ){
   992   1001       goto create_cursor_exception;
   993   1002     }
   994   1003     rc = initPage(pBt, pCur->pPage, pCur->pgnoRoot, 0);
   995   1004     if( rc!=SQLITE_OK ){
   996   1005       goto create_cursor_exception;
   997   1006     }
   998         -  nLock = (ptr)sqliteHashFind(&pBt->locks, 0, iTable);
   999         -  if( nLock<0 || (nLock>0 && wrFlag) ){
  1000         -    rc = SQLITE_LOCKED;
  1001         -    goto create_cursor_exception;
  1002         -  }
  1003         -  nLock = wrFlag ? -1 : nLock+1;
  1004         -  sqliteHashInsert(&pBt->locks, 0, iTable, (void*)nLock);
  1005   1007     pCur->pBt = pBt;
  1006   1008     pCur->wrFlag = wrFlag;
  1007   1009     pCur->idx = 0;
  1008   1010     pCur->pNext = pBt->pCursor;
  1009   1011     if( pCur->pNext ){
  1010   1012       pCur->pNext->pPrev = pCur;
  1011   1013     }
  1012   1014     pCur->pPrev = 0;
         1015  +  pRing = pBt->pCursor;
         1016  +  while( pRing && pRing->pgnoRoot!=pCur->pgnoRoot ){ pRing = pRing->pNext; }
         1017  +  if( pRing ){
         1018  +    pCur->pShared = pRing->pShared;
         1019  +    pRing->pShared = pCur;
         1020  +  }else{
         1021  +    pCur->pShared = pCur;
         1022  +  }
  1013   1023     pBt->pCursor = pCur;
  1014   1024     *ppCur = pCur;
  1015   1025     return SQLITE_OK;
  1016   1026   
  1017   1027   create_cursor_exception:
  1018   1028     *ppCur = 0;
  1019   1029     if( pCur ){
................................................................................
  1025   1035   }
  1026   1036   
  1027   1037   /*
  1028   1038   ** Close a cursor.  The read lock on the database file is released
  1029   1039   ** when the last cursor is closed.
  1030   1040   */
  1031   1041   int sqliteBtreeCloseCursor(BtCursor *pCur){
  1032         -  ptr nLock;
  1033   1042     Btree *pBt = pCur->pBt;
  1034   1043     if( pCur->pPrev ){
  1035   1044       pCur->pPrev->pNext = pCur->pNext;
  1036   1045     }else{
  1037   1046       pBt->pCursor = pCur->pNext;
  1038   1047     }
  1039   1048     if( pCur->pNext ){
  1040   1049       pCur->pNext->pPrev = pCur->pPrev;
  1041   1050     }
  1042   1051     if( pCur->pPage ){
  1043   1052       sqlitepager_unref(pCur->pPage);
  1044   1053     }
         1054  +  if( pCur->pShared!=pCur ){
         1055  +    BtCursor *pRing = pCur->pShared;
         1056  +    while( pRing->pShared!=pCur ){ pRing = pRing->pShared; }
         1057  +    pRing->pShared = pCur->pShared;
         1058  +  }
  1045   1059     unlockBtreeIfUnused(pBt);
  1046         -  nLock = (ptr)sqliteHashFind(&pBt->locks, 0, pCur->pgnoRoot);
  1047         -  assert( nLock!=0 || sqlite_malloc_failed );
  1048         -  nLock = nLock<0 ? 0 : nLock-1;
  1049         -  sqliteHashInsert(&pBt->locks, 0, pCur->pgnoRoot, (void*)nLock);
  1050   1060     sqliteFree(pCur);
  1051   1061     return SQLITE_OK;
  1052   1062   }
  1053   1063   
  1054   1064   /*
  1055   1065   ** Make a temporary cursor by filling in the fields of pTempCur.
  1056   1066   ** The temporary cursor is not on the cursor list for the Btree.
................................................................................
  2383   2393       pCur->pPage = pParent;
  2384   2394       pCur->idx = 0;
  2385   2395     }else{
  2386   2396       sqlitepager_unref(pParent);
  2387   2397     }
  2388   2398     return rc;
  2389   2399   }
         2400  +
         2401  +/*
         2402  +** This routine checks all cursors that point to the same table
         2403  +** as pCur points to.  If any of those cursors were opened with
         2404  +** wrFlag==0 then this routine returns SQLITE_LOCKED.  If all
         2405  +** cursors point to the same table were opened with wrFlag==1
         2406  +** then this routine returns SQLITE_OK.
         2407  +**
         2408  +** In addition to checking for read-locks (where a read-lock 
         2409  +** means a cursor opened with wrFlag==0) this routine also moves
         2410  +** all cursors other than pCur so that they are pointing to the 
         2411  +** first Cell on root page.  This is necessary because an insert 
         2412  +** or delete might change the number of cells on a page or delete
         2413  +** a page entirely and we do not want to leave any cursors 
         2414  +** pointing to non-existant pages or cells.
         2415  +*/
         2416  +static int checkReadLocks(BtCursor *pCur){
         2417  +  BtCursor *p;
         2418  +  assert( pCur->wrFlag );
         2419  +  for(p=pCur->pShared; p!=pCur; p=p->pShared){
         2420  +    assert( p );
         2421  +    assert( p->pgnoRoot==pCur->pgnoRoot );
         2422  +    if( p->wrFlag==0 ) return SQLITE_LOCKED;
         2423  +    if( sqlitepager_pagenumber(p->pPage)!=p->pgnoRoot ){
         2424  +      moveToRoot(p);
         2425  +    }
         2426  +  }
         2427  +  return SQLITE_OK;
         2428  +}
  2390   2429   
  2391   2430   /*
  2392   2431   ** Insert a new record into the BTree.  The key is given by (pKey,nKey)
  2393   2432   ** and the data is given by (pData,nData).  The cursor is used only to
  2394   2433   ** define what database the record should be inserted into.  The cursor
  2395   2434   ** is left pointing at the new record.
  2396   2435   */
................................................................................
  2405   2444     int szNew;
  2406   2445     MemPage *pPage;
  2407   2446     Btree *pBt = pCur->pBt;
  2408   2447   
  2409   2448     if( pCur->pPage==0 ){
  2410   2449       return SQLITE_ABORT;  /* A rollback destroyed this cursor */
  2411   2450     }
  2412         -  if( !pCur->pBt->inTrans || nKey+nData==0 ){
  2413         -    return SQLITE_ERROR;  /* Must start a transaction first */
         2451  +  if( !pBt->inTrans || nKey+nData==0 ){
         2452  +    /* Must start a transaction before doing an insert */
         2453  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2414   2454     }
         2455  +  assert( !pBt->readOnly );
  2415   2456     if( !pCur->wrFlag ){
  2416   2457       return SQLITE_PERM;   /* Cursor not open for writing */
  2417   2458     }
         2459  +  if( checkReadLocks(pCur) ){
         2460  +    return SQLITE_LOCKED; /* The table pCur points to has a read lock */
         2461  +  }
  2418   2462     rc = sqliteBtreeMoveto(pCur, pKey, nKey, &loc);
  2419   2463     if( rc ) return rc;
  2420   2464     pPage = pCur->pPage;
  2421   2465     assert( pPage->isInit );
  2422   2466     rc = sqlitepager_write(pPage);
  2423   2467     if( rc ) return rc;
  2424   2468     rc = fillInCell(pBt, &newCell, pKey, nKey, pData, nData);
................................................................................
  2459   2503     Pgno pgnoChild;
  2460   2504     Btree *pBt = pCur->pBt;
  2461   2505   
  2462   2506     assert( pPage->isInit );
  2463   2507     if( pCur->pPage==0 ){
  2464   2508       return SQLITE_ABORT;  /* A rollback destroyed this cursor */
  2465   2509     }
  2466         -  if( !pCur->pBt->inTrans ){
  2467         -    return SQLITE_ERROR;  /* Must start a transaction first */
         2510  +  if( !pBt->inTrans ){
         2511  +    /* Must start a transaction before doing a delete */
         2512  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2468   2513     }
         2514  +  assert( !pBt->readOnly );
  2469   2515     if( pCur->idx >= pPage->nCell ){
  2470   2516       return SQLITE_ERROR;  /* The cursor is not pointing to anything */
  2471   2517     }
  2472   2518     if( !pCur->wrFlag ){
  2473   2519       return SQLITE_PERM;   /* Did not open this cursor for writing */
  2474   2520     }
         2521  +  if( checkReadLocks(pCur) ){
         2522  +    return SQLITE_LOCKED; /* The table pCur points to has a read lock */
         2523  +  }
  2475   2524     rc = sqlitepager_write(pPage);
  2476   2525     if( rc ) return rc;
  2477   2526     pCell = pPage->apCell[pCur->idx];
  2478   2527     pgnoChild = SWAB32(pBt, pCell->h.leftChild);
  2479   2528     clearCell(pBt, pCell);
  2480   2529     if( pgnoChild ){
  2481   2530       /*
................................................................................
  2534   2583   ** BTree indices are restricted to having an arbitrary key and no data.
  2535   2584   */
  2536   2585   int sqliteBtreeCreateTable(Btree *pBt, int *piTable){
  2537   2586     MemPage *pRoot;
  2538   2587     Pgno pgnoRoot;
  2539   2588     int rc;
  2540   2589     if( !pBt->inTrans ){
  2541         -    return SQLITE_ERROR;  /* Must start a transaction first */
         2590  +    /* Must start a transaction first */
         2591  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2542   2592     }
  2543   2593     if( pBt->readOnly ){
  2544   2594       return SQLITE_READONLY;
  2545   2595     }
  2546   2596     rc = allocatePage(pBt, &pRoot, &pgnoRoot, 0);
  2547   2597     if( rc ) return rc;
  2548   2598     assert( sqlitepager_iswriteable(pRoot) );
................................................................................
  2606   2656   }
  2607   2657   
  2608   2658   /*
  2609   2659   ** Delete all information from a single table in the database.
  2610   2660   */
  2611   2661   int sqliteBtreeClearTable(Btree *pBt, int iTable){
  2612   2662     int rc;
  2613         -  ptr nLock;
         2663  +  BtCursor *pCur;
  2614   2664     if( !pBt->inTrans ){
  2615         -    return SQLITE_ERROR;  /* Must start a transaction first */
         2665  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2616   2666     }
  2617         -  if( pBt->readOnly ){
  2618         -    return SQLITE_READONLY;
  2619         -  }
  2620         -  nLock = (ptr)sqliteHashFind(&pBt->locks, 0, iTable);
  2621         -  if( nLock ){
  2622         -    return SQLITE_LOCKED;
         2667  +  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
         2668  +    if( pCur->pgnoRoot==(Pgno)iTable ){
         2669  +      if( pCur->wrFlag==0 ) return SQLITE_LOCKED;
         2670  +      moveToRoot(pCur);
         2671  +    }
  2623   2672     }
  2624   2673     rc = clearDatabasePage(pBt, (Pgno)iTable, 0);
  2625   2674     if( rc ){
  2626   2675       sqliteBtreeRollback(pBt);
  2627   2676     }
  2628   2677     return rc;
  2629   2678   }
................................................................................
  2632   2681   ** Erase all information in a table and add the root of the table to
  2633   2682   ** the freelist.  Except, the root of the principle table (the one on
  2634   2683   ** page 2) is never added to the freelist.
  2635   2684   */
  2636   2685   int sqliteBtreeDropTable(Btree *pBt, int iTable){
  2637   2686     int rc;
  2638   2687     MemPage *pPage;
         2688  +  BtCursor *pCur;
  2639   2689     if( !pBt->inTrans ){
  2640         -    return SQLITE_ERROR;  /* Must start a transaction first */
         2690  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2641   2691     }
  2642         -  if( pBt->readOnly ){
  2643         -    return SQLITE_READONLY;
         2692  +  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
         2693  +    if( pCur->pgnoRoot==(Pgno)iTable ){
         2694  +      return SQLITE_LOCKED;  /* Cannot drop a table that has a cursor */
         2695  +    }
  2644   2696     }
  2645   2697     rc = sqlitepager_get(pBt->pPager, (Pgno)iTable, (void**)&pPage);
  2646   2698     if( rc ) return rc;
  2647   2699     rc = sqliteBtreeClearTable(pBt, iTable);
  2648   2700     if( rc ) return rc;
  2649   2701     if( iTable>2 ){
  2650   2702       rc = freePage(pBt, pPage, iTable);
................................................................................
  2676   2728   /*
  2677   2729   ** Write meta-information back into the database.
  2678   2730   */
  2679   2731   int sqliteBtreeUpdateMeta(Btree *pBt, int *aMeta){
  2680   2732     PageOne *pP1;
  2681   2733     int rc, i;
  2682   2734     if( !pBt->inTrans ){
  2683         -    return SQLITE_ERROR;  /* Must start a transaction first */
  2684         -  }
  2685         -  if( pBt->readOnly ){
  2686         -    return SQLITE_READONLY;
         2735  +    return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
  2687   2736     }
  2688   2737     pP1 = pBt->page1;
  2689   2738     rc = sqlitepager_write(pP1);
  2690   2739     if( rc ) return rc;   
  2691   2740     for(i=0; i<sizeof(pP1->aMeta)/sizeof(pP1->aMeta[0]); i++){
  2692   2741       pP1->aMeta[i] = SWAB32(pBt, aMeta[i+1]);
  2693   2742     }

Changes to src/vdbe.c.

    26     26   ** type to the other occurs as necessary.
    27     27   ** 
    28     28   ** Most of the code in this file is taken up by the sqliteVdbeExec()
    29     29   ** function which does the work of interpreting a VDBE program.
    30     30   ** But other routines are also provided to help in building up
    31     31   ** a program instruction by instruction.
    32     32   **
    33         -** $Id: vdbe.c,v 1.173 2002/08/28 03:01:00 drh Exp $
           33  +** $Id: vdbe.c,v 1.174 2002/09/01 23:20:46 drh Exp $
    34     34   */
    35     35   #include "sqliteInt.h"
    36     36   #include <ctype.h>
    37     37   
    38     38   /*
    39     39   ** The following global variable is incremented every time a cursor
    40     40   ** moves, either by the OP_MoveTo or the OP_Next opcode.  The test
................................................................................
  2955   2955         case SQLITE_BUSY: {
  2956   2956           if( xBusy==0 || (*xBusy)(pBusyArg, "", ++busy)==0 ){
  2957   2957             sqliteSetString(pzErrMsg, sqlite_error_string(rc), 0);
  2958   2958             busy = 0;
  2959   2959           }
  2960   2960           break;
  2961   2961         }
         2962  +      case SQLITE_READONLY: {
         2963  +        rc = SQLITE_OK;
         2964  +        /* Fall thru into the next case */
         2965  +      }
  2962   2966         case SQLITE_OK: {
  2963   2967           busy = 0;
  2964   2968           break;
  2965   2969         }
  2966   2970         default: {
  2967   2971           goto abort_due_to_error;
  2968   2972         }