/ Check-in [da118e02]
Login

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

Overview
Comment:Merge the nx-devkit changes into trunk. This includes the new SQLITE_FCNTL_VFSNAME file-control.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:da118e02c0576ce16f7a26663f59413316223d55
User & Date: drh 2011-12-14 18:33:13
Context
2011-12-15
17:44
Add stdio support to the quota VFS. check-in: 322bd15f user: drh tags: trunk
2011-12-14
18:33
Merge the nx-devkit changes into trunk. This includes the new SQLITE_FCNTL_VFSNAME file-control. check-in: da118e02 user: drh tags: trunk
18:28
Merge the VFSNAME file-control addition into the nx-devkit branch. check-in: 08c1dc51 user: drh tags: nx-devkit
00:37
Treat a zero return value from the Win32 APIs MultiByteToWideChar and WideCharToMultiByte as an error condition. check-in: c65e5a36 user: mistachkin tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/main.c.

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#endif

  /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT
  ** compile-time option.
  */
#ifdef SQLITE_EXTRA_INIT
  if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
    int SQLITE_EXTRA_INIT(void);
    rc = SQLITE_EXTRA_INIT();
  }
#endif

  return rc;
}

/*







|
|







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#endif

  /* Do extra initialization steps requested by the SQLITE_EXTRA_INIT
  ** compile-time option.
  */
#ifdef SQLITE_EXTRA_INIT
  if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
    int SQLITE_EXTRA_INIT(const char*);
    rc = SQLITE_EXTRA_INIT(0);
  }
#endif

  return rc;
}

/*

Changes to src/os_unix.c.

202
203
204
205
206
207
208

209
210
211
212
213
214
215
....
3529
3530
3531
3532
3533
3534
3535




3536
3537
3538
3539
3540
3541
3542
....
4556
4557
4558
4559
4560
4561
4562

4563
4564
4565
4566
4567
4568
4569
/*
** The unixFile structure is subclass of sqlite3_file specific to the unix
** VFS implementations.
*/
typedef struct unixFile unixFile;
struct unixFile {
  sqlite3_io_methods const *pMethod;  /* Always the first entry */

  unixInodeInfo *pInode;              /* Info about locks on this inode */
  int h;                              /* The file descriptor */
  unsigned char eFileLock;            /* The type of lock held on this fd */
  unsigned char ctrlFlags;            /* Behavioral bits.  UNIXFILE_* flags */
  int lastErrno;                      /* The unix errno from last I/O error */
  void *lockingContext;               /* Locking style specific state */
  UnixUnusedFd *pUnused;              /* Pre-allocated UnixUnusedFd */
................................................................................
      }else if( bPersist==0 ){
        pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
      }else{
        pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
      }
      return SQLITE_OK;
    }




#ifndef NDEBUG
    /* The pager calls this method to signal that it has done
    ** a rollback and that the database is therefore unchanged and
    ** it hence it is OK for the transaction change counter to be
    ** unchanged.
    */
    case SQLITE_FCNTL_DB_UNCHANGED: {
................................................................................
#endif

  /* No locking occurs in temporary files */
  assert( zFilename!=0 || noLock );

  OSTRACE(("OPEN    %-3d %s\n", h, zFilename));
  pNew->h = h;

  pNew->zPath = zFilename;
  if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
    pNew->ctrlFlags = UNIXFILE_EXCL;
  }else{
    pNew->ctrlFlags = 0;
  }
  if( isReadOnly ){







>







 







>
>
>
>







 







>







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
....
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
....
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
4573
4574
4575
/*
** The unixFile structure is subclass of sqlite3_file specific to the unix
** VFS implementations.
*/
typedef struct unixFile unixFile;
struct unixFile {
  sqlite3_io_methods const *pMethod;  /* Always the first entry */
  sqlite3_vfs *pVfs;                  /* The VFS that created this unixFile */
  unixInodeInfo *pInode;              /* Info about locks on this inode */
  int h;                              /* The file descriptor */
  unsigned char eFileLock;            /* The type of lock held on this fd */
  unsigned char ctrlFlags;            /* Behavioral bits.  UNIXFILE_* flags */
  int lastErrno;                      /* The unix errno from last I/O error */
  void *lockingContext;               /* Locking style specific state */
  UnixUnusedFd *pUnused;              /* Pre-allocated UnixUnusedFd */
................................................................................
      }else if( bPersist==0 ){
        pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
      }else{
        pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
      }
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_VFSNAME: {
      *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
      return SQLITE_OK;
    }
#ifndef NDEBUG
    /* The pager calls this method to signal that it has done
    ** a rollback and that the database is therefore unchanged and
    ** it hence it is OK for the transaction change counter to be
    ** unchanged.
    */
    case SQLITE_FCNTL_DB_UNCHANGED: {
................................................................................
#endif

  /* No locking occurs in temporary files */
  assert( zFilename!=0 || noLock );

  OSTRACE(("OPEN    %-3d %s\n", h, zFilename));
  pNew->h = h;
  pNew->pVfs = pVfs;
  pNew->zPath = zFilename;
  if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
    pNew->ctrlFlags = UNIXFILE_EXCL;
  }else{
    pNew->ctrlFlags = 0;
  }
  if( isReadOnly ){

Changes to src/os_win.c.

2163
2164
2165
2166
2167
2168
2169




2170
2171
2172
2173
2174
2175
2176
      int bPersist = *(int*)pArg;
      if( bPersist<0 ){
        *(int*)pArg = pFile->bPersistWal;
      }else{
        pFile->bPersistWal = bPersist!=0;
      }
      return SQLITE_OK;




    }
    case SQLITE_FCNTL_SYNC_OMITTED: {
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_WIN32_AV_RETRY: {
      int *a = (int*)pArg;
      if( a[0]>0 ){







>
>
>
>







2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
      int bPersist = *(int*)pArg;
      if( bPersist<0 ){
        *(int*)pArg = pFile->bPersistWal;
      }else{
        pFile->bPersistWal = bPersist!=0;
      }
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_VFSNAME: {
      *(char**)pArg = sqlite3_mprintf("win32");
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_SYNC_OMITTED: {
      return SQLITE_OK;
    }
    case SQLITE_FCNTL_WIN32_AV_RETRY: {
      int *a = (int*)pArg;
      if( a[0]>0 ){

Changes to src/shell.c.

1401
1402
1403
1404
1405
1406
1407

1408
1409
1410
1411
1412
1413
1414
....
2343
2344
2345
2346
2347
2348
2349












2350
2351
2352
2353
2354
2355
2356
  ".separator STRING      Change separator used by output mode and .import\n"
  ".show                  Show the current values for various settings\n"
  ".stats ON|OFF          Turn stats on or off\n"
  ".tables ?TABLE?        List names of tables\n"
  "                         If TABLE specified, only list tables matching\n"
  "                         LIKE pattern TABLE.\n"
  ".timeout MS            Try opening locked tables for MS milliseconds\n"

  ".width NUM1 NUM2 ...   Set column widths for \"column\" mode\n"
;

static char zTimerHelp[] =
  ".timer ON|OFF          Turn the CPU timer measurement on or off\n"
;

................................................................................
    enableTimer = booleanValue(azArg[1]);
  }else
  
  if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
    printf("SQLite %s %s\n" /*extra-version-info*/,
        sqlite3_libversion(), sqlite3_sourceid());
  }else













  if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
    int j;
    assert( nArg<=ArraySize(azArg) );
    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
      p->colWidth[j-1] = atoi(azArg[j]);
    }







>







 







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







1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
....
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
  ".separator STRING      Change separator used by output mode and .import\n"
  ".show                  Show the current values for various settings\n"
  ".stats ON|OFF          Turn stats on or off\n"
  ".tables ?TABLE?        List names of tables\n"
  "                         If TABLE specified, only list tables matching\n"
  "                         LIKE pattern TABLE.\n"
  ".timeout MS            Try opening locked tables for MS milliseconds\n"
  ".vfsname ?AUX?         Print the name of the VFS stack\n"
  ".width NUM1 NUM2 ...   Set column widths for \"column\" mode\n"
;

static char zTimerHelp[] =
  ".timer ON|OFF          Turn the CPU timer measurement on or off\n"
;

................................................................................
    enableTimer = booleanValue(azArg[1]);
  }else
  
  if( c=='v' && strncmp(azArg[0], "version", n)==0 ){
    printf("SQLite %s %s\n" /*extra-version-info*/,
        sqlite3_libversion(), sqlite3_sourceid());
  }else

  if( c=='v' && strncmp(azArg[0], "vfsname", n)==0 ){
    const char *zDbName = nArg==2 ? azArg[1] : "main";
    char *zVfsName = 0;
    if( p->db ){
      sqlite3_file_control(p->db, zDbName, SQLITE_FCNTL_VFSNAME, &zVfsName);
      if( zVfsName ){
        printf("%s\n", zVfsName);
        sqlite3_free(zVfsName);
      }
    }
  }else

  if( c=='w' && strncmp(azArg[0], "width", n)==0 && nArg>1 ){
    int j;
    assert( nArg<=ArraySize(azArg) );
    for(j=1; j<nArg && j<ArraySize(p->colWidth); j++){
      p->colWidth[j-1] = atoi(azArg[j]);
    }

Changes to src/sqlite.h.in.

767
768
769
770
771
772
773











774
775
776
777
778
779
780
781
782
783
784
785

786
787
788
789
790
791
792
** WAL mode.  If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
**
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current 
** transaction. This is used by VACUUM operations.











*/
#define SQLITE_FCNTL_LOCKSTATE        1
#define SQLITE_GET_LOCKPROXYFILE      2
#define SQLITE_SET_LOCKPROXYFILE      3
#define SQLITE_LAST_ERRNO             4
#define SQLITE_FCNTL_SIZE_HINT        5
#define SQLITE_FCNTL_CHUNK_SIZE       6
#define SQLITE_FCNTL_FILE_POINTER     7
#define SQLITE_FCNTL_SYNC_OMITTED     8
#define SQLITE_FCNTL_WIN32_AV_RETRY   9
#define SQLITE_FCNTL_PERSIST_WAL     10
#define SQLITE_FCNTL_OVERWRITE       11


/*
** CAPI3REF: Mutex Handle
**
** The mutex module within SQLite defines [sqlite3_mutex] to be an
** abstract type for a mutex object.  The SQLite core never looks
** at the internal representation of an [sqlite3_mutex].  It only







>
>
>
>
>
>
>
>
>
>
>












>







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
** WAL mode.  If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
**
** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
** a write transaction to indicate that, unless it is rolled back for some
** reason, the entire database file will be overwritten by the current 
** transaction. This is used by VACUUM operations.
**
** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
** all [VFSes] in the VFS stack.  The names are of all VFS shims and the
** final bottom-level VFS are written into memory obtained from 
** [sqlite3_malloc()] and the result is stored in the char* variable
** that the fourth parameter of [sqlite3_file_control()] points to.
** The caller is responsible for freeing the memory when done.  As with
** all file-control actions, there is no guarantee that this will actually
** do anything.  Callers should initialize the char* variable to a NULL
** pointer in case this file-control is not implemented.  This file-control
** is intended for diagnostic use only.
*/
#define SQLITE_FCNTL_LOCKSTATE        1
#define SQLITE_GET_LOCKPROXYFILE      2
#define SQLITE_SET_LOCKPROXYFILE      3
#define SQLITE_LAST_ERRNO             4
#define SQLITE_FCNTL_SIZE_HINT        5
#define SQLITE_FCNTL_CHUNK_SIZE       6
#define SQLITE_FCNTL_FILE_POINTER     7
#define SQLITE_FCNTL_SYNC_OMITTED     8
#define SQLITE_FCNTL_WIN32_AV_RETRY   9
#define SQLITE_FCNTL_PERSIST_WAL     10
#define SQLITE_FCNTL_OVERWRITE       11
#define SQLITE_FCNTL_VFSNAME         12

/*
** CAPI3REF: Mutex Handle
**
** The mutex module within SQLite defines [sqlite3_mutex] to be an
** abstract type for a mutex object.  The SQLite core never looks
** at the internal representation of an [sqlite3_mutex].  It only

Changes to src/test1.c.

5231
5232
5233
5234
5235
5236
5237

































5238
5239
5240
5241
5242
5243
5244
....
6056
6057
6058
6059
6060
6061
6062

6063
6064
6065
6066
6067
6068
6069
  if( Tcl_GetIntFromObj(interp, objv[2], &bPersist) ) return TCL_ERROR;
  rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, (void*)&bPersist);
  sqlite3_snprintf(sizeof(z), z, "%d %d", rc, bPersist);
  Tcl_AppendResult(interp, z, (char*)0);
  return TCL_OK;  
}



































/*
** tclcmd:   sqlite3_vfs_list
**
**   Return a tcl list containing the names of all registered vfs's.
*/
static int vfs_list(
................................................................................
     { "file_control_test",          file_control_test,   0   },
     { "file_control_lasterrno_test", file_control_lasterrno_test,  0   },
     { "file_control_lockproxy_test", file_control_lockproxy_test,  0   },
     { "file_control_chunksize_test", file_control_chunksize_test,  0   },
     { "file_control_sizehint_test",  file_control_sizehint_test,   0   },
     { "file_control_win32_av_retry", file_control_win32_av_retry,  0   },
     { "file_control_persist_wal",    file_control_persist_wal,     0   },

     { "sqlite3_vfs_list",           vfs_list,     0   },
     { "sqlite3_create_function_v2", test_create_function_v2, 0 },

     /* Functions from os.h */
#ifndef SQLITE_OMIT_UTF16
     { "add_test_collate",        test_collate, 0            },
     { "add_test_collate_needed", test_collate_needed, 0     },







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







 







>







5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
....
6089
6090
6091
6092
6093
6094
6095
6096
6097
6098
6099
6100
6101
6102
6103
  if( Tcl_GetIntFromObj(interp, objv[2], &bPersist) ) return TCL_ERROR;
  rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_PERSIST_WAL, (void*)&bPersist);
  sqlite3_snprintf(sizeof(z), z, "%d %d", rc, bPersist);
  Tcl_AppendResult(interp, z, (char*)0);
  return TCL_OK;  
}


/*
** tclcmd:   file_control_vfsname DB ?AUXDB?
**
** Return a string that describes the stack of VFSes.
*/
static int file_control_vfsname(
  ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
  int objc,              /* Number of arguments */
  Tcl_Obj *CONST objv[]  /* Command arguments */
){
  sqlite3 *db;
  const char *zDbName = "main";
  char *zVfsName = 0;

  if( objc!=2 && objc!=3 ){
    Tcl_AppendResult(interp, "wrong # args: should be \"",
        Tcl_GetStringFromObj(objv[0], 0), " DB ?AUXDB?", 0);
    return TCL_ERROR;
  }
  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
    return TCL_ERROR;
  }
  if( objc==3 ){
    zDbName = Tcl_GetString(objv[2]);
  }
  sqlite3_file_control(db, zDbName, SQLITE_FCNTL_VFSNAME,(void*)&zVfsName);
  Tcl_AppendResult(interp, zVfsName, (char*)0);
  sqlite3_free(zVfsName);
  return TCL_OK;  
}


/*
** tclcmd:   sqlite3_vfs_list
**
**   Return a tcl list containing the names of all registered vfs's.
*/
static int vfs_list(
................................................................................
     { "file_control_test",          file_control_test,   0   },
     { "file_control_lasterrno_test", file_control_lasterrno_test,  0   },
     { "file_control_lockproxy_test", file_control_lockproxy_test,  0   },
     { "file_control_chunksize_test", file_control_chunksize_test,  0   },
     { "file_control_sizehint_test",  file_control_sizehint_test,   0   },
     { "file_control_win32_av_retry", file_control_win32_av_retry,  0   },
     { "file_control_persist_wal",    file_control_persist_wal,     0   },
     { "file_control_vfsname",        file_control_vfsname,         0   },
     { "sqlite3_vfs_list",           vfs_list,     0   },
     { "sqlite3_create_function_v2", test_create_function_v2, 0 },

     /* Functions from os.h */
#ifndef SQLITE_OMIT_UTF16
     { "add_test_collate",        test_collate, 0            },
     { "add_test_collate_needed", test_collate_needed, 0     },

Changes to src/test_multiplex.c.

77
78
79
80
81
82
83



84
85
86
87
88
89
90
...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
260
261
262
263
264
265
266












267
268
269
270
271
272
273
...
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
...
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
...
841
842
843
844
845
846
847



848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X)      SQLITE_OK
#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X)     ((void)(X),1)
#define sqlite3_mutex_notheld(X)  ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */





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

#ifndef SQLITE_MULTIPLEX_VFS_NAME
# define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
#endif

................................................................................
    if( iChunk>0 ){
#ifdef SQLITE_ENABLE_8_3_NAMES
      int i;
      for(i=n-1; i>0 && i>=n-4 && z[i]!='.'; i--){}
      if( i>=n-4 ) n = i+1;
      if( pGroup->flags & (SQLITE_OPEN_MAIN_JOURNAL|SQLITE_OPEN_TEMP_JOURNAL) ){
        /* The extensions on overflow files for main databases are 001, 002,
        ** 003 and so forth.  To avoid name collisions, add 100 to the 
        ** extensions of journal files so that they are 101, 102, 103, ....
        */
        iChunk += 100;
      }
#endif
      sqlite3_snprintf(4,&z[n],"%03d",iChunk);
    }
  }
  return SQLITE_OK;
}
................................................................................
  multiplexGroup *pGroup,
  int iChunk,
  int *rc,
  int *pOutFlags
){
  sqlite3_file *pSubOpen = 0;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */












  *rc = multiplexSubFilename(pGroup, iChunk);
  if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
    pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
    if( pSubOpen==0 ){
      *rc = SQLITE_NOMEM;
      return 0;
    }
................................................................................
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
    }
    if( pSubOpen ){
      int exists, rc2, rc3;
      sqlite3_int64 sz;

      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
      if( rc2==SQLITE_OK ){
        /* If the first overflow file exists and if the size of the main file
        ** is different from the chunk size, that means the chunk size is set
        ** set incorrectly.  So fix it.
        **
        ** Or, if the first overflow file does not exist and the main file is
        ** larger than the chunk size, that means the chunk size is too small.
        ** But we have no way of determining the intended chunk size, so 
................................................................................
    sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
    if( pSubOpen==0 ){
      rc = SQLITE_IOERR_WRITE;
    }else{
      rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
    }
  }else{
    while( iAmt > 0 ){
      int i = (int)(iOfst / pGroup->szChunk);
      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
      if( pSubOpen ){
        int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
                    pGroup->szChunk;
        if( extra<0 ) extra = 0;
        iAmt -= extra;
        rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
                                        iOfst % pGroup->szChunk);
        if( rc!=SQLITE_OK ) break;
        pBuf = (char *)pBuf + iAmt;
        iOfst += iAmt;
        iAmt = extra;
      }else{
        rc = SQLITE_IOERR_WRITE;
        break;
      }
    }
  }
  multiplexLeave();
  return rc;
}

................................................................................
      /* no-op these */
      rc = SQLITE_OK;
      break;
    default:
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
      if( pSubOpen ){
        rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);



      }
      break;
  }
  return rc;
}

/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int multiplexSectorSize(sqlite3_file *pConn){
  multiplexConn *p = (multiplexConn*)pConn;
  int rc;
  sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
  if( pSubOpen ){
    return pSubOpen->pMethods->xSectorSize(pSubOpen);
  }
  return DEFAULT_SECTOR_SIZE;
}

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







>
>
>







 







|
|

|







 







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







 







|







 







|









<



<
<
<







 







>
>
>












|







77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
...
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
...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
...
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
...
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X)      SQLITE_OK
#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X)     ((void)(X),1)
#define sqlite3_mutex_notheld(X)  ((void)(X),1)
#endif /* SQLITE_THREADSAFE==0 */

/* First chunk for rollback journal files */
#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400


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

#ifndef SQLITE_MULTIPLEX_VFS_NAME
# define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
#endif

................................................................................
    if( iChunk>0 ){
#ifdef SQLITE_ENABLE_8_3_NAMES
      int i;
      for(i=n-1; i>0 && i>=n-4 && z[i]!='.'; i--){}
      if( i>=n-4 ) n = i+1;
      if( pGroup->flags & (SQLITE_OPEN_MAIN_JOURNAL|SQLITE_OPEN_TEMP_JOURNAL) ){
        /* The extensions on overflow files for main databases are 001, 002,
        ** 003 and so forth.  To avoid name collisions, add 400 to the 
        ** extensions of journal files so that they are 401, 402, 403, ....
        */
        iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
      }
#endif
      sqlite3_snprintf(4,&z[n],"%03d",iChunk);
    }
  }
  return SQLITE_OK;
}
................................................................................
  multiplexGroup *pGroup,
  int iChunk,
  int *rc,
  int *pOutFlags
){
  sqlite3_file *pSubOpen = 0;
  sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs;        /* Real VFS */

#ifdef SQLITE_ENABLE_8_3_NAMES
  /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are 
  ** part of a database journal are named db.401, db.402, and so on. A 
  ** database may therefore not grow to larger than 400 chunks. Attempting
  ** to open chunk 401 indicates the database is full. */
  if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
    *rc = SQLITE_FULL;
    return 0;
  }
#endif

  *rc = multiplexSubFilename(pGroup, iChunk);
  if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
    pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
    if( pSubOpen==0 ){
      *rc = SQLITE_NOMEM;
      return 0;
    }
................................................................................
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags);
    }
    if( pSubOpen ){
      int exists, rc2, rc3;
      sqlite3_int64 sz;

      rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
      if( rc2==SQLITE_OK && zName ){
        /* If the first overflow file exists and if the size of the main file
        ** is different from the chunk size, that means the chunk size is set
        ** set incorrectly.  So fix it.
        **
        ** Or, if the first overflow file does not exist and the main file is
        ** larger than the chunk size, that means the chunk size is too small.
        ** But we have no way of determining the intended chunk size, so 
................................................................................
    sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
    if( pSubOpen==0 ){
      rc = SQLITE_IOERR_WRITE;
    }else{
      rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
    }
  }else{
    while( rc==SQLITE_OK && iAmt>0 ){
      int i = (int)(iOfst / pGroup->szChunk);
      sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL);
      if( pSubOpen ){
        int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
                    pGroup->szChunk;
        if( extra<0 ) extra = 0;
        iAmt -= extra;
        rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
                                        iOfst % pGroup->szChunk);

        pBuf = (char *)pBuf + iAmt;
        iOfst += iAmt;
        iAmt = extra;



      }
    }
  }
  multiplexLeave();
  return rc;
}

................................................................................
      /* no-op these */
      rc = SQLITE_OK;
      break;
    default:
      pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL);
      if( pSubOpen ){
        rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
        if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
         *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
        }
      }
      break;
  }
  return rc;
}

/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int multiplexSectorSize(sqlite3_file *pConn){
  multiplexConn *p = (multiplexConn*)pConn;
  int rc;
  sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL);
  if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
    return pSubOpen->pMethods->xSectorSize(pSubOpen);
  }
  return DEFAULT_SECTOR_SIZE;
}

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

Changes to src/test_osinst.c.

385
386
387
388
389
390
391
392




393
394
395
396
397
398
399
}

/*
** File control method. For custom operations on an vfslog-file.
*/
static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
  VfslogFile *p = (VfslogFile *)pFile;
  return p->pReal->pMethods->xFileControl(p->pReal, op, pArg);




}

/*
** Return the sector-size in bytes for an vfslog-file.
*/
static int vfslogSectorSize(sqlite3_file *pFile){
  int rc;







|
>
>
>
>







385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
}

/*
** File control method. For custom operations on an vfslog-file.
*/
static int vfslogFileControl(sqlite3_file *pFile, int op, void *pArg){
  VfslogFile *p = (VfslogFile *)pFile;
  int rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
    *(char**)pArg = sqlite3_mprintf("vfslog/%z", *(char**)pArg);
  }
  return rc;
}

/*
** Return the sector-size in bytes for an vfslog-file.
*/
static int vfslogSectorSize(sqlite3_file *pFile){
  int rc;

Changes to src/test_quota.c.

585
586
587
588
589
590
591
592




593
594
595
596
597
598
599
  return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}

/* Pass xFileControl requests through to the original VFS unchanged.
*/
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
  sqlite3_file *pSubOpen = quotaSubOpen(pConn);
  return pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);




}

/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int quotaSectorSize(sqlite3_file *pConn){
  sqlite3_file *pSubOpen = quotaSubOpen(pConn);
  return pSubOpen->pMethods->xSectorSize(pSubOpen);







|
>
>
>
>







585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
  return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
}

/* Pass xFileControl requests through to the original VFS unchanged.
*/
static int quotaFileControl(sqlite3_file *pConn, int op, void *pArg){
  sqlite3_file *pSubOpen = quotaSubOpen(pConn);
  int rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
    *(char**)pArg = sqlite3_mprintf("quota/%z", *(char**)pArg);
  }
  return rc;
}

/* Pass xSectorSize requests through to the original VFS unchanged.
*/
static int quotaSectorSize(sqlite3_file *pConn){
  sqlite3_file *pSubOpen = quotaSubOpen(pConn);
  return pSubOpen->pMethods->xSectorSize(pSubOpen);

Changes to src/test_vfstrace.c.

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
    case SQLITE_FCNTL_CHUNK_SIZE: {
      sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg);
      zOp = zBuf;
      break;
    }
    case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER";       break;
    case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED";       break;




    case 0xca093fa0:                zOp = "DB_UNCHANGED";       break;
    default: {
      sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
      zOp = zBuf;
      break;
    }
  }
  vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)",
                  pInfo->zVfsName, p->zFName, zOp);
  rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
  vfstrace_print_errcode(pInfo, " -> %s\n", rc);




  return rc;
}

/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){







>
>
>
>











>
>
>
>







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
    case SQLITE_FCNTL_CHUNK_SIZE: {
      sqlite3_snprintf(sizeof(zBuf), zBuf, "CHUNK_SIZE,%d", *(int*)pArg);
      zOp = zBuf;
      break;
    }
    case SQLITE_FCNTL_FILE_POINTER: zOp = "FILE_POINTER";       break;
    case SQLITE_FCNTL_SYNC_OMITTED: zOp = "SYNC_OMITTED";       break;
    case SQLITE_FCNTL_WIN32_AV_RETRY: zOp = "WIN32_AV_RETRY";   break;
    case SQLITE_FCNTL_PERSIST_WAL:  zOp = "PERSIST_WAL";        break;
    case SQLITE_FCNTL_OVERWRITE:    zOp = "OVERWRITE";          break;
    case SQLITE_FCNTL_VFSNAME:      zOp = "VFSNAME";            break;
    case 0xca093fa0:                zOp = "DB_UNCHANGED";       break;
    default: {
      sqlite3_snprintf(sizeof zBuf, zBuf, "%d", op);
      zOp = zBuf;
      break;
    }
  }
  vfstrace_printf(pInfo, "%s.xFileControl(%s,%s)",
                  pInfo->zVfsName, p->zFName, zOp);
  rc = p->pReal->pMethods->xFileControl(p->pReal, op, pArg);
  vfstrace_print_errcode(pInfo, " -> %s\n", rc);
  if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
    *(char**)pArg = sqlite3_mprintf("vfstrace.%s/%z",
                                    pInfo->zVfsName, *(char**)pArg);
  }
  return rc;
}

/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){

Changes to test/multiplex3.test.

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
..
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
  foreach f [glob -nocomplain sv_test.*] { forcedelete $f }
  foreach f [glob -nocomplain test.*]    { forcecopy $f "sv_$f" }
}
proc multiplex_restore_db {} {
  foreach f [glob -nocomplain test.*]    {forcedelete $f}
  foreach f [glob -nocomplain sv_test.*] {forcecopy $f [string range $f 3 end]} }


do_test 1.0 {
  multiplex_delete_db
  sqlite3 db file:test.db?8_3_names=1
  sqlite3_multiplex_control db main chunk_size [expr 256*1024]
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(randomblob(15), randomblob(2000));
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    --   2
................................................................................
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 128
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 256
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 512
  }
  set ::cksum1 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
  db close
  multiplex_save_db
} {}



do_faultsim_test 1 -prep {
  multiplex_restore_db
  sqlite3 db file:test.db?8_3_names=1
  sqlite3_multiplex_control db main chunk_size [expr 256*1024]
} -body {

  execsql "UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0"

} -test {
  faultsim_test_result {0 {}}
  if {$testrc!=0} {
    set cksum2 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
    if {$cksum2 != $::cksum1} { error "data mismatch" }
  }
}
































catch { db close }



sqlite3_multiplex_shutdown
finish_test







|
<







 







<
|
>
>





>
|
>








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


51
52
53
54
55
56
57
58

59
60
61
62
63
64
65
..
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
  foreach f [glob -nocomplain sv_test.*] { forcedelete $f }
  foreach f [glob -nocomplain test.*]    { forcecopy $f "sv_$f" }
}
proc multiplex_restore_db {} {
  foreach f [glob -nocomplain test.*]    {forcedelete $f}
  foreach f [glob -nocomplain sv_test.*] {forcecopy $f [string range $f 3 end]} }

proc setup_and_save_db {} {

  multiplex_delete_db
  sqlite3 db file:test.db?8_3_names=1
  sqlite3_multiplex_control db main chunk_size [expr 256*1024]
  execsql {
    CREATE TABLE t1(a PRIMARY KEY, b);
    INSERT INTO t1 VALUES(randomblob(15), randomblob(2000));
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    --   2
................................................................................
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 128
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 256
    INSERT INTO t1 SELECT randomblob(15), randomblob(2000) FROM t1;    -- 512
  }
  set ::cksum1 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
  db close
  multiplex_save_db

}

do_test 1.0 { setup_and_save_db } {}
do_faultsim_test 1 -prep {
  multiplex_restore_db
  sqlite3 db file:test.db?8_3_names=1
  sqlite3_multiplex_control db main chunk_size [expr 256*1024]
} -body {
  execsql {
    UPDATE t1 SET a=randomblob(12), b=randomblob(1500) WHERE (rowid%32)=0
  }
} -test {
  faultsim_test_result {0 {}}
  if {$testrc!=0} {
    set cksum2 [execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a}]
    if {$cksum2 != $::cksum1} { error "data mismatch" }
  }
}

#-------------------------------------------------------------------------
# The following tests verify that hot-journal rollback works. As follows:
#
#   1. Create a large database.
#   2. Set the pager cache to be very small.
#   3. Open a transaction. 
#   4. Run the following 100 times:
#      a. Update a row.
#      b. Copy all files on disk to a new db location, including the journal.
#      c. Verify that the new db can be opened and that the content matches
#         the database created in step 1 (proving the journal was rolled
#         back).

do_test 2.0 { 
  setup_and_save_db
  multiplex_restore_db
  sqlite3 db file:test.db?8_3_names=1
  execsql { PRAGMA cache_size = 10 }
  execsql { BEGIN }
} {}

for {set iTest 1} {$iTest<=100} {incr iTest} {
  do_test 2.$iTest {
    execsql { 
      UPDATE t1 SET a=randomblob(12), b=randomblob(1400) WHERE rowid=5*$iTest
    }
    foreach f [glob -nocomplain test.*] {forcecopy $f "xx_$f"}
    sqlite3 db2 file:xx_test.db?8_3_names=1
    execsql {SELECT md5sum(a, b) FROM t1 ORDER BY a} db2
  } $::cksum1

  db2 close
}

catch { db close }
sqlite3_multiplex_shutdown
finish_test

Changes to test/quota.test.

10
11
12
13
14
15
16


17
18
19
20
21
22
23
..
69
70
71
72
73
74
75



76
77
78
79
80
81
82
#***********************************************************************
#

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



db close

do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR}
do_test quota-1.2 { sqlite3_quota_initialize "" 1 }        {SQLITE_OK}
do_test quota-1.3 { sqlite3_quota_initialize "" 1 }        {SQLITE_MISUSE}
do_test quota-1.4 { sqlite3_quota_shutdown }               {SQLITE_OK}

................................................................................
  execsql {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, randomblob(1100));
    INSERT INTO t1 VALUES(2, randomblob(1100));
  }
  set ::quota
} {}



do_test quota-2.1.3 { file size test.db } {4096}
do_test quota-2.1.4 {
  catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {1 {database or disk is full}}
do_test quota-2.1.5 { set ::quota } {4096 5120}

set ::quota_request_ok 1







>
>







 







>
>
>







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
..
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#***********************************************************************
#

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

unset -nocomplain defaultVfs
set defaultVfs [file_control_vfsname db]
db close

do_test quota-1.1 { sqlite3_quota_initialize nosuchvfs 1 } {SQLITE_ERROR}
do_test quota-1.2 { sqlite3_quota_initialize "" 1 }        {SQLITE_OK}
do_test quota-1.3 { sqlite3_quota_initialize "" 1 }        {SQLITE_MISUSE}
do_test quota-1.4 { sqlite3_quota_shutdown }               {SQLITE_OK}

................................................................................
  execsql {
    CREATE TABLE t1(a, b);
    INSERT INTO t1 VALUES(1, randomblob(1100));
    INSERT INTO t1 VALUES(2, randomblob(1100));
  }
  set ::quota
} {}
do_test quota-2.1.2.1 {
  file_control_vfsname db
} quota/$defaultVfs
do_test quota-2.1.3 { file size test.db } {4096}
do_test quota-2.1.4 {
  catchsql { INSERT INTO t1 VALUES(3, randomblob(1100)) }
} {1 {database or disk is full}}
do_test quota-2.1.5 { set ::quota } {4096 5120}

set ::quota_request_ok 1