SQLite

Check-in [9ef795d1d7]
Login

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: 9ef795d1d756a404d2123ebc19df1985b19c9888
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
Unified Diff Ignore Whitespace Patch
Changes to src/btree.c.
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.61 2002/05/15 11:44:14 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.











|







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
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
  if( pBt->inTrans ) return SQLITE_ERROR;
  if( pBt->page1==0 ){
    rc = lockBtree(pBt);
    if( rc!=SQLITE_OK ){
      return rc;
    }
  }
  if( sqlitepager_isreadonly(pBt->pPager) ){
    return SQLITE_READONLY;
  }
  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 = sqlitepager_commit(pBt->pPager);
  pBt->inTrans = 0;
  pBt->inCkpt = 0;
  unlockBtreeIfUnused(pBt);
  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
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
  pBt->inCkpt = 0;
  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
    if( pCur->pPage ){
      sqlitepager_unref(pCur->pPage);
      pCur->pPage = 0;
    }
  }
  rc = 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 = 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 ){
    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 ) 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);







|



















|











|



















|







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
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
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){
  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);
  *piIndex = (int)pgnoRoot;
  return SQLITE_OK;
}

/*
** Erase the given database page and all its children.  Return
** the page to the freelist.
*/
static int clearDatabasePage(Btree *pBt, Pgno pgno, int freePageFlag){







>
>
>



















<
<
<
<
<
<
<
<
<
<
<
<
|







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
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.152 2002/06/01 21:41:10 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







|







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
4867
4868
4869
4870
4871
4872
4873
4874
4875
4876
4877
4878
4879
4880
4881
4882

4883
4884
4885
4886
4887
4888
4889
          break;
        }
        /* Fall through to ROLLBACK */
      }
      case OE_Rollback: {
        sqliteBtreeRollback(pBt);
        if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
        sqliteRollbackInternalChanges(db);
        db->flags &= ~SQLITE_InTrans;
        db->onError = OE_Default;
        break;
      }
      default: {
        if( undoTransOnError ){
          sqliteBtreeCommit(pBt);
          if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp);
          sqliteCommitInternalChanges(db);
          db->flags &= ~SQLITE_InTrans;
          db->onError = OE_Default;
        }
        break;
      }
    }

  }
  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()







<








<






>







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
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.5 2002/05/10 13:14:08 drh Exp $

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Create an alternative connection to the database
#
do_test temptable-1.0 {







|







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