/ Check-in [b6c70037]
Login

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 Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/attach.c.

     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     12   ** This file contains code used to implement the ATTACH and DETACH commands.
    13     13   **
    14         -** $Id: attach.c,v 1.54 2007/03/15 15:35:29 danielk1977 Exp $
           14  +** $Id: attach.c,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
    15     15   */
    16     16   #include "sqliteInt.h"
    17     17   
    18     18   /*
    19     19   ** Resolve an expression that was part of an ATTACH or DETACH statement. This
    20     20   ** is slightly different from resolving a normal SQL expression, because simple
    21     21   ** identifiers are treated as strings, not possible column names or aliases.
................................................................................
   129    129       if( !aNew->pSchema ){
   130    130         rc = SQLITE_NOMEM;
   131    131       }else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
   132    132         strcpy(zErr, 
   133    133           "attached databases must use the same text encoding as main database");
   134    134         goto attach_error;
   135    135       }
          136  +    sqlite3PagerLockingMode(sqlite3BtreePager(aNew->pBt), db->dfltLockMode);
   136    137     }
   137    138     aNew->zName = sqliteStrDup(zName);
   138    139     aNew->safety_level = 3;
   139    140   
   140    141   #if SQLITE_HAS_CODEC
   141    142     {
   142    143       extern int sqlite3CodecAttach(sqlite3*, int, void*, int);

Changes to src/pager.c.

    14     14   ** The pager is used to access a database disk file.  It implements
    15     15   ** atomic commit and rollback through the use of a journal file that
    16     16   ** is separate from the database file.  The pager also implements file
    17     17   ** locking to prevent two processes from writing the same database
    18     18   ** file simultaneously, or one process from reading the database while
    19     19   ** another is writing.
    20     20   **
    21         -** @(#) $Id: pager.c,v 1.293 2007/03/23 18:12:07 danielk1977 Exp $
           21  +** @(#) $Id: pager.c,v 1.294 2007/03/24 16:45:05 danielk1977 Exp $
    22     22   */
    23     23   #ifndef SQLITE_OMIT_DISKIO
    24     24   #include "sqliteInt.h"
    25     25   #include "os.h"
    26     26   #include "pager.h"
    27     27   #include <assert.h>
    28     28   #include <string.h>
................................................................................
   281    281     void *pCodecArg;            /* First argument to xCodec() */
   282    282     int nHash;                  /* Size of the pager hash table */
   283    283     PgHdr **aHash;              /* Hash table to map page number to PgHdr */
   284    284   #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   285    285     Pager *pNext;               /* Linked list of pagers in this thread */
   286    286   #endif
   287    287     char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
   288         -  int doNotSync;              /* While true, do not spill the cache */
   289    288     u32 iChangeCount;           /* Db change-counter for which cache is valid */
          289  +  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
          290  +  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
          291  +  u8 changeCountDone;         /* Set after incrementing the change-counter */
   290    292   };
   291    293   
   292    294   /*
   293    295   ** If SQLITE_TEST is defined then increment the variable given in
   294    296   ** the argument
   295    297   */
   296    298   #ifdef SQLITE_TEST
................................................................................
   850    852     return p;
   851    853   }
   852    854   
   853    855   /*
   854    856   ** Unlock the database file.
   855    857   */
   856    858   static void pager_unlock(Pager *pPager){
   857         -  if( !MEMDB ){
   858         -    sqlite3OsUnlock(pPager->fd, NO_LOCK);
   859         -    pPager->dbSize = -1;
   860         -    IOTRACE(("UNLOCK %p\n", pPager))
          859  +  if( !pPager->exclusiveMode ){
          860  +    if( !MEMDB ){
          861  +      sqlite3OsUnlock(pPager->fd, NO_LOCK);
          862  +      pPager->dbSize = -1;
          863  +      IOTRACE(("UNLOCK %p\n", pPager))
          864  +    }
          865  +    pPager->state = PAGER_UNLOCK;
          866  +    pPager->changeCountDone = 0;
   861    867     }
   862         -  pPager->state = PAGER_UNLOCK;
   863    868   }
   864    869   
   865    870   /*
   866    871   ** Execute a rollback if a transaction is active and unlock the 
   867    872   ** database file. This is a no-op if the pager has already entered
   868    873   ** the error-state.
   869    874   */
   870    875   static void pagerUnlockAndRollback(Pager *pPager){
   871    876     if( pPager->errCode ) return;
   872    877     if( pPager->state>=PAGER_RESERVED ){
   873    878       sqlite3PagerRollback(pPager);
   874    879     }
   875    880     pager_unlock(pPager);
   876         -  assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
   877    881   }
   878    882   
   879    883   
   880    884   /*
   881    885   ** Unlock the database and clear the in-memory cache.  This routine
   882    886   ** sets the state of the pager back to what it was when it was first
   883    887   ** opened.  Any outstanding pages are invalidated and subsequent attempts
................................................................................
   898    902     pPager->nHash = 0;
   899    903     sqliteFree(pPager->aHash);
   900    904     pPager->nPage = 0;
   901    905     pPager->aHash = 0;
   902    906     pPager->nRef = 0;
   903    907   }
   904    908   
   905         -/*
   906         -** This function resets the various pager flags to their initial
   907         -** state but does not discard the cached content.
   908         -*/
   909         -static void pagerSoftReset(Pager *pPager){
   910         -  PgHdr *pPg;
   911         -
   912         -  assert(pPager->pStmt==0);
   913         -  assert(pPager->nRef==0);
   914         -  assert(pPager->pFirstSynced==pPager->pFirst);
   915         -  assert(pPager->aInJournal==0);
   916         -
   917         -  for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
   918         -    assert( pPg->nRef==0 );
   919         -    pPg->inJournal = 0;
   920         -    pPg->inStmt = 0;
   921         -    pPg->dirty = 0;
   922         -    pPg->needSync = 0;
   923         -    pPg->alwaysRollback = 0;
   924         -  }
   925         -}
   926         -
   927    909   /*
   928    910   ** When this routine is called, the pager has the journal file open and
   929    911   ** a RESERVED or EXCLUSIVE lock on the database.  This routine releases
   930    912   ** the database lock and acquires a SHARED lock in its place.  The journal
   931    913   ** file is deleted and closed.
   932    914   **
   933    915   ** TODO: Consider keeping the journal file open for temporary databases.
   934    916   ** This might give a performance improvement on windows where opening
   935    917   ** a file is an expensive operation.
   936    918   */
   937    919   static int pager_unwritelock(Pager *pPager){
   938    920     PgHdr *pPg;
   939         -  int rc;
          921  +  int rc = SQLITE_OK;
   940    922     assert( !MEMDB );
   941    923     if( pPager->state<PAGER_RESERVED ){
   942    924       return SQLITE_OK;
   943    925     }
   944    926     sqlite3PagerStmtCommit(pPager);
   945    927     if( pPager->stmtOpen ){
   946    928       sqlite3OsClose(&pPager->stfd);
   947    929       pPager->stmtOpen = 0;
   948    930     }
   949    931     if( pPager->journalOpen ){
   950         -    sqlite3OsClose(&pPager->jfd);
   951         -    pPager->journalOpen = 0;
   952         -    sqlite3OsDelete(pPager->zJournal);
          932  +    if( pPager->exclusiveMode ){
          933  +      sqlite3OsTruncate(pPager->jfd, 0);
          934  +      sqlite3OsSeek(pPager->jfd, 0);
          935  +      pPager->journalOff = 0;
          936  +    }else{
          937  +      sqlite3OsClose(&pPager->jfd);
          938  +      pPager->journalOpen = 0;
          939  +      sqlite3OsDelete(pPager->zJournal);
          940  +    }
   953    941       sqliteFree( pPager->aInJournal );
   954    942       pPager->aInJournal = 0;
   955    943       for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
   956    944         pPg->inJournal = 0;
   957    945         pPg->dirty = 0;
   958    946         pPg->needSync = 0;
          947  +      pPg->alwaysRollback = 0;
   959    948   #ifdef SQLITE_CHECK_PAGES
   960    949         pPg->pageHash = pager_pagehash(pPg);
   961    950   #endif
   962    951       }
   963    952       pPager->pDirty = 0;
   964    953       pPager->dirtyCache = 0;
   965    954       pPager->nRec = 0;
   966    955     }else{
   967    956       assert( pPager->aInJournal==0 );
   968    957       assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
   969    958     }
   970         -  rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
   971         -  pPager->state = PAGER_SHARED;
   972         -  pPager->origDbSize = 0;
          959  +  if( !pPager->exclusiveMode ){
          960  +    rc = sqlite3OsUnlock(pPager->fd, SHARED_LOCK);
          961  +    pPager->state = PAGER_SHARED;
          962  +    pPager->origDbSize = 0;
          963  +  }else{
          964  +    sqlite3PagerPagecount(pPager);
          965  +    pPager->origDbSize = pPager->dbSize;
          966  +    pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
          967  +    if( !pPager->aInJournal ){
          968  +      rc = SQLITE_NOMEM;
          969  +    }
          970  +  }
   973    971     pPager->setMaster = 0;
   974    972     pPager->needSync = 0;
   975    973     pPager->pFirstSynced = pPager->pFirst;
   976    974     pPager->dbSize = -1;
   977    975     return rc;
   978    976   }
   979    977   
................................................................................
  1105   1103       if( pPager->xDestructor ){  /*** FIX ME:  Should this be xReinit? ***/
  1106   1104         pPager->xDestructor(pPg, pPager->pageSize);
  1107   1105       }
  1108   1106   #ifdef SQLITE_CHECK_PAGES
  1109   1107       pPg->pageHash = pager_pagehash(pPg);
  1110   1108   #endif
  1111   1109       CODEC1(pPager, pData, pPg->pgno, 3);
         1110  +
         1111  +    /* If this was page 1, then restore the value of Pager.iChangeCount */
         1112  +    if( pgno==1 ){
         1113  +      pPager->iChangeCount = retrieve32bits(pPg, 24);
         1114  +    }
  1112   1115     }
  1113   1116     return rc;
  1114   1117   }
  1115   1118   
  1116   1119   /*
  1117   1120   ** Parameter zMaster is the name of a master journal file. A single journal
  1118   1121   ** file that referred to the master journal file has just been rolled back.
................................................................................
  1195   1198     }  
  1196   1199     if( master_open ){
  1197   1200       sqlite3OsClose(&master);
  1198   1201     }
  1199   1202     return rc;
  1200   1203   }
  1201   1204   
         1205  +#if 0
  1202   1206   /*
  1203   1207   ** Make every page in the cache agree with what is on disk.  In other words,
  1204   1208   ** reread the disk to reset the state of the cache.
  1205   1209   **
  1206   1210   ** This routine is called after a rollback in which some of the dirty cache
  1207   1211   ** pages had never been written out to disk.  We need to roll back the
  1208   1212   ** cache content and the easiest way to do that is to reread the old content
................................................................................
  1238   1242   #ifdef SQLITE_CHECK_PAGES
  1239   1243       pPg->pageHash = pager_pagehash(pPg);
  1240   1244   #endif
  1241   1245     }
  1242   1246     pPager->pDirty = 0;
  1243   1247     return rc;
  1244   1248   }
         1249  +#endif
  1245   1250   
  1246   1251   /*
  1247   1252   ** Truncate the main file of the given pager to the number of pages
  1248   1253   ** indicated.
  1249   1254   */
  1250   1255   static int pager_truncate(Pager *pPager, int nPage){
  1251   1256     assert( pPager->state>=PAGER_EXCLUSIVE );
................................................................................
  2122   2127     ThreadData *pTsd = sqlite3ThreadData();
  2123   2128     assert( pPager );
  2124   2129     assert( pTsd && pTsd->nAlloc );
  2125   2130   #endif
  2126   2131   
  2127   2132     disable_simulated_io_errors();
  2128   2133     pPager->errCode = 0;
         2134  +  pPager->exclusiveMode = 0;
  2129   2135     pager_reset(pPager);
  2130   2136     pagerUnlockAndRollback(pPager);
  2131   2137     enable_simulated_io_errors();
  2132   2138     TRACE2("CLOSE %d\n", PAGERID(pPager));
  2133   2139     IOTRACE(("CLOSE %p\n", pPager))
  2134   2140     assert( pPager->errCode || (pPager->journalOpen==0 && pPager->stmtOpen==0) );
  2135   2141     if( pPager->journalOpen ){
................................................................................
  2730   2736             */
  2731   2737             u32 iChangeCount = retrieve32bits(pPage1, 24);
  2732   2738             pPager->nRef++;
  2733   2739             sqlite3PagerUnref(pPage1);
  2734   2740             pPager->nRef--;
  2735   2741             if( iChangeCount!=pPager->iChangeCount ){
  2736   2742               pager_reset(pPager);
  2737         -          }else{
  2738         -            pagerSoftReset(pPager);
  2739   2743             }
  2740   2744             pPager->iChangeCount = iChangeCount;
  2741   2745           }
  2742   2746         }
  2743   2747       }
  2744   2748       pPager->state = PAGER_SHARED;
  2745   2749     }
................................................................................
  3606   3610     TRACE2("ROLLBACK %d\n", PAGERID(pPager));
  3607   3611     if( MEMDB ){
  3608   3612       PgHdr *p;
  3609   3613       for(p=pPager->pAll; p; p=p->pNextAll){
  3610   3614         PgHistory *pHist;
  3611   3615         assert( !p->alwaysRollback );
  3612   3616         if( !p->dirty ){
         3617  +        assert( p->inJournal==0 );
         3618  +        assert( p->inStmt==0 );
  3613   3619           assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pOrig );
  3614   3620           assert( !((PgHistory *)PGHDR_TO_HIST(p, pPager))->pStmt );
  3615   3621           continue;
  3616   3622         }
  3617   3623   
  3618   3624         pHist = PGHDR_TO_HIST(p, pPager);
  3619   3625         if( pHist->pOrig ){
................................................................................
  3886   3892     /* Increment the value just read and write it back to byte 24. */
  3887   3893     change_counter++;
  3888   3894     put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
  3889   3895     pPager->iChangeCount = change_counter;
  3890   3896   
  3891   3897     /* Release the page reference. */
  3892   3898     sqlite3PagerUnref(pPgHdr);
         3899  +  pPager->changeCountDone = 1;
  3893   3900     return SQLITE_OK;
  3894   3901   }
  3895   3902   
  3896   3903   /*
  3897   3904   ** Sync the database file for the pager pPager. zMaster points to the name
  3898   3905   ** of a master journal file that should be written into the individual
  3899   3906   ** journal file. zMaster may be NULL, which is interpreted as no master
................................................................................
  4093   4100   ** Return a pointer to the Pager.nExtra bytes of "extra" space 
  4094   4101   ** allocated along with the specified page.
  4095   4102   */
  4096   4103   void *sqlite3PagerGetExtra(DbPage *pPg){
  4097   4104     Pager *pPager = pPg->pPager;
  4098   4105     return (pPager?PGHDR_TO_EXTRA(pPg, pPager):0);
  4099   4106   }
         4107  +
         4108  +/*
         4109  +** Get/set the locking-mode for this pager. Parameter eMode must be one
         4110  +** of PAGER_LOCKINGMODE_QUERY, PAGER_LOCKINGMODE_NORMAL or 
         4111  +** PAGER_LOCKINGMODE_EXCLUSIVE. If the parameter is not _QUERY, then
         4112  +** the locking-mode is set to the value specified.
         4113  +**
         4114  +** The returned value is either PAGER_LOCKINGMODE_NORMAL or
         4115  +** PAGER_LOCKINGMODE_EXCLUSIVE, indicating the current (possibly updated)
         4116  +** locking-mode.
         4117  +*/
         4118  +int sqlite3PagerLockingMode(Pager *pPager, int eMode){
         4119  +  if( eMode>=0 ){
         4120  +    pPager->exclusiveMode = eMode;
         4121  +  }
         4122  +  return (int)pPager->exclusiveMode;
         4123  +}
  4100   4124   
  4101   4125   #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  4102   4126   /*
  4103   4127   ** Return the current state of the file lock for the given pager.
  4104   4128   ** The return value is one of NO_LOCK, SHARED_LOCK, RESERVED_LOCK,
  4105   4129   ** PENDING_LOCK, or EXCLUSIVE_LOCK.
  4106   4130   */

Changes to src/pager.h.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   ** This header file defines the interface that the sqlite page cache
    13     13   ** subsystem.  The page cache subsystem reads and writes a file a page
    14     14   ** at a time and provides a journal for rollback.
    15     15   **
    16         -** @(#) $Id: pager.h,v 1.54 2007/03/19 17:44:27 danielk1977 Exp $
           16  +** @(#) $Id: pager.h,v 1.55 2007/03/24 16:45:05 danielk1977 Exp $
    17     17   */
    18     18   
    19     19   #ifndef _PAGER_H_
    20     20   #define _PAGER_H_
    21     21   
    22     22   /*
    23     23   ** The default size of a database page.
................................................................................
    65     65   ** Allowed values for the flags parameter to sqlite3PagerOpen().
    66     66   **
    67     67   ** NOTE: This values must match the corresponding BTREE_ values in btree.h.
    68     68   */
    69     69   #define PAGER_OMIT_JOURNAL  0x0001    /* Do not use a rollback journal */
    70     70   #define PAGER_NO_READLOCK   0x0002    /* Omit readlocks on readonly files */
    71     71   
           72  +/*
           73  +** Valid values for the second argument to sqlite3PagerLockingMode().
           74  +*/
           75  +#define PAGER_LOCKINGMODE_QUERY      -1
           76  +#define PAGER_LOCKINGMODE_NORMAL      0
           77  +#define PAGER_LOCKINGMODE_EXCLUSIVE   1
    72     78   
    73     79   /*
    74     80   ** See source code comments for a detailed description of the following
    75     81   ** routines:
    76     82   */
    77     83   int sqlite3PagerOpen(Pager **ppPager, const char *zFilename,
    78     84                        int nExtra, int flags);
................................................................................
   115    121   void sqlite3PagerSetCodec(Pager*,void*(*)(void*,void*,Pgno,int),void*);
   116    122   int sqlite3PagerMovepage(Pager*,DbPage*,Pgno);
   117    123   int sqlite3PagerReset(Pager*);
   118    124   int sqlite3PagerReleaseMemory(int);
   119    125   
   120    126   void *sqlite3PagerGetData(DbPage *); 
   121    127   void *sqlite3PagerGetExtra(DbPage *); 
          128  +int sqlite3PagerLockingMode(Pager *, int);
   122    129   
   123    130   #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
   124    131   int sqlite3PagerLockstate(Pager*);
   125    132   #endif
   126    133   
   127    134   #ifdef SQLITE_TEST
   128    135   void sqlite3PagerRefdump(Pager*);
   129    136   int pager3_refinfo_enable;
   130    137   #endif
   131    138   
   132    139   #endif /* _PAGER_H_ */

Changes to src/pragma.c.

     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     12   ** This file contains code used to implement the PRAGMA command.
    13     13   **
    14         -** $Id: pragma.c,v 1.129 2007/03/19 17:44:28 danielk1977 Exp $
           14  +** $Id: pragma.c,v 1.130 2007/03/24 16:45:05 danielk1977 Exp $
    15     15   */
    16     16   #include "sqliteInt.h"
    17     17   #include "os.h"
    18     18   #include <ctype.h>
    19     19   
    20     20   /* Ignore this whole file if pragmas are disabled
    21     21   */
................................................................................
    57     57   
    58     58   /*
    59     59   ** Interpret the given string as a boolean value.
    60     60   */
    61     61   static int getBoolean(const char *z){
    62     62     return getSafetyLevel(z)&1;
    63     63   }
           64  +
           65  +/*
           66  +** Interpret the given string as a locking mode value.
           67  +*/
           68  +static int getLockingMode(const char *z){
           69  +  if( z ){
           70  +    if( 0==sqlite3StrICmp(z, "exclusive") ) return PAGER_LOCKINGMODE_EXCLUSIVE;
           71  +    if( 0==sqlite3StrICmp(z, "normal") ) return PAGER_LOCKINGMODE_NORMAL;
           72  +  }
           73  +  return PAGER_LOCKINGMODE_QUERY;
           74  +}
    64     75   
    65     76   #ifndef SQLITE_OMIT_PAGER_PRAGMAS
    66     77   /*
    67     78   ** Interpret the given string as a temp db location. Return 1 for file
    68     79   ** backed temporary databases, 2 for the Red-Black tree in memory database
    69     80   ** and 0 to use the compile-time default.
    70     81   */
................................................................................
   311    322       if( !zRight ){
   312    323         int size = pBt ? sqlite3BtreeGetPageSize(pBt) : 0;
   313    324         returnSingleInt(pParse, "page_size", size);
   314    325       }else{
   315    326         sqlite3BtreeSetPageSize(pBt, atoi(zRight), -1);
   316    327       }
   317    328     }else
          329  +
          330  +  /*
          331  +  **  PRAGMA [database.]locking_mode
          332  +  **  PRAGMA [database.]locking_mode = (normal|exclusive)
          333  +  */
          334  +  if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
          335  +    const char *zRet = "normal";
          336  +    int eMode = getLockingMode(zRight);
          337  +
          338  +    if( pId2->n==0 && eMode==PAGER_LOCKINGMODE_QUERY ){
          339  +      /* Simple "PRAGMA locking_mode;" statement. This is a query for
          340  +      ** the current default locking mode (which may be different to
          341  +      ** the locking-mode of the main database).
          342  +      */
          343  +      eMode = db->dfltLockMode;
          344  +    }else{
          345  +      Pager *pPager;
          346  +      if( pId2->n==0 ){
          347  +        /* This indicates that no database name was specified as part
          348  +        ** of the PRAGMA command. In this case the locking-mode must be
          349  +        ** set on all attached databases, as well as the main db file.
          350  +        **
          351  +        ** Also, the sqlite3.dfltLockMode variable is set so that
          352  +        ** any subsequently attached databases also use the specified
          353  +        ** locking mode.
          354  +        */
          355  +        int ii;
          356  +        assert(pDb==&db->aDb[0]);
          357  +        for(ii=2; ii<db->nDb; ii++){
          358  +          pPager = sqlite3BtreePager(db->aDb[ii].pBt);
          359  +          sqlite3PagerLockingMode(pPager, eMode);
          360  +        }
          361  +        db->dfltLockMode = eMode;
          362  +      }
          363  +      pPager = sqlite3BtreePager(pDb->pBt);
          364  +      eMode = sqlite3PagerLockingMode(pPager, eMode);
          365  +    }
          366  +
          367  +    assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
          368  +    if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
          369  +      zRet = "exclusive";
          370  +    }
          371  +    sqlite3VdbeSetNumCols(v, 1);
          372  +    sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", P3_STATIC);
          373  +    sqlite3VdbeOp3(v, OP_String8, 0, 0, zRet, 0);
          374  +    sqlite3VdbeAddOp(v, OP_Callback, 1, 0);
          375  +  }else
   318    376   #endif /* SQLITE_OMIT_PAGER_PRAGMAS */
   319    377   
   320    378     /*
   321    379     **  PRAGMA [database.]auto_vacuum
   322    380     **  PRAGMA [database.]auto_vacuum=N
   323    381     **
   324    382     ** Get or set the (boolean) value of the database 'auto-vacuum' parameter.

Changes to src/sqliteInt.h.

     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     12   ** Internal interface definitions for SQLite.
    13     13   **
    14         -** @(#) $Id: sqliteInt.h,v 1.542 2007/03/14 15:37:04 danielk1977 Exp $
           14  +** @(#) $Id: sqliteInt.h,v 1.543 2007/03/24 16:45:05 danielk1977 Exp $
    15     15   */
    16     16   #ifndef _SQLITEINT_H_
    17     17   #define _SQLITEINT_H_
    18     18   
    19     19   /*
    20     20   ** Extra interface definitions for those who need them
    21     21   */
................................................................................
   506    506     Hash aCollSeq;                /* All collating sequences */
   507    507     BusyHandler busyHandler;      /* Busy callback */
   508    508     int busyTimeout;              /* Busy handler timeout, in msec */
   509    509     Db aDbStatic[2];              /* Static space for the 2 default backends */
   510    510   #ifdef SQLITE_SSE
   511    511     sqlite3_stmt *pFetch;         /* Used by SSE to fetch stored statements */
   512    512   #endif
          513  +  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
   513    514   };
   514    515   
   515    516   /*
   516    517   ** A macro to discover the encoding of a database.
   517    518   */
   518    519   #define ENC(db) ((db)->aDb[0].pSchema->enc)
   519    520   

Added test/exclusive.test.

            1  +# 2007 March 24
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +# This file implements regression tests for SQLite library.
           12  +#
           13  +# $Id: exclusive.test,v 1.1 2007/03/24 16:45:05 danielk1977 Exp $
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +
           18  +ifcapable {!pager_pragmas} {
           19  +  finish_test
           20  +  return
           21  +}
           22  +
           23  +file delete -force test2.db-journal
           24  +file delete -force test2.db
           25  +file delete -force test3.db-journal
           26  +file delete -force test3.db
           27  +file delete -force test4.db-journal
           28  +file delete -force test4.db
           29  +
           30  +#----------------------------------------------------------------------
           31  +# Test cases exclusive-1.X test the PRAGMA logic.
           32  +#
           33  +do_test exclusive-1.0 {
           34  +  execsql {
           35  +    pragma locking_mode;
           36  +    pragma main.locking_mode;
           37  +  } 
           38  +} {normal normal}
           39  +do_test exclusive-1.1 {
           40  +  execsql {
           41  +    pragma locking_mode = exclusive;
           42  +  } 
           43  +} {exclusive}
           44  +do_test exclusive-1.2 {
           45  +  execsql {
           46  +    pragma locking_mode;
           47  +    pragma main.locking_mode;
           48  +  } 
           49  +} {exclusive exclusive}
           50  +do_test exclusive-1.3 {
           51  +  execsql {
           52  +    pragma locking_mode = normal;
           53  +  } 
           54  +} {normal}
           55  +do_test exclusive-1.4 {
           56  +  execsql {
           57  +    pragma locking_mode;
           58  +    pragma main.locking_mode;
           59  +  } 
           60  +} {normal normal}
           61  +do_test exclusive-1.5 {
           62  +  execsql {
           63  +    pragma locking_mode = invalid;
           64  +  } 
           65  +} {normal}
           66  +do_test exclusive-1.6 {
           67  +  execsql {
           68  +    pragma locking_mode;
           69  +    pragma main.locking_mode;
           70  +  } 
           71  +} {normal normal}
           72  +do_test exclusive-1.7 {
           73  +  execsql {
           74  +    pragma locking_mode = exclusive;
           75  +    ATTACH 'test2.db' as aux;
           76  +  }
           77  +  execsql {
           78  +    pragma main.locking_mode;
           79  +    pragma aux.locking_mode;
           80  +  }
           81  +} {exclusive exclusive}
           82  +do_test exclusive-1.8 {
           83  +  execsql {
           84  +    pragma main.locking_mode = normal;
           85  +  }
           86  +  execsql {
           87  +    pragma main.locking_mode;
           88  +    pragma aux.locking_mode;
           89  +  }
           90  +} {normal exclusive}
           91  +do_test exclusive-1.9 {
           92  +  execsql {
           93  +    pragma locking_mode;
           94  +  }
           95  +} {exclusive}
           96  +do_test exclusive-1.10 {
           97  +  execsql {
           98  +    ATTACH 'test3.db' as aux2;
           99  +  }
          100  +  execsql {
          101  +    pragma main.locking_mode;
          102  +    pragma aux.locking_mode;
          103  +    pragma aux2.locking_mode;
          104  +  }
          105  +} {normal exclusive exclusive}
          106  +do_test exclusive-1.11 {
          107  +  execsql {
          108  +    pragma aux.locking_mode = normal;
          109  +  }
          110  +  execsql {
          111  +    pragma main.locking_mode;
          112  +    pragma aux.locking_mode;
          113  +    pragma aux2.locking_mode;
          114  +  }
          115  +} {normal normal exclusive}
          116  +do_test exclusive-1.12 {
          117  +  execsql {
          118  +    pragma locking_mode = normal;
          119  +  }
          120  +  execsql {
          121  +    pragma main.locking_mode;
          122  +    pragma aux.locking_mode;
          123  +    pragma aux2.locking_mode;
          124  +  }
          125  +} {normal normal normal}
          126  +do_test exclusive-1.13 {
          127  +  execsql {
          128  +    ATTACH 'test4.db' as aux3;
          129  +  }
          130  +  execsql {
          131  +    pragma main.locking_mode;
          132  +    pragma aux.locking_mode;
          133  +    pragma aux2.locking_mode;
          134  +    pragma aux3.locking_mode;
          135  +  }
          136  +} {normal normal normal normal}
          137  +
          138  +do_test exclusive-1.99 {
          139  +  execsql {
          140  +    DETACH aux;
          141  +    DETACH aux2;
          142  +    DETACH aux3;
          143  +  }
          144  +} {}
          145  +
          146  +#----------------------------------------------------------------------
          147  +# Test cases exclusive-2.X verify that connections in exclusive 
          148  +# locking_mode do not relinquish locks.
          149  +#
          150  +do_test exclusive-2.0 {
          151  +  execsql {
          152  +    CREATE TABLE abc(a, b, c);
          153  +    INSERT INTO abc VALUES(1, 2, 3);
          154  +    PRAGMA locking_mode = exclusive;
          155  +  }
          156  +} {exclusive}
          157  +do_test exclusive-2.1 {
          158  +  sqlite3 db2 test.db
          159  +  execsql {
          160  +    INSERT INTO abc VALUES(4, 5, 6);
          161  +    SELECT * FROM abc;
          162  +  } db2
          163  +} {1 2 3 4 5 6}
          164  +do_test exclusive-2.2 {
          165  +  # This causes connection 'db' (in exclusive mode) to establish 
          166  +  # a shared-lock on the db. The other connection should now be
          167  +  # locked out as a writer.
          168  +  execsql {
          169  +    SELECT * FROM abc;
          170  +  } db
          171  +} {1 2 3 4 5 6}
          172  +do_test exclusive-2.4 {
          173  +  execsql {
          174  +    SELECT * FROM abc;
          175  +  } db2
          176  +} {1 2 3 4 5 6}
          177  +do_test exclusive-2.5 {
          178  +  catchsql {
          179  +    INSERT INTO abc VALUES(7, 8, 9);
          180  +  } db2
          181  +} {1 {database is locked}}
          182  +do_test exclusive-2.6 {
          183  +  # Because connection 'db' only has a shared-lock, the other connection
          184  +  # will be able to get a RESERVED, but will fail to upgrade to EXCLUSIVE.
          185  +  execsql {
          186  +    BEGIN;
          187  +    INSERT INTO abc VALUES(7, 8, 9);
          188  +  } db2
          189  +  catchsql {
          190  +    COMMIT
          191  +  } db2
          192  +} {1 {database is locked}}
          193  +do_test exclusive-2.7 {
          194  +  catchsql {
          195  +    COMMIT
          196  +  } db2
          197  +} {1 {database is locked}}
          198  +do_test exclusive-2.8 {
          199  +  execsql {
          200  +    ROLLBACK;
          201  +  } db2
          202  +} {}
          203  +
          204  +do_test exclusive-2.9 {
          205  +  # Write the database to establish the exclusive lock with connection 'db.
          206  +  execsql {
          207  +    INSERT INTO abc VALUES(7, 8, 9);
          208  +  } db
          209  +  catchsql {
          210  +    SELECT * FROM abc;
          211  +  } db2
          212  +} {1 {database is locked}}
          213  +do_test exclusive-2.10 {
          214  +  # Changing the locking-mode does not release any locks.
          215  +  execsql {
          216  +    PRAGMA locking_mode = normal;
          217  +  } db
          218  +  catchsql {
          219  +    SELECT * FROM abc;
          220  +  } db2
          221  +} {1 {database is locked}}
          222  +do_test exclusive-2.11 {
          223  +  # After changing the locking mode, accessing the db releases locks.
          224  +  execsql {
          225  +    SELECT * FROM abc;
          226  +  } db
          227  +  execsql {
          228  +    SELECT * FROM abc;
          229  +  } db2
          230  +} {1 2 3 4 5 6 7 8 9}
          231  +db2 close
          232  +
          233  +#----------------------------------------------------------------------
          234  +# Tests exclusive-3.X - test that a connection in exclusive mode 
          235  +# truncates instead of deletes the journal file when committing 
          236  +# a transaction.
          237  +#
          238  +proc filestate {fname} {
          239  +  set exists 0
          240  +  set content 0
          241  +  if {[file exists $fname]} {
          242  +    set exists 1
          243  +    set content [expr {[file size $fname] > 0}]
          244  +  }
          245  +  list $exists $content
          246  +}
          247  +do_test exclusive-3.0 {
          248  +  filestate test.db-journal
          249  +} {0 0}
          250  +do_test exclusive-3.1 {
          251  +  execsql {
          252  +    PRAGMA locking_mode = exclusive;
          253  +    BEGIN;
          254  +    DELETE FROM abc;
          255  +  }
          256  +  filestate test.db-journal
          257  +} {1 1}
          258  +do_test exclusive-3.2 {
          259  +  execsql {
          260  +    COMMIT;
          261  +  }
          262  +  filestate test.db-journal
          263  +} {1 0}
          264  +do_test exclusive-3.3 {
          265  +  execsql {
          266  +    INSERT INTO abc VALUES('A', 'B', 'C');
          267  +    SELECT * FROM abc;
          268  +  }
          269  +} {A B C}
          270  +do_test exclusive-3.4 {
          271  +  execsql {
          272  +    BEGIN;
          273  +    UPDATE abc SET a = 1, b = 2, c = 3;
          274  +    ROLLBACK;
          275  +    SELECT * FROM abc;
          276  +  }
          277  +} {1 2 3}
          278  +do_test exclusive-3.5 {
          279  +  filestate test.db-journal
          280  +} {1 0}
          281  +do_test exclusive-3.6 {
          282  +  execsql {
          283  +    PRAGMA locking_mode = normal;
          284  +    SELECT * FROM abc;
          285  +  }
          286  +  filestate test.db-journal
          287  +} {0 0}
          288  +
          289  +# The following procedure computes a "signature" for table "t3".  If
          290  +# T3 changes in any way, the signature should change.  
          291  +#
          292  +# This is used to test ROLLBACK.  We gather a signature for t3, then
          293  +# make lots of changes to t3, then rollback and take another signature.
          294  +# The two signatures should be the same.
          295  +#
          296  +proc signature {} {
          297  +  return [db eval {SELECT count(*), md5sum(x) FROM t3}]
          298  +}
          299  +
          300  +if 0 {
          301  +
          302  +do_test exclusive-4.0 {
          303  +  execsql { PRAGMA default_cache_size=10; }
          304  +  db close
          305  +  sqlite3 db test.db
          306  +  execsql { PRAGMA locking_mode = exclusive; }
          307  +  execsql {
          308  +    BEGIN;
          309  +    CREATE TABLE t3(x TEXT);
          310  +    INSERT INTO t3 VALUES(randstr(10,400));
          311  +    INSERT INTO t3 VALUES(randstr(10,400));
          312  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          313  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          314  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          315  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          316  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          317  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          318  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          319  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          320  +    INSERT INTO t3 SELECT randstr(10,400) FROM t3;
          321  +    COMMIT;
          322  +    SELECT count(*) FROM t3;
          323  +  }
          324  +} {1024}
          325  +set sig [signature]
          326  +do_test exclusive-4.1 {
          327  +  execsql {
          328  +    BEGIN;
          329  +    DELETE FROM t3 WHERE random()%10!=0;
          330  +    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
          331  +    INSERT INTO t3 SELECT randstr(10,10)||x FROM t3;
          332  +    ROLLBACK;
          333  +  }
          334  +  signature
          335  +} $sig
          336  +
          337  +}
          338  +
          339  +finish_test
          340  +

Changes to test/trans.test.

     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   # This file implements regression tests for SQLite library.  The
    12     12   # focus of this script is database locks.
    13     13   #
    14         -# $Id: trans.test,v 1.32 2006/06/20 11:01:09 danielk1977 Exp $
           14  +# $Id: trans.test,v 1.33 2007/03/24 16:45:05 danielk1977 Exp $
    15     15   
    16     16   
    17     17   set testdir [file dirname $argv0]
    18     18   source $testdir/tester.tcl
    19     19   
    20     20   
    21     21   # Create several tables to work with.
................................................................................
   811    811   #
   812    812   do_test trans-9.1 {
   813    813     execsql {
   814    814       PRAGMA default_cache_size=10;
   815    815     }
   816    816     db close
   817    817     sqlite3 db test.db
          818  +  # execsql { PRAGMA locking_mode = exclusive; }
   818    819     execsql {
   819    820       BEGIN;
   820    821       CREATE TABLE t3(x TEXT);
   821    822       INSERT INTO t3 VALUES(randstr(10,400));
   822    823       INSERT INTO t3 VALUES(randstr(10,400));
   823    824       INSERT INTO t3 SELECT randstr(10,400) FROM t3;
   824    825       INSERT INTO t3 SELECT randstr(10,400) FROM t3;