/ Check-in [4b5c93bc]
Login

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

Overview
Comment:Merge with latest trunk changes.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:4b5c93bc7c43c80962ddae65c58037bf5977b94b
User & Date: dan 2010-11-09 17:49:40
Context
2010-11-11
10:36
Modifications to test cases to account for new EXPLAIN QUERY PLAN output. check-in: 30904ef8 user: dan tags: experimental
2010-11-09
17:49
Merge with latest trunk changes. check-in: 4b5c93bc user: dan tags: experimental
17:35
Add missing comments and fix other issues with routines used by new EQP features. check-in: 925f35c5 user: dan tags: experimental
2010-11-08
19:16
Changes to the multiplex VFS to optionally (compiler define) allow the "chunk extension" to overwrite the right-most chars of the filename instead of simply being appended. check-in: 07da0a0b user: shaneh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_config.c.

518
519
520
521
522
523
524






525
526
527
528
529
530
531
#endif

#ifdef SQLITE_SECURE_DELETE
  Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY);
#endif







#ifdef YYTRACKMAXSTACKDEPTH
  Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY);
#endif








>
>
>
>
>
>







518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
#endif

#ifdef SQLITE_SECURE_DELETE
  Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "secure_delete", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef SQLITE_MULTIPLEX_EXT_OVWR
  Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "multiplex_ext_overwrite", "0", TCL_GLOBAL_ONLY);
#endif

#ifdef YYTRACKMAXSTACKDEPTH
  Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "1", TCL_GLOBAL_ONLY);
#else
  Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY);
#endif

Changes to src/test_multiplex.c.

22
23
24
25
26
27
28



29





30









31

32
33
34
35
36
37
38
..
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
...
133
134
135
136
137
138
139
140




141


142
143
144
145
146
147
148
149
...
172
173
174
175
176
177
178

179
180
181
182
183
184
185
186
187
188
189
190










191
192
193


194
195
196



197
198

199

200


201
202
203
204
205
206
207
...
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
...
238
239
240
241
242
243
244




245


246
247
248
249
250
251
252
...
368
369
370
371
372
373
374

375
376
377
378
379
380
381
382
383



384

385
386
387
388
389
390
391
392
...
431
432
433
434
435
436
437
438




439


440
441
442
443
444
445
446
447
...
449
450
451
452
453
454
455

456
457
458
459
460
461
462
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include "sqliteInt.h"

/************************ Shim Definitions ******************************/




#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000





#define SQLITE_MULTIPLEX_MAX_CHUNKS 32









#define SQLITE_MULTIPLEX_EXT_FMT    "-%04d"


/************************ Object Definitions ******************************/

/* Forward declaration of all object types */
typedef struct multiplexGroup multiplexGroup;
typedef struct multiplexConn multiplexConn;

................................................................................
** makeup a single SQLite DB file.  This allows the size of the DB
** to exceed the limits imposed by the file system.
**
** There is an instance of the following object for each defined multiplex
** group.
*/
struct multiplexGroup {
  sqlite3_file *pReal[SQLITE_MULTIPLEX_MAX_CHUNKS]; /* Handles to each chunk */
  char bOpen[SQLITE_MULTIPLEX_MAX_CHUNKS];          /* 0 if chunk not opened */
  char *zName;                                      /* Base filename of this group */
  int nName;                                        /* Length of base filename */
  int flags;                                        /* Flags used for original opening */
  multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
};

/*
** An instance of the following object represents each open connection
** to a file that is multiplex'ed.  This object is a 
** subclass of sqlite3_file.  The sqlite3_file object for the underlying
................................................................................
*/
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
  multiplexGroup *pGroup = pConn->pGroup;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
  if( iChunk<gMultiplex.nMaxChunks ){
    sqlite3_file *pSubOpen = pGroup->pReal[iChunk];    /* Real file descriptor */
    if( !pGroup->bOpen[iChunk] ){
      pGroup->zName[pGroup->nName] = '\0';




      if( iChunk ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);


      *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->zName, pSubOpen, pGroup->flags, pOutFlags);
      if( *rc==SQLITE_OK ){
        pGroup->bOpen[iChunk] = -1;
        return pSubOpen;
      }
      return NULL;
    }
    *rc = SQLITE_OK;
................................................................................
  int rc;                                        /* Result code */
  multiplexConn *pMultiplexOpen;                 /* The new multiplex file descriptor */
  multiplexGroup *pGroup;                        /* Corresponding multiplexGroup object */
  sqlite3_file *pSubOpen;                        /* Real file descriptor */
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  int nName = sqlite3Strlen30(zName);
  int i;


  UNUSED_PARAMETER(pVfs);

  /* We need to create a group structure and manage
  ** access to this group of files.
  */
  multiplexEnter();
  pMultiplexOpen = (multiplexConn*)pConn;
  /* allocate space for group, file handles, 
  ** and file name (+ extra for "-0000\0") 
  */
  pGroup = sqlite3_malloc( sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6 );










  if( pGroup==0 ){
    rc=SQLITE_NOMEM;
  }else{


    pMultiplexOpen->pGroup = pGroup;
    memset(pGroup, 0, sizeof(multiplexGroup) + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks) + nName + 6);
    /* assign pointers to extra space for file handles */



    for(i=0; i<gMultiplex.nMaxChunks; i++){
      pGroup->pReal[i] = (sqlite3_file *)((char *)&pGroup[1] + (pOrigVfs->szOsFile*i));

    }

    pGroup->zName = (char *)&pGroup[1] + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks);


    memcpy(pGroup->zName, zName, nName+1);
    pGroup->nName = nName;
    pGroup->flags = flags;
    pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
    if( pSubOpen ){
      if( pSubOpen->pMethods->iVersion==1 ){
        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
................................................................................
  multiplexLeave();
  return rc;
}

/*
** This is the xDelete method used for the "multiplex" VFS.
** It attempts to delete the filename specified, as well
** as addiitional files with the "-####" extension.
*/
static int multiplexDelete(
  sqlite3_vfs *pVfs,         /* The multiplex VFS */
  const char *zName,         /* Name of file to delete */
  int syncDir
){
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
................................................................................
  UNUSED_PARAMETER(pVfs);

  multiplexEnter();
  memcpy(gMultiplex.zName, zName, nName+1);
  for(i=0; i<gMultiplex.nMaxChunks; i++){
    int rc2;
    int exists = 0;




    if( i ) sqlite3_snprintf(nName+6, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);


    rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
    if( rc2==SQLITE_OK && exists){
      /* if it exists, delete it */
      rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
      if( rc2!=SQLITE_OK ) rc = rc2;
    }else{
      /* stop at first "gap" */
................................................................................
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  int rc2;
  int i;
  sqlite3_file *pSubOpen;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  multiplexEnter();

  /* delete the chunks above the truncate limit */
  for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
    /* close any open chunks before deleting them */
    if( pGroup->bOpen[i] ){
      pSubOpen = pGroup->pReal[i];
      rc2 = pSubOpen->pMethods->xClose(pSubOpen);
      if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
      pGroup->bOpen[i] = 0;
    }



    sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);

    rc2 = pOrigVfs->xDelete(pOrigVfs, pGroup->zName, 0);
    if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
  }
  pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
  if( pSubOpen ){
    rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
    if( rc2!=SQLITE_OK ) rc = rc2;
  }else{
................................................................................
    sqlite3_file *pSubOpen = NULL;
    /* if not opened already, check to see if the chunk exists */
    if( pGroup->bOpen[i] ){
      pSubOpen = pGroup->pReal[i];
    }else{
      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
      int exists = 0;
      pGroup->zName[pGroup->nName] = '\0';




      if( i ) sqlite3_snprintf(pGroup->nName+6, pGroup->zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);


      rc2 = pOrigVfs->xAccess(pOrigVfs, pGroup->zName, SQLITE_ACCESS_EXISTS, &exists);
      if( rc2==SQLITE_OK && exists){
        /* if it exists, open it */
        pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
      }else{
        /* stop at first "gap" */
        break;
      }
................................................................................
    if( pSubOpen ){
      sqlite3_int64 sz;
      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
      if( rc2!=SQLITE_OK ){
        rc = rc2;
      }else{
        *pSize += sz;

      }
    }else{
      break;
    }
  }
  multiplexLeave();
  return rc;
................................................................................
  int nChunkSize,                 /* Max chunk size */
  int nMaxChunks                  /* Max number of chunks */
){
  if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
  if( gMultiplex.pGroups ) return SQLITE_MISUSE;
  if( nChunkSize<32 ) return SQLITE_MISUSE;
  if( nMaxChunks<1 ) return SQLITE_MISUSE;
  if( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS ) return SQLITE_MISUSE;
  multiplexEnter();
  gMultiplex.nChunkSize = nChunkSize;
  gMultiplex.nMaxChunks = nMaxChunks;
  multiplexLeave();
  return SQLITE_OK;
}








>
>
>

>
>
>
>
>

>
>
>
>
>
>
>
>
>
|
>







 







|
|
|
|
|







 







|
>
>
>
>
|
>
>
|







 







>








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



>
>

<
<
>
>
>

|
>

>
|
>
>







 







|







 







>
>
>
>
|
>
>







 







>









>
>
>
|
>
|







 







|
>
>
>
>
|
>
>
|







 







>







 







|







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
..
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
...
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231


232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
...
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
...
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
...
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
#include "sqlite3.h"
#include <string.h>
#include <assert.h>
#include "sqliteInt.h"

/************************ Shim Definitions ******************************/

/* This is the limit on the chunk size.  It may be changed by calling
** the sqlite3_multiplex_set() interface.
*/
#define SQLITE_MULTIPLEX_CHUNK_SIZE 0x40000000
/* Default limit on number of chunks.  Care should be taken
** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT
** format specifier. It may be changed by calling
** the sqlite3_multiplex_set() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32

/* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the 
** last SQLITE_MULTIPLEX_EXT_SZ characters of the 
** filename will be overwritten, otherwise, the 
** multiplex extension is simply appended to the filename.
** Ex.  (undefined) test.db -> test.db01
**      (defined)   test.db -> test.01
** Chunk 0 does not have a modified extension.
*/
#define SQLITE_MULTIPLEX_EXT_FMT    "%02d"
#define SQLITE_MULTIPLEX_EXT_SZ     2

/************************ Object Definitions ******************************/

/* Forward declaration of all object types */
typedef struct multiplexGroup multiplexGroup;
typedef struct multiplexConn multiplexConn;

................................................................................
** makeup a single SQLite DB file.  This allows the size of the DB
** to exceed the limits imposed by the file system.
**
** There is an instance of the following object for each defined multiplex
** group.
*/
struct multiplexGroup {
  sqlite3_file **pReal;            /* Handles to each chunk */
  char *bOpen;                     /* 0 if chunk not opened */
  char *zName;                     /* Base filename of this group */
  int nName;                       /* Length of base filename */
  int flags;                       /* Flags used for original opening */
  multiplexGroup *pNext, *pPrev;   /* Doubly linked list of all group objects */
};

/*
** An instance of the following object represents each open connection
** to a file that is multiplex'ed.  This object is a 
** subclass of sqlite3_file.  The sqlite3_file object for the underlying
................................................................................
*/
static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){
  multiplexGroup *pGroup = pConn->pGroup;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */
  if( iChunk<gMultiplex.nMaxChunks ){
    sqlite3_file *pSubOpen = pGroup->pReal[iChunk];    /* Real file descriptor */
    if( !pGroup->bOpen[iChunk] ){
      memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
      if( iChunk ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
#else
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, iChunk);
#endif
      }
      *rc = pOrigVfs->xOpen(pOrigVfs, gMultiplex.zName, pSubOpen, pGroup->flags, pOutFlags);
      if( *rc==SQLITE_OK ){
        pGroup->bOpen[iChunk] = -1;
        return pSubOpen;
      }
      return NULL;
    }
    *rc = SQLITE_OK;
................................................................................
  int rc;                                        /* Result code */
  multiplexConn *pMultiplexOpen;                 /* The new multiplex file descriptor */
  multiplexGroup *pGroup;                        /* Corresponding multiplexGroup object */
  sqlite3_file *pSubOpen;                        /* Real file descriptor */
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  int nName = sqlite3Strlen30(zName);
  int i;
  int sz;

  UNUSED_PARAMETER(pVfs);

  /* We need to create a group structure and manage
  ** access to this group of files.
  */
  multiplexEnter();
  pMultiplexOpen = (multiplexConn*)pConn;
  /* allocate space for group */
  sz = sizeof(multiplexGroup)                         /* multiplexGroup */
     + (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks) /* pReal[] */
     + (pOrigVfs->szOsFile*gMultiplex.nMaxChunks)     /* *pReal */
     + gMultiplex.nMaxChunks                          /* bOpen[] */
     + nName + 1;                                     /* zName */
#ifndef SQLITE_MULTIPLEX_EXT_OVWR
  sz += SQLITE_MULTIPLEX_EXT_SZ;
  assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname);
#else
  assert(nName >= SQLITE_MULTIPLEX_EXT_SZ);
  assert(nName < pOrigVfs->mxPathname);
#endif
  pGroup = sqlite3_malloc( sz );
  if( pGroup==0 ){
    rc=SQLITE_NOMEM;
  }else{
    /* assign pointers to extra space allocated */
    char *p = (char *)&pGroup[1];
    pMultiplexOpen->pGroup = pGroup;


    memset(pGroup, 0, sz);
    pGroup->pReal = (sqlite3_file **)p;
    p += (sizeof(sqlite3_file *)*gMultiplex.nMaxChunks);
    for(i=0; i<gMultiplex.nMaxChunks; i++){
      pGroup->pReal[i] = (sqlite3_file *)p;
      p += pOrigVfs->szOsFile;
    }
    pGroup->bOpen = p;
    p += gMultiplex.nMaxChunks;
    pGroup->zName = p;
    /* save off base filename, name length, and original open flags  */
    memcpy(pGroup->zName, zName, nName+1);
    pGroup->nName = nName;
    pGroup->flags = flags;
    pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags);
    if( pSubOpen ){
      if( pSubOpen->pMethods->iVersion==1 ){
        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
................................................................................
  multiplexLeave();
  return rc;
}

/*
** This is the xDelete method used for the "multiplex" VFS.
** It attempts to delete the filename specified, as well
** as additional files with the SQLITE_MULTIPLEX_EXT_FMT extension.
*/
static int multiplexDelete(
  sqlite3_vfs *pVfs,         /* The multiplex VFS */
  const char *zName,         /* Name of file to delete */
  int syncDir
){
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
................................................................................
  UNUSED_PARAMETER(pVfs);

  multiplexEnter();
  memcpy(gMultiplex.zName, zName, nName+1);
  for(i=0; i<gMultiplex.nMaxChunks; i++){
    int rc2;
    int exists = 0;
    if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
    }
    rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
    if( rc2==SQLITE_OK && exists){
      /* if it exists, delete it */
      rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir);
      if( rc2!=SQLITE_OK ) rc = rc2;
    }else{
      /* stop at first "gap" */
................................................................................
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  int rc2;
  int i;
  sqlite3_file *pSubOpen;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  multiplexEnter();
  memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
  /* delete the chunks above the truncate limit */
  for(i=(int)(size/gMultiplex.nChunkSize)+1; i<gMultiplex.nMaxChunks; i++){
    /* close any open chunks before deleting them */
    if( pGroup->bOpen[i] ){
      pSubOpen = pGroup->pReal[i];
      rc2 = pSubOpen->pMethods->xClose(pSubOpen);
      if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
      pGroup->bOpen[i] = 0;
    }
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
    sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
    sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
    rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0);
    if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE;
  }
  pSubOpen = multiplexSubOpen(p, (int)(size/gMultiplex.nChunkSize), &rc2, NULL);
  if( pSubOpen ){
    rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size%gMultiplex.nChunkSize);
    if( rc2!=SQLITE_OK ) rc = rc2;
  }else{
................................................................................
    sqlite3_file *pSubOpen = NULL;
    /* if not opened already, check to see if the chunk exists */
    if( pGroup->bOpen[i] ){
      pSubOpen = pGroup->pReal[i];
    }else{
      sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
      int exists = 0;
      memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
      if( i ){
#ifdef SQLITE_MULTIPLEX_EXT_OVWR
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i);
#else
        sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i);
#endif
      }
      rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists);
      if( rc2==SQLITE_OK && exists){
        /* if it exists, open it */
        pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
      }else{
        /* stop at first "gap" */
        break;
      }
................................................................................
    if( pSubOpen ){
      sqlite3_int64 sz;
      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
      if( rc2!=SQLITE_OK ){
        rc = rc2;
      }else{
        *pSize += sz;
        assert(sz<=gMultiplex.nChunkSize);
      }
    }else{
      break;
    }
  }
  multiplexLeave();
  return rc;
................................................................................
  int nChunkSize,                 /* Max chunk size */
  int nMaxChunks                  /* Max number of chunks */
){
  if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
  if( gMultiplex.pGroups ) return SQLITE_MISUSE;
  if( nChunkSize<32 ) return SQLITE_MISUSE;
  if( nMaxChunks<1 ) return SQLITE_MISUSE;
  if( nMaxChunks>99 ) return SQLITE_MISUSE;
  multiplexEnter();
  gMultiplex.nChunkSize = nChunkSize;
  gMultiplex.nMaxChunks = nMaxChunks;
  multiplexLeave();
  return SQLITE_OK;
}

Changes to test/multiplex.test.

13
14
15
16
17
18
19
















20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
..
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

set g_chunk_size 2147483648
set g_max_chunks 32

















proc multiplex_set {chunk_size max_chunks} {
  global g_chunk_size
  global g_max_chunks
  set g_chunk_size $chunk_size
  set g_max_chunks $max_chunks
  sqlite3_multiplex_set $chunk_size $max_chunks
}

proc multiplex_delete {name} {
  global g_max_chunks
  forcedelete $name
  forcedelete $name-journal
  forcedelete $name-wal
  for {set i 1} {$i<$g_max_chunks} {incr i} {
    set num [format "%04d" $i]
    forcedelete $name-$num
    forcedelete $name-journal-$num
    forcedelete $name-wal-$num
  }
}

db close

#-------------------------------------------------------------------------
#   multiplex-1.1.*: Test initialize and shutdown.
................................................................................
do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}

do_test multiplex-1.9  { sqlite3_multiplex_initialize "" 1 }       {SQLITE_OK}
do_test multiplex-1.10.1 { multiplex_set 32768 16 }                {SQLITE_OK}
do_test multiplex-1.10.2 { multiplex_set 32768 -1 }                {SQLITE_MISUSE}
do_test multiplex-1.10.3 { multiplex_set -1 16 }                   {SQLITE_MISUSE}
do_test multiplex-1.10.4 { multiplex_set 31 16 }                   {SQLITE_MISUSE}
do_test multiplex-1.10.5 { multiplex_set 32768 33 }                {SQLITE_MISUSE}
do_test multiplex-1.11 { sqlite3_multiplex_shutdown }              {SQLITE_OK}


#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback 
# mode:
#
................................................................................
  }
  execsql {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, randomblob(1100));
    INSERT INTO t1 VALUES(2, randomblob(1100));
  }
} {}
do_test multiplex-2.1.3 { file size test.db } {4096}
do_test multiplex-2.1.4 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}

do_test multiplex-2.2.1 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}
do_test multiplex-2.2.3 { file size test.db } {6144}

do_test multiplex-2.3.1 {
  sqlite3 db2 bak.db
  db2 close
} {}

do_test multiplex-2.4.1 {
  sqlite3_multiplex_shutdown
} {SQLITE_MISUSE}
do_test multiplex-2.4.2 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}
do_test multiplex-2.4.4 { file size test.db } {7168}
do_test multiplex-2.4.99 {
  db close
  sqlite3_multiplex_shutdown
} {SQLITE_OK}


do_test multiplex-2.5.1 {
................................................................................
  db eval {SELECT a,length(b) FROM t1 WHERE a=2}
} {2 4000}

do_test multiplex-2.5.8 {
  db eval {SELECT a,length(b) FROM t1 WHERE a=4}
} {4 4000}

do_test multiplex-2.5.9 { file size test.db } [list $g_chunk_size]
do_test multiplex-2.5.10 { file size test.db-0001 } [list $g_chunk_size]

do_test multiplex-2.5.99 {
  db close
  sqlite3_multiplex_shutdown
} {SQLITE_OK}


................................................................................
      db eval {SELECT b FROM t1 WHERE a=1}
    } {one}

    do_test multiplex-2.6.5.$sz.$jmode {
      db eval {SELECT length(b) FROM t1 WHERE a=2}
    } [list $g_chunk_size]

    do_test multiplex-2.6.6.$sz.$jmode { file size test.db } [list $g_chunk_size]

    do_test multiplex-2.6.99.$sz.$jmode {
      db close
      sqlite3_multiplex_shutdown
    } {SQLITE_OK}

  }
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = delete;
    PRAGMA auto_vacuum = off;
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, 'one');
  }
  file size test.db
} {3072}
do_test multiplex-3.1.3 {
  sqlite3 db2 test.db
  execsql { CREATE TABLE t2(a, b) } db2
} {}
do_test multiplex-3.1.4 {
  execsql { CREATE TABLE t3(a, b) }
................................................................................
      PRAGMA page_size = 1024;
      PRAGMA journal_mode = delete;
      PRAGMA auto_vacuum = off;
      CREATE TABLE t1(a, b);
    } $db
  }

  list [file size test.db] [file size test2.db]
} {2048 2048}

do_test multiplex-3.2.1b {
  sqlite3 db1b test.db
  sqlite3 db2b test2.db
} {}








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








|
|
|
|
|
|
|
|
<
<







 







|







 







|







|












|







 







|
|







 







|







 







|







 







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


52
53
54
55
56
57
58
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
...
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
...
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/malloc_common.tcl

set g_chunk_size 2147483648
set g_max_chunks 32

# This handles appending the chunk number
# to the end of the filename.  if 
# SQLITE_MULTIPLEX_EXT_OVWR is defined, then
# it overwrites the last 2 bytes of the 
# file name with the chunk number.
proc multiplex_name {name chunk} {
  if {$chunk==0} { return $name }
  set num [format "%02d" $chunk]
  ifcapable {multiplex_ext_overwrite} {
    set name [string range $name 0 [expr [string length $name]-2-1]]
  }
  return $name$num
}

# This saves off the parameters and calls the 
# underlying sqlite3_multiplex_set() API.
proc multiplex_set {chunk_size max_chunks} {
  global g_chunk_size
  global g_max_chunks
  set g_chunk_size $chunk_size
  set g_max_chunks $max_chunks
  sqlite3_multiplex_set $chunk_size $max_chunks
}

# This attempts to delete the base file and 
# and files with the chunk extension.
proc multiplex_delete {name} {
  global g_max_chunks
  for {set i 0} {$i<$g_max_chunks} {incr i} {
    forcedelete [multiplex_name $name $i]
    forcedelete [multiplex_name $name-journal $i]
    forcedelete [multiplex_name $name-wal $i]


  }
}

db close

#-------------------------------------------------------------------------
#   multiplex-1.1.*: Test initialize and shutdown.
................................................................................
do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}

do_test multiplex-1.9  { sqlite3_multiplex_initialize "" 1 }       {SQLITE_OK}
do_test multiplex-1.10.1 { multiplex_set 32768 16 }                {SQLITE_OK}
do_test multiplex-1.10.2 { multiplex_set 32768 -1 }                {SQLITE_MISUSE}
do_test multiplex-1.10.3 { multiplex_set -1 16 }                   {SQLITE_MISUSE}
do_test multiplex-1.10.4 { multiplex_set 31 16 }                   {SQLITE_MISUSE}
do_test multiplex-1.10.5 { multiplex_set 32768 100 }               {SQLITE_MISUSE}
do_test multiplex-1.11 { sqlite3_multiplex_shutdown }              {SQLITE_OK}


#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback 
# mode:
#
................................................................................
  }
  execsql {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, randomblob(1100));
    INSERT INTO t1 VALUES(2, randomblob(1100));
  }
} {}
do_test multiplex-2.1.3 { file size [multiplex_name test.db 0] } {4096}
do_test multiplex-2.1.4 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}

do_test multiplex-2.2.1 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}
do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144}

do_test multiplex-2.3.1 {
  sqlite3 db2 bak.db
  db2 close
} {}

do_test multiplex-2.4.1 {
  sqlite3_multiplex_shutdown
} {SQLITE_MISUSE}
do_test multiplex-2.4.2 {
  execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {}
do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168}
do_test multiplex-2.4.99 {
  db close
  sqlite3_multiplex_shutdown
} {SQLITE_OK}


do_test multiplex-2.5.1 {
................................................................................
  db eval {SELECT a,length(b) FROM t1 WHERE a=2}
} {2 4000}

do_test multiplex-2.5.8 {
  db eval {SELECT a,length(b) FROM t1 WHERE a=4}
} {4 4000}

do_test multiplex-2.5.9 { file size [multiplex_name test.db 0] } [list $g_chunk_size]
do_test multiplex-2.5.10 { file size [multiplex_name test.db 1] } [list $g_chunk_size]

do_test multiplex-2.5.99 {
  db close
  sqlite3_multiplex_shutdown
} {SQLITE_OK}


................................................................................
      db eval {SELECT b FROM t1 WHERE a=1}
    } {one}

    do_test multiplex-2.6.5.$sz.$jmode {
      db eval {SELECT length(b) FROM t1 WHERE a=2}
    } [list $g_chunk_size]

    do_test multiplex-2.6.6.$sz.$jmode { file size [multiplex_name test.db 0] } [list $g_chunk_size]

    do_test multiplex-2.6.99.$sz.$jmode {
      db close
      sqlite3_multiplex_shutdown
    } {SQLITE_OK}

  }
................................................................................
  execsql {
    PRAGMA page_size = 1024;
    PRAGMA journal_mode = delete;
    PRAGMA auto_vacuum = off;
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, 'one');
  }
  file size [multiplex_name test.db 0]
} {3072}
do_test multiplex-3.1.3 {
  sqlite3 db2 test.db
  execsql { CREATE TABLE t2(a, b) } db2
} {}
do_test multiplex-3.1.4 {
  execsql { CREATE TABLE t3(a, b) }
................................................................................
      PRAGMA page_size = 1024;
      PRAGMA journal_mode = delete;
      PRAGMA auto_vacuum = off;
      CREATE TABLE t1(a, b);
    } $db
  }

  list [file size [multiplex_name test.db 0]] [file size [multiplex_name test2.db 0]]
} {2048 2048}

do_test multiplex-3.2.1b {
  sqlite3 db1b test.db
  sqlite3 db2b test2.db
} {}