/ Check-in [1635d927]
Login

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

Overview
Comment:Merge the multiplexer VFS demo into the trunk.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:1635d927cd434052303e0e75f58a08a1e2fefd86
User & Date: drh 2010-11-05 00:01:40
Context
2010-11-05
00:04
Update the configure script for version 3.7.4. check-in: 18a73795 user: drh tags: trunk
00:01
Merge the multiplexer VFS demo into the trunk. check-in: 1635d927 user: drh tags: trunk
00:00
Fix the Makefile.in so that it works with the multiplexer VFS. Closed-Leaf check-in: 54e2efb6 user: drh tags: experimental
2010-11-04
04:47
Include the print_pager_state() function only if SQLITE_DEBUG is defined. check-in: 3104f17e user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

   360    360     $(TOP)/src/test_devsym.c \
   361    361     $(TOP)/src/test_func.c \
   362    362     $(TOP)/src/test_hexio.c \
   363    363     $(TOP)/src/test_init.c \
   364    364     $(TOP)/src/test_intarray.c \
   365    365     $(TOP)/src/test_journal.c \
   366    366     $(TOP)/src/test_malloc.c \
          367  +  $(TOP)/src/test_multiplex.c \
   367    368     $(TOP)/src/test_mutex.c \
   368    369     $(TOP)/src/test_onefile.c \
   369    370     $(TOP)/src/test_osinst.c \
   370    371     $(TOP)/src/test_pcache.c \
   371    372     $(TOP)/src/test_quota.c \
   372    373     $(TOP)/src/test_rtree.c \
   373    374     $(TOP)/src/test_schema.c \

Changes to art/sqlite370.ico.

cannot compute difference between binary files

Changes to main.mk.

   237    237     $(TOP)/src/test_devsym.c \
   238    238     $(TOP)/src/test_func.c \
   239    239     $(TOP)/src/test_hexio.c \
   240    240     $(TOP)/src/test_init.c \
   241    241     $(TOP)/src/test_intarray.c \
   242    242     $(TOP)/src/test_journal.c \
   243    243     $(TOP)/src/test_malloc.c \
          244  +  $(TOP)/src/test_multiplex.c \
   244    245     $(TOP)/src/test_mutex.c \
   245    246     $(TOP)/src/test_onefile.c \
   246    247     $(TOP)/src/test_osinst.c \
   247    248     $(TOP)/src/test_pcache.c \
   248    249     $(TOP)/src/test_quota.c \
   249    250     $(TOP)/src/test_rtree.c \
   250    251     $(TOP)/src/test_schema.c \

Changes to src/tclsqlite.c.

  3575   3575       extern int SqlitetestOsinst_Init(Tcl_Interp*);
  3576   3576       extern int Sqlitetestbackup_Init(Tcl_Interp*);
  3577   3577       extern int Sqlitetestintarray_Init(Tcl_Interp*);
  3578   3578       extern int Sqlitetestvfs_Init(Tcl_Interp *);
  3579   3579       extern int SqlitetestStat_Init(Tcl_Interp*);
  3580   3580       extern int Sqlitetestrtree_Init(Tcl_Interp*);
  3581   3581       extern int Sqlitequota_Init(Tcl_Interp*);
         3582  +    extern int Sqlitemultiplex_Init(Tcl_Interp*);
  3582   3583   
  3583   3584       Sqliteconfig_Init(interp);
  3584   3585       Sqlitetest1_Init(interp);
  3585   3586       Sqlitetest2_Init(interp);
  3586   3587       Sqlitetest3_Init(interp);
  3587   3588       Sqlitetest4_Init(interp);
  3588   3589       Sqlitetest5_Init(interp);
................................................................................
  3605   3606       SqlitetestOsinst_Init(interp);
  3606   3607       Sqlitetestbackup_Init(interp);
  3607   3608       Sqlitetestintarray_Init(interp);
  3608   3609       Sqlitetestvfs_Init(interp);
  3609   3610       SqlitetestStat_Init(interp);
  3610   3611       Sqlitetestrtree_Init(interp);
  3611   3612       Sqlitequota_Init(interp);
         3613  +    Sqlitemultiplex_Init(interp);
  3612   3614   
  3613   3615       Tcl_CreateObjCommand(interp,"load_testfixture_extensions",init_all_cmd,0,0);
  3614   3616   
  3615   3617   #ifdef SQLITE_SSE
  3616   3618       Sqlitetestsse_Init(interp);
  3617   3619   #endif
  3618   3620     }

Added src/test_multiplex.c.

            1  +/*
            2  +** 2010 October 28
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +**
           13  +** This file contains a VFS "shim" - a layer that sits in between the
           14  +** pager and the real VFS.
           15  +**
           16  +** This particular shim enforces a multiplex system on DB files.  
           17  +** This shim shards/partitions a single DB file into smaller 
           18  +** "chunks" such that the total DB file size may exceed the maximum
           19  +** file size of the underlying file system.
           20  +**
           21  +*/
           22  +#include "sqlite3.h"
           23  +#include <string.h>
           24  +#include <assert.h>
           25  +#include "sqliteInt.h"
           26  +
           27  +/************************ Shim Definitions ******************************/
           28  +
           29  +#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x80000000
           30  +#define SQLITE_MULTIPLEX_MAX_CHUNKS 32
           31  +
           32  +/************************ Object Definitions ******************************/
           33  +
           34  +/* Forward declaration of all object types */
           35  +typedef struct multiplexGroup multiplexGroup;
           36  +typedef struct multiplexConn multiplexConn;
           37  +
           38  +/*
           39  +** A "multiplex group" is a collection of files that collectively
           40  +** makeup a single SQLite DB file.  This allows the size of the DB
           41  +** to exceed the limits imposed by the file system.
           42  +**
           43  +** There is an instance of the following object for each defined multiplex
           44  +** group.
           45  +*/
           46  +struct multiplexGroup {
           47  +  sqlite3_file *pReal[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* Handles to each chunk */
           48  +  char bOpen[SQLITE_MULTIPLEX_MAX_CHUNKS];          /* 0 if chunk not opened */
           49  +  char *zName;                                      /* Base filename of this group */
           50  +  int nName;                                        /* Length of base filename */
           51  +  int flags;                                        /* Flags used for original opening */
           52  +  multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
           53  +};
           54  +
           55  +/*
           56  +** An instance of the following object represents each open connection
           57  +** to a file that is multiplex'ed.  This object is a 
           58  +** subclass of sqlite3_file.  The sqlite3_file object for the underlying
           59  +** VFS is appended to this structure.
           60  +*/
           61  +struct multiplexConn {
           62  +  sqlite3_file base;              /* Base class - must be first */
           63  +  multiplexGroup *pGroup;         /* The underlying group of files */
           64  +};
           65  +
           66  +/************************* Global Variables **********************************/
           67  +/*
           68  +** All global variables used by this file are containing within the following
           69  +** gMultiplex structure.
           70  +*/
           71  +static struct {
           72  +  /* The pOrigVfs is the real, original underlying VFS implementation.
           73  +  ** Most operations pass-through to the real VFS.  This value is read-only
           74  +  ** during operation.  It is only modified at start-time and thus does not
           75  +  ** require a mutex.
           76  +  */
           77  +  sqlite3_vfs *pOrigVfs;
           78  +
           79  +  /* The sThisVfs is the VFS structure used by this shim.  It is initialized
           80  +  ** at start-time and thus does not require a mutex
           81  +  */
           82  +  sqlite3_vfs sThisVfs;
           83  +
           84  +  /* The sIoMethods defines the methods used by sqlite3_file objects 
           85  +  ** associated with this shim.  It is initialized at start-time and does
           86  +  ** not require a mutex.
           87  +  **
           88  +  ** When the underlying VFS is called to open a file, it might return 
           89  +  ** either a version 1 or a version 2 sqlite3_file object.  This shim
           90  +  ** has to create a wrapper sqlite3_file of the same version.  Hence
           91  +  ** there are two I/O method structures, one for version 1 and the other
           92  +  ** for version 2.
           93  +  */
           94  +  sqlite3_io_methods sIoMethodsV1;
           95  +  sqlite3_io_methods sIoMethodsV2;
           96  +
           97  +  /* True when this shim as been initialized.
           98  +  */
           99  +  int isInitialized;
          100  +
          101  +  /* For run-time access any of the other global data structures in this
          102  +  ** shim, the following mutex must be held.
          103  +  */
          104  +  sqlite3_mutex *pMutex;
          105  +
          106  +  /* List of multiplexGroup objects.
          107  +  */
          108  +  multiplexGroup *pGroups;
          109  +
          110  +  /* Chunk params
          111  +  */
          112  +  int nChunkSize;
          113  +  int nMaxChunks;
          114  +
          115  +} gMultiplex;
          116  +
          117  +/************************* Utility Routines *********************************/
          118  +/*
          119  +** Acquire and release the mutex used to serialize access to the
          120  +** list of multiplexGroups.
          121  +*/
          122  +static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
          123  +static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
          124  +
          125  +/* Translate an sqlite3_file* that is really a multiplexGroup* into
          126  +** the sqlite3_file* for the underlying original VFS.
          127  +*/
          128  +static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
          129  +  multiplexGroup *pGroup = pConn->pGroup;
          130  +  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
          131  +  if( iChunk<gMultiplex.nMaxChunks ){
          132  +    sqlite3_file *pSubOpen = pGroup->pReal[iChunk];    /* Real file descriptor */
          133  +    if( !pGroup->bOpen[iChunk] ){
          134  +      pGroup->zName[pGroup->nName] = '\0';
          135  +      if( iChunk ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", iChunk);
          136  +      *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->zName, pSubOpen, pGroup->flags, pOutFlags);
          137  +      if( *rc==SQLITE_OK ){
          138  +        pGroup->bOpen[iChunk] = -1;
          139  +        return pSubOpen;
          140  +      }
          141  +      return NULL;
          142  +    }
          143  +    *rc = SQLITE_OK;
          144  +    return pSubOpen;
          145  +  }
          146  +  *rc = SQLITE_ERROR;
          147  +  return NULL;
          148  +}
          149  +
          150  +/************************* VFS Method Wrappers *****************************/
          151  +/*
          152  +** This is the xOpen method used for the "multiplex" VFS.
          153  +**
          154  +** Most of the work is done by the underlying original VFS.  This method
          155  +** simply links the new file into the appropriate multiplex group if it is a
          156  +** file that needs to be tracked.
          157  +*/
          158  +static int multiplexOpen(
          159  +  sqlite3_vfs *pVfs,         /* The multiplex VFS */
          160  +  const char *zName,         /* Name of file to be opened */
          161  +  sqlite3_file *pConn,       /* Fill in this file descriptor */
          162  +  int flags,                 /* Flags to control the opening */
          163  +  int *pOutFlags             /* Flags showing results of opening */
          164  +){
          165  +  int rc;                                        /* Result code */
          166  +  multiplexConn *pMultiplexOpen;                 /* The new multiplex file descriptor */
          167  +  multiplexGroup *pGroup;                        /* Corresponding multiplexGroup object */
          168  +  sqlite3_file *pSubOpen;                        /* Real file descriptor */
          169  +  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
          170  +  int nName = sqlite3Strlen30(zName);
          171  +  int i;
          172  +
          173  +  UNUSED_PARAMETER(pVfs);
          174  +
          175  +  /* If the file is not a main database file or a WAL, then use the
          176  +  ** normal xOpen method.
          177  +  */
          178  +  if( (flags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL))==0 ){
          179  +    return pOrigVfs->xOpen(pOrigVfs, zName, pConn, flags, pOutFlags);
          180  +  }
          181  +
          182  +  /* We need to create a group structure and manage
          183  +  ** access to this group of files.
          184  +  */
          185  +  multiplexEnter();
          186  +  pMultiplexOpen = (multiplexConn*)pConn;
          187  +  /* -0000\0 */
          188  +  pGroup = sqlite3_malloc( sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6 );
          189  +  if( pGroup==0 ){
          190  +    rc=SQLITE_NOMEM;
          191  +  }else{
          192  +    pMultiplexOpen->pGroup = pGroup;
          193  +    memset(pGroup, 0, sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6);
          194  +    for(i=0; i<gMultiplex.nMaxChunks; i++){
          195  +      pGroup->pReal[i] = (sqlite3_file *)((char *)&pGroup[1] + (pOrigVfs->szOsFile*i));
          196  +    }
          197  +    pGroup->zName = (char *)&pGroup[1] + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks);
          198  +    memcpy(pGroup->zName, zName, nName+1);
          199  +    pGroup->nName = nName;
          200  +    pGroup->flags = flags;
          201  +    pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
          202  +    if( pSubOpen ){
          203  +      if( pSubOpen->pMethods->iVersion==1 ){
          204  +        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
          205  +      }else{
          206  +        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
          207  +      }
          208  +      /* place this group at the head of our list */
          209  +      pGroup->pNext = gMultiplex.pGroups;
          210  +      if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
          211  +      gMultiplex.pGroups = pGroup;
          212  +    }else{
          213  +      sqlite3_free(pGroup);
          214  +    }
          215  +  }
          216  +  multiplexLeave();
          217  +  return rc;
          218  +}
          219  +
          220  +/************************ I/O Method Wrappers *******************************/
          221  +
          222  +/* xClose requests get passed through to the original VFS.
          223  +** We loop over all open chunk handles and close them.
          224  +** The group structure for this file is unlinked from 
          225  +** our list of groups and freed.
          226  +*/
          227  +static int multiplexClose(sqlite3_file *pConn){
          228  +  multiplexConn *p = (multiplexConn*)pConn;
          229  +  multiplexGroup *pGroup = p->pGroup;
          230  +  int rc = SQLITE_OK;
          231  +  int i;
          232  +  multiplexEnter();
          233  +  for(i=0; i<gMultiplex.nMaxChunks; i++){
          234  +    if( pGroup->bOpen[i] ){
          235  +      sqlite3_file *pSubOpen = pGroup->pReal[i];
          236  +      int rc2 = pSubOpen->pMethods->xClose(pSubOpen);
          237  +      if( rc2!=SQLITE_OK ) rc = rc2;
          238  +      pGroup->bOpen[i] = 0;
          239  +    }
          240  +  }
          241  +  if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
          242  +  if( pGroup->pPrev ){
          243  +    pGroup->pPrev->pNext = pGroup->pNext;
          244  +  }else{
          245  +    gMultiplex.pGroups = pGroup->pNext;
          246  +  }
          247  +  sqlite3_free(pGroup);
          248  +  multiplexLeave();
          249  +  return rc;
          250  +}
          251  +
          252  +/* Pass xRead requests thru to the original VFS after
          253  +** determining the correct chunk to operate on.
          254  +*/
          255  +static int multiplexRead(
          256  +  sqlite3_file *pConn,
          257  +  void *pBuf,
          258  +  int iAmt,
          259  +  sqlite3_int64 iOfst
          260  +){
          261  +  multiplexConn *p = (multiplexConn*)pConn;
          262  +  int rc = SQLITE_OK;
          263  +  multiplexEnter();
          264  +  while( iAmt > 0 ){
          265  +    int i = (int)(iOfst/gMultiplex.nChunkSize);
          266  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          267  +    if( pSubOpen ){
          268  +      int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
          269  +      if( extra<0 ) extra = 0;
          270  +      iAmt -= extra;
          271  +      rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
          272  +      if( rc!=SQLITE_OK ) break;
          273  +      pBuf = (char *)pBuf + iAmt;
          274  +      iOfst += iAmt;
          275  +      iAmt = extra;
          276  +    }else{
          277  +      rc = SQLITE_IOERR_READ;
          278  +      break;
          279  +    }
          280  +  }
          281  +  multiplexLeave();
          282  +  return rc;
          283  +}
          284  +
          285  +/* Pass xWrite requests thru to the original VFS after
          286  +** determining the correct chunk to operate on.
          287  +*/
          288  +static int multiplexWrite(
          289  +  sqlite3_file *pConn,
          290  +  const void *pBuf,
          291  +  int iAmt,
          292  +  sqlite3_int64 iOfst
          293  +){
          294  +  multiplexConn *p = (multiplexConn*)pConn;
          295  +  int rc = SQLITE_OK;
          296  +  multiplexEnter();
          297  +  while( iAmt > 0 ){
          298  +    int i = (int)(iOfst/gMultiplex.nChunkSize);
          299  +    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          300  +    if( pSubOpen ){
          301  +      int extra = ((int)(iOfst % gMultiplex.nChunkSize) + iAmt) - gMultiplex.nChunkSize;
          302  +      if( extra<0 ) extra = 0;
          303  +      iAmt -= extra;
          304  +      rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst%gMultiplex.nChunkSize);
          305  +      if( rc!=SQLITE_OK ) break;
          306  +      pBuf = (char *)pBuf + iAmt;
          307  +      iOfst += iAmt;
          308  +      iAmt = extra;
          309  +    }else{
          310  +      rc = SQLITE_IOERR_WRITE;
          311  +      break;
          312  +    }
          313  +  }
          314  +  multiplexLeave();
          315  +  return rc;
          316  +}
          317  +
          318  +/* Pass xTruncate requests thru to the original VFS after
          319  +** determining the correct chunk to operate on.  Delete any
          320  +** chunks above the truncate mark.
          321  +*/
          322  +static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
          323  +  multiplexConn *p = (multiplexConn*)pConn;
          324  +  multiplexGroup *pGroup = p->pGroup;
          325  +  int rc = SQLITE_OK;
          326  +  int rc2;
          327  +  int i;
          328  +  sqlite3_file *pSubOpen;
          329  +  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
          330  +  multiplexEnter();
          331  +  /* delete the chunks above the truncate limit */
          332  +  for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
          333  +    if( pGroup->bOpen[i] ){
          334  +      pSubOpen = pGroup->pReal[i];
          335  +      rc2 = pSubOpen->pMethods->xClose(pSubOpen);
          336  +      if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
          337  +    }
          338  +    pGroup->zName[pGroup->nName] = '\0';
          339  +    if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);
          340  +    rc2 = pOrigVfs->xDelete(pOrigVfs, pGroup->zName, 0);
          341  +    if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
          342  +  }
          343  +  pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
          344  +  if( pSubOpen ){
          345  +    rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
          346  +    if( rc2!=SQLITE_OK ) rc = rc2;
          347  +  }else{
          348  +    rc = SQLITE_IOERR_TRUNCATE;
          349  +  }
          350  +  multiplexLeave();
          351  +  return rc;
          352  +}
          353  +
          354  +/* Pass xSync requests through to the original VFS without change
          355  +*/
          356  +static int multiplexSync(sqlite3_file *pConn, int flags){
          357  +  multiplexConn *p = (multiplexConn*)pConn;
          358  +  multiplexGroup *pGroup = p->pGroup;
          359  +  int rc = SQLITE_OK;
          360  +  int i;
          361  +  multiplexEnter();
          362  +  for(i=0; i<gMultiplex.nMaxChunks; i++){
          363  +    /* if we don't have it open, we don't need to sync it */
          364  +    if( pGroup->bOpen[i] ){
          365  +      sqlite3_file *pSubOpen = pGroup->pReal[i];
          366  +      int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
          367  +      if( rc2!=SQLITE_OK ) rc = rc2;
          368  +    }
          369  +  }
          370  +  multiplexLeave();
          371  +  return rc;
          372  +}
          373  +
          374  +/* Pass xFileSize requests through to the original VFS.
          375  +** Aggregate the size of all the chunks before returning.
          376  +*/
          377  +static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
          378  +  multiplexConn *p = (multiplexConn*)pConn;
          379  +  multiplexGroup *pGroup = p->pGroup;
          380  +  int rc = SQLITE_OK;
          381  +  int rc2;
          382  +  int i;
          383  +  multiplexEnter();
          384  +  *pSize = 0;
          385  +  for(i=0; i<gMultiplex.nMaxChunks; i++){
          386  +    sqlite3_file *pSubOpen = NULL;
          387  +    sqlite3_int64 sz;
          388  +    /* if not opened already, check to see if the chunk exists */
          389  +    if( pGroup->bOpen[i] ){
          390  +      pSubOpen = pGroup->pReal[i];
          391  +    }else{
          392  +      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
          393  +      int exists = 0;
          394  +      pGroup->zName[pGroup->nName] = '\0';
          395  +      if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, "-%04d", i);
          396  +      rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->zName, SQLITE_ACCESS_EXISTS, &exists);
          397  +      if( rc2==SQLITE_OK && exists){
          398  +        /* if it exists, open it */
          399  +        pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
          400  +      }else{
          401  +        /* stop at first "gap" */
          402  +        break;
          403  +      }
          404  +    }
          405  +    if( pSubOpen ){
          406  +      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
          407  +      if( rc2!=SQLITE_OK ){
          408  +        rc = rc2;
          409  +      }else{
          410  +        *pSize += sz;
          411  +      }
          412  +    }else{
          413  +      break;
          414  +    }
          415  +  }
          416  +  multiplexLeave();
          417  +  return rc;
          418  +}
          419  +
          420  +/* Pass xLock requests through to the original VFS unchanged.
          421  +*/
          422  +static int multiplexLock(sqlite3_file *pConn, int lock){
          423  +  multiplexConn *p = (multiplexConn*)pConn;
          424  +  int rc;
          425  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          426  +  if( pSubOpen ){
          427  +    return pSubOpen->pMethods->xLock(pSubOpen, lock);
          428  +  }
          429  +  return SQLITE_BUSY;
          430  +}
          431  +
          432  +/* Pass xUnlock requests through to the original VFS unchanged.
          433  +*/
          434  +static int multiplexUnlock(sqlite3_file *pConn, int lock){
          435  +  multiplexConn *p = (multiplexConn*)pConn;
          436  +  int rc;
          437  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          438  +  if( pSubOpen ){
          439  +    return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
          440  +  }
          441  +  return SQLITE_IOERR_UNLOCK;
          442  +}
          443  +
          444  +/* Pass xCheckReservedLock requests through to the original VFS unchanged.
          445  +*/
          446  +static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
          447  +  multiplexConn *p = (multiplexConn*)pConn;
          448  +  int rc;
          449  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          450  +  if( pSubOpen ){
          451  +    return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
          452  +  }
          453  +  return SQLITE_IOERR_CHECKRESERVEDLOCK;
          454  +}
          455  +
          456  +/* Pass xFileControl requests through to the original VFS unchanged.
          457  +*/
          458  +static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
          459  +  multiplexConn *p = (multiplexConn*)pConn;
          460  +  int rc;
          461  +  sqlite3_file *pSubOpen;
          462  +  if ( op==SQLITE_FCNTL_SIZE_HINT || op==SQLITE_FCNTL_CHUNK_SIZE ) return SQLITE_OK;
          463  +  pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          464  +  if( pSubOpen ){
          465  +    return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
          466  +  }
          467  +  return SQLITE_ERROR;
          468  +}
          469  +
          470  +/* Pass xSectorSize requests through to the original VFS unchanged.
          471  +*/
          472  +static int multiplexSectorSize(sqlite3_file *pConn){
          473  +  multiplexConn *p = (multiplexConn*)pConn;
          474  +  int rc;
          475  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          476  +  if( pSubOpen ){
          477  +    return pSubOpen->pMethods->xSectorSize(pSubOpen);
          478  +  }
          479  +  return SQLITE_DEFAULT_SECTOR_SIZE;
          480  +}
          481  +
          482  +/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
          483  +*/
          484  +static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
          485  +  multiplexConn *p = (multiplexConn*)pConn;
          486  +  int rc;
          487  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          488  +  if( pSubOpen ){
          489  +    return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
          490  +  }
          491  +  return 0;
          492  +}
          493  +
          494  +/* Pass xShmMap requests through to the original VFS unchanged.
          495  +*/
          496  +static int multiplexShmMap(
          497  +  sqlite3_file *pConn,            /* Handle open on database file */
          498  +  int iRegion,                    /* Region to retrieve */
          499  +  int szRegion,                   /* Size of regions */
          500  +  int bExtend,                    /* True to extend file if necessary */
          501  +  void volatile **pp              /* OUT: Mapped memory */
          502  +){
          503  +  multiplexConn *p = (multiplexConn*)pConn;
          504  +  int rc;
          505  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          506  +  if( pSubOpen ){
          507  +    return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend, pp);
          508  +  }
          509  +  return SQLITE_IOERR;
          510  +}
          511  +
          512  +/* Pass xShmLock requests through to the original VFS unchanged.
          513  +*/
          514  +static int multiplexShmLock(
          515  +  sqlite3_file *pConn,       /* Database file holding the shared memory */
          516  +  int ofst,                  /* First lock to acquire or release */
          517  +  int n,                     /* Number of locks to acquire or release */
          518  +  int flags                  /* What to do with the lock */
          519  +){
          520  +  multiplexConn *p = (multiplexConn*)pConn;
          521  +  int rc;
          522  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          523  +  if( pSubOpen ){
          524  +    return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
          525  +  }
          526  +  return SQLITE_BUSY;
          527  +}
          528  +
          529  +/* Pass xShmBarrier requests through to the original VFS unchanged.
          530  +*/
          531  +static void multiplexShmBarrier(sqlite3_file *pConn){
          532  +  multiplexConn *p = (multiplexConn*)pConn;
          533  +  int rc;
          534  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          535  +  if( pSubOpen ){
          536  +    pSubOpen->pMethods->xShmBarrier(pSubOpen);
          537  +  }
          538  +}
          539  +
          540  +/* Pass xShmUnmap requests through to the original VFS unchanged.
          541  +*/
          542  +static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
          543  +  multiplexConn *p = (multiplexConn*)pConn;
          544  +  int rc;
          545  +  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
          546  +  if( pSubOpen ){
          547  +    return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
          548  +  }
          549  +  return SQLITE_OK;
          550  +}
          551  +
          552  +/************************** Public Interfaces *****************************/
          553  +/*
          554  +** Initialize the multiplex VFS shim.  Use the VFS named zOrigVfsName
          555  +** as the VFS that does the actual work.  Use the default if
          556  +** zOrigVfsName==NULL.  
          557  +**
          558  +** The multiplex VFS shim is named "multiplex".  It will become the default
          559  +** VFS if makeDefault is non-zero.
          560  +**
          561  +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once
          562  +** during start-up.
          563  +*/
          564  +int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
          565  +  sqlite3_vfs *pOrigVfs;
          566  +  if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
          567  +  pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
          568  +  if( pOrigVfs==0 ) return SQLITE_ERROR;
          569  +  assert( pOrigVfs!=&gMultiplex.sThisVfs );
          570  +  gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
          571  +  if( !gMultiplex.pMutex ){
          572  +    return SQLITE_NOMEM;
          573  +  }
          574  +  gMultiplex.nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
          575  +  gMultiplex.nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
          576  +  gMultiplex.pGroups = NULL;
          577  +  gMultiplex.isInitialized = 1;
          578  +  gMultiplex.pOrigVfs = pOrigVfs;
          579  +  gMultiplex.sThisVfs = *pOrigVfs;
          580  +  gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
          581  +  gMultiplex.sThisVfs.zName = "multiplex";
          582  +  gMultiplex.sThisVfs.xOpen = multiplexOpen;
          583  +  gMultiplex.sIoMethodsV1.iVersion = 1;
          584  +  gMultiplex.sIoMethodsV1.xClose = multiplexClose;
          585  +  gMultiplex.sIoMethodsV1.xRead = multiplexRead;
          586  +  gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
          587  +  gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
          588  +  gMultiplex.sIoMethodsV1.xSync = multiplexSync;
          589  +  gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
          590  +  gMultiplex.sIoMethodsV1.xLock = multiplexLock;
          591  +  gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
          592  +  gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
          593  +  gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
          594  +  gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
          595  +  gMultiplex.sIoMethodsV1.xDeviceCharacteristics = multiplexDeviceCharacteristics;
          596  +  gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
          597  +  gMultiplex.sIoMethodsV2.iVersion = 2;
          598  +  gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
          599  +  gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
          600  +  gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
          601  +  gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
          602  +  sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
          603  +  return SQLITE_OK;
          604  +}
          605  +
          606  +/*
          607  +** Shutdown the multiplex system.
          608  +**
          609  +** All SQLite database connections must be closed before calling this
          610  +** routine.
          611  +**
          612  +** THIS ROUTINE IS NOT THREADSAFE.  Call this routine exactly once while
          613  +** shutting down in order to free all remaining multiplex groups.
          614  +*/
          615  +int sqlite3_multiplex_shutdown(void){
          616  +  if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
          617  +  if( gMultiplex.pGroups ) return SQLITE_MISUSE;
          618  +  gMultiplex.isInitialized = 0;
          619  +  sqlite3_mutex_free(gMultiplex.pMutex);
          620  +  sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
          621  +  memset(&gMultiplex, 0, sizeof(gMultiplex));
          622  +  return SQLITE_OK;
          623  +}
          624  +
          625  +/*
          626  +** Adjust chunking params.  VFS should be initialized first.
          627  +** No files should be open.  Re-intializing will reset these
          628  +** to the default.
          629  +*/
          630  +int sqlite3_multiplex_set(
          631  +  int nChunkSize,                 /* Max chunk size */
          632  +  int nMaxChunks                  /* Max number of chunks */
          633  +){
          634  +  if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
          635  +  if( gMultiplex.pGroups ) return SQLITE_MISUSE;
          636  +  if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ) return SQLITE_MISUSE;
          637  +  multiplexEnter();
          638  +  gMultiplex.nChunkSize = nChunkSize;
          639  +  gMultiplex.nMaxChunks = nMaxChunks;
          640  +  multiplexLeave();
          641  +  return SQLITE_OK;
          642  +}
          643  +
          644  +/***************************** Test Code ***********************************/
          645  +#ifdef SQLITE_TEST
          646  +#include <tcl.h>
          647  +
          648  +extern const char *sqlite3TestErrorName(int);
          649  +
          650  +
          651  +/*
          652  +** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
          653  +*/
          654  +static int test_multiplex_initialize(
          655  +  void * clientData,
          656  +  Tcl_Interp *interp,
          657  +  int objc,
          658  +  Tcl_Obj *CONST objv[]
          659  +){
          660  +  const char *zName;              /* Name of new multiplex VFS */
          661  +  int makeDefault;                /* True to make the new VFS the default */
          662  +  int rc;                         /* Value returned by multiplex_initialize() */
          663  +
          664  +  UNUSED_PARAMETER(clientData);
          665  +
          666  +  /* Process arguments */
          667  +  if( objc!=3 ){
          668  +    Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
          669  +    return TCL_ERROR;
          670  +  }
          671  +  zName = Tcl_GetString(objv[1]);
          672  +  if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
          673  +  if( zName[0]=='\0' ) zName = 0;
          674  +
          675  +  /* Call sqlite3_multiplex_initialize() */
          676  +  rc = sqlite3_multiplex_initialize(zName, makeDefault);
          677  +  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
          678  +
          679  +  return TCL_OK;
          680  +}
          681  +
          682  +/*
          683  +** tclcmd: sqlite3_multiplex_shutdown
          684  +*/
          685  +static int test_multiplex_shutdown(
          686  +  void * clientData,
          687  +  Tcl_Interp *interp,
          688  +  int objc,
          689  +  Tcl_Obj *CONST objv[]
          690  +){
          691  +  int rc;                         /* Value returned by multiplex_shutdown() */
          692  +
          693  +  UNUSED_PARAMETER(clientData);
          694  +
          695  +  if( objc!=1 ){
          696  +    Tcl_WrongNumArgs(interp, 1, objv, "");
          697  +    return TCL_ERROR;
          698  +  }
          699  +
          700  +  /* Call sqlite3_multiplex_shutdown() */
          701  +  rc = sqlite3_multiplex_shutdown();
          702  +  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
          703  +
          704  +  return TCL_OK;
          705  +}
          706  +
          707  +/*
          708  +** tclcmd: sqlite3_multiplex_set CHUNK_SIZE MAX_CHUNKS
          709  +*/
          710  +static int test_multiplex_set(
          711  +  void * clientData,
          712  +  Tcl_Interp *interp,
          713  +  int objc,
          714  +  Tcl_Obj *CONST objv[]
          715  +){
          716  +  int nChunkSize;                 /* Max chunk size */
          717  +  int nMaxChunks;                 /* Max number of chunks */
          718  +  int rc;                         /* Value returned by sqlite3_multiplex_set() */
          719  +
          720  +  UNUSED_PARAMETER(clientData);
          721  +
          722  +  /* Process arguments */
          723  +  if( objc!=3 ){
          724  +    Tcl_WrongNumArgs(interp, 1, objv, "CHUNK_SIZE MAX_CHUNKS");
          725  +    return TCL_ERROR;
          726  +  }
          727  +  if( Tcl_GetIntFromObj(interp, objv[1], &nChunkSize) ) return TCL_ERROR;
          728  +  if( Tcl_GetIntFromObj(interp, objv[2], &nMaxChunks) ) return TCL_ERROR;
          729  +
          730  +  if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ){
          731  +    Tcl_WrongNumArgs(interp, 1, objv, "MAX_CHUNKS > SQLITE_MULTIPLEX_MAX_CHUNKS");
          732  +    return TCL_ERROR;
          733  +  }
          734  +
          735  +  /* Invoke sqlite3_multiplex_set() */
          736  +  rc = sqlite3_multiplex_set(nChunkSize, nMaxChunks);
          737  +
          738  +  Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
          739  +  return TCL_OK;
          740  +}
          741  +
          742  +/*
          743  +** tclcmd:  sqlite3_multiplex_dump
          744  +*/
          745  +static int test_multiplex_dump(
          746  +  void * clientData,
          747  +  Tcl_Interp *interp,
          748  +  int objc,
          749  +  Tcl_Obj *CONST objv[]
          750  +){
          751  +  Tcl_Obj *pResult;
          752  +  Tcl_Obj *pGroupTerm;
          753  +  multiplexGroup *pGroup;
          754  +  int i;
          755  +  int nChunks = 0;
          756  +
          757  +  UNUSED_PARAMETER(clientData);
          758  +  UNUSED_PARAMETER(objc);
          759  +  UNUSED_PARAMETER(objv);
          760  +
          761  +  pResult = Tcl_NewObj();
          762  +  multiplexEnter();
          763  +  for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
          764  +    pGroupTerm = Tcl_NewObj();
          765  +
          766  +    pGroup->zName[pGroup->nName] = '\0';
          767  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          768  +          Tcl_NewStringObj(pGroup->zName, -1));
          769  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          770  +          Tcl_NewIntObj(pGroup->nName));
          771  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          772  +          Tcl_NewIntObj(pGroup->flags));
          773  +
          774  +    /* count number of chunks with open handles */
          775  +    for(i=0; i<gMultiplex.nMaxChunks; i++){
          776  +      if( pGroup->bOpen[i] ) nChunks++;
          777  +    }
          778  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          779  +          Tcl_NewIntObj(nChunks));
          780  +
          781  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          782  +          Tcl_NewIntObj(gMultiplex.nChunkSize));
          783  +    Tcl_ListObjAppendElement(interp, pGroupTerm,
          784  +          Tcl_NewIntObj(gMultiplex.nMaxChunks));
          785  +
          786  +    Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
          787  +  }
          788  +  multiplexLeave();
          789  +  Tcl_SetObjResult(interp, pResult);
          790  +  return TCL_OK;
          791  +}
          792  +
          793  +/*
          794  +** This routine registers the custom TCL commands defined in this
          795  +** module.  This should be the only procedure visible from outside
          796  +** of this module.
          797  +*/
          798  +int Sqlitemultiplex_Init(Tcl_Interp *interp){
          799  +  static struct {
          800  +     char *zName;
          801  +     Tcl_ObjCmdProc *xProc;
          802  +  } aCmd[] = {
          803  +    { "sqlite3_multiplex_initialize", test_multiplex_initialize },
          804  +    { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
          805  +    { "sqlite3_multiplex_set", test_multiplex_set },
          806  +    { "sqlite3_multiplex_dump", test_multiplex_dump },
          807  +  };
          808  +  int i;
          809  +
          810  +  for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
          811  +    Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
          812  +  }
          813  +
          814  +  return TCL_OK;
          815  +}
          816  +#endif

Changes to test/attachmalloc.test.

    22     22     finish_test
    23     23     return
    24     24   }
    25     25   
    26     26   source $testdir/malloc_common.tcl
    27     27   
    28     28   do_malloc_test attachmalloc-1 -tclprep {
    29         -  db close
           29  +  catch { db close }
    30     30     for {set i 2} {$i<=4} {incr i} {
           31  +    catch { db$i close }
    31     32       file delete -force test$i.db
    32     33       file delete -force test$i.db-journal
    33     34     }
    34     35   } -tclbody {
    35     36     if {[catch {sqlite3 db test.db}]} {
    36     37       error "out of memory"
    37     38     }
................................................................................
    57     58     db2 close
    58     59   } -sqlbody {
    59     60     CREATE TABLE t1(d, e, f);
    60     61     ATTACH 'test2.db' AS db1;
    61     62   }
    62     63   
    63     64   set enable_shared_cache [sqlite3_enable_shared_cache 1]
    64         -sqlite3 dbaux test2.db
           65  +sqlite3 dbaux test3.db
    65     66   dbaux eval {SELECT * FROM sqlite_master}
    66     67   do_malloc_test attachmalloc-3 -sqlbody {
    67     68     SELECT * FROM sqlite_master;
    68         -  ATTACH 'test2.db' AS two;
           69  +  ATTACH 'test3.db' AS three;
    69     70   } -cleanup {
    70         -  db eval { DETACH two }
           71  +  db eval { DETACH three }
    71     72   }
    72     73   dbaux close
    73     74   sqlite3_enable_shared_cache $enable_shared_cache
    74     75   
    75     76   
    76     77   finish_test

Added test/multiplex.test.

            1  +# 2010 October 29
            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  +#
           12  +
           13  +set testdir [file dirname $argv0]
           14  +source $testdir/tester.tcl
           15  +source $testdir/malloc_common.tcl
           16  +
           17  +proc multiplex_delete {name} {
           18  +  file delete -force $name
           19  +  file delete -force $name-journal
           20  +  file delete -force $name-wal
           21  +  for {set i 1} {$i<=15} {incr i} {
           22  +    file delete -force $name-000$i
           23  +    file delete -force $name-00$i
           24  +  }
           25  +}
           26  +
           27  +db close
           28  +
           29  +do_test multiplex-1.1 { sqlite3_multiplex_initialize nosuchvfs 1 } {SQLITE_ERROR}
           30  +do_test multiplex-1.2 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
           31  +do_test multiplex-1.3 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_MISUSE}
           32  +do_test multiplex-1.4 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
           33  +
           34  +do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 }        {SQLITE_OK}
           35  +do_test multiplex-1.6 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
           36  +do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
           37  +do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
           38  +
           39  +
           40  +#-------------------------------------------------------------------------
           41  +# Some simple warm-body tests with a single database file in rollback 
           42  +# mode:
           43  +#
           44  +#   multiplex-2.1.*: Test simple writing to a multiplex file.
           45  +#
           46  +#   multiplex-2.2.*: More writing.
           47  +#
           48  +#   multiplex-2.3.*: Open and close a second db.
           49  +#
           50  +#   multiplex-2.4.*: Try to shutdown the multiplex system before closing the db
           51  +#                file. Check that this fails and the multiplex system still works
           52  +#                afterwards. Then close the database and successfully shut
           53  +#                down the multiplex system.
           54  +#
           55  +
           56  +sqlite3_multiplex_initialize "" 1
           57  +sqlite3_multiplex_set 0x8000 16
           58  +
           59  +do_test multiplex-2.1.2 {
           60  +  sqlite3 db test.db
           61  +  execsql {
           62  +    PRAGMA page_size=1024;
           63  +    PRAGMA auto_vacuum=OFF;
           64  +    PRAGMA journal_mode=DELETE;
           65  +  }
           66  +  execsql {
           67  +    CREATE TABLE t1(a, b);
           68  +    INSERT INTO t1 VALUES(1, randomblob(1100));
           69  +    INSERT INTO t1 VALUES(2, randomblob(1100));
           70  +  }
           71  +} {}
           72  +do_test multiplex-2.1.3 { file size test.db } {4096}
           73  +do_test multiplex-2.1.4 {
           74  +  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
           75  +} {}
           76  +
           77  +do_test multiplex-2.2.1 {
           78  +  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
           79  +} {}
           80  +do_test multiplex-2.2.3 { file size test.db } {6144}
           81  +
           82  +do_test multiplex-2.3.1 {
           83  +  sqlite3 db2 bak.db
           84  +  db2 close
           85  +} {}
           86  +
           87  +do_test multiplex-2.4.1 {
           88  +  sqlite3_multiplex_shutdown
           89  +} {SQLITE_MISUSE}
           90  +do_test multiplex-2.4.2 {
           91  +  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
           92  +} {}
           93  +do_test multiplex-2.4.4 { file size test.db } {7168}
           94  +do_test multiplex-2.4.99 {
           95  +  db close
           96  +  sqlite3_multiplex_shutdown
           97  +} {SQLITE_OK}
           98  +
           99  +#-------------------------------------------------------------------------
          100  +# Try some tests with more than one connection to a database file. Still
          101  +# in rollback mode.
          102  +#
          103  +#   multiplex-3.1.*: Two connections to a single database file.
          104  +#
          105  +#   multiplex-3.2.*: Two connections to each of several database files (that
          106  +#                are in the same multiplex group).
          107  +#
          108  +do_test multiplex-3.1.1 {
          109  +  multiplex_delete test.db
          110  +  sqlite3_multiplex_initialize "" 1
          111  +  sqlite3_multiplex_set 0x8000 16
          112  +} {SQLITE_OK}
          113  +do_test multiplex-3.1.2 {
          114  +  sqlite3 db test.db
          115  +  execsql {
          116  +    PRAGMA page_size = 1024;
          117  +    PRAGMA journal_mode = delete;
          118  +    PRAGMA auto_vacuum = off;
          119  +    CREATE TABLE t1(a PRIMARY KEY, b);
          120  +    INSERT INTO t1 VALUES(1, 'one');
          121  +  }
          122  +  file size test.db
          123  +} {3072}
          124  +do_test multiplex-3.1.3 {
          125  +  sqlite3 db2 test.db
          126  +  execsql { CREATE TABLE t2(a, b) } db2
          127  +} {}
          128  +do_test multiplex-3.1.4 {
          129  +  execsql { CREATE TABLE t3(a, b) }
          130  +} {}
          131  +do_test multiplex-3.1.5 {
          132  +  catchsql { CREATE TABLE t3(a, b) }
          133  +} {1 {table t3 already exists}}
          134  +do_test multiplex-3.1.6 {
          135  +  db close
          136  +  db2 close
          137  +} {}
          138  +
          139  +do_test multiplex-3.2.1a {
          140  +
          141  +  multiplex_delete test.db
          142  +  multiplex_delete test2.db
          143  +
          144  +  sqlite3 db1a test.db
          145  +  sqlite3 db2a test2.db
          146  +
          147  +  foreach db {db1a db2a} {
          148  +    execsql {
          149  +      PRAGMA page_size = 1024;
          150  +      PRAGMA journal_mode = delete;
          151  +      PRAGMA auto_vacuum = off;
          152  +      CREATE TABLE t1(a, b);
          153  +    } $db
          154  +  }
          155  +
          156  +  list [file size test.db] [file size test2.db]
          157  +} {2048 2048}
          158  +
          159  +do_test multiplex-3.2.1b {
          160  +  sqlite3 db1b test.db
          161  +  sqlite3 db2b test2.db
          162  +} {}
          163  +
          164  +do_test multiplex-3.2.2 { execsql { INSERT INTO t1 VALUES('x', 'y') } db1a } {}
          165  +do_test multiplex-3.2.3 { execsql { INSERT INTO t1 VALUES('v', 'w') } db1b } {}
          166  +do_test multiplex-3.2.4 { execsql { INSERT INTO t1 VALUES('t', 'u') } db2a } {}
          167  +do_test multiplex-3.2.5 { execsql { INSERT INTO t1 VALUES('r', 's') } db2b } {}
          168  +
          169  +do_test multiplex-3.2.6 { 
          170  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
          171  +} {}
          172  +do_test multiplex-3.2.7 { 
          173  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
          174  +} {}
          175  +do_test multiplex-3.2.8 { 
          176  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
          177  +} {}
          178  +do_test multiplex-3.2.9 { 
          179  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
          180  +} {}
          181  +
          182  +do_test multiplex-3.3.1 { 
          183  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1a
          184  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db1b
          185  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2a
          186  +  execsql { INSERT INTO t1 VALUES(randomblob(500), randomblob(500)) } db2b
          187  +} {}
          188  +
          189  +do_test multiplex-3.2.X {
          190  +  foreach db {db1a db2a db2b db1b} { catch { $db close } }
          191  +} {}
          192  +
          193  +#-------------------------------------------------------------------------
          194  +#
          195  +
          196  +sqlite3_multiplex_initialize "" 1
          197  +sqlite3_multiplex_set 0x8000 16
          198  +
          199  +# Return a list of all currently defined multiplexs.
          200  +proc multiplex_list {} {
          201  +  set allq {}
          202  +  foreach q [sqlite3_multiplex_dump] {
          203  +    lappend allq [lindex $q 0]
          204  +  }
          205  +  return [lsort $allq]
          206  +}
          207  +
          208  +do_test multiplex-4.1.6 {
          209  +  multiplex_delete test2.db
          210  +  sqlite3 db test2.db
          211  +  db eval {CREATE TABLE t2(x); INSERT INTO t2 VALUES('tab-t2');}
          212  +  set res [multiplex_list]
          213  +  list [regexp {test2.db} $res]
          214  +} {1}
          215  +do_test multiplex-4.1.6a {
          216  +  sqlite3 db2 test2.db
          217  +  db2 eval {SELECT * FROM t2}
          218  +} {tab-t2}
          219  +do_test multiplex-4.1.7 {
          220  +  execsql {INSERT INTO t2 VALUES(zeroblob(200000))}
          221  +} {}
          222  +do_test multiplex-4.1.8 {
          223  +  sqlite3 db2 test2.db
          224  +  db2 eval {SELECT count(*) FROM t2}
          225  +} {2}
          226  +do_test multiplex-4.1.8a {
          227  +   db2 eval { DELETE FROM t2 WHERE x = 'tab-t2' }
          228  +} {}
          229  +do_test multiplex-4.1.8b {
          230  +  sqlite3 db2 test2.db
          231  +  db2 eval {SELECT count(*) FROM t2}
          232  +} {1}
          233  +
          234  +
          235  +do_test multiplex-4.1.9 {
          236  +  execsql {INSERT INTO t2 VALUES(zeroblob(200000))}
          237  +} {}
          238  +do_test multiplex-4.1.10 {
          239  +  set res [multiplex_list]
          240  +  list [regexp {test2.db} $res]
          241  +} {1}
          242  +do_test multiplex-4.1.11 {
          243  +  db2 close
          244  +  set res [multiplex_list]
          245  +  list [regexp {test2.db} $res]
          246  +} {1}
          247  +do_test multiplex-4.1.12 {
          248  +  db close
          249  +  multiplex_list
          250  +} {}
          251  +
          252  +
          253  +#-------------------------------------------------------------------------
          254  +# The following tests test that the multiplex VFS handles malloc and IO 
          255  +# errors.
          256  +#
          257  +
          258  +sqlite3_multiplex_initialize "" 1
          259  +sqlite3_multiplex_set 0x8000 16
          260  +
          261  +do_faultsim_test multiplex-5.1 -prep {
          262  +  catch {db close}
          263  +} -body {
          264  +  sqlite3 db test2.db
          265  +}
          266  +do_faultsim_test multiplex-5.2 -prep {
          267  +  catch {db close}
          268  +} -body {
          269  +  sqlite3 db test.db
          270  +}
          271  +
          272  +catch { db close }
          273  +multiplex_delete test.db
          274  +
          275  +do_test multiplex-5.3.prep {
          276  +  sqlite3 db test.db
          277  +  execsql {
          278  +    PRAGMA auto_vacuum = 1;
          279  +    PRAGMA page_size = 1024;
          280  +    CREATE TABLE t1(a, b);
          281  +    INSERT INTO t1 VALUES(10, zeroblob(1200));
          282  +  }
          283  +  faultsim_save_and_close
          284  +} {}
          285  +do_faultsim_test multiplex-5.3 -prep {
          286  +  faultsim_restore_and_reopen
          287  +} -body {
          288  +  execsql { DELETE FROM t1 }
          289  +}
          290  +
          291  +do_test multiplex-5.4.1 {
          292  +  catch { db close }
          293  +  multiplex_delete test.db
          294  +  file mkdir test.db
          295  +  list [catch { sqlite3 db test.db } msg] $msg
          296  +} {1 {unable to open database file}}
          297  +
          298  +do_faultsim_test multiplex-5.5 -prep {
          299  +  catch { sqlite3_multiplex_shutdown }
          300  +} -body {
          301  +  sqlite3_multiplex_initialize "" 1
          302  +  sqlite3_multiplex_set 0x8000 16
          303  +}
          304  +
          305  +catch { sqlite3_multiplex_shutdown }
          306  +finish_test

Changes to test/pagerfault.test.

  1096   1096   #
  1097   1097   #   PagerCommitPhaseOne(<in-memory-db>)   ->   SQLITE_OK
  1098   1098   #   PagerCommitPhaseOne(<file-db>)        ->   SQLITE_IOERR
  1099   1099   #   PagerRollback(<in-memory-db>)
  1100   1100   #   PagerRollback(<file-db>)
  1101   1101   #
  1102   1102   do_faultsim_test pagerfault-23 -prep {
  1103         -  foreach f [glob -nocomplain test.db*] { file delete -force $f }
  1104   1103     sqlite3 db :memory:
         1104  +  foreach f [glob -nocomplain test.db*] { file delete -force $f }
  1105   1105     db eval { 
  1106   1106       ATTACH 'test.db2' AS aux;
  1107   1107       CREATE TABLE t1(a, b);
  1108   1108       CREATE TABLE aux.t2(a, b);
  1109   1109     }
  1110   1110   } -body {
  1111   1111     execsql {