/ Check-in [b6c70037]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Changes for exclusive access mode. There are still some bugs. (CVS 3712)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: b6c700370be29db2b974f9abd719c3e56abf8058
User & Date: danielk1977 2007-03-24 16:45:05
Context
2007-03-25
19:08
Add the sqlite3_prepare_v2 and sqlite3_prepare16_v2 APIs to the loadable extension interface. (CVS 3713) check-in: f02ba56d user: drh tags: trunk
2007-03-24
16:45
Changes for exclusive access mode. There are still some bugs. (CVS 3712) check-in: b6c70037 user: danielk1977 tags: trunk
2007-03-23
18:12
Discard the contents of the pager-cache only when the change-counter indicates that it is stale. (CVS 3711) check-in: 07b56965 user: danielk1977 tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/attach.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
129
130
131
132
133
134
135

136
137
138
139
140
141
142
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
** $Id: attach.c,v 1.54 2007/03/15 15:35:29 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** Resolve an expression that was part of an ATTACH or DETACH statement. This
** is slightly different from resolving a normal SQL expression, because simple
** identifiers are treated as strings, not possible column names or aliases.
................................................................................
    if( !aNew->pSchema ){
      rc = SQLITE_NOMEM;
    }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
      strcpy(zErr, 
        "attached databases must use the same text encoding as main database");
      goto attach_error;
    }

  }
  aNew->zName = sqliteStrDup(zName);
  aNew->safety_level = 3;

#if SQLITE_HAS_CODEC
  {
    extern int sqlite3CodecAttach(sqlite3*, int, void*, int);







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
**
** $Id: attach.c,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
*/
#include "sqliteInt.h"

/*
** Resolve an expression that was part of an ATTACH or DETACH statement. This
** is slightly different from resolving a normal SQL expression, because simple
** identifiers are treated as strings, not possible column names or aliases.
................................................................................
    if( !aNew->pSchema ){
      rc = SQLITE_NOMEM;
    }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
      strcpy(zErr, 
        "attached databases must use the same text encoding as main database");
      goto attach_error;
    }
    sqlite3PagerLockingMode(sqlite3BtreePager(aNew->pBt), db->dfltLockMode);
  }
  aNew->zName = sqliteStrDup(zName);
  aNew->safety_level = 3;

#if SQLITE_HAS_CODEC
  {
    extern int sqlite3CodecAttach(sqlite3*, int, void*, int);

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
281
282
283
284
285
286
287
288
289



290
291
292
293
294
295
296
...
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
...
898
899
900
901
902
903
904
905
906
907
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
949





950
951
952

953
954
955
956
957
958

959
960
961
962
963
964
965
966
967
968
969

970
971
972








973
974
975
976
977
978
979
....
1105
1106
1107
1108
1109
1110
1111





1112
1113
1114
1115
1116
1117
1118
....
1195
1196
1197
1198
1199
1200
1201

1202
1203
1204
1205
1206
1207
1208
....
1238
1239
1240
1241
1242
1243
1244

1245
1246
1247
1248
1249
1250
1251
....
2122
2123
2124
2125
2126
2127
2128

2129
2130
2131
2132
2133
2134
2135
....
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
....
3606
3607
3608
3609
3610
3611
3612


3613
3614
3615
3616
3617
3618
3619
....
3886
3887
3888
3889
3890
3891
3892

3893
3894
3895
3896
3897
3898
3899
....
4093
4094
4095
4096
4097
4098
4099

















4100
4101
4102
4103
4104
4105
4106
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.293 2007/03/23 18:12:07 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
  void *pCodecArg;            /* First argument to xCodec() */
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Linked list of pagers in this thread */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  int doNotSync;              /* While true, do not spill the cache */
  u32 iChangeCount;           /* Db change-counter for which cache is valid */



};

/*
** If SQLITE_TEST is defined then increment the variable given in
** the argument
*/
#ifdef SQLITE_TEST
................................................................................
  return p;
}

/*
** Unlock the database file.
*/
static void pager_unlock(Pager *pPager){

  if( !MEMDB ){
    sqlite3OsUnlock(pPager->fd, NO_LOCK);
    pPager->dbSize = -1;
    IOTRACE(("UNLOCK %p\n", pPager))
  }
  pPager->state = PAGER_UNLOCK;


}

/*
** Execute a rollback if a transaction is active and unlock the 
** database file. This is a no-op if the pager has already entered
** the error-state.
*/
static void pagerUnlockAndRollback(Pager *pPager){
  if( pPager->errCode ) return;
  if( pPager->state>=PAGER_RESERVED ){
    sqlite3PagerRollback(pPager);
  }
  pager_unlock(pPager);
  assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
}


/*
** Unlock the database and clear the in-memory cache.  This routine
** sets the state of the pager back to what it was when it was first
** opened.  Any outstanding pages are invalidated and subsequent attempts
................................................................................
  pPager->nHash = 0;
  sqliteFree(pPager->aHash);
  pPager->nPage = 0;
  pPager->aHash = 0;
  pPager->nRef = 0;
}

/*
** This function resets the various pager flags to their initial
** state but does not discard the cached content.
*/
static void pagerSoftReset(Pager *pPager){
  PgHdr *pPg;

  assert(pPager->pStmt==0);
  assert(pPager->nRef==0);
  assert(pPager->pFirstSynced==pPager->pFirst);
  assert(pPager->aInJournal==0);

  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
    assert( pPg->nRef==0 );
    pPg->inJournal = 0;
    pPg->inStmt = 0;
    pPg->dirty = 0;
    pPg->needSync = 0;
    pPg->alwaysRollback = 0;
  }
}

/*
** When this routine is called, the pager has the journal file open and
** a RESERVED or EXCLUSIVE lock on the database.  This routine releases
** the database lock and acquires a SHARED lock in its place.  The journal
** file is deleted and closed.
**
** TODO: Consider keeping the journal file open for temporary databases.
** This might give a performance improvement on windows where opening
** a file is an expensive operation.
*/
static int pager_unwritelock(Pager *pPager){
  PgHdr *pPg;
  int rc;
  assert( !MEMDB );
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_OK;
  }
  sqlite3PagerStmtCommit(pPager);
  if( pPager->stmtOpen ){
    sqlite3OsClose(&pPager->stfd);
    pPager->stmtOpen = 0;
  }
  if( pPager->journalOpen ){





    sqlite3OsClose(&pPager->jfd);
    pPager->journalOpen = 0;
    sqlite3OsDelete(pPager->zJournal);

    sqliteFree( pPager->aInJournal );
    pPager->aInJournal = 0;
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;

#ifdef SQLITE_CHECK_PAGES
      pPg->pageHash = pager_pagehash(pPg);
#endif
    }
    pPager->pDirty = 0;
    pPager->dirtyCache = 0;
    pPager->nRec = 0;
  }else{
    assert( pPager->aInJournal==0 );
    assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
  }

  rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
  pPager->state = PAGER_SHARED;
  pPager->origDbSize = 0;








  pPager->setMaster = 0;
  pPager->needSync = 0;
  pPager->pFirstSynced = pPager->pFirst;
  pPager->dbSize = -1;
  return rc;
}

................................................................................
    if( pPager->xDestructor ){  /*** FIX ME:  Should this be xReinit? ***/
      pPager->xDestructor(pPg, pPager->pageSize);
    }
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
    CODEC1(pPager, pData, pPg->pgno, 3);





  }
  return rc;
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
................................................................................
  }  
  if( master_open ){
    sqlite3OsClose(&master);
  }
  return rc;
}


/*
** Make every page in the cache agree with what is on disk.  In other words,
** reread the disk to reset the state of the cache.
**
** This routine is called after a rollback in which some of the dirty cache
** pages had never been written out to disk.  We need to roll back the
** cache content and the easiest way to do that is to reread the old content
................................................................................
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
  }
  pPager->pDirty = 0;
  return rc;
}


/*
** Truncate the main file of the given pager to the number of pages
** indicated.
*/
static int pager_truncate(Pager *pPager, int nPage){
  assert( pPager->state>=PAGER_EXCLUSIVE );
................................................................................
  ThreadData *pTsd = sqlite3ThreadData();
  assert( pPager );
  assert( pTsd && pTsd->nAlloc );
#endif

  disable_simulated_io_errors();
  pPager->errCode = 0;

  pager_reset(pPager);
  pagerUnlockAndRollback(pPager);
  enable_simulated_io_errors();
  TRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
  if( pPager->journalOpen ){
................................................................................
          */
          u32 iChangeCount = retrieve32bits(pPage1, 24);
          pPager->nRef++;
          sqlite3PagerUnref(pPage1);
          pPager->nRef--;
          if( iChangeCount!=pPager->iChangeCount ){
            pager_reset(pPager);
          }else{
            pagerSoftReset(pPager);
          }
          pPager->iChangeCount = iChangeCount;
        }
      }
    }
    pPager->state = PAGER_SHARED;
  }
................................................................................
  TRACE2("ROLLBACK %d\n", PAGERID(pPager));
  if( MEMDB ){
    PgHdr *p;
    for(p=pPager->pAll; p; p=p->pNextAll){
      PgHistory *pHist;
      assert( !p->alwaysRollback );
      if( !p->dirty ){


        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
        continue;
      }

      pHist = PGHDR_TO_HIST(p, pPager);
      if( pHist->pOrig ){
................................................................................
  /* Increment the value just read and write it back to byte 24. */
  change_counter++;
  put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
  pPager->iChangeCount = change_counter;

  /* Release the page reference. */
  sqlite3PagerUnref(pPgHdr);

  return SQLITE_OK;
}

/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
................................................................................
** Return a pointer to the Pager.nExtra bytes of "extra" space 
** allocated along with the specified page.
*/
void *sqlite3PagerGetExtra(DbPage *pPg){
  Pager *pPager = pPg->pPager;
  return (pPager?PGHDR_TO_EXTRA(pPg, pPager):0);
}


















#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Return the current state of the file lock for the given pager.
** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK,
** PENDING_LOCK, or EXCLUSIVE_LOCK.
*/







|







 







<

>
>
>







 







>
|
|
|
|
|
|
>
>













<







 







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<












|










>
>
>
>
>
|
|
|
>






>











>
|
|
|
>
>
>
>
>
>
>
>







 







>
>
>
>
>







 







>







 







>







 







>







 







<
<







 







>
>







 







>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
281
282
283
284
285
286
287

288
289
290
291
292
293
294
295
296
297
298
...
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
...
902
903
904
905
906
907
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
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
....
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
....
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
....
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
....
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
....
2736
2737
2738
2739
2740
2741
2742


2743
2744
2745
2746
2747
2748
2749
....
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
....
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
....
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.294 2007/03/24 16:45:05 danielk1977 Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include "os.h"
#include "pager.h"
#include <assert.h>
#include <string.h>
................................................................................
  void *pCodecArg;            /* First argument to xCodec() */
  int nHash;                  /* Size of the pager hash table */
  PgHdr **aHash;              /* Hash table to map page number to PgHdr */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  Pager *pNext;               /* Linked list of pagers in this thread */
#endif
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */

  u32 iChangeCount;           /* Db change-counter for which cache is valid */
  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
  u8 changeCountDone;         /* Set after incrementing the change-counter */
};

/*
** If SQLITE_TEST is defined then increment the variable given in
** the argument
*/
#ifdef SQLITE_TEST
................................................................................
  return p;
}

/*
** Unlock the database file.
*/
static void pager_unlock(Pager *pPager){
  if( !pPager->exclusiveMode ){
    if( !MEMDB ){
      sqlite3OsUnlock(pPager->fd, NO_LOCK);
      pPager->dbSize = -1;
      IOTRACE(("UNLOCK %p\n", pPager))
    }
    pPager->state = PAGER_UNLOCK;
    pPager->changeCountDone = 0;
  }
}

/*
** Execute a rollback if a transaction is active and unlock the 
** database file. This is a no-op if the pager has already entered
** the error-state.
*/
static void pagerUnlockAndRollback(Pager *pPager){
  if( pPager->errCode ) return;
  if( pPager->state>=PAGER_RESERVED ){
    sqlite3PagerRollback(pPager);
  }
  pager_unlock(pPager);

}


/*
** Unlock the database and clear the in-memory cache.  This routine
** sets the state of the pager back to what it was when it was first
** opened.  Any outstanding pages are invalidated and subsequent attempts
................................................................................
  pPager->nHash = 0;
  sqliteFree(pPager->aHash);
  pPager->nPage = 0;
  pPager->aHash = 0;
  pPager->nRef = 0;
}























/*
** When this routine is called, the pager has the journal file open and
** a RESERVED or EXCLUSIVE lock on the database.  This routine releases
** the database lock and acquires a SHARED lock in its place.  The journal
** file is deleted and closed.
**
** TODO: Consider keeping the journal file open for temporary databases.
** This might give a performance improvement on windows where opening
** a file is an expensive operation.
*/
static int pager_unwritelock(Pager *pPager){
  PgHdr *pPg;
  int rc = SQLITE_OK;
  assert( !MEMDB );
  if( pPager->state<PAGER_RESERVED ){
    return SQLITE_OK;
  }
  sqlite3PagerStmtCommit(pPager);
  if( pPager->stmtOpen ){
    sqlite3OsClose(&pPager->stfd);
    pPager->stmtOpen = 0;
  }
  if( pPager->journalOpen ){
    if( pPager->exclusiveMode ){
      sqlite3OsTruncate(pPager->jfd, 0);
      sqlite3OsSeek(pPager->jfd, 0);
      pPager->journalOff = 0;
    }else{
      sqlite3OsClose(&pPager->jfd);
      pPager->journalOpen = 0;
      sqlite3OsDelete(pPager->zJournal);
    }
    sqliteFree( pPager->aInJournal );
    pPager->aInJournal = 0;
    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
      pPg->inJournal = 0;
      pPg->dirty = 0;
      pPg->needSync = 0;
      pPg->alwaysRollback = 0;
#ifdef SQLITE_CHECK_PAGES
      pPg->pageHash = pager_pagehash(pPg);
#endif
    }
    pPager->pDirty = 0;
    pPager->dirtyCache = 0;
    pPager->nRec = 0;
  }else{
    assert( pPager->aInJournal==0 );
    assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
  }
  if( !pPager->exclusiveMode ){
    rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
    pPager->state = PAGER_SHARED;
    pPager->origDbSize = 0;
  }else{
    sqlite3PagerPagecount(pPager);
    pPager->origDbSize = pPager->dbSize;
    pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
    if( !pPager->aInJournal ){
      rc = SQLITE_NOMEM;
    }
  }
  pPager->setMaster = 0;
  pPager->needSync = 0;
  pPager->pFirstSynced = pPager->pFirst;
  pPager->dbSize = -1;
  return rc;
}

................................................................................
    if( pPager->xDestructor ){  /*** FIX ME:  Should this be xReinit? ***/
      pPager->xDestructor(pPg, pPager->pageSize);
    }
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
    CODEC1(pPager, pData, pPg->pgno, 3);

    /* If this was page 1, then restore the value of Pager.iChangeCount */
    if( pgno==1 ){
      pPager->iChangeCount = retrieve32bits(pPg, 24);
    }
  }
  return rc;
}

/*
** Parameter zMaster is the name of a master journal file. A single journal
** file that referred to the master journal file has just been rolled back.
................................................................................
  }  
  if( master_open ){
    sqlite3OsClose(&master);
  }
  return rc;
}

#if 0
/*
** Make every page in the cache agree with what is on disk.  In other words,
** reread the disk to reset the state of the cache.
**
** This routine is called after a rollback in which some of the dirty cache
** pages had never been written out to disk.  We need to roll back the
** cache content and the easiest way to do that is to reread the old content
................................................................................
#ifdef SQLITE_CHECK_PAGES
    pPg->pageHash = pager_pagehash(pPg);
#endif
  }
  pPager->pDirty = 0;
  return rc;
}
#endif

/*
** Truncate the main file of the given pager to the number of pages
** indicated.
*/
static int pager_truncate(Pager *pPager, int nPage){
  assert( pPager->state>=PAGER_EXCLUSIVE );
................................................................................
  ThreadData *pTsd = sqlite3ThreadData();
  assert( pPager );
  assert( pTsd && pTsd->nAlloc );
#endif

  disable_simulated_io_errors();
  pPager->errCode = 0;
  pPager->exclusiveMode = 0;
  pager_reset(pPager);
  pagerUnlockAndRollback(pPager);
  enable_simulated_io_errors();
  TRACE2("CLOSE %d\n", PAGERID(pPager));
  IOTRACE(("CLOSE %p\n", pPager))
  assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
  if( pPager->journalOpen ){
................................................................................
          */
          u32 iChangeCount = retrieve32bits(pPage1, 24);
          pPager->nRef++;
          sqlite3PagerUnref(pPage1);
          pPager->nRef--;
          if( iChangeCount!=pPager->iChangeCount ){
            pager_reset(pPager);


          }
          pPager->iChangeCount = iChangeCount;
        }
      }
    }
    pPager->state = PAGER_SHARED;
  }
................................................................................
  TRACE2("ROLLBACK %d\n", PAGERID(pPager));
  if( MEMDB ){
    PgHdr *p;
    for(p=pPager->pAll; p; p=p->pNextAll){
      PgHistory *pHist;
      assert( !p->alwaysRollback );
      if( !p->dirty ){
        assert( p->inJournal==0 );
        assert( p->inStmt==0 );
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
        assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
        continue;
      }

      pHist = PGHDR_TO_HIST(p, pPager);
      if( pHist->pOrig ){
................................................................................
  /* Increment the value just read and write it back to byte 24. */
  change_counter++;
  put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
  pPager->iChangeCount = change_counter;

  /* Release the page reference. */
  sqlite3PagerUnref(pPgHdr);
  pPager->changeCountDone = 1;
  return SQLITE_OK;
}

/*
** Sync the database file for the pager pPager. zMaster points to the name
** of a master journal file that should be written into the individual
** journal file. zMaster may be NULL, which is interpreted as no master
................................................................................
** Return a pointer to the Pager.nExtra bytes of "extra" space 
** allocated along with the specified page.
*/
void *sqlite3PagerGetExtra(DbPage *pPg){
  Pager *pPager = pPg->pPager;
  return (pPager?PGHDR_TO_EXTRA(pPg, pPager):0);
}

/*
** Get/set the locking-mode for this pager. Parameter eMode must be one
** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or 
** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then
** the locking-mode is set to the value specified.
**
** The returned value is either PAGER_LOCKINGMODE_NORMAL or
** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated)
** locking-mode.
*/
int sqlite3PagerLockingMode(Pager *pPager, int eMode){
  if( eMode>=0 ){
    pPager->exclusiveMode = eMode;
  }
  return (int)pPager->exclusiveMode;
}

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Return the current state of the file lock for the given pager.
** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK,
** PENDING_LOCK, or EXCLUSIVE_LOCK.
*/

Changes to src/pager.h.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
65
66
67
68
69
70
71






72
73
74
75
76
77
78
...
115
116
117
118
119
120
121

122
123
124
125
126
127
128
129
130
131
132
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.54 2007/03/19 17:44:27 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** The default size of a database page.
................................................................................
** Allowed values for the flags parameter to sqlite3PagerOpen().
**
** NOTE: This values must match the corresponding BTREE_ values in btree.h.
*/
#define PAGER_OMIT_JOURNAL  0x0001    /* Do not use a rollback journal */
#define PAGER_NO_READLOCK   0x0002    /* Omit readlocks on readonly files */








/*
** See source code comments for a detailed description of the following
** routines:
*/
int sqlite3PagerOpen(Pager **ppPager, const char *zFilename,
                     int nExtra, int flags);
................................................................................
void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
int sqlite3PagerMovepage(Pager*,DbPage*,Pgno);
int sqlite3PagerReset(Pager*);
int sqlite3PagerReleaseMemory(int);

void *sqlite3PagerGetData(DbPage *); 
void *sqlite3PagerGetExtra(DbPage *); 


#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
int sqlite3PagerLockstate(Pager*);
#endif

#ifdef SQLITE_TEST
void sqlite3PagerRefdump(Pager*);
int pager3_refinfo_enable;
#endif

#endif /* _PAGER_H_ */







|







 







>
>
>
>
>
>







 







>











9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
..
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
...
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This header file defines the interface that the sqlite page cache
** subsystem.  The page cache subsystem reads and writes a file a page
** at a time and provides a journal for rollback.
**
** @(#) $Id: pager.h,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
*/

#ifndef _PAGER_H_
#define _PAGER_H_

/*
** The default size of a database page.
................................................................................
** Allowed values for the flags parameter to sqlite3PagerOpen().
**
** NOTE: This values must match the corresponding BTREE_ values in btree.h.
*/
#define PAGER_OMIT_JOURNAL  0x0001    /* Do not use a rollback journal */
#define PAGER_NO_READLOCK   0x0002    /* Omit readlocks on readonly files */

/*
** Valid values for the second argument to sqlite3PagerLockingMode().
*/
#define PAGER_LOCKINGMODE_QUERY      -1
#define PAGER_LOCKINGMODE_NORMAL      0
#define PAGER_LOCKINGMODE_EXCLUSIVE   1

/*
** See source code comments for a detailed description of the following
** routines:
*/
int sqlite3PagerOpen(Pager **ppPager, const char *zFilename,
                     int nExtra, int flags);
................................................................................
void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
int sqlite3PagerMovepage(Pager*,DbPage*,Pgno);
int sqlite3PagerReset(Pager*);
int sqlite3PagerReleaseMemory(int);

void *sqlite3PagerGetData(DbPage *); 
void *sqlite3PagerGetExtra(DbPage *); 
int sqlite3PagerLockingMode(Pager *, int);

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
int sqlite3PagerLockstate(Pager*);
#endif

#ifdef SQLITE_TEST
void sqlite3PagerRefdump(Pager*);
int pager3_refinfo_enable;
#endif

#endif /* _PAGER_H_ */

Changes to src/pragma.c.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
57
58
59
60
61
62
63











64
65
66
67
68
69
70
...
311
312
313
314
315
316
317















































318
319
320
321
322
323
324
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.129 2007/03/19 17:44:28 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/* Ignore this whole file if pragmas are disabled
*/
................................................................................

/*
** Interpret the given string as a boolean value.
*/
static int getBoolean(const char *z){
  return getSafetyLevel(z)&1;
}












#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
** Interpret the given string as a temp db location. Return 1 for file
** backed temporary databases, 2 for the Red-Black tree in memory database
** and 0 to use the compile-time default.
*/
................................................................................
    if( !zRight ){
      int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
      returnSingleInt(pParse, "page_size", size);
    }else{
      sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
    }
  }else















































#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

  /*
  **  PRAGMA [database.]auto_vacuum
  **  PRAGMA [database.]auto_vacuum=N
  **
  ** Get or set the (boolean) value of the database 'auto-vacuum' parameter.







|







 







>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
..
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
...
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
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** This file contains code used to implement the PRAGMA command.
**
** $Id: pragma.c,v 1.130 2007/03/24 16:45:05 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/* Ignore this whole file if pragmas are disabled
*/
................................................................................

/*
** Interpret the given string as a boolean value.
*/
static int getBoolean(const char *z){
  return getSafetyLevel(z)&1;
}

/*
** Interpret the given string as a locking mode value.
*/
static int getLockingMode(const char *z){
  if( z ){
    if( 0==sqlite3StrICmp(z, "exclusive") ) return PAGER_LOCKINGMODE_EXCLUSIVE;
    if( 0==sqlite3StrICmp(z, "normal") ) return PAGER_LOCKINGMODE_NORMAL;
  }
  return PAGER_LOCKINGMODE_QUERY;
}

#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
** Interpret the given string as a temp db location. Return 1 for file
** backed temporary databases, 2 for the Red-Black tree in memory database
** and 0 to use the compile-time default.
*/
................................................................................
    if( !zRight ){
      int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
      returnSingleInt(pParse, "page_size", size);
    }else{
      sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
    }
  }else

  /*
  **  PRAGMA [database.]locking_mode
  **  PRAGMA [database.]locking_mode = (normal|exclusive)
  */
  if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
    const char *zRet = "normal";
    int eMode = getLockingMode(zRight);

    if( pId2->n==0 && eMode==PAGER_LOCKINGMODE_QUERY ){
      /* Simple "PRAGMA locking_mode;" statement. This is a query for
      ** the current default locking mode (which may be different to
      ** the locking-mode of the main database).
      */
      eMode = db->dfltLockMode;
    }else{
      Pager *pPager;
      if( pId2->n==0 ){
        /* This indicates that no database name was specified as part
        ** of the PRAGMA command. In this case the locking-mode must be
        ** set on all attached databases, as well as the main db file.
        **
        ** Also, the sqlite3.dfltLockMode variable is set so that
        ** any subsequently attached databases also use the specified
        ** locking mode.
        */
        int ii;
        assert(pDb==&db->aDb[0]);
        for(ii=2; ii<db->nDb; ii++){
          pPager = sqlite3BtreePager(db->aDb[ii].pBt);
          sqlite3PagerLockingMode(pPager, eMode);
        }
        db->dfltLockMode = eMode;
      }
      pPager = sqlite3BtreePager(pDb->pBt);
      eMode = sqlite3PagerLockingMode(pPager, eMode);
    }

    assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
    if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
      zRet = "exclusive";
    }
    sqlite3VdbeSetNumCols(v, 1);
    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P3_STATIC);
    sqlite3VdbeOp3(v, OP_String8, 0, 0, zRet, 0);
    sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
  }else
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */

  /*
  **  PRAGMA [database.]auto_vacuum
  **  PRAGMA [database.]auto_vacuum=N
  **
  ** Get or set the (boolean) value of the database 'auto-vacuum' parameter.

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
506
507
508
509
510
511
512

513
514
515
516
517
518
519
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.542 2007/03/14 15:37:04 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Extra interface definitions for those who need them
*/
................................................................................
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  int busyTimeout;              /* Busy handler timeout, in msec */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
#ifdef SQLITE_SSE
  sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
#endif

};

/*
** A macro to discover the encoding of a database.
*/
#define ENC(db) ((db)->aDb[0].pSchema->enc)








|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
**    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.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.543 2007/03/24 16:45:05 danielk1977 Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_

/*
** Extra interface definitions for those who need them
*/
................................................................................
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  int busyTimeout;              /* Busy handler timeout, in msec */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
#ifdef SQLITE_SSE
  sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
#endif
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
};

/*
** A macro to discover the encoding of a database.
*/
#define ENC(db) ((db)->aDb[0].pSchema->enc)

Added test/exclusive.test.









































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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
89
90
91
92
93
94
95
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
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
# 2007 March 24
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
#
# $Id: exclusive.test,v 1.1 2007/03/24 16:45:05 danielk1977 Exp $

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

ifcapable {!pager_pragmas} {
  finish_test
  return
}

file delete -force test2.db-journal
file delete -force test2.db
file delete -force test3.db-journal
file delete -force test3.db
file delete -force test4.db-journal
file delete -force test4.db

#----------------------------------------------------------------------
# Test cases exclusive-1.X test the PRAGMA logic.
#
do_test exclusive-1.0 {
  execsql {
    pragma locking_mode;
    pragma main.locking_mode;
  } 
} {normal normal}
do_test exclusive-1.1 {
  execsql {
    pragma locking_mode = exclusive;
  } 
} {exclusive}
do_test exclusive-1.2 {
  execsql {
    pragma locking_mode;
    pragma main.locking_mode;
  } 
} {exclusive exclusive}
do_test exclusive-1.3 {
  execsql {
    pragma locking_mode = normal;
  } 
} {normal}
do_test exclusive-1.4 {
  execsql {
    pragma locking_mode;
    pragma main.locking_mode;
  } 
} {normal normal}
do_test exclusive-1.5 {
  execsql {
    pragma locking_mode = invalid;
  } 
} {normal}
do_test exclusive-1.6 {
  execsql {
    pragma locking_mode;
    pragma main.locking_mode;
  } 
} {normal normal}
do_test exclusive-1.7 {
  execsql {
    pragma locking_mode = exclusive;
    ATTACH 'test2.db' as aux;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
  }
} {exclusive exclusive}
do_test exclusive-1.8 {
  execsql {
    pragma main.locking_mode = normal;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
  }
} {normal exclusive}
do_test exclusive-1.9 {
  execsql {
    pragma locking_mode;
  }
} {exclusive}
do_test exclusive-1.10 {
  execsql {
    ATTACH 'test3.db' as aux2;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
    pragma aux2.locking_mode;
  }
} {normal exclusive exclusive}
do_test exclusive-1.11 {
  execsql {
    pragma aux.locking_mode = normal;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
    pragma aux2.locking_mode;
  }
} {normal normal exclusive}
do_test exclusive-1.12 {
  execsql {
    pragma locking_mode = normal;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
    pragma aux2.locking_mode;
  }
} {normal normal normal}
do_test exclusive-1.13 {
  execsql {
    ATTACH 'test4.db' as aux3;
  }
  execsql {
    pragma main.locking_mode;
    pragma aux.locking_mode;
    pragma aux2.locking_mode;
    pragma aux3.locking_mode;
  }
} {normal normal normal normal}

do_test exclusive-1.99 {
  execsql {
    DETACH aux;
    DETACH aux2;
    DETACH aux3;
  }
} {}

#----------------------------------------------------------------------
# Test cases exclusive-2.X verify that connections in exclusive 
# locking_mode do not relinquish locks.
#
do_test exclusive-2.0 {
  execsql {
    CREATE TABLE abc(a, b, c);
    INSERT INTO abc VALUES(1, 2, 3);
    PRAGMA locking_mode = exclusive;
  }
} {exclusive}
do_test exclusive-2.1 {
  sqlite3 db2 test.db
  execsql {
    INSERT INTO abc VALUES(4, 5, 6);
    SELECT * FROM abc;
  } db2
} {1 2 3 4 5 6}
do_test exclusive-2.2 {
  # This causes connection 'db' (in exclusive mode) to establish 
  # a shared-lock on the db. The other connection should now be
  # locked out as a writer.
  execsql {
    SELECT * FROM abc;
  } db
} {1 2 3 4 5 6}
do_test exclusive-2.4 {
  execsql {
    SELECT * FROM abc;
  } db2
} {1 2 3 4 5 6}
do_test exclusive-2.5 {
  catchsql {
    INSERT INTO abc VALUES(7, 8, 9);
  } db2
} {1 {database is locked}}
do_test exclusive-2.6 {
  # Because connection 'db' only has a shared-lock, the other connection
  # will be able to get a RESERVED, but will fail to upgrade to EXCLUSIVE.
  execsql {
    BEGIN;
    INSERT INTO abc VALUES(7, 8, 9);
  } db2
  catchsql {
    COMMIT
  } db2
} {1 {database is locked}}
do_test exclusive-2.7 {
  catchsql {
    COMMIT
  } db2
} {1 {database is locked}}
do_test exclusive-2.8 {
  execsql {
    ROLLBACK;
  } db2
} {}

do_test exclusive-2.9 {
  # Write the database to establish the exclusive lock with connection 'db.
  execsql {
    INSERT INTO abc VALUES(7, 8, 9);
  } db
  catchsql {
    SELECT * FROM abc;
  } db2
} {1 {database is locked}}
do_test exclusive-2.10 {
  # Changing the locking-mode does not release any locks.
  execsql {
    PRAGMA locking_mode = normal;
  } db
  catchsql {
    SELECT * FROM abc;
  } db2
} {1 {database is locked}}
do_test exclusive-2.11 {
  # After changing the locking mode, accessing the db releases locks.
  execsql {
    SELECT * FROM abc;
  } db
  execsql {
    SELECT * FROM abc;
  } db2
} {1 2 3 4 5 6 7 8 9}
db2 close

#----------------------------------------------------------------------
# Tests exclusive-3.X - test that a connection in exclusive mode 
# truncates instead of deletes the journal file when committing 
# a transaction.
#
proc filestate {fname} {
  set exists 0
  set content 0
  if {[file exists $fname]} {
    set exists 1
    set content [expr {[file size $fname] > 0}]
  }
  list $exists $content
}
do_test exclusive-3.0 {
  filestate test.db-journal
} {0 0}
do_test exclusive-3.1 {
  execsql {
    PRAGMA locking_mode = exclusive;
    BEGIN;
    DELETE FROM abc;
  }
  filestate test.db-journal
} {1 1}
do_test exclusive-3.2 {
  execsql {
    COMMIT;
  }
  filestate test.db-journal
} {1 0}
do_test exclusive-3.3 {
  execsql {
    INSERT INTO abc VALUES('A', 'B', 'C');
    SELECT * FROM abc;
  }
} {A B C}
do_test exclusive-3.4 {
  execsql {
    BEGIN;
    UPDATE abc SET a = 1, b = 2, c = 3;
    ROLLBACK;
    SELECT * FROM abc;
  }
} {1 2 3}
do_test exclusive-3.5 {
  filestate test.db-journal
} {1 0}
do_test exclusive-3.6 {
  execsql {
    PRAGMA locking_mode = normal;
    SELECT * FROM abc;
  }
  filestate test.db-journal
} {0 0}

# The following procedure computes a "signature" for table "t3".  If
# T3 changes in any way, the signature should change.  
#
# This is used to test ROLLBACK.  We gather a signature for t3, then
# make lots of changes to t3, then rollback and take another signature.
# The two signatures should be the same.
#
proc signature {} {
  return [db eval {SELECT count(*), md5sum(x) FROM t3}]
}

if 0 {

do_test exclusive-4.0 {
  execsql { PRAGMA default_cache_size=10; }
  db close
  sqlite3 db test.db
  execsql { PRAGMA locking_mode = exclusive; }
  execsql {
    BEGIN;
    CREATE TABLE t3(x TEXT);
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    COMMIT;
    SELECT count(*) FROM t3;
  }
} {1024}
set sig [signature]
do_test exclusive-4.1 {
  execsql {
    BEGIN;
    DELETE FROM t3 WHERE random()%10!=0;
    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
    ROLLBACK;
  }
  signature
} $sig

}

finish_test

Changes to test/trans.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
811
812
813
814
815
816
817

818
819
820
821
822
823
824
#    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: trans.test,v 1.32 2006/06/20 11:01:09 danielk1977 Exp $


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


# Create several tables to work with.
................................................................................
#
do_test trans-9.1 {
  execsql {
    PRAGMA default_cache_size=10;
  }
  db close
  sqlite3 db test.db

  execsql {
    BEGIN;
    CREATE TABLE t3(x TEXT);
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
#    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: trans.test,v 1.33 2007/03/24 16:45:05 danielk1977 Exp $


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


# Create several tables to work with.
................................................................................
#
do_test trans-9.1 {
  execsql {
    PRAGMA default_cache_size=10;
  }
  db close
  sqlite3 db test.db
  # execsql { PRAGMA locking_mode = exclusive; }
  execsql {
    BEGIN;
    CREATE TABLE t3(x TEXT);
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 VALUES(randstr(10,400));
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
    INSERT INTO t3 SELECT randstr(10,400) FROM t3;