/ Check-in [b3c6d9aa]
Login

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

Overview
Comment:Enable/disable support.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | multiplex-enhancements
Files: files | file ages | folders
SHA1: b3c6d9aa9e2124a2f2a1a5f9dbbd7db3b1d01a31
User & Date: shaneh 2011-03-31 15:11:53
Context
2011-04-01
14:22
Removed dependency on sqliteInt.h so that multiplex VFS shim can be compiled as loadable module. Closed-Leaf check-in: 718f1ad7 user: shaneh tags: multiplex-enhancements
2011-03-31
15:11
Enable/disable support. check-in: b3c6d9aa user: shaneh tags: multiplex-enhancements
13:14
Add tests and fixes for SELECT multiplex_control(op, val); check-in: fee9734c user: shaneh tags: multiplex-enhancements
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_multiplex.c.

46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
...
301
302
303
304
305
306
307

308
309
310
311
312
313
314
...
319
320
321
322
323
324
325








326
327
328
329
330
331
332
...
461
462
463
464
465
466
467




468
469
470
471
472
473
474
475
476
477
478
479
480
481
482

483
484
485
486
487
488
489
...
496
497
498
499
500
501
502




503
504
505
506
507
508
509
510
511
512
513
514
515
516
517

518
519
520
521
522
523
524
...
525
526
527
528
529
530
531





532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560

561
562
563
564
565
566
567
...
589
590
591
592
593
594
595




596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618















619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
...
670
671
672
673
674
675
676
677

678
679
680
681
682
683
684
....
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
....
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079

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

#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"

/* This is the limit on the chunk size.  It may be changed by calling
** the xFileControl() interface.  It will be rounded up to a 
** multiple of SQLITE_MAX_PAGE_SIZE.
*/
#define SQLITE_MULTIPLEX_CHUNK_SIZE (SQLITE_MAX_PAGE_SIZE*32)

/* 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 xFileControl() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32

................................................................................
  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->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
    pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
    pGroup->pReal = (sqlite3_file **)p;
    p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
    for(i=0; i<pGroup->nMaxChunks; i++){
      pGroup->pReal[i] = (sqlite3_file *)p;
      p += pOrigVfs->szOsFile;
................................................................................
    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;
      }else{
        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
      }
      /* place this group at the head of our list */
      pGroup->pNext = gMultiplex.pGroups;
................................................................................
  int iAmt,
  sqlite3_int64 iOfst
){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  multiplexEnter();




  while( iAmt > 0 ){
    int i = (int)(iOfst / pGroup->nChunkSize);
    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
    if( pSubOpen ){
      int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
      if( extra<0 ) extra = 0;
      iAmt -= extra;
      rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
      if( rc!=SQLITE_OK ) break;
      pBuf = (char *)pBuf + iAmt;
      iOfst += iAmt;
      iAmt = extra;
    }else{
      rc = SQLITE_IOERR_READ;
      break;

    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xWrite requests thru to the original VFS after
................................................................................
  int iAmt,
  sqlite3_int64 iOfst
){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  multiplexEnter();




  while( iAmt > 0 ){
    int i = (int)(iOfst / pGroup->nChunkSize);
    sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
    if( pSubOpen ){
      int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
      if( extra<0 ) extra = 0;
      iAmt -= extra;
      rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
      if( rc!=SQLITE_OK ) break;
      pBuf = (char *)pBuf + iAmt;
      iOfst += iAmt;
      iAmt = extra;
    }else{
      rc = SQLITE_IOERR_WRITE;
      break;

    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xTruncate requests thru to the original VFS after
................................................................................
** determining the correct chunk to operate on.  Delete any
** chunks above the truncate mark.
*/
static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
  multiplexConn *p = (multiplexConn*)pConn;
  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 / pGroup->nChunkSize)+1; i<pGroup->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 / pGroup->nChunkSize), &rc2, NULL);
  if( pSubOpen ){
    rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
    if( rc2!=SQLITE_OK ) rc = rc2;
  }else{
    rc = SQLITE_IOERR_TRUNCATE;

  }
  multiplexLeave();
  return rc;
}

/* Pass xSync requests through to the original VFS without change
*/
................................................................................
static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  int rc2;
  int i;
  multiplexEnter();




  *pSize = 0;
  for(i=0; i<pGroup->nMaxChunks; i++){
    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{
        if( sz>pGroup->nChunkSize ){
          rc = SQLITE_IOERR_FSTAT;
        }
        *pSize += sz;
      }
    }else{
      break;
    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xLock requests through to the original VFS unchanged.
*/
................................................................................
  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
  if( pSubOpen ){
    return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
  }
  return SQLITE_IOERR_CHECKRESERVEDLOCK;
}

/* Pass xFileControl requests through to the original VFS unchanged.

*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_ERROR;
  sqlite3_file *pSubOpen;

................................................................................
  } aSub[] = {
    { "enable",       MULTIPLEX_CTRL_ENABLE,           1 },
    { "chunk_size",   MULTIPLEX_CTRL_SET_CHUNK_SIZE,   1 },
    { "max_chunks",   MULTIPLEX_CTRL_SET_MAX_CHUNKS,   1 },
    { 0, 0, 0 }
  };

  if( objc!=4 && objc!=5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND ?INT-VALUE?");
    return TCL_ERROR;
  }

  if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
    Tcl_AppendResult(interp, "expected database handle, got \"", 0);
    Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
    return TCL_ERROR;
................................................................................
  rc = Tcl_GetIndexFromObjStruct(
      interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
  );
  if( rc!=TCL_OK ) return rc;

  switch( aSub[idx].argtype ){
    case 1:
      if( objc!=5 ){
        Tcl_WrongNumArgs(interp, 4, objv, "INT-VALUE");
        return TCL_ERROR;
      }
      if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
        return TCL_ERROR;
      }
      pArg = (void *)&iValue;
      break;
    default:
      Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");







|

|
>







 







>







 







>
>
>
>
>
>
>
>







 







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







 







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







 







>
>
>
>
>
|
|
|
|
<
|
|
|
|
|
|
|
|
|
|

|

|

|
|
|
|
|
|
|
|
|
>







 







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

|

|

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



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







 







|
>







 







|
|







 







<
<
<
<







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
...
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
...
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
...
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
...
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560

561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
...
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665














666
667
668
669
670
671
672
...
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
....
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
....
1093
1094
1095
1096
1097
1098
1099




1100
1101
1102
1103
1104
1105
1106

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

#define SQLITE_MULTIPLEX_VFS_NAME "multiplex"

/* This is the limit on the chunk size.  It may be changed by calling
** the xFileControl() interface.  It will be rounded up to a 
** multiple of SQLITE_MAX_PAGE_SIZE.  We default it here to 1GB.
*/
#define SQLITE_MULTIPLEX_CHUNK_SIZE (SQLITE_MAX_PAGE_SIZE*16384)

/* 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 xFileControl() interface.
*/
#define SQLITE_MULTIPLEX_MAX_CHUNKS 32

................................................................................
  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->bEnabled = -1;
    pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE;
    pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS;
    pGroup->pReal = (sqlite3_file **)p;
    p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks);
    for(i=0; i<pGroup->nMaxChunks; i++){
      pGroup->pReal[i] = (sqlite3_file *)p;
      p += pOrigVfs->szOsFile;
................................................................................
    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 this file is already larger than chunk size, disable 
      ** the multiplex feature.
      */
      sqlite3_int64 sz;
      int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
      if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){
        pGroup->bEnabled = 0;
      }
      if( pSubOpen->pMethods->iVersion==1 ){
        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
      }else{
        pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
      }
      /* place this group at the head of our list */
      pGroup->pNext = gMultiplex.pGroups;
................................................................................
  int iAmt,
  sqlite3_int64 iOfst
){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  multiplexEnter();
  if( !pGroup->bEnabled ){
    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
    rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
  }else{
    while( iAmt > 0 ){
      int i = (int)(iOfst / pGroup->nChunkSize);
      sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
      if( pSubOpen ){
        int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
        if( extra<0 ) extra = 0;
        iAmt -= extra;
        rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
        if( rc!=SQLITE_OK ) break;
        pBuf = (char *)pBuf + iAmt;
        iOfst += iAmt;
        iAmt = extra;
      }else{
        rc = SQLITE_IOERR_READ;
        break;
      }
    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xWrite requests thru to the original VFS after
................................................................................
  int iAmt,
  sqlite3_int64 iOfst
){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  multiplexEnter();
  if( !pGroup->bEnabled ){
    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
    rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
  }else{
    while( iAmt > 0 ){
      int i = (int)(iOfst / pGroup->nChunkSize);
      sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL);
      if( pSubOpen ){
        int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize;
        if( extra<0 ) extra = 0;
        iAmt -= extra;
        rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize);
        if( rc!=SQLITE_OK ) break;
        pBuf = (char *)pBuf + iAmt;
        iOfst += iAmt;
        iAmt = extra;
      }else{
        rc = SQLITE_IOERR_WRITE;
        break;
      }
    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xTruncate requests thru to the original VFS after
................................................................................
** determining the correct chunk to operate on.  Delete any
** chunks above the truncate mark.
*/
static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  multiplexEnter();
  if( !pGroup->bEnabled ){
    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
    rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size);
  }else{
    int rc2;
    int i;
    sqlite3_file *pSubOpen;
    sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */

    memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1);
    /* delete the chunks above the truncate limit */
    for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->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 / pGroup->nChunkSize), &rc2, NULL);
    if( pSubOpen ){
      rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize);
      if( rc2!=SQLITE_OK ) rc = rc2;
    }else{
      rc = SQLITE_IOERR_TRUNCATE;
    }
  }
  multiplexLeave();
  return rc;
}

/* Pass xSync requests through to the original VFS without change
*/
................................................................................
static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_OK;
  int rc2;
  int i;
  multiplexEnter();
  if( !pGroup->bEnabled ){
    sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
    rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
  }else{
    *pSize = 0;
    for(i=0; i<pGroup->nMaxChunks; i++){
      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{
          if( sz>pGroup->nChunkSize ){
            rc = SQLITE_IOERR_FSTAT;
          }
          *pSize += sz;
        }
      }else{
        break;
      }
    }














  }
  multiplexLeave();
  return rc;
}

/* Pass xLock requests through to the original VFS unchanged.
*/
................................................................................
  sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL);
  if( pSubOpen ){
    return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
  }
  return SQLITE_IOERR_CHECKRESERVEDLOCK;
}

/* Pass xFileControl requests through to the original VFS unchanged,
** except for any MULTIPLEX_CTRL_* requests here.
*/
static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
  multiplexConn *p = (multiplexConn*)pConn;
  multiplexGroup *pGroup = p->pGroup;
  int rc = SQLITE_ERROR;
  sqlite3_file *pSubOpen;

................................................................................
  } aSub[] = {
    { "enable",       MULTIPLEX_CTRL_ENABLE,           1 },
    { "chunk_size",   MULTIPLEX_CTRL_SET_CHUNK_SIZE,   1 },
    { "max_chunks",   MULTIPLEX_CTRL_SET_MAX_CHUNKS,   1 },
    { 0, 0, 0 }
  };

  if( objc!=5 ){
    Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
    return TCL_ERROR;
  }

  if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
    Tcl_AppendResult(interp, "expected database handle, got \"", 0);
    Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
    return TCL_ERROR;
................................................................................
  rc = Tcl_GetIndexFromObjStruct(
      interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
  );
  if( rc!=TCL_OK ) return rc;

  switch( aSub[idx].argtype ){
    case 1:




      if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
        return TCL_ERROR;
      }
      pArg = (void *)&iValue;
      break;
    default:
      Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");

Changes to test/multiplex.test.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
70
71
72
73
74
75
76

77
78
79
80
81
82
83

84
85
86
87
88
89
90
91
92
93









94
95








96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115



116
117
118
119
120
121
122
...
256
257
258
259
260
261
262


























263
264
265
266
267
268
269
#***********************************************************************
#

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.
................................................................................
do_test multiplex-1.4 { sqlite3_multiplex_shutdown }               {SQLITE_OK}

do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 }        {SQLITE_OK}
do_test multiplex-1.6 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}


do_test multiplex-1.9.1 { sqlite3_multiplex_initialize "" 1 }     {SQLITE_OK}
do_test multiplex-1.9.2 { sqlite3 db test.db }                    {}
do_test multiplex-1.9.3 { multiplex_set db main 32768 16 }        {SQLITE_OK}
do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 }        {SQLITE_MISUSE}
do_test multiplex-1.9.5 { multiplex_set db main -1 16 }           {SQLITE_MISUSE}
do_test multiplex-1.9.6 { multiplex_set db main 31 16 }           {SQLITE_OK}
do_test multiplex-1.9.7 { multiplex_set db main 32768 100 }       {SQLITE_MISUSE}

do_test multiplex-1.9.8 { db close }                              {}
do_test multiplex-1.9.9 { sqlite3_multiplex_shutdown }            {SQLITE_OK}

do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 }                {SQLITE_OK}
do_test multiplex-1.10.2 { sqlite3 db test.db }                               {}
do_test multiplex-1.10.3 { execsql { SELECT multiplex_control(2, 32768); } }  {SQLITE_OK}
do_test multiplex-1.10.4 { execsql { SELECT multiplex_control(3, -1); } }     {SQLITE_MISUSE}
do_test multiplex-1.10.5 { execsql { SELECT multiplex_control(2, -1); } }     {SQLITE_MISUSE}
do_test multiplex-1.10.6 { execsql { SELECT multiplex_control(2, 31); } }     {SQLITE_OK}
do_test multiplex-1.10.7 { execsql { SELECT multiplex_control(3, 100); } }    {SQLITE_MISUSE}









do_test multiplex-1.10.8 { db close }                                         {}
do_test multiplex-1.10.9 { sqlite3_multiplex_shutdown }                       {SQLITE_OK}









#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback 
# mode:
#
#   multiplex-2.1.*: Test simple writing to a multiplex file.
#
#   multiplex-2.2.*: More writing.
#
#   multiplex-2.3.*: Open and close a second db.
#
#   multiplex-2.4.*: Try to shutdown the multiplex system befor e closing the db
#                file. Check that this fails and the multiplex system still works
#                afterwards. Then close the database and successfully shut
#                down the multiplex system.
#
#   multiplex-2.5.*: More reading/writing.
#
#   multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
#                well as varying journal mode.




sqlite3_multiplex_initialize "" 1
multiplex_set db main 32768 16

do_test multiplex-2.1.2 {
  sqlite3 db test.db
  execsql {
................................................................................
    do_test multiplex-2.6.99.$sz.$jmode {
      db close
      sqlite3_multiplex_shutdown
    } {SQLITE_OK}

  }
}



























#-------------------------------------------------------------------------
# Try some tests with more than one connection to a database file. Still
# in rollback mode.
#
#   multiplex-3.1.*: Two connections to a single database file.
#







|







 







>
|
|
|
|
|
|
|
>
|
|

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











|








>
>
>







 







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







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
#***********************************************************************
#

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

set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ]
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.
................................................................................
do_test multiplex-1.4 { sqlite3_multiplex_shutdown }               {SQLITE_OK}

do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 }        {SQLITE_OK}
do_test multiplex-1.6 { sqlite3_multiplex_shutdown }               {SQLITE_OK}
do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 }        {SQLITE_OK}
do_test multiplex-1.8 { sqlite3_multiplex_shutdown }               {SQLITE_OK}


do_test multiplex-1.9.1  { sqlite3_multiplex_initialize "" 1 }     {SQLITE_OK}
do_test multiplex-1.9.2  { sqlite3 db test.db }                    {}
do_test multiplex-1.9.3  { multiplex_set db main 32768 16 }        {SQLITE_OK}
do_test multiplex-1.9.4  { multiplex_set db main 32768 -1 }        {SQLITE_MISUSE}
do_test multiplex-1.9.5  { multiplex_set db main -1 16 }           {SQLITE_MISUSE}
do_test multiplex-1.9.6  { multiplex_set db main 31 16 }           {SQLITE_OK}
do_test multiplex-1.9.7  { multiplex_set db main 32768 100 }       {SQLITE_MISUSE}
do_test multiplex-1.9.8  { multiplex_set db main 1073741824 1 }    {SQLITE_OK}
do_test multiplex-1.9.9  { db close }                              {}
do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown }            {SQLITE_OK}

do_test multiplex-1.10.1  { sqlite3_multiplex_initialize "" 1 }                     {SQLITE_OK}
do_test multiplex-1.10.2  { sqlite3 db test.db }                                    {}
do_test multiplex-1.10.3  { execsql { SELECT multiplex_control(2, 32768); } }       {SQLITE_OK}
do_test multiplex-1.10.4  { execsql { SELECT multiplex_control(3, -1); } }          {SQLITE_MISUSE}
do_test multiplex-1.10.5  { execsql { SELECT multiplex_control(2, -1); } }          {SQLITE_MISUSE}
do_test multiplex-1.10.6  { execsql { SELECT multiplex_control(2, 31); } }          {SQLITE_OK}
do_test multiplex-1.10.7  { execsql { SELECT multiplex_control(3, 100); } }         {SQLITE_MISUSE}
do_test multiplex-1.10.8  { execsql { SELECT multiplex_control(2, 1073741824); } }  {SQLITE_OK}
do_test multiplex-1.10.9  { db close }                                              {}
do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown }                            {SQLITE_OK}

do_test multiplex-1.11.1  { sqlite3_multiplex_initialize "" 1 }                 {SQLITE_OK}
do_test multiplex-1.11.2  { sqlite3 db test.db }                                {}
do_test multiplex-1.11.3  { sqlite3_multiplex_control db main enable 0  }       {SQLITE_OK}
do_test multiplex-1.11.4  { sqlite3_multiplex_control db main enable 1  }       {SQLITE_OK}
do_test multiplex-1.11.5  { sqlite3_multiplex_control db main enable -1 }       {SQLITE_OK}
do_test multiplex-1.11.6  { db close }                                          {}
do_test multiplex-1.11.7  { sqlite3_multiplex_shutdown }                        {SQLITE_OK}

do_test multiplex-1.12.1  { sqlite3_multiplex_initialize "" 1 }                 {SQLITE_OK}
do_test multiplex-1.12.2  { sqlite3 db test.db }                                {}
do_test multiplex-1.12.3  { execsql { SELECT multiplex_control(1, 0); } }       {SQLITE_OK}
do_test multiplex-1.12.4  { execsql { SELECT multiplex_control(1, 1); } }       {SQLITE_OK}
do_test multiplex-1.12.5  { execsql { SELECT multiplex_control(1, -1); } }      {SQLITE_OK}
do_test multiplex-1.12.6  { db close }                                          {}
do_test multiplex-1.12.7  { sqlite3_multiplex_shutdown }                        {SQLITE_OK}

#-------------------------------------------------------------------------
# Some simple warm-body tests with a single database file in rollback 
# mode:
#
#   multiplex-2.1.*: Test simple writing to a multiplex file.
#
#   multiplex-2.2.*: More writing.
#
#   multiplex-2.3.*: Open and close a second db.
#
#   multiplex-2.4.*: Try to shutdown the multiplex system before closing the db
#                file. Check that this fails and the multiplex system still works
#                afterwards. Then close the database and successfully shut
#                down the multiplex system.
#
#   multiplex-2.5.*: More reading/writing.
#
#   multiplex-2.6.*: More reading/writing with varying small chunk sizes, as
#                well as varying journal mode.
#
#   multiplex-2.7.*: Disable/enable tests.
#

sqlite3_multiplex_initialize "" 1
multiplex_set db main 32768 16

do_test multiplex-2.1.2 {
  sqlite3 db test.db
  execsql {
................................................................................
    do_test multiplex-2.6.99.$sz.$jmode {
      db close
      sqlite3_multiplex_shutdown
    } {SQLITE_OK}

  }
}

do_test multiplex-2.7.1  { multiplex_delete test.db }                          {}
do_test multiplex-2.7.2  { sqlite3_multiplex_initialize "" 1 }                 {SQLITE_OK}
do_test multiplex-2.7.3  { sqlite3 db test.db }                                {}
do_test multiplex-2.7.4  { execsql { SELECT multiplex_control(2, 65536); } }   {SQLITE_OK}
do_test multiplex-2.7.5  { execsql { SELECT multiplex_control(1, 0); } }       {SQLITE_OK}
do_test multiplex-2.7.6 { 
  execsql { 
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(1, randomblob(1000));
  }
} {}
# verify only one file, and file size is less than chunks size
do_test multiplex-2.7.7  { expr ([file size [multiplex_name test.db 0]] < 65536) } {1}
do_test multiplex-2.7.8  { file exists [multiplex_name test.db 1] }                {0}
do_test multiplex-2.7.9 { 
  execsql { 
    INSERT INTO t1 VALUES(1, randomblob(65536));
  }
} {}
# verify only one file, and file size exceeds chunks size
do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1}
do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] }                {0}
do_test multiplex-2.7.12 { db close }                                              {}
do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown }                            {SQLITE_OK}


#-------------------------------------------------------------------------
# Try some tests with more than one connection to a database file. Still
# in rollback mode.
#
#   multiplex-3.1.*: Two connections to a single database file.
#