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

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

Overview
Comment:This branch merged with nx-devkit and from there into trunk. Was: In the multiplexor, instead of generating a unique file-name when SQLite opens a temp file, allow the underlying VFS to generate a different temp file for each chunk. Given the changes to the xFileSize method, it is no longer necessary for the different chunks of a temp file to use the same base name.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | experimental
Files: files | file ages | folders
SHA1: 199f52bceddfb032db8beb301aaa154eb4a32cc1
User & Date: dan 2011-12-13 12:10:19
Original Comment: In the multiplexor, instead of generating a unique file-name when SQLite opens a temp file, allow the underlying VFS to generate a different temp file for each chunk. Given the changes to the xFileSize method, it is no longer necessary for the different chunks of a temp file to use the same base name.
References
2011-12-13
15:02
Move the multiplexor changes in the experimental branch (check-ins [255d21499b] and [199f52bced]) into the nx-devkit branch. check-in: eb95d2f7 user: drh tags: nx-devkit
Context
2011-12-13
12:10
This branch merged with nx-devkit and from there into trunk. Was: In the multiplexor, instead of generating a unique file-name when SQLite opens a temp file, allow the underlying VFS to generate a different temp file for each chunk. Given the changes to the xFileSize method, it is no longer necessary for the different chunks of a temp file to use the same base name. Closed-Leaf check-in: 199f52bc user: dan tags: experimental
11:15
Change the way IO errors are handled in the xFileSize method of the multiplexor VFS. Add test file multiplex3.test. check-in: 255d2149 user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/test_multiplex.c.

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
...
457
458
459
460
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
490
491
492
493
494
495
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
...
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
....
1180
1181
1182
1183
1184
1185
1186

1187
1188
1189



1190
1191
1192
1193
1194
1195
1196
static int multiplexStrlen30(const char *z){
  const char *z2 = z;
  if( z==0 ) return 0;
  while( *z2 ){ z2++; }
  return 0x3fffffff & (int)(z2 - z);
}

/*
** Create a temporary file name in zBuf.  zBuf must be big enough to
** hold at pOrigVfs->mxPathname characters.  This function departs
** from the traditional temporary name generation in the os_win
** and os_unix VFS in several ways, but is necessary so that 
** the file name is known for temporary files (like those used 
** during vacuum.)
**
** N.B. This routine assumes your underlying VFS is ok with using
** "/" as a directory seperator.  This is the default for UNIXs
** and is allowed (even mixed) for most versions of Windows.
*/
static int multiplexGetTempname(sqlite3_vfs *pOrigVfs, int nBuf, char *zBuf){
  static char zChars[] =
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789";
  int i,j;
  int attempts = 0;
  int exists = 0;
  int rc = SQLITE_ERROR;

  /* Check that the output buffer is large enough for 
  ** pVfs->mxPathname characters.
  */
  if( pOrigVfs->mxPathname <= nBuf ){
    char *zTmp = sqlite3_malloc(pOrigVfs->mxPathname);
    if( zTmp==0 ) return SQLITE_NOMEM;

    /* sqlite3_temp_directory should always be less than
    ** pVfs->mxPathname characters.
    */
    sqlite3_snprintf(pOrigVfs->mxPathname,
                     zTmp,
                     "%s/",
                     sqlite3_temp_directory ? sqlite3_temp_directory : ".");
    rc = pOrigVfs->xFullPathname(pOrigVfs, zTmp, nBuf, zBuf);
    sqlite3_free(zTmp);
    if( rc ) return rc;

    /* Check that the output buffer is large enough for the temporary file 
    ** name.
    */
    j = multiplexStrlen30(zBuf);
    if( (j + 8 + 1 + 3 + 1) <= nBuf ){
      /* Make 3 attempts to generate a unique name. */
      do {
        attempts++;
        sqlite3_randomness(8, &zBuf[j]);
        for(i=0; i<8; i++){
          unsigned char uc = (unsigned char)zBuf[j+i];
          zBuf[j+i] = (char)zChars[uc%(sizeof(zChars)-1)];
        }
        memcpy(&zBuf[j+i], ".tmp", 5);
        rc = pOrigVfs->xAccess(pOrigVfs, zBuf, SQLITE_ACCESS_EXISTS, &exists);
      } while ( (rc==SQLITE_OK) && exists && (attempts<3) );
      if( rc==SQLITE_OK && exists ){
        rc = SQLITE_ERROR;
      }
    }
  }

  return rc;
}

/* Compute the filename for the iChunk-th chunk
*/
static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
  if( iChunk>=pGroup->nReal ){
    struct multiplexReal *p;
    p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
    if( p==0 ){
      return SQLITE_NOMEM;
    }
    memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
    pGroup->aReal = p;
    pGroup->nReal = iChunk+1;
  }
  if( pGroup->aReal[iChunk].z==0 ){
    char *z;
    int n = pGroup->nName;
    pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+4 );
    if( z==0 ){
      return SQLITE_NOMEM;
    }
    memcpy(z, pGroup->zName, n+1);
................................................................................
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  int nName;
  int sz;
  char *zToFree = 0;

  UNUSED_PARAMETER(pVfs);
  memset(pConn, 0, pVfs->szOsFile);


  /* We need to create a group structure and manage
  ** access to this group of files.
  */
  multiplexEnter();
  pMultiplexOpen = (multiplexConn*)pConn;

  /* If the second argument to this function is NULL, generate a 
  ** temporary file name to use.  This will be handled by the
  ** original xOpen method.  We just need to allocate space for
  ** it.
  */
  if( !zName ){
    zName = zToFree = sqlite3_malloc( pOrigVfs->mxPathname + 10 );
    if( zName==0 ){
      rc = SQLITE_NOMEM;
    }else{
      rc = multiplexGetTempname(pOrigVfs, pOrigVfs->mxPathname, zToFree);
    }
  }

  if( rc==SQLITE_OK ){
    /* allocate space for group */
    nName = multiplexStrlen30(zName);
    sz = sizeof(multiplexGroup)                             /* multiplexGroup */
       + nName + 1;                                         /* zName */
    pGroup = sqlite3_malloc( sz );
    if( pGroup==0 ){
      rc = SQLITE_NOMEM;
    }
  }

  if( rc==SQLITE_OK ){
    /* assign pointers to extra space allocated */
    char *p = (char *)&pGroup[1];
    pMultiplexOpen->pGroup = pGroup;
    memset(pGroup, 0, sz);
    pGroup->bEnabled = -1;
    pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;



    if( flags & SQLITE_OPEN_URI ){
      const char *zChunkSize;
      zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
      if( zChunkSize ){
        unsigned int n = 0;
        int i;
        for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
          n = n*10 + zChunkSize[i] - '0';
        }
        if( n>0 ){
          pGroup->szChunk = (n+0xffff)&~0xffff;
        }else{
          /* A zero or negative chunksize disabled the multiplexor */
          pGroup->bEnabled = 0;
        }
      }
    }
    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;
    rc = multiplexSubFilename(pGroup, 1);
    if( rc==SQLITE_OK ){
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
    }
    if( pSubOpen ){
      int exists, rc2, rc3;
................................................................................
    for(i=0; rc==SQLITE_OK; i++){
      sqlite3_file *pSubOpen = 0;
      int exists = 0;
      rc = multiplexSubFilename(pGroup, i);
      if( rc!=SQLITE_OK ) break;
      if( pGroup->nReal>i && pGroup->aReal[i].p!=0 ){
        exists = 1;
      }else{
        const char *zReal = pGroup->aReal[i].z;
        rc = pOrigVfs->xAccess(pOrigVfs, zReal, SQLITE_ACCESS_EXISTS, &exists);
      }
      if( exists==0 ){
        /* stop at first "gap" or IO error. */
        break;
      }
................................................................................
  UNUSED_PARAMETER(objv);

  pResult = Tcl_NewObj();
  multiplexEnter();
  for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
    pGroupTerm = Tcl_NewObj();


    pGroup->zName[pGroup->nName] = '\0';
    Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewStringObj(pGroup->zName, -1));



    Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewIntObj(pGroup->nName));
    Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewIntObj(pGroup->flags));

    /* count number of chunks with open handles */
    for(i=0; i<pGroup->nReal; i++){







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













|







 







>







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


|










|

<


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







 







|







 







>
|
|

>
>
>







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
...
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406














407
408
409
410
411
412
413
414
415
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
446
447
448
449
450
451
452
453
454
...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
....
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
static int multiplexStrlen30(const char *z){
  const char *z2 = z;
  if( z==0 ) return 0;
  while( *z2 ){ z2++; }
  return 0x3fffffff & (int)(z2 - z);
}


































































/* Compute the filename for the iChunk-th chunk
*/
static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
  if( iChunk>=pGroup->nReal ){
    struct multiplexReal *p;
    p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
    if( p==0 ){
      return SQLITE_NOMEM;
    }
    memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
    pGroup->aReal = p;
    pGroup->nReal = iChunk+1;
  }
  if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
    char *z;
    int n = pGroup->nName;
    pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+4 );
    if( z==0 ){
      return SQLITE_NOMEM;
    }
    memcpy(z, pGroup->zName, n+1);
................................................................................
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;   /* Real VFS */
  int nName;
  int sz;
  char *zToFree = 0;

  UNUSED_PARAMETER(pVfs);
  memset(pConn, 0, pVfs->szOsFile);
  assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );

  /* We need to create a group structure and manage
  ** access to this group of files.
  */
  multiplexEnter();
  pMultiplexOpen = (multiplexConn*)pConn;















  if( rc==SQLITE_OK ){
    /* allocate space for group */
    nName = zName ? multiplexStrlen30(zName) : 0;
    sz = sizeof(multiplexGroup)                             /* multiplexGroup */
       + nName + 1;                                         /* zName */
    pGroup = sqlite3_malloc( sz );
    if( pGroup==0 ){
      rc = SQLITE_NOMEM;
    }
  }

  if( rc==SQLITE_OK ){
    /* assign pointers to extra space allocated */
    memset(pGroup, 0, sz);
    pMultiplexOpen->pGroup = pGroup;

    pGroup->bEnabled = -1;
    pGroup->szChunk = SQLITE_MULTIPLEX_CHUNK_SIZE;

    if( zName ){
      char *p = (char *)&pGroup[1];
      if( flags & SQLITE_OPEN_URI ){
        const char *zChunkSize;
        zChunkSize = sqlite3_uri_parameter(zName, "chunksize");
        if( zChunkSize ){
          unsigned int n = 0;
          int i;
          for(i=0; zChunkSize[i]>='0' && zChunkSize[i]<='9'; i++){
            n = n*10 + zChunkSize[i] - '0';
          }
          if( n>0 ){
            pGroup->szChunk = (n+0xffff)&~0xffff;
          }else{
            /* A zero or negative chunksize disabled the multiplexor */
            pGroup->bEnabled = 0;
          }
        }
      }
      pGroup->zName = p;

      memcpy(pGroup->zName, zName, nName+1);
      pGroup->nName = nName;
    }
    pGroup->flags = flags;
    rc = multiplexSubFilename(pGroup, 1);
    if( rc==SQLITE_OK ){
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
    }
    if( pSubOpen ){
      int exists, rc2, rc3;
................................................................................
    for(i=0; rc==SQLITE_OK; i++){
      sqlite3_file *pSubOpen = 0;
      int exists = 0;
      rc = multiplexSubFilename(pGroup, i);
      if( rc!=SQLITE_OK ) break;
      if( pGroup->nReal>i && pGroup->aReal[i].p!=0 ){
        exists = 1;
      }else if( (pGroup->flags & SQLITE_OPEN_DELETEONCLOSE)==0 ){
        const char *zReal = pGroup->aReal[i].z;
        rc = pOrigVfs->xAccess(pOrigVfs, zReal, SQLITE_ACCESS_EXISTS, &exists);
      }
      if( exists==0 ){
        /* stop at first "gap" or IO error. */
        break;
      }
................................................................................
  UNUSED_PARAMETER(objv);

  pResult = Tcl_NewObj();
  multiplexEnter();
  for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
    pGroupTerm = Tcl_NewObj();

    if( pGroup->zName ){
      pGroup->zName[pGroup->nName] = '\0';
      Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewStringObj(pGroup->zName, -1));
    }else{
      Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
    }
    Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewIntObj(pGroup->nName));
    Tcl_ListObjAppendElement(interp, pGroupTerm,
          Tcl_NewIntObj(pGroup->flags));

    /* count number of chunks with open handles */
    for(i=0; i<pGroup->nReal; i++){