AryaWu/sqlite
0
1/*2** 2010 October 283**4** The author disclaims copyright to this source code. In place of5** a legal notice, here is a blessing:6**7** May you do good and not evil.8** May you find forgiveness for yourself and forgive others.9** May you share freely, never taking more than you give.10**11*************************************************************************12**13** This file contains a VFS "shim" - a layer that sits in between the14** pager and the real VFS - that breaks up a very large database file15** into two or more smaller files on disk. This is useful, for example,16** in order to support large, multi-gigabyte databases on older filesystems17** that limit the maximum file size to 2 GiB.18**19** USAGE:20**21** Compile this source file and link it with your application. Then22** at start-time, invoke the following procedure:23**24** int sqlite3_multiplex_initialize(25** const char *zOrigVfsName, // The underlying real VFS26** int makeDefault // True to make multiplex the default VFS27** );28**29** The procedure call above will create and register a new VFS shim named30** "multiplex". The multiplex VFS will use the VFS named by zOrigVfsName to31** do the actual disk I/O. (The zOrigVfsName parameter may be NULL, in32** which case the default VFS at the moment sqlite3_multiplex_initialize()33** is called will be used as the underlying real VFS.)34**35** If the makeDefault parameter is TRUE then multiplex becomes the new36** default VFS. Otherwise, you can use the multiplex VFS by specifying37** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing38** URI filenames and adding "vfs=multiplex" as a parameter to the filename39** URI.40**41** The multiplex VFS allows databases up to 32 GiB in size. But it splits42** the files up into smaller pieces, so that they will work even on43** filesystems that do not support large files. The default chunk size44** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be45** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE46** macro. Use the "chunksize=NNNN" query parameter with a URI filename47** in order to select an alternative chunk size for individual connections48** at run-time.49*/50#include "sqlite3.h"51#include <string.h>52#include <assert.h>53#include <stdlib.h>54#include "test_multiplex.h"55 56#ifndef SQLITE_CORE57 #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */58#endif59#include "sqlite3ext.h"60 61/*62** These should be defined to be the same as the values in63** sqliteInt.h. They are defined separately here so that64** the multiplex VFS shim can be built as a loadable65** module.66*/67#define UNUSED_PARAMETER(x) (void)(x)68#define MAX_PAGE_SIZE 0x1000069#define DEFAULT_SECTOR_SIZE 0x100070 71/* Maximum chunk number */72#define MX_CHUNK_NUMBER 29973 74/* First chunk for rollback journal files */75#define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 40076#define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 70077 78 79/************************ Shim Definitions ******************************/80 81#ifndef SQLITE_MULTIPLEX_VFS_NAME82# define SQLITE_MULTIPLEX_VFS_NAME "multiplex"83#endif84 85/* This is the limit on the chunk size. It may be changed by calling86** the xFileControl() interface. It will be rounded up to a87** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.88*/89#ifndef SQLITE_MULTIPLEX_CHUNK_SIZE90# define SQLITE_MULTIPLEX_CHUNK_SIZE 214741811291#endif92 93/* This used to be the default limit on number of chunks, but94** it is no longer enforced. There is currently no limit to the95** number of chunks.96**97** May be changed by calling the xFileControl() interface.98*/99#ifndef SQLITE_MULTIPLEX_MAX_CHUNKS100# define SQLITE_MULTIPLEX_MAX_CHUNKS 12101#endif102 103/************************ Object Definitions ******************************/104 105/* Forward declaration of all object types */106typedef struct multiplexGroup multiplexGroup;107typedef struct multiplexConn multiplexConn;108 109/*110** A "multiplex group" is a collection of files that collectively111** makeup a single SQLite DB file. This allows the size of the DB112** to exceed the limits imposed by the file system.113**114** There is an instance of the following object for each defined multiplex115** group.116*/117struct multiplexGroup {118 struct multiplexReal { /* For each chunk */119 sqlite3_file *p; /* Handle for the chunk */120 char *z; /* Name of this chunk */121 } *aReal; /* list of all chunks */122 int nReal; /* Number of chunks */123 char *zName; /* Base filename of this group */124 int nName; /* Length of base filename */125 int flags; /* Flags used for original opening */126 unsigned int szChunk; /* Chunk size used for this group */127 unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */128 unsigned char bTruncate; /* TRUE to enable truncation of databases */129};130 131/*132** An instance of the following object represents each open connection133** to a file that is multiplex'ed. This object is a134** subclass of sqlite3_file. The sqlite3_file object for the underlying135** VFS is appended to this structure.136*/137struct multiplexConn {138 sqlite3_file base; /* Base class - must be first */139 multiplexGroup *pGroup; /* The underlying group of files */140};141 142/************************* Global Variables **********************************/143/*144** All global variables used by this file are containing within the following145** gMultiplex structure.146*/147static struct {148 /* The pOrigVfs is the real, original underlying VFS implementation.149 ** Most operations pass-through to the real VFS. This value is read-only150 ** during operation. It is only modified at start-time and thus does not151 ** require a mutex.152 */153 sqlite3_vfs *pOrigVfs;154 155 /* The sThisVfs is the VFS structure used by this shim. It is initialized156 ** at start-time and thus does not require a mutex157 */158 sqlite3_vfs sThisVfs;159 160 /* The sIoMethods defines the methods used by sqlite3_file objects161 ** associated with this shim. It is initialized at start-time and does162 ** not require a mutex.163 **164 ** When the underlying VFS is called to open a file, it might return165 ** either a version 1 or a version 2 sqlite3_file object. This shim166 ** has to create a wrapper sqlite3_file of the same version. Hence167 ** there are two I/O method structures, one for version 1 and the other168 ** for version 2.169 */170 sqlite3_io_methods sIoMethodsV1;171 sqlite3_io_methods sIoMethodsV2;172 173 /* True when this shim has been initialized.174 */175 int isInitialized;176} gMultiplex;177 178/************************* Utility Routines *********************************/179/*180** Compute a string length that is limited to what can be stored in181** lower 30 bits of a 32-bit signed integer.182**183** The value returned will never be negative. Nor will it ever be greater184** than the actual length of the string. For very long strings (greater185** than 1GiB) the value returned might be less than the true string length.186*/187static int multiplexStrlen30(const char *z){188 const char *z2 = z;189 if( z==0 ) return 0;190 while( *z2 ){ z2++; }191 return 0x3fffffff & (int)(z2 - z);192}193 194/*195** Generate the file-name for chunk iChunk of the group with base name196** zBase. The file-name is written to buffer zOut before returning. Buffer197** zOut must be allocated by the caller so that it is at least (nBase+5)198** bytes in size, where nBase is the length of zBase, not including the199** nul-terminator.200**201** If iChunk is 0 (or 400 - the number for the first journal file chunk),202** the output is a copy of the input string. Otherwise, if203** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain204** a "." character, then the output is a copy of the input string with the205** three-digit zero-padded decimal representation if iChunk appended to it.206** For example:207**208** zBase="test.db", iChunk=4 -> zOut="test.db004"209**210** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains211** a "." character, then everything after the "." is replaced by the212** three-digit representation of iChunk.213**214** zBase="test.db", iChunk=4 -> zOut="test.004"215**216** The output buffer string is terminated by 2 0x00 bytes. This makes it safe217** to pass to sqlite3_uri_parameter() and similar.218*/219static void multiplexFilename(220 const char *zBase, /* Filename for chunk 0 */221 int nBase, /* Size of zBase in bytes (without \0) */222 int flags, /* Flags used to open file */223 int iChunk, /* Chunk to generate filename for */224 char *zOut /* Buffer to write generated name to */225){226 int n = nBase;227 memcpy(zOut, zBase, n+1);228 if( iChunk!=0 && iChunk<=MX_CHUNK_NUMBER ){229#ifdef SQLITE_ENABLE_8_3_NAMES230 int i;231 for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}232 if( i>=n-4 ) n = i+1;233 if( flags & SQLITE_OPEN_MAIN_JOURNAL ){234 /* The extensions on overflow files for main databases are 001, 002,235 ** 003 and so forth. To avoid name collisions, add 400 to the236 ** extensions of journal files so that they are 401, 402, 403, ....237 */238 iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;239 }else if( flags & SQLITE_OPEN_WAL ){240 /* To avoid name collisions, add 700 to the241 ** extensions of WAL files so that they are 701, 702, 703, ....242 */243 iChunk += SQLITE_MULTIPLEX_WAL_8_3_OFFSET;244 }245#endif246 sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);247 n += 3;248 }249 250 assert( zOut[n]=='\0' );251 zOut[n+1] = '\0';252}253 254/* Compute the filename for the iChunk-th chunk255*/256static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){257 if( iChunk>=pGroup->nReal ){258 struct multiplexReal *p;259 p = sqlite3_realloc64(pGroup->aReal, (iChunk+1)*sizeof(*p));260 if( p==0 ){261 return SQLITE_NOMEM;262 }263 memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));264 pGroup->aReal = p;265 pGroup->nReal = iChunk+1;266 }267 if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){268 char *z;269 int n = pGroup->nName;270 z = sqlite3_malloc64( n+5 );271 if( z==0 ){272 return SQLITE_NOMEM;273 }274 multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);275 pGroup->aReal[iChunk].z = (char*)sqlite3_create_filename(z,"","",0,0);276 sqlite3_free(z);277 if( pGroup->aReal[iChunk].z==0 ) return SQLITE_NOMEM;278 }279 return SQLITE_OK;280}281 282/* Translate an sqlite3_file* that is really a multiplexGroup* into283** the sqlite3_file* for the underlying original VFS.284**285** For chunk 0, the pGroup->flags determines whether or not a new file286** is created if it does not already exist. For chunks 1 and higher, the287** file is created only if createFlag is 1.288*/289static sqlite3_file *multiplexSubOpen(290 multiplexGroup *pGroup, /* The multiplexor group */291 int iChunk, /* Which chunk to open. 0==original file */292 int *rc, /* Result code in and out */293 int *pOutFlags, /* Output flags */294 int createFlag /* True to create if iChunk>0 */295){296 sqlite3_file *pSubOpen = 0;297 sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */298 299#ifdef SQLITE_ENABLE_8_3_NAMES300 /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are301 ** part of a database journal are named db.401, db.402, and so on. A302 ** database may therefore not grow to larger than 400 chunks. Attempting303 ** to open chunk 401 indicates the database is full. */304 if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){305 sqlite3_log(SQLITE_FULL, "multiplexed chunk overflow: %s", pGroup->zName);306 *rc = SQLITE_FULL;307 return 0;308 }309#endif310 311 *rc = multiplexSubFilename(pGroup, iChunk);312 if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){313 int flags, bExists;314 flags = pGroup->flags;315 if( createFlag ){316 flags |= SQLITE_OPEN_CREATE;317 }else if( iChunk==0 ){318 /* Fall through */319 }else if( pGroup->aReal[iChunk].z==0 ){320 return 0;321 }else{322 *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,323 SQLITE_ACCESS_EXISTS, &bExists);324 if( *rc || !bExists ){325 if( *rc ){326 sqlite3_log(*rc, "multiplexor.xAccess failure on %s",327 pGroup->aReal[iChunk].z);328 }329 return 0;330 }331 flags &= ~SQLITE_OPEN_CREATE;332 }333 pSubOpen = sqlite3_malloc64( pOrigVfs->szOsFile );334 if( pSubOpen==0 ){335 *rc = SQLITE_IOERR_NOMEM;336 return 0;337 }338 pGroup->aReal[iChunk].p = pSubOpen;339 *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,340 flags, pOutFlags);341 if( (*rc)!=SQLITE_OK ){342 sqlite3_log(*rc, "multiplexor.xOpen failure on %s",343 pGroup->aReal[iChunk].z);344 sqlite3_free(pSubOpen);345 pGroup->aReal[iChunk].p = 0;346 return 0;347 }348 }349 return pSubOpen;350}351 352/*353** Return the size, in bytes, of chunk number iChunk. If that chunk354** does not exist, then return 0. This function does not distinguish between355** non-existent files and zero-length files.356*/357static sqlite3_int64 multiplexSubSize(358 multiplexGroup *pGroup, /* The multiplexor group */359 int iChunk, /* Which chunk to open. 0==original file */360 int *rc /* Result code in and out */361){362 sqlite3_file *pSub;363 sqlite3_int64 sz = 0;364 365 if( *rc ) return 0;366 pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);367 if( pSub==0 ) return 0;368 *rc = pSub->pMethods->xFileSize(pSub, &sz);369 return sz;370} 371 372/*373** This is the implementation of the multiplex_control() SQL function.374*/375static void multiplexControlFunc(376 sqlite3_context *context,377 int argc,378 sqlite3_value **argv379){380 int rc = SQLITE_OK;381 sqlite3 *db = sqlite3_context_db_handle(context);382 int op = 0;383 int iVal;384 385 if( !db || argc!=2 ){386 rc = SQLITE_ERROR;387 }else{388 /* extract params */389 op = sqlite3_value_int(argv[0]);390 iVal = sqlite3_value_int(argv[1]);391 /* map function op to file_control op */392 switch( op ){393 case 1:394 op = MULTIPLEX_CTRL_ENABLE;395 break;396 case 2:397 op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;398 break;399 case 3:400 op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;401 break;402 default:403 rc = SQLITE_NOTFOUND;404 break;405 }406 }407 if( rc==SQLITE_OK ){408 rc = sqlite3_file_control(db, 0, op, &iVal);409 }410 sqlite3_result_error_code(context, rc);411}412 413/*414** This is the entry point to register the auto-extension for the415** multiplex_control() function.416*/417static int multiplexFuncInit(418 sqlite3 *db,419 char **pzErrMsg,420 const sqlite3_api_routines *pApi421){422 int rc;423 rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,424 0, multiplexControlFunc, 0, 0);425 return rc;426}427 428/*429** Close a single sub-file in the connection group.430*/431static void multiplexSubClose(432 multiplexGroup *pGroup,433 int iChunk,434 sqlite3_vfs *pOrigVfs435){436 sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;437 if( pSubOpen ){438 pSubOpen->pMethods->xClose(pSubOpen);439 if( pOrigVfs && pGroup->aReal[iChunk].z ){440 pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);441 }442 sqlite3_free(pGroup->aReal[iChunk].p);443 }444 sqlite3_free_filename(pGroup->aReal[iChunk].z);445 memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));446}447 448/*449** Deallocate memory held by a multiplexGroup450*/451static void multiplexFreeComponents(multiplexGroup *pGroup){452 int i;453 for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }454 sqlite3_free(pGroup->aReal);455 pGroup->aReal = 0;456 pGroup->nReal = 0;457}458 459 460/************************* VFS Method Wrappers *****************************/461 462/*463** This is the xOpen method used for the "multiplex" VFS.464**465** Most of the work is done by the underlying original VFS. This method466** simply links the new file into the appropriate multiplex group if it is a467** file that needs to be tracked.468*/469static int multiplexOpen(470 sqlite3_vfs *pVfs, /* The multiplex VFS */471 const char *zName, /* Name of file to be opened */472 sqlite3_file *pConn, /* Fill in this file descriptor */473 int flags, /* Flags to control the opening */474 int *pOutFlags /* Flags showing results of opening */475){476 int rc = SQLITE_OK; /* Result code */477 multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */478 multiplexGroup *pGroup = 0; /* Corresponding multiplexGroup object */479 sqlite3_file *pSubOpen = 0; /* Real file descriptor */480 sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */481 int nName = 0;482 int sz = 0;483 char *zToFree = 0;484 485 UNUSED_PARAMETER(pVfs);486 memset(pConn, 0, pVfs->szOsFile);487 assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );488 489 /* We need to create a group structure and manage490 ** access to this group of files.491 */492 pMultiplexOpen = (multiplexConn*)pConn;493 494 if( rc==SQLITE_OK ){495 /* allocate space for group */496 nName = zName ? multiplexStrlen30(zName) : 0;497 sz = sizeof(multiplexGroup) /* multiplexGroup */498 + nName + 1; /* zName */499 pGroup = sqlite3_malloc64( sz );500 if( pGroup==0 ){501 rc = SQLITE_NOMEM;502 }503 }504 505 if( rc==SQLITE_OK ){506 const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0;507 /* assign pointers to extra space allocated */508 memset(pGroup, 0, sz);509 pMultiplexOpen->pGroup = pGroup;510 pGroup->bEnabled = (unsigned char)-1;511 pGroup->bTruncate = (unsigned char)sqlite3_uri_boolean(zUri, "truncate",512 (flags & SQLITE_OPEN_MAIN_DB)==0);513 pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize",514 SQLITE_MULTIPLEX_CHUNK_SIZE);515 pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;516 if( zName ){517 char *p = (char *)&pGroup[1];518 pGroup->zName = p;519 memcpy(pGroup->zName, zName, nName+1);520 pGroup->nName = nName;521 }522 if( pGroup->bEnabled ){523 /* Make sure that the chunksize is such that the pending byte does not524 ** falls at the end of a chunk. A region of up to 64K following525 ** the pending byte is never written, so if the pending byte occurs526 ** near the end of a chunk, that chunk will be too small. */527#ifndef SQLITE_OMIT_WSD528 extern int sqlite3PendingByte;529#else530 int sqlite3PendingByte = 0x40000000;531#endif532 while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){533 pGroup->szChunk += 65536;534 }535 }536 pGroup->flags = (flags & ~SQLITE_OPEN_URI);537 rc = multiplexSubFilename(pGroup, 1);538 if( rc==SQLITE_OK ){539 pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);540 if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;541 }542 if( rc==SQLITE_OK ){543 sqlite3_int64 sz64;544 545 rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz64);546 if( rc==SQLITE_OK && zName ){547 int bExists;548 if( flags & SQLITE_OPEN_SUPER_JOURNAL ){549 pGroup->bEnabled = 0;550 }else551 if( sz64==0 ){552 if( flags & SQLITE_OPEN_MAIN_JOURNAL ){553 /* If opening a main journal file and the first chunk is zero554 ** bytes in size, delete any subsequent chunks from the555 ** file-system. */556 int iChunk = 1;557 do {558 rc = pOrigVfs->xAccess(pOrigVfs,559 pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists560 );561 if( rc==SQLITE_OK && bExists ){562 rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);563 if( rc==SQLITE_OK ){564 rc = multiplexSubFilename(pGroup, ++iChunk);565 }566 }567 }while( rc==SQLITE_OK && bExists );568 }569 }else{570 /* If the first overflow file exists and if the size of the main file571 ** is different from the chunk size, that means the chunk size is set572 ** set incorrectly. So fix it.573 **574 ** Or, if the first overflow file does not exist and the main file is575 ** larger than the chunk size, that means the chunk size is too small.576 ** But we have no way of determining the intended chunk size, so577 ** just disable the multiplexor all together.578 */579 rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,580 SQLITE_ACCESS_EXISTS, &bExists);581 bExists = multiplexSubSize(pGroup, 1, &rc)>0;582 if( rc==SQLITE_OK && bExists && sz64==(sz64&0xffff0000) && sz64>0583 && sz64!=pGroup->szChunk ){584 pGroup->szChunk = (int)sz64;585 }else if( rc==SQLITE_OK && !bExists && sz64>pGroup->szChunk ){586 pGroup->bEnabled = 0;587 }588 }589 }590 }591 592 if( rc==SQLITE_OK ){593 if( pSubOpen->pMethods->iVersion==1 ){594 pConn->pMethods = &gMultiplex.sIoMethodsV1;595 }else{596 pConn->pMethods = &gMultiplex.sIoMethodsV2;597 }598 }else{599 multiplexFreeComponents(pGroup);600 sqlite3_free(pGroup);601 }602 }603 sqlite3_free(zToFree);604 return rc;605}606 607/*608** This is the xDelete method used for the "multiplex" VFS.609** It attempts to delete the filename specified.610*/611static int multiplexDelete(612 sqlite3_vfs *pVfs, /* The multiplex VFS */613 const char *zName, /* Name of file to delete */614 int syncDir615){616 int rc;617 sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */618 rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);619 if( rc==SQLITE_OK ){620 /* If the main chunk was deleted successfully, also delete any subsequent621 ** chunks - starting with the last (highest numbered).622 */623 int nName = (int)strlen(zName);624 char *z;625 z = sqlite3_malloc64(nName + 5);626 if( z==0 ){627 rc = SQLITE_IOERR_NOMEM;628 }else{629 int iChunk = 0;630 int bExists;631 do{632 multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);633 rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);634 }while( rc==SQLITE_OK && bExists );635 while( rc==SQLITE_OK && iChunk>1 ){636 multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);637 rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);638 }639 if( rc==SQLITE_OK ){640 iChunk = 0;641 do{642 multiplexFilename(zName, nName, SQLITE_OPEN_WAL, ++iChunk, z);643 rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);644 }while( rc==SQLITE_OK && bExists );645 while( rc==SQLITE_OK && iChunk>1 ){646 multiplexFilename(zName, nName, SQLITE_OPEN_WAL, --iChunk, z);647 rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);648 }649 }650 }651 sqlite3_free(z);652 }653 return rc;654}655 656static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){657 return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);658}659static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){660 return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);661}662static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){663 return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);664}665static void multiplexDlError(sqlite3_vfs *a, int b, char *c){666 gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);667}668static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){669 return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);670}671static void multiplexDlClose(sqlite3_vfs *a, void *b){672 gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);673}674static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){675 return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);676}677static int multiplexSleep(sqlite3_vfs *a, int b){678 return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);679}680static int multiplexCurrentTime(sqlite3_vfs *a, double *b){681 return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);682}683static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){684 if( gMultiplex.pOrigVfs->xGetLastError ){685 return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);686 }else{687 return 0;688 }689}690static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){691 return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);692}693 694/************************ I/O Method Wrappers *******************************/695 696/* xClose requests get passed through to the original VFS.697** We loop over all open chunk handles and close them.698** The group structure for this file is unlinked from699** our list of groups and freed.700*/701static int multiplexClose(sqlite3_file *pConn){702 multiplexConn *p = (multiplexConn*)pConn;703 multiplexGroup *pGroup = p->pGroup;704 int rc = SQLITE_OK;705 multiplexFreeComponents(pGroup);706 sqlite3_free(pGroup);707 return rc;708}709 710/* Pass xRead requests thru to the original VFS after711** determining the correct chunk to operate on.712** Break up reads across chunk boundaries.713*/714static int multiplexRead(715 sqlite3_file *pConn,716 void *pBuf,717 int iAmt,718 sqlite3_int64 iOfst719){720 multiplexConn *p = (multiplexConn*)pConn;721 multiplexGroup *pGroup = p->pGroup;722 int rc = SQLITE_OK;723 if( !pGroup->bEnabled ){724 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);725 if( pSubOpen==0 ){726 rc = SQLITE_IOERR_READ;727 }else{728 rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);729 }730 }else{731 while( iAmt > 0 ){732 int i = (int)(iOfst / pGroup->szChunk);733 sqlite3_file *pSubOpen;734 pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);735 if( pSubOpen ){736 int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;737 if( extra<0 ) extra = 0;738 iAmt -= extra;739 rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,740 iOfst % pGroup->szChunk);741 if( rc!=SQLITE_OK ) break;742 pBuf = (char *)pBuf + iAmt;743 iOfst += iAmt;744 iAmt = extra;745 }else{746 rc = SQLITE_IOERR_READ;747 break;748 }749 }750 }751 752 return rc;753}754 755/* Pass xWrite requests thru to the original VFS after756** determining the correct chunk to operate on.757** Break up writes across chunk boundaries.758*/759static int multiplexWrite(760 sqlite3_file *pConn,761 const void *pBuf,762 int iAmt,763 sqlite3_int64 iOfst764){765 multiplexConn *p = (multiplexConn*)pConn;766 multiplexGroup *pGroup = p->pGroup;767 int rc = SQLITE_OK;768 if( !pGroup->bEnabled ){769 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);770 if( pSubOpen==0 ){771 rc = SQLITE_IOERR_WRITE;772 }else{773 rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);774 }775 }else{776 while( rc==SQLITE_OK && iAmt>0 ){777 int i = (int)(iOfst / pGroup->szChunk);778 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);779 if( pSubOpen ){780 int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -781 pGroup->szChunk;782 if( extra<0 ) extra = 0;783 iAmt -= extra;784 rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,785 iOfst % pGroup->szChunk);786 pBuf = (char *)pBuf + iAmt;787 iOfst += iAmt;788 iAmt = extra;789 }790 }791 }792 return rc;793}794 795/* Pass xTruncate requests thru to the original VFS after796** determining the correct chunk to operate on. Delete any797** chunks above the truncate mark.798*/799static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){800 multiplexConn *p = (multiplexConn*)pConn;801 multiplexGroup *pGroup = p->pGroup;802 int rc = SQLITE_OK;803 if( !pGroup->bEnabled ){804 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);805 if( pSubOpen==0 ){806 rc = SQLITE_IOERR_TRUNCATE;807 }else{808 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);809 }810 }else{811 int i;812 int iBaseGroup = (int)(size / pGroup->szChunk);813 sqlite3_file *pSubOpen;814 sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */815 /* delete the chunks above the truncate limit */816 for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){817 if( pGroup->bTruncate ){818 multiplexSubClose(pGroup, i, pOrigVfs);819 }else{820 pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);821 if( pSubOpen ){822 rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);823 }824 }825 }826 if( rc==SQLITE_OK ){827 pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);828 if( pSubOpen ){829 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);830 }831 }832 if( rc ) rc = SQLITE_IOERR_TRUNCATE;833 }834 return rc;835}836 837/* Pass xSync requests through to the original VFS without change838*/839static int multiplexSync(sqlite3_file *pConn, int flags){840 multiplexConn *p = (multiplexConn*)pConn;841 multiplexGroup *pGroup = p->pGroup;842 int rc = SQLITE_OK;843 int i;844 for(i=0; i<pGroup->nReal; i++){845 sqlite3_file *pSubOpen = pGroup->aReal[i].p;846 if( pSubOpen ){847 int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);848 if( rc2!=SQLITE_OK ) rc = rc2;849 }850 }851 return rc;852}853 854/* Pass xFileSize requests through to the original VFS.855** Aggregate the size of all the chunks before returning.856*/857static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){858 multiplexConn *p = (multiplexConn*)pConn;859 multiplexGroup *pGroup = p->pGroup;860 int rc = SQLITE_OK;861 int i;862 if( !pGroup->bEnabled ){863 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);864 if( pSubOpen==0 ){865 rc = SQLITE_IOERR_FSTAT;866 }else{867 rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);868 }869 }else{870 *pSize = 0;871 for(i=0; rc==SQLITE_OK; i++){872 sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);873 if( sz==0 ) break;874 *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;875 }876 }877 return rc;878}879 880/* Pass xLock requests through to the original VFS unchanged.881*/882static int multiplexLock(sqlite3_file *pConn, int lock){883 multiplexConn *p = (multiplexConn*)pConn;884 int rc;885 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);886 if( pSubOpen ){887 return pSubOpen->pMethods->xLock(pSubOpen, lock);888 }889 return SQLITE_BUSY;890}891 892/* Pass xUnlock requests through to the original VFS unchanged.893*/894static int multiplexUnlock(sqlite3_file *pConn, int lock){895 multiplexConn *p = (multiplexConn*)pConn;896 int rc;897 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);898 if( pSubOpen ){899 return pSubOpen->pMethods->xUnlock(pSubOpen, lock);900 }901 return SQLITE_IOERR_UNLOCK;902}903 904/* Pass xCheckReservedLock requests through to the original VFS unchanged.905*/906static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){907 multiplexConn *p = (multiplexConn*)pConn;908 int rc;909 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);910 if( pSubOpen ){911 return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);912 }913 return SQLITE_IOERR_CHECKRESERVEDLOCK;914}915 916/* Pass xFileControl requests through to the original VFS unchanged,917** except for any MULTIPLEX_CTRL_* requests here.918*/919static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){920 multiplexConn *p = (multiplexConn*)pConn;921 multiplexGroup *pGroup = p->pGroup;922 int rc = SQLITE_ERROR;923 sqlite3_file *pSubOpen;924 925 if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;926 switch( op ){927 case MULTIPLEX_CTRL_ENABLE:928 if( pArg ) {929 int bEnabled = *(int *)pArg;930 pGroup->bEnabled = (unsigned char)bEnabled;931 rc = SQLITE_OK;932 }933 break;934 case MULTIPLEX_CTRL_SET_CHUNK_SIZE:935 if( pArg ) {936 unsigned int szChunk = *(unsigned*)pArg;937 if( szChunk<1 ){938 rc = SQLITE_MISUSE;939 }else{940 /* Round up to nearest multiple of MAX_PAGE_SIZE. */941 szChunk = (szChunk + (MAX_PAGE_SIZE-1));942 szChunk &= ~(MAX_PAGE_SIZE-1);943 pGroup->szChunk = szChunk;944 rc = SQLITE_OK;945 }946 }947 break;948 case MULTIPLEX_CTRL_SET_MAX_CHUNKS:949 rc = SQLITE_OK;950 break;951 case SQLITE_FCNTL_SIZE_HINT:952 case SQLITE_FCNTL_CHUNK_SIZE:953 /* no-op these */954 rc = SQLITE_OK;955 break;956 case SQLITE_FCNTL_PRAGMA: {957 char **aFcntl = (char**)pArg;958 /*959 ** EVIDENCE-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA960 ** file control is an array of pointers to strings (char**) in which the961 ** second element of the array is the name of the pragma and the third962 ** element is the argument to the pragma or NULL if the pragma has no963 ** argument.964 */965 if( aFcntl[1] && sqlite3_strnicmp(aFcntl[1],"multiplex_",10)==0 ){966 sqlite3_int64 sz = 0;967 (void)multiplexFileSize(pConn, &sz);968 /*969 ** PRAGMA multiplex_truncate=BOOLEAN;970 ** PRAGMA multiplex_truncate;971 **972 ** Turn the multiplexor truncate feature on or off. Return either973 ** "on" or "off" to indicate the new setting. If the BOOLEAN argument974 ** is omitted, just return the current value for the truncate setting.975 */976 if( sqlite3_stricmp(aFcntl[1],"multiplex_truncate")==0 ){977 if( aFcntl[2] && aFcntl[2][0] ){978 if( sqlite3_stricmp(aFcntl[2], "on")==0979 || sqlite3_stricmp(aFcntl[2], "1")==0 ){980 pGroup->bTruncate = 1;981 }else982 if( sqlite3_stricmp(aFcntl[2], "off")==0983 || sqlite3_stricmp(aFcntl[2], "0")==0 ){984 pGroup->bTruncate = 0;985 }986 }987 /* EVIDENCE-OF: R-27806-26076 The handler for an SQLITE_FCNTL_PRAGMA988 ** file control can optionally make the first element of the char**989 ** argument point to a string obtained from sqlite3_mprintf() or the990 ** equivalent and that string will become the result of the pragma991 ** or the error message if the pragma fails.992 */993 aFcntl[0] = sqlite3_mprintf(pGroup->bTruncate ? "on" : "off");994 rc = SQLITE_OK;995 break;996 }997 /*998 ** PRAGMA multiplex_enabled;999 **1000 ** Return 0 or 1 depending on whether the multiplexor is enabled or1001 ** disabled, respectively.1002 */1003 if( sqlite3_stricmp(aFcntl[1],"multiplex_enabled")==0 ){1004 aFcntl[0] = sqlite3_mprintf("%d", pGroup->bEnabled!=0);1005 rc = SQLITE_OK;1006 break;1007 }1008 /*1009 ** PRAGMA multiplex_chunksize;1010 **1011 ** Return the chunksize for the multiplexor, or no-op if the1012 ** multiplexor is not active.1013 */1014 if( sqlite3_stricmp(aFcntl[1],"multiplex_chunksize")==01015 && pGroup->bEnabled1016 ){1017 aFcntl[0] = sqlite3_mprintf("%u", pGroup->szChunk);1018 rc = SQLITE_OK;1019 break;1020 }1021 /*1022 ** PRAGMA multiplex_filecount;1023 **1024 ** Return the number of disk files currently in use by the1025 ** multiplexor. This should be the total database size size1026 ** divided by the chunksize and rounded up.1027 */1028 if( sqlite3_stricmp(aFcntl[1],"multiplex_filecount")==0 ){1029 int n = 0;1030 int ii;1031 for(ii=0; ii<pGroup->nReal; ii++){1032 if( pGroup->aReal[ii].p!=0 ) n++;1033 }1034 aFcntl[0] = sqlite3_mprintf("%d", n);1035 rc = SQLITE_OK;1036 break;1037 }1038 }1039 /* If the multiplexor does not handle the pragma, pass it through1040 ** into the default case. */1041 }1042 default:1043 pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);1044 if( pSubOpen ){1045 rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);1046 if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){1047 *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);1048 }1049 }1050 break;1051 }1052 return rc;1053}1054 1055/* Pass xSectorSize requests through to the original VFS unchanged.1056*/1057static int multiplexSectorSize(sqlite3_file *pConn){1058 multiplexConn *p = (multiplexConn*)pConn;1059 int rc;1060 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1061 if( pSubOpen && pSubOpen->pMethods->xSectorSize ){1062 return pSubOpen->pMethods->xSectorSize(pSubOpen);1063 }1064 return DEFAULT_SECTOR_SIZE;1065}1066 1067/* Pass xDeviceCharacteristics requests through to the original VFS unchanged.1068*/1069static int multiplexDeviceCharacteristics(sqlite3_file *pConn){1070 multiplexConn *p = (multiplexConn*)pConn;1071 int rc;1072 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1073 if( pSubOpen ){1074 return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);1075 }1076 return 0;1077}1078 1079/* Pass xShmMap requests through to the original VFS unchanged.1080*/1081static int multiplexShmMap(1082 sqlite3_file *pConn, /* Handle open on database file */1083 int iRegion, /* Region to retrieve */1084 int szRegion, /* Size of regions */1085 int bExtend, /* True to extend file if necessary */1086 void volatile **pp /* OUT: Mapped memory */1087){1088 multiplexConn *p = (multiplexConn*)pConn;1089 int rc;1090 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1091 if( pSubOpen ){1092 return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);1093 }1094 return SQLITE_IOERR;1095}1096 1097/* Pass xShmLock requests through to the original VFS unchanged.1098*/1099static int multiplexShmLock(1100 sqlite3_file *pConn, /* Database file holding the shared memory */1101 int ofst, /* First lock to acquire or release */1102 int n, /* Number of locks to acquire or release */1103 int flags /* What to do with the lock */1104){1105 multiplexConn *p = (multiplexConn*)pConn;1106 int rc;1107 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1108 if( pSubOpen ){1109 return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);1110 }1111 return SQLITE_BUSY;1112}1113 1114/* Pass xShmBarrier requests through to the original VFS unchanged.1115*/1116static void multiplexShmBarrier(sqlite3_file *pConn){1117 multiplexConn *p = (multiplexConn*)pConn;1118 int rc;1119 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1120 if( pSubOpen ){1121 pSubOpen->pMethods->xShmBarrier(pSubOpen);1122 }1123}1124 1125/* Pass xShmUnmap requests through to the original VFS unchanged.1126*/1127static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){1128 multiplexConn *p = (multiplexConn*)pConn;1129 int rc;1130 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);1131 if( pSubOpen ){1132 return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);1133 }1134 return SQLITE_OK;1135}1136 1137/************************** Public Interfaces *****************************/1138/*1139** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()1140**1141** Use the VFS named zOrigVfsName as the VFS that does the actual work.1142** Use the default if zOrigVfsName==NULL.1143**1144** The multiplex VFS shim is named "multiplex". It will become the default1145** VFS if makeDefault is non-zero.1146**1147** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once1148** during start-up.1149*/1150int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){1151 sqlite3_vfs *pOrigVfs;1152 if( gMultiplex.isInitialized ) return SQLITE_MISUSE;1153 pOrigVfs = sqlite3_vfs_find(zOrigVfsName);1154 if( pOrigVfs==0 ) return SQLITE_ERROR;1155 assert( pOrigVfs!=&gMultiplex.sThisVfs );1156 gMultiplex.isInitialized = 1;1157 gMultiplex.pOrigVfs = pOrigVfs;1158 gMultiplex.sThisVfs = *pOrigVfs;1159 gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);1160 gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;1161 gMultiplex.sThisVfs.xOpen = multiplexOpen;1162 gMultiplex.sThisVfs.xDelete = multiplexDelete;1163 gMultiplex.sThisVfs.xAccess = multiplexAccess;1164 gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;1165 gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;1166 gMultiplex.sThisVfs.xDlError = multiplexDlError;1167 gMultiplex.sThisVfs.xDlSym = multiplexDlSym;1168 gMultiplex.sThisVfs.xDlClose = multiplexDlClose;1169 gMultiplex.sThisVfs.xRandomness = multiplexRandomness;1170 gMultiplex.sThisVfs.xSleep = multiplexSleep;1171 gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;1172 gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;1173 gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;1174 1175 gMultiplex.sIoMethodsV1.iVersion = 1;1176 gMultiplex.sIoMethodsV1.xClose = multiplexClose;1177 gMultiplex.sIoMethodsV1.xRead = multiplexRead;1178 gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;1179 gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;1180 gMultiplex.sIoMethodsV1.xSync = multiplexSync;1181 gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;1182 gMultiplex.sIoMethodsV1.xLock = multiplexLock;1183 gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;1184 gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;1185 gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;1186 gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;1187 gMultiplex.sIoMethodsV1.xDeviceCharacteristics =1188 multiplexDeviceCharacteristics;1189 gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;1190 gMultiplex.sIoMethodsV2.iVersion = 2;1191 gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;1192 gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;1193 gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;1194 gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;1195 sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);1196 1197 sqlite3_auto_extension((void(*)(void))multiplexFuncInit);1198 1199 return SQLITE_OK;1200}