Annotation of embedaddon/sqlite3/src/test_multiplex.c, revision 1.1
1.1 ! misho 1: /*
! 2: ** 2010 October 28
! 3: **
! 4: ** The author disclaims copyright to this source code. In place of
! 5: ** 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 the
! 14: ** pager and the real VFS - that breaks up a very large database file
! 15: ** into two or more smaller files on disk. This is useful, for example,
! 16: ** in order to support large, multi-gigabyte databases on older filesystems
! 17: ** that limit the maximum file size to 2 GiB.
! 18: **
! 19: ** USAGE:
! 20: **
! 21: ** Compile this source file and link it with your application. Then
! 22: ** at start-time, invoke the following procedure:
! 23: **
! 24: ** int sqlite3_multiplex_initialize(
! 25: ** const char *zOrigVfsName, // The underlying real VFS
! 26: ** int makeDefault // True to make multiplex the default VFS
! 27: ** );
! 28: **
! 29: ** The procedure call above will create and register a new VFS shim named
! 30: ** "multiplex". The multiplex VFS will use the VFS named by zOrigVfsName to
! 31: ** do the actual disk I/O. (The zOrigVfsName parameter may be NULL, in
! 32: ** 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 new
! 36: ** default VFS. Otherwise, you can use the multiplex VFS by specifying
! 37: ** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing
! 38: ** URI filenames and adding "vfs=multiplex" as a parameter to the filename
! 39: ** URI.
! 40: **
! 41: ** The multiplex VFS allows databases up to 32 GiB in size. But it splits
! 42: ** the files up into smaller pieces, so that they will work even on
! 43: ** filesystems that do not support large files. The default chunk size
! 44: ** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be
! 45: ** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE
! 46: ** macro. Use the "chunksize=NNNN" query parameter with a URI filename
! 47: ** in order to select an alternative chunk size for individual connections
! 48: ** 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_CORE
! 57: #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
! 58: #endif
! 59: #include "sqlite3ext.h"
! 60:
! 61: /*
! 62: ** These should be defined to be the same as the values in
! 63: ** sqliteInt.h. They are defined seperately here so that
! 64: ** the multiplex VFS shim can be built as a loadable
! 65: ** module.
! 66: */
! 67: #define UNUSED_PARAMETER(x) (void)(x)
! 68: #define MAX_PAGE_SIZE 0x10000
! 69: #define DEFAULT_SECTOR_SIZE 0x1000
! 70:
! 71: /*
! 72: ** For a build without mutexes, no-op the mutex calls.
! 73: */
! 74: #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
! 75: #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
! 76: #define sqlite3_mutex_free(X)
! 77: #define sqlite3_mutex_enter(X)
! 78: #define sqlite3_mutex_try(X) SQLITE_OK
! 79: #define sqlite3_mutex_leave(X)
! 80: #define sqlite3_mutex_held(X) ((void)(X),1)
! 81: #define sqlite3_mutex_notheld(X) ((void)(X),1)
! 82: #endif /* SQLITE_THREADSAFE==0 */
! 83:
! 84: /* First chunk for rollback journal files */
! 85: #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
! 86:
! 87:
! 88: /************************ Shim Definitions ******************************/
! 89:
! 90: #ifndef SQLITE_MULTIPLEX_VFS_NAME
! 91: # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
! 92: #endif
! 93:
! 94: /* This is the limit on the chunk size. It may be changed by calling
! 95: ** the xFileControl() interface. It will be rounded up to a
! 96: ** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.
! 97: */
! 98: #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
! 99: # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
! 100: #endif
! 101:
! 102: /* This used to be the default limit on number of chunks, but
! 103: ** it is no longer enforced. There is currently no limit to the
! 104: ** number of chunks.
! 105: **
! 106: ** May be changed by calling the xFileControl() interface.
! 107: */
! 108: #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
! 109: # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
! 110: #endif
! 111:
! 112: /************************ Object Definitions ******************************/
! 113:
! 114: /* Forward declaration of all object types */
! 115: typedef struct multiplexGroup multiplexGroup;
! 116: typedef struct multiplexConn multiplexConn;
! 117:
! 118: /*
! 119: ** A "multiplex group" is a collection of files that collectively
! 120: ** makeup a single SQLite DB file. This allows the size of the DB
! 121: ** to exceed the limits imposed by the file system.
! 122: **
! 123: ** There is an instance of the following object for each defined multiplex
! 124: ** group.
! 125: */
! 126: struct multiplexGroup {
! 127: struct multiplexReal { /* For each chunk */
! 128: sqlite3_file *p; /* Handle for the chunk */
! 129: char *z; /* Name of this chunk */
! 130: } *aReal; /* list of all chunks */
! 131: int nReal; /* Number of chunks */
! 132: char *zName; /* Base filename of this group */
! 133: int nName; /* Length of base filename */
! 134: int flags; /* Flags used for original opening */
! 135: unsigned int szChunk; /* Chunk size used for this group */
! 136: unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */
! 137: unsigned char bTruncate; /* TRUE to enable truncation of databases */
! 138: multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
! 139: };
! 140:
! 141: /*
! 142: ** An instance of the following object represents each open connection
! 143: ** to a file that is multiplex'ed. This object is a
! 144: ** subclass of sqlite3_file. The sqlite3_file object for the underlying
! 145: ** VFS is appended to this structure.
! 146: */
! 147: struct multiplexConn {
! 148: sqlite3_file base; /* Base class - must be first */
! 149: multiplexGroup *pGroup; /* The underlying group of files */
! 150: };
! 151:
! 152: /************************* Global Variables **********************************/
! 153: /*
! 154: ** All global variables used by this file are containing within the following
! 155: ** gMultiplex structure.
! 156: */
! 157: static struct {
! 158: /* The pOrigVfs is the real, original underlying VFS implementation.
! 159: ** Most operations pass-through to the real VFS. This value is read-only
! 160: ** during operation. It is only modified at start-time and thus does not
! 161: ** require a mutex.
! 162: */
! 163: sqlite3_vfs *pOrigVfs;
! 164:
! 165: /* The sThisVfs is the VFS structure used by this shim. It is initialized
! 166: ** at start-time and thus does not require a mutex
! 167: */
! 168: sqlite3_vfs sThisVfs;
! 169:
! 170: /* The sIoMethods defines the methods used by sqlite3_file objects
! 171: ** associated with this shim. It is initialized at start-time and does
! 172: ** not require a mutex.
! 173: **
! 174: ** When the underlying VFS is called to open a file, it might return
! 175: ** either a version 1 or a version 2 sqlite3_file object. This shim
! 176: ** has to create a wrapper sqlite3_file of the same version. Hence
! 177: ** there are two I/O method structures, one for version 1 and the other
! 178: ** for version 2.
! 179: */
! 180: sqlite3_io_methods sIoMethodsV1;
! 181: sqlite3_io_methods sIoMethodsV2;
! 182:
! 183: /* True when this shim has been initialized.
! 184: */
! 185: int isInitialized;
! 186:
! 187: /* For run-time access any of the other global data structures in this
! 188: ** shim, the following mutex must be held.
! 189: */
! 190: sqlite3_mutex *pMutex;
! 191:
! 192: /* List of multiplexGroup objects.
! 193: */
! 194: multiplexGroup *pGroups;
! 195: } gMultiplex;
! 196:
! 197: /************************* Utility Routines *********************************/
! 198: /*
! 199: ** Acquire and release the mutex used to serialize access to the
! 200: ** list of multiplexGroups.
! 201: */
! 202: static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
! 203: static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
! 204:
! 205: /*
! 206: ** Compute a string length that is limited to what can be stored in
! 207: ** lower 30 bits of a 32-bit signed integer.
! 208: **
! 209: ** The value returned will never be negative. Nor will it ever be greater
! 210: ** than the actual length of the string. For very long strings (greater
! 211: ** than 1GiB) the value returned might be less than the true string length.
! 212: */
! 213: static int multiplexStrlen30(const char *z){
! 214: const char *z2 = z;
! 215: if( z==0 ) return 0;
! 216: while( *z2 ){ z2++; }
! 217: return 0x3fffffff & (int)(z2 - z);
! 218: }
! 219:
! 220: /*
! 221: ** Generate the file-name for chunk iChunk of the group with base name
! 222: ** zBase. The file-name is written to buffer zOut before returning. Buffer
! 223: ** zOut must be allocated by the caller so that it is at least (nBase+5)
! 224: ** bytes in size, where nBase is the length of zBase, not including the
! 225: ** nul-terminator.
! 226: **
! 227: ** If iChunk is 0 (or 400 - the number for the first journal file chunk),
! 228: ** the output is a copy of the input string. Otherwise, if
! 229: ** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
! 230: ** a "." character, then the output is a copy of the input string with the
! 231: ** three-digit zero-padded decimal representation if iChunk appended to it.
! 232: ** For example:
! 233: **
! 234: ** zBase="test.db", iChunk=4 -> zOut="test.db004"
! 235: **
! 236: ** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
! 237: ** a "." character, then everything after the "." is replaced by the
! 238: ** three-digit representation of iChunk.
! 239: **
! 240: ** zBase="test.db", iChunk=4 -> zOut="test.004"
! 241: **
! 242: ** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
! 243: ** to pass to sqlite3_uri_parameter() and similar.
! 244: */
! 245: static void multiplexFilename(
! 246: const char *zBase, /* Filename for chunk 0 */
! 247: int nBase, /* Size of zBase in bytes (without \0) */
! 248: int flags, /* Flags used to open file */
! 249: int iChunk, /* Chunk to generate filename for */
! 250: char *zOut /* Buffer to write generated name to */
! 251: ){
! 252: int n = nBase;
! 253: memcpy(zOut, zBase, n+1);
! 254: if( iChunk!=0 && iChunk!=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
! 255: #ifdef SQLITE_ENABLE_8_3_NAMES
! 256: int i;
! 257: for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
! 258: if( i>=n-4 ) n = i+1;
! 259: if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
! 260: /* The extensions on overflow files for main databases are 001, 002,
! 261: ** 003 and so forth. To avoid name collisions, add 400 to the
! 262: ** extensions of journal files so that they are 401, 402, 403, ....
! 263: */
! 264: iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
! 265: }
! 266: #endif
! 267: sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
! 268: n += 3;
! 269: }
! 270:
! 271: assert( zOut[n]=='\0' );
! 272: zOut[n+1] = '\0';
! 273: }
! 274:
! 275: /* Compute the filename for the iChunk-th chunk
! 276: */
! 277: static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
! 278: if( iChunk>=pGroup->nReal ){
! 279: struct multiplexReal *p;
! 280: p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
! 281: if( p==0 ){
! 282: return SQLITE_NOMEM;
! 283: }
! 284: memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
! 285: pGroup->aReal = p;
! 286: pGroup->nReal = iChunk+1;
! 287: }
! 288: if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
! 289: char *z;
! 290: int n = pGroup->nName;
! 291: pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+5 );
! 292: if( z==0 ){
! 293: return SQLITE_NOMEM;
! 294: }
! 295: multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
! 296: }
! 297: return SQLITE_OK;
! 298: }
! 299:
! 300: /* Translate an sqlite3_file* that is really a multiplexGroup* into
! 301: ** the sqlite3_file* for the underlying original VFS.
! 302: **
! 303: ** For chunk 0, the pGroup->flags determines whether or not a new file
! 304: ** is created if it does not already exist. For chunks 1 and higher, the
! 305: ** file is created only if createFlag is 1.
! 306: */
! 307: static sqlite3_file *multiplexSubOpen(
! 308: multiplexGroup *pGroup, /* The multiplexor group */
! 309: int iChunk, /* Which chunk to open. 0==original file */
! 310: int *rc, /* Result code in and out */
! 311: int *pOutFlags, /* Output flags */
! 312: int createFlag /* True to create if iChunk>0 */
! 313: ){
! 314: sqlite3_file *pSubOpen = 0;
! 315: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
! 316:
! 317: #ifdef SQLITE_ENABLE_8_3_NAMES
! 318: /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
! 319: ** part of a database journal are named db.401, db.402, and so on. A
! 320: ** database may therefore not grow to larger than 400 chunks. Attempting
! 321: ** to open chunk 401 indicates the database is full. */
! 322: if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
! 323: *rc = SQLITE_FULL;
! 324: return 0;
! 325: }
! 326: #endif
! 327:
! 328: *rc = multiplexSubFilename(pGroup, iChunk);
! 329: if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
! 330: int flags, bExists;
! 331: flags = pGroup->flags;
! 332: if( createFlag ){
! 333: flags |= SQLITE_OPEN_CREATE;
! 334: }else if( iChunk==0 ){
! 335: /* Fall through */
! 336: }else if( pGroup->aReal[iChunk].z==0 ){
! 337: return 0;
! 338: }else{
! 339: *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
! 340: SQLITE_ACCESS_EXISTS, &bExists);
! 341: if( *rc || !bExists ) return 0;
! 342: flags &= ~SQLITE_OPEN_CREATE;
! 343: }
! 344: pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
! 345: if( pSubOpen==0 ){
! 346: *rc = SQLITE_IOERR_NOMEM;
! 347: return 0;
! 348: }
! 349: pGroup->aReal[iChunk].p = pSubOpen;
! 350: *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
! 351: flags, pOutFlags);
! 352: if( (*rc)!=SQLITE_OK ){
! 353: sqlite3_free(pSubOpen);
! 354: pGroup->aReal[iChunk].p = 0;
! 355: return 0;
! 356: }
! 357: }
! 358: return pSubOpen;
! 359: }
! 360:
! 361: /*
! 362: ** Return the size, in bytes, of chunk number iChunk. If that chunk
! 363: ** does not exist, then return 0. This function does not distingish between
! 364: ** non-existant files and zero-length files.
! 365: */
! 366: static sqlite3_int64 multiplexSubSize(
! 367: multiplexGroup *pGroup, /* The multiplexor group */
! 368: int iChunk, /* Which chunk to open. 0==original file */
! 369: int *rc /* Result code in and out */
! 370: ){
! 371: sqlite3_file *pSub;
! 372: sqlite3_int64 sz = 0;
! 373:
! 374: if( *rc ) return 0;
! 375: pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
! 376: if( pSub==0 ) return 0;
! 377: *rc = pSub->pMethods->xFileSize(pSub, &sz);
! 378: return sz;
! 379: }
! 380:
! 381: /*
! 382: ** This is the implementation of the multiplex_control() SQL function.
! 383: */
! 384: static void multiplexControlFunc(
! 385: sqlite3_context *context,
! 386: int argc,
! 387: sqlite3_value **argv
! 388: ){
! 389: int rc = SQLITE_OK;
! 390: sqlite3 *db = sqlite3_context_db_handle(context);
! 391: int op;
! 392: int iVal;
! 393:
! 394: if( !db || argc!=2 ){
! 395: rc = SQLITE_ERROR;
! 396: }else{
! 397: /* extract params */
! 398: op = sqlite3_value_int(argv[0]);
! 399: iVal = sqlite3_value_int(argv[1]);
! 400: /* map function op to file_control op */
! 401: switch( op ){
! 402: case 1:
! 403: op = MULTIPLEX_CTRL_ENABLE;
! 404: break;
! 405: case 2:
! 406: op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
! 407: break;
! 408: case 3:
! 409: op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
! 410: break;
! 411: default:
! 412: rc = SQLITE_NOTFOUND;
! 413: break;
! 414: }
! 415: }
! 416: if( rc==SQLITE_OK ){
! 417: rc = sqlite3_file_control(db, 0, op, &iVal);
! 418: }
! 419: sqlite3_result_error_code(context, rc);
! 420: }
! 421:
! 422: /*
! 423: ** This is the entry point to register the auto-extension for the
! 424: ** multiplex_control() function.
! 425: */
! 426: static int multiplexFuncInit(
! 427: sqlite3 *db,
! 428: char **pzErrMsg,
! 429: const sqlite3_api_routines *pApi
! 430: ){
! 431: int rc;
! 432: rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
! 433: 0, multiplexControlFunc, 0, 0);
! 434: return rc;
! 435: }
! 436:
! 437: /*
! 438: ** Close a single sub-file in the connection group.
! 439: */
! 440: static void multiplexSubClose(
! 441: multiplexGroup *pGroup,
! 442: int iChunk,
! 443: sqlite3_vfs *pOrigVfs
! 444: ){
! 445: sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
! 446: if( pSubOpen ){
! 447: pSubOpen->pMethods->xClose(pSubOpen);
! 448: if( pOrigVfs && pGroup->aReal[iChunk].z ){
! 449: pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
! 450: }
! 451: sqlite3_free(pGroup->aReal[iChunk].p);
! 452: }
! 453: sqlite3_free(pGroup->aReal[iChunk].z);
! 454: memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));
! 455: }
! 456:
! 457: /*
! 458: ** Deallocate memory held by a multiplexGroup
! 459: */
! 460: static void multiplexFreeComponents(multiplexGroup *pGroup){
! 461: int i;
! 462: for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
! 463: sqlite3_free(pGroup->aReal);
! 464: pGroup->aReal = 0;
! 465: pGroup->nReal = 0;
! 466: }
! 467:
! 468:
! 469: /************************* VFS Method Wrappers *****************************/
! 470:
! 471: /*
! 472: ** This is the xOpen method used for the "multiplex" VFS.
! 473: **
! 474: ** Most of the work is done by the underlying original VFS. This method
! 475: ** simply links the new file into the appropriate multiplex group if it is a
! 476: ** file that needs to be tracked.
! 477: */
! 478: static int multiplexOpen(
! 479: sqlite3_vfs *pVfs, /* The multiplex VFS */
! 480: const char *zName, /* Name of file to be opened */
! 481: sqlite3_file *pConn, /* Fill in this file descriptor */
! 482: int flags, /* Flags to control the opening */
! 483: int *pOutFlags /* Flags showing results of opening */
! 484: ){
! 485: int rc = SQLITE_OK; /* Result code */
! 486: multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
! 487: multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
! 488: sqlite3_file *pSubOpen = 0; /* Real file descriptor */
! 489: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
! 490: int nName;
! 491: int sz;
! 492: char *zToFree = 0;
! 493:
! 494: UNUSED_PARAMETER(pVfs);
! 495: memset(pConn, 0, pVfs->szOsFile);
! 496: assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
! 497:
! 498: /* We need to create a group structure and manage
! 499: ** access to this group of files.
! 500: */
! 501: multiplexEnter();
! 502: pMultiplexOpen = (multiplexConn*)pConn;
! 503:
! 504: if( rc==SQLITE_OK ){
! 505: /* allocate space for group */
! 506: nName = zName ? multiplexStrlen30(zName) : 0;
! 507: sz = sizeof(multiplexGroup) /* multiplexGroup */
! 508: + nName + 1; /* zName */
! 509: pGroup = sqlite3_malloc( sz );
! 510: if( pGroup==0 ){
! 511: rc = SQLITE_NOMEM;
! 512: }
! 513: }
! 514:
! 515: if( rc==SQLITE_OK ){
! 516: const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0;
! 517: /* assign pointers to extra space allocated */
! 518: memset(pGroup, 0, sz);
! 519: pMultiplexOpen->pGroup = pGroup;
! 520: pGroup->bEnabled = -1;
! 521: pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
! 522: (flags & SQLITE_OPEN_MAIN_DB)==0);
! 523: pGroup->szChunk = sqlite3_uri_int64(zUri, "chunksize",
! 524: SQLITE_MULTIPLEX_CHUNK_SIZE);
! 525: pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
! 526: if( zName ){
! 527: char *p = (char *)&pGroup[1];
! 528: pGroup->zName = p;
! 529: memcpy(pGroup->zName, zName, nName+1);
! 530: pGroup->nName = nName;
! 531: }
! 532: if( pGroup->bEnabled ){
! 533: /* Make sure that the chunksize is such that the pending byte does not
! 534: ** falls at the end of a chunk. A region of up to 64K following
! 535: ** the pending byte is never written, so if the pending byte occurs
! 536: ** near the end of a chunk, that chunk will be too small. */
! 537: #ifndef SQLITE_OMIT_WSD
! 538: extern int sqlite3PendingByte;
! 539: #else
! 540: int sqlite3PendingByte = 0x40000000;
! 541: #endif
! 542: while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
! 543: pGroup->szChunk += 65536;
! 544: }
! 545: }
! 546: pGroup->flags = flags;
! 547: rc = multiplexSubFilename(pGroup, 1);
! 548: if( rc==SQLITE_OK ){
! 549: pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
! 550: if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
! 551: }
! 552: if( rc==SQLITE_OK ){
! 553: sqlite3_int64 sz;
! 554:
! 555: rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
! 556: if( rc==SQLITE_OK && zName ){
! 557: int bExists;
! 558: if( sz==0 ){
! 559: if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
! 560: /* If opening a main journal file and the first chunk is zero
! 561: ** bytes in size, delete any subsequent chunks from the
! 562: ** file-system. */
! 563: int iChunk = 1;
! 564: do {
! 565: rc = pOrigVfs->xAccess(pOrigVfs,
! 566: pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists
! 567: );
! 568: if( rc==SQLITE_OK && bExists ){
! 569: rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
! 570: if( rc==SQLITE_OK ){
! 571: rc = multiplexSubFilename(pGroup, ++iChunk);
! 572: }
! 573: }
! 574: }while( rc==SQLITE_OK && bExists );
! 575: }
! 576: }else{
! 577: /* If the first overflow file exists and if the size of the main file
! 578: ** is different from the chunk size, that means the chunk size is set
! 579: ** set incorrectly. So fix it.
! 580: **
! 581: ** Or, if the first overflow file does not exist and the main file is
! 582: ** larger than the chunk size, that means the chunk size is too small.
! 583: ** But we have no way of determining the intended chunk size, so
! 584: ** just disable the multiplexor all togethre.
! 585: */
! 586: rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
! 587: SQLITE_ACCESS_EXISTS, &bExists);
! 588: bExists = multiplexSubSize(pGroup, 1, &rc)>0;
! 589: if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
! 590: && sz!=pGroup->szChunk ){
! 591: pGroup->szChunk = sz;
! 592: }else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
! 593: pGroup->bEnabled = 0;
! 594: }
! 595: }
! 596: }
! 597: }
! 598:
! 599: if( rc==SQLITE_OK ){
! 600: if( pSubOpen->pMethods->iVersion==1 ){
! 601: pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
! 602: }else{
! 603: pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
! 604: }
! 605: /* place this group at the head of our list */
! 606: pGroup->pNext = gMultiplex.pGroups;
! 607: if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
! 608: gMultiplex.pGroups = pGroup;
! 609: }else{
! 610: multiplexFreeComponents(pGroup);
! 611: sqlite3_free(pGroup);
! 612: }
! 613: }
! 614: multiplexLeave();
! 615: sqlite3_free(zToFree);
! 616: return rc;
! 617: }
! 618:
! 619: /*
! 620: ** This is the xDelete method used for the "multiplex" VFS.
! 621: ** It attempts to delete the filename specified.
! 622: */
! 623: static int multiplexDelete(
! 624: sqlite3_vfs *pVfs, /* The multiplex VFS */
! 625: const char *zName, /* Name of file to delete */
! 626: int syncDir
! 627: ){
! 628: int rc;
! 629: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
! 630: rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
! 631: if( rc==SQLITE_OK ){
! 632: /* If the main chunk was deleted successfully, also delete any subsequent
! 633: ** chunks - starting with the last (highest numbered).
! 634: */
! 635: int nName = strlen(zName);
! 636: char *z;
! 637: z = sqlite3_malloc(nName + 5);
! 638: if( z==0 ){
! 639: rc = SQLITE_IOERR_NOMEM;
! 640: }else{
! 641: int iChunk = 0;
! 642: int bExists;
! 643: do{
! 644: multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);
! 645: rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
! 646: }while( rc==SQLITE_OK && bExists );
! 647: while( rc==SQLITE_OK && iChunk>1 ){
! 648: multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
! 649: rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
! 650: }
! 651: }
! 652: sqlite3_free(z);
! 653: }
! 654: return rc;
! 655: }
! 656:
! 657: static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
! 658: return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
! 659: }
! 660: static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
! 661: return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
! 662: }
! 663: static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
! 664: return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
! 665: }
! 666: static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
! 667: gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
! 668: }
! 669: static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
! 670: return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
! 671: }
! 672: static void multiplexDlClose(sqlite3_vfs *a, void *b){
! 673: gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
! 674: }
! 675: static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
! 676: return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
! 677: }
! 678: static int multiplexSleep(sqlite3_vfs *a, int b){
! 679: return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
! 680: }
! 681: static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
! 682: return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
! 683: }
! 684: static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
! 685: return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
! 686: }
! 687: static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
! 688: return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
! 689: }
! 690:
! 691: /************************ I/O Method Wrappers *******************************/
! 692:
! 693: /* xClose requests get passed through to the original VFS.
! 694: ** We loop over all open chunk handles and close them.
! 695: ** The group structure for this file is unlinked from
! 696: ** our list of groups and freed.
! 697: */
! 698: static int multiplexClose(sqlite3_file *pConn){
! 699: multiplexConn *p = (multiplexConn*)pConn;
! 700: multiplexGroup *pGroup = p->pGroup;
! 701: int rc = SQLITE_OK;
! 702: multiplexEnter();
! 703: multiplexFreeComponents(pGroup);
! 704: /* remove from linked list */
! 705: if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
! 706: if( pGroup->pPrev ){
! 707: pGroup->pPrev->pNext = pGroup->pNext;
! 708: }else{
! 709: gMultiplex.pGroups = pGroup->pNext;
! 710: }
! 711: sqlite3_free(pGroup);
! 712: multiplexLeave();
! 713: return rc;
! 714: }
! 715:
! 716: /* Pass xRead requests thru to the original VFS after
! 717: ** determining the correct chunk to operate on.
! 718: ** Break up reads across chunk boundaries.
! 719: */
! 720: static int multiplexRead(
! 721: sqlite3_file *pConn,
! 722: void *pBuf,
! 723: int iAmt,
! 724: sqlite3_int64 iOfst
! 725: ){
! 726: multiplexConn *p = (multiplexConn*)pConn;
! 727: multiplexGroup *pGroup = p->pGroup;
! 728: int rc = SQLITE_OK;
! 729: multiplexEnter();
! 730: if( !pGroup->bEnabled ){
! 731: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
! 732: if( pSubOpen==0 ){
! 733: rc = SQLITE_IOERR_READ;
! 734: }else{
! 735: rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
! 736: }
! 737: }else{
! 738: while( iAmt > 0 ){
! 739: int i = (int)(iOfst / pGroup->szChunk);
! 740: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
! 741: if( pSubOpen ){
! 742: int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
! 743: if( extra<0 ) extra = 0;
! 744: iAmt -= extra;
! 745: rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
! 746: iOfst % pGroup->szChunk);
! 747: if( rc!=SQLITE_OK ) break;
! 748: pBuf = (char *)pBuf + iAmt;
! 749: iOfst += iAmt;
! 750: iAmt = extra;
! 751: }else{
! 752: rc = SQLITE_IOERR_READ;
! 753: break;
! 754: }
! 755: }
! 756: }
! 757: multiplexLeave();
! 758: return rc;
! 759: }
! 760:
! 761: /* Pass xWrite requests thru to the original VFS after
! 762: ** determining the correct chunk to operate on.
! 763: ** Break up writes across chunk boundaries.
! 764: */
! 765: static int multiplexWrite(
! 766: sqlite3_file *pConn,
! 767: const void *pBuf,
! 768: int iAmt,
! 769: sqlite3_int64 iOfst
! 770: ){
! 771: multiplexConn *p = (multiplexConn*)pConn;
! 772: multiplexGroup *pGroup = p->pGroup;
! 773: int rc = SQLITE_OK;
! 774: multiplexEnter();
! 775: if( !pGroup->bEnabled ){
! 776: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
! 777: if( pSubOpen==0 ){
! 778: rc = SQLITE_IOERR_WRITE;
! 779: }else{
! 780: rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
! 781: }
! 782: }else{
! 783: while( rc==SQLITE_OK && iAmt>0 ){
! 784: int i = (int)(iOfst / pGroup->szChunk);
! 785: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
! 786: if( pSubOpen ){
! 787: int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
! 788: pGroup->szChunk;
! 789: if( extra<0 ) extra = 0;
! 790: iAmt -= extra;
! 791: rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
! 792: iOfst % pGroup->szChunk);
! 793: pBuf = (char *)pBuf + iAmt;
! 794: iOfst += iAmt;
! 795: iAmt = extra;
! 796: }
! 797: }
! 798: }
! 799: multiplexLeave();
! 800: return rc;
! 801: }
! 802:
! 803: /* Pass xTruncate requests thru to the original VFS after
! 804: ** determining the correct chunk to operate on. Delete any
! 805: ** chunks above the truncate mark.
! 806: */
! 807: static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
! 808: multiplexConn *p = (multiplexConn*)pConn;
! 809: multiplexGroup *pGroup = p->pGroup;
! 810: int rc = SQLITE_OK;
! 811: multiplexEnter();
! 812: if( !pGroup->bEnabled ){
! 813: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
! 814: if( pSubOpen==0 ){
! 815: rc = SQLITE_IOERR_TRUNCATE;
! 816: }else{
! 817: rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
! 818: }
! 819: }else{
! 820: int i;
! 821: int iBaseGroup = (int)(size / pGroup->szChunk);
! 822: sqlite3_file *pSubOpen;
! 823: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
! 824: /* delete the chunks above the truncate limit */
! 825: for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
! 826: if( pGroup->bTruncate ){
! 827: multiplexSubClose(pGroup, i, pOrigVfs);
! 828: }else{
! 829: pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
! 830: if( pSubOpen ){
! 831: rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
! 832: }
! 833: }
! 834: }
! 835: if( rc==SQLITE_OK ){
! 836: pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
! 837: if( pSubOpen ){
! 838: rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
! 839: }
! 840: }
! 841: if( rc ) rc = SQLITE_IOERR_TRUNCATE;
! 842: }
! 843: multiplexLeave();
! 844: return rc;
! 845: }
! 846:
! 847: /* Pass xSync requests through to the original VFS without change
! 848: */
! 849: static int multiplexSync(sqlite3_file *pConn, int flags){
! 850: multiplexConn *p = (multiplexConn*)pConn;
! 851: multiplexGroup *pGroup = p->pGroup;
! 852: int rc = SQLITE_OK;
! 853: int i;
! 854: multiplexEnter();
! 855: for(i=0; i<pGroup->nReal; i++){
! 856: sqlite3_file *pSubOpen = pGroup->aReal[i].p;
! 857: if( pSubOpen ){
! 858: int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
! 859: if( rc2!=SQLITE_OK ) rc = rc2;
! 860: }
! 861: }
! 862: multiplexLeave();
! 863: return rc;
! 864: }
! 865:
! 866: /* Pass xFileSize requests through to the original VFS.
! 867: ** Aggregate the size of all the chunks before returning.
! 868: */
! 869: static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
! 870: multiplexConn *p = (multiplexConn*)pConn;
! 871: multiplexGroup *pGroup = p->pGroup;
! 872: int rc = SQLITE_OK;
! 873: int i;
! 874: multiplexEnter();
! 875: if( !pGroup->bEnabled ){
! 876: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
! 877: if( pSubOpen==0 ){
! 878: rc = SQLITE_IOERR_FSTAT;
! 879: }else{
! 880: rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
! 881: }
! 882: }else{
! 883: *pSize = 0;
! 884: for(i=0; rc==SQLITE_OK; i++){
! 885: sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
! 886: if( sz==0 ) break;
! 887: *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
! 888: }
! 889: }
! 890: multiplexLeave();
! 891: return rc;
! 892: }
! 893:
! 894: /* Pass xLock requests through to the original VFS unchanged.
! 895: */
! 896: static int multiplexLock(sqlite3_file *pConn, int lock){
! 897: multiplexConn *p = (multiplexConn*)pConn;
! 898: int rc;
! 899: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 900: if( pSubOpen ){
! 901: return pSubOpen->pMethods->xLock(pSubOpen, lock);
! 902: }
! 903: return SQLITE_BUSY;
! 904: }
! 905:
! 906: /* Pass xUnlock requests through to the original VFS unchanged.
! 907: */
! 908: static int multiplexUnlock(sqlite3_file *pConn, int lock){
! 909: multiplexConn *p = (multiplexConn*)pConn;
! 910: int rc;
! 911: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 912: if( pSubOpen ){
! 913: return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
! 914: }
! 915: return SQLITE_IOERR_UNLOCK;
! 916: }
! 917:
! 918: /* Pass xCheckReservedLock requests through to the original VFS unchanged.
! 919: */
! 920: static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
! 921: multiplexConn *p = (multiplexConn*)pConn;
! 922: int rc;
! 923: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 924: if( pSubOpen ){
! 925: return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
! 926: }
! 927: return SQLITE_IOERR_CHECKRESERVEDLOCK;
! 928: }
! 929:
! 930: /* Pass xFileControl requests through to the original VFS unchanged,
! 931: ** except for any MULTIPLEX_CTRL_* requests here.
! 932: */
! 933: static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
! 934: multiplexConn *p = (multiplexConn*)pConn;
! 935: multiplexGroup *pGroup = p->pGroup;
! 936: int rc = SQLITE_ERROR;
! 937: sqlite3_file *pSubOpen;
! 938:
! 939: if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
! 940: switch( op ){
! 941: case MULTIPLEX_CTRL_ENABLE:
! 942: if( pArg ) {
! 943: int bEnabled = *(int *)pArg;
! 944: pGroup->bEnabled = bEnabled;
! 945: rc = SQLITE_OK;
! 946: }
! 947: break;
! 948: case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
! 949: if( pArg ) {
! 950: unsigned int szChunk = *(unsigned*)pArg;
! 951: if( szChunk<1 ){
! 952: rc = SQLITE_MISUSE;
! 953: }else{
! 954: /* Round up to nearest multiple of MAX_PAGE_SIZE. */
! 955: szChunk = (szChunk + (MAX_PAGE_SIZE-1));
! 956: szChunk &= ~(MAX_PAGE_SIZE-1);
! 957: pGroup->szChunk = szChunk;
! 958: rc = SQLITE_OK;
! 959: }
! 960: }
! 961: break;
! 962: case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
! 963: rc = SQLITE_OK;
! 964: break;
! 965: case SQLITE_FCNTL_SIZE_HINT:
! 966: case SQLITE_FCNTL_CHUNK_SIZE:
! 967: /* no-op these */
! 968: rc = SQLITE_OK;
! 969: break;
! 970: default:
! 971: pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
! 972: if( pSubOpen ){
! 973: rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
! 974: if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
! 975: *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
! 976: }
! 977: }
! 978: break;
! 979: }
! 980: return rc;
! 981: }
! 982:
! 983: /* Pass xSectorSize requests through to the original VFS unchanged.
! 984: */
! 985: static int multiplexSectorSize(sqlite3_file *pConn){
! 986: multiplexConn *p = (multiplexConn*)pConn;
! 987: int rc;
! 988: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 989: if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
! 990: return pSubOpen->pMethods->xSectorSize(pSubOpen);
! 991: }
! 992: return DEFAULT_SECTOR_SIZE;
! 993: }
! 994:
! 995: /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
! 996: */
! 997: static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
! 998: multiplexConn *p = (multiplexConn*)pConn;
! 999: int rc;
! 1000: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 1001: if( pSubOpen ){
! 1002: return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
! 1003: }
! 1004: return 0;
! 1005: }
! 1006:
! 1007: /* Pass xShmMap requests through to the original VFS unchanged.
! 1008: */
! 1009: static int multiplexShmMap(
! 1010: sqlite3_file *pConn, /* Handle open on database file */
! 1011: int iRegion, /* Region to retrieve */
! 1012: int szRegion, /* Size of regions */
! 1013: int bExtend, /* True to extend file if necessary */
! 1014: void volatile **pp /* OUT: Mapped memory */
! 1015: ){
! 1016: multiplexConn *p = (multiplexConn*)pConn;
! 1017: int rc;
! 1018: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 1019: if( pSubOpen ){
! 1020: return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
! 1021: }
! 1022: return SQLITE_IOERR;
! 1023: }
! 1024:
! 1025: /* Pass xShmLock requests through to the original VFS unchanged.
! 1026: */
! 1027: static int multiplexShmLock(
! 1028: sqlite3_file *pConn, /* Database file holding the shared memory */
! 1029: int ofst, /* First lock to acquire or release */
! 1030: int n, /* Number of locks to acquire or release */
! 1031: int flags /* What to do with the lock */
! 1032: ){
! 1033: multiplexConn *p = (multiplexConn*)pConn;
! 1034: int rc;
! 1035: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 1036: if( pSubOpen ){
! 1037: return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
! 1038: }
! 1039: return SQLITE_BUSY;
! 1040: }
! 1041:
! 1042: /* Pass xShmBarrier requests through to the original VFS unchanged.
! 1043: */
! 1044: static void multiplexShmBarrier(sqlite3_file *pConn){
! 1045: multiplexConn *p = (multiplexConn*)pConn;
! 1046: int rc;
! 1047: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 1048: if( pSubOpen ){
! 1049: pSubOpen->pMethods->xShmBarrier(pSubOpen);
! 1050: }
! 1051: }
! 1052:
! 1053: /* Pass xShmUnmap requests through to the original VFS unchanged.
! 1054: */
! 1055: static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
! 1056: multiplexConn *p = (multiplexConn*)pConn;
! 1057: int rc;
! 1058: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
! 1059: if( pSubOpen ){
! 1060: return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
! 1061: }
! 1062: return SQLITE_OK;
! 1063: }
! 1064:
! 1065: /************************** Public Interfaces *****************************/
! 1066: /*
! 1067: ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
! 1068: **
! 1069: ** Use the VFS named zOrigVfsName as the VFS that does the actual work.
! 1070: ** Use the default if zOrigVfsName==NULL.
! 1071: **
! 1072: ** The multiplex VFS shim is named "multiplex". It will become the default
! 1073: ** VFS if makeDefault is non-zero.
! 1074: **
! 1075: ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
! 1076: ** during start-up.
! 1077: */
! 1078: int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
! 1079: sqlite3_vfs *pOrigVfs;
! 1080: if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
! 1081: pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
! 1082: if( pOrigVfs==0 ) return SQLITE_ERROR;
! 1083: assert( pOrigVfs!=&gMultiplex.sThisVfs );
! 1084: gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
! 1085: if( !gMultiplex.pMutex ){
! 1086: return SQLITE_NOMEM;
! 1087: }
! 1088: gMultiplex.pGroups = NULL;
! 1089: gMultiplex.isInitialized = 1;
! 1090: gMultiplex.pOrigVfs = pOrigVfs;
! 1091: gMultiplex.sThisVfs = *pOrigVfs;
! 1092: gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
! 1093: gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
! 1094: gMultiplex.sThisVfs.xOpen = multiplexOpen;
! 1095: gMultiplex.sThisVfs.xDelete = multiplexDelete;
! 1096: gMultiplex.sThisVfs.xAccess = multiplexAccess;
! 1097: gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
! 1098: gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
! 1099: gMultiplex.sThisVfs.xDlError = multiplexDlError;
! 1100: gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
! 1101: gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
! 1102: gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
! 1103: gMultiplex.sThisVfs.xSleep = multiplexSleep;
! 1104: gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
! 1105: gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
! 1106: gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
! 1107:
! 1108: gMultiplex.sIoMethodsV1.iVersion = 1;
! 1109: gMultiplex.sIoMethodsV1.xClose = multiplexClose;
! 1110: gMultiplex.sIoMethodsV1.xRead = multiplexRead;
! 1111: gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
! 1112: gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
! 1113: gMultiplex.sIoMethodsV1.xSync = multiplexSync;
! 1114: gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
! 1115: gMultiplex.sIoMethodsV1.xLock = multiplexLock;
! 1116: gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
! 1117: gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
! 1118: gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
! 1119: gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
! 1120: gMultiplex.sIoMethodsV1.xDeviceCharacteristics =
! 1121: multiplexDeviceCharacteristics;
! 1122: gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
! 1123: gMultiplex.sIoMethodsV2.iVersion = 2;
! 1124: gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
! 1125: gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
! 1126: gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
! 1127: gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
! 1128: sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
! 1129:
! 1130: sqlite3_auto_extension((void*)multiplexFuncInit);
! 1131:
! 1132: return SQLITE_OK;
! 1133: }
! 1134:
! 1135: /*
! 1136: ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
! 1137: **
! 1138: ** All SQLite database connections must be closed before calling this
! 1139: ** routine.
! 1140: **
! 1141: ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
! 1142: ** shutting down in order to free all remaining multiplex groups.
! 1143: */
! 1144: int sqlite3_multiplex_shutdown(void){
! 1145: if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
! 1146: if( gMultiplex.pGroups ) return SQLITE_MISUSE;
! 1147: gMultiplex.isInitialized = 0;
! 1148: sqlite3_mutex_free(gMultiplex.pMutex);
! 1149: sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
! 1150: memset(&gMultiplex, 0, sizeof(gMultiplex));
! 1151: return SQLITE_OK;
! 1152: }
! 1153:
! 1154: /***************************** Test Code ***********************************/
! 1155: #ifdef SQLITE_TEST
! 1156: #include <tcl.h>
! 1157: extern const char *sqlite3TestErrorName(int);
! 1158:
! 1159:
! 1160: /*
! 1161: ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
! 1162: */
! 1163: static int test_multiplex_initialize(
! 1164: void * clientData,
! 1165: Tcl_Interp *interp,
! 1166: int objc,
! 1167: Tcl_Obj *CONST objv[]
! 1168: ){
! 1169: const char *zName; /* Name of new multiplex VFS */
! 1170: int makeDefault; /* True to make the new VFS the default */
! 1171: int rc; /* Value returned by multiplex_initialize() */
! 1172:
! 1173: UNUSED_PARAMETER(clientData);
! 1174:
! 1175: /* Process arguments */
! 1176: if( objc!=3 ){
! 1177: Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
! 1178: return TCL_ERROR;
! 1179: }
! 1180: zName = Tcl_GetString(objv[1]);
! 1181: if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
! 1182: if( zName[0]=='\0' ) zName = 0;
! 1183:
! 1184: /* Call sqlite3_multiplex_initialize() */
! 1185: rc = sqlite3_multiplex_initialize(zName, makeDefault);
! 1186: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
! 1187:
! 1188: return TCL_OK;
! 1189: }
! 1190:
! 1191: /*
! 1192: ** tclcmd: sqlite3_multiplex_shutdown
! 1193: */
! 1194: static int test_multiplex_shutdown(
! 1195: void * clientData,
! 1196: Tcl_Interp *interp,
! 1197: int objc,
! 1198: Tcl_Obj *CONST objv[]
! 1199: ){
! 1200: int rc; /* Value returned by multiplex_shutdown() */
! 1201:
! 1202: UNUSED_PARAMETER(clientData);
! 1203:
! 1204: if( objc!=1 ){
! 1205: Tcl_WrongNumArgs(interp, 1, objv, "");
! 1206: return TCL_ERROR;
! 1207: }
! 1208:
! 1209: /* Call sqlite3_multiplex_shutdown() */
! 1210: rc = sqlite3_multiplex_shutdown();
! 1211: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
! 1212:
! 1213: return TCL_OK;
! 1214: }
! 1215:
! 1216: /*
! 1217: ** tclcmd: sqlite3_multiplex_dump
! 1218: */
! 1219: static int test_multiplex_dump(
! 1220: void * clientData,
! 1221: Tcl_Interp *interp,
! 1222: int objc,
! 1223: Tcl_Obj *CONST objv[]
! 1224: ){
! 1225: Tcl_Obj *pResult;
! 1226: Tcl_Obj *pGroupTerm;
! 1227: multiplexGroup *pGroup;
! 1228: int i;
! 1229: int nChunks = 0;
! 1230:
! 1231: UNUSED_PARAMETER(clientData);
! 1232: UNUSED_PARAMETER(objc);
! 1233: UNUSED_PARAMETER(objv);
! 1234:
! 1235: pResult = Tcl_NewObj();
! 1236: multiplexEnter();
! 1237: for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
! 1238: pGroupTerm = Tcl_NewObj();
! 1239:
! 1240: if( pGroup->zName ){
! 1241: pGroup->zName[pGroup->nName] = '\0';
! 1242: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1243: Tcl_NewStringObj(pGroup->zName, -1));
! 1244: }else{
! 1245: Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
! 1246: }
! 1247: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1248: Tcl_NewIntObj(pGroup->nName));
! 1249: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1250: Tcl_NewIntObj(pGroup->flags));
! 1251:
! 1252: /* count number of chunks with open handles */
! 1253: for(i=0; i<pGroup->nReal; i++){
! 1254: if( pGroup->aReal[i].p!=0 ) nChunks++;
! 1255: }
! 1256: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1257: Tcl_NewIntObj(nChunks));
! 1258:
! 1259: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1260: Tcl_NewIntObj(pGroup->szChunk));
! 1261: Tcl_ListObjAppendElement(interp, pGroupTerm,
! 1262: Tcl_NewIntObj(pGroup->nReal));
! 1263:
! 1264: Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
! 1265: }
! 1266: multiplexLeave();
! 1267: Tcl_SetObjResult(interp, pResult);
! 1268: return TCL_OK;
! 1269: }
! 1270:
! 1271: /*
! 1272: ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
! 1273: */
! 1274: static int test_multiplex_control(
! 1275: ClientData cd,
! 1276: Tcl_Interp *interp,
! 1277: int objc,
! 1278: Tcl_Obj *CONST objv[]
! 1279: ){
! 1280: int rc; /* Return code from file_control() */
! 1281: int idx; /* Index in aSub[] */
! 1282: Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */
! 1283: sqlite3 *db; /* Underlying db handle for HANDLE */
! 1284: int iValue = 0;
! 1285: void *pArg = 0;
! 1286:
! 1287: struct SubCommand {
! 1288: const char *zName;
! 1289: int op;
! 1290: int argtype;
! 1291: } aSub[] = {
! 1292: { "enable", MULTIPLEX_CTRL_ENABLE, 1 },
! 1293: { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 },
! 1294: { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 },
! 1295: { 0, 0, 0 }
! 1296: };
! 1297:
! 1298: if( objc!=5 ){
! 1299: Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
! 1300: return TCL_ERROR;
! 1301: }
! 1302:
! 1303: if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
! 1304: Tcl_AppendResult(interp, "expected database handle, got \"", 0);
! 1305: Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
! 1306: return TCL_ERROR;
! 1307: }else{
! 1308: db = *(sqlite3 **)cmdInfo.objClientData;
! 1309: }
! 1310:
! 1311: rc = Tcl_GetIndexFromObjStruct(
! 1312: interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
! 1313: );
! 1314: if( rc!=TCL_OK ) return rc;
! 1315:
! 1316: switch( aSub[idx].argtype ){
! 1317: case 1:
! 1318: if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
! 1319: return TCL_ERROR;
! 1320: }
! 1321: pArg = (void *)&iValue;
! 1322: break;
! 1323: default:
! 1324: Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
! 1325: return TCL_ERROR;
! 1326: }
! 1327:
! 1328: rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
! 1329: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
! 1330: return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
! 1331: }
! 1332:
! 1333: /*
! 1334: ** This routine registers the custom TCL commands defined in this
! 1335: ** module. This should be the only procedure visible from outside
! 1336: ** of this module.
! 1337: */
! 1338: int Sqlitemultiplex_Init(Tcl_Interp *interp){
! 1339: static struct {
! 1340: char *zName;
! 1341: Tcl_ObjCmdProc *xProc;
! 1342: } aCmd[] = {
! 1343: { "sqlite3_multiplex_initialize", test_multiplex_initialize },
! 1344: { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
! 1345: { "sqlite3_multiplex_dump", test_multiplex_dump },
! 1346: { "sqlite3_multiplex_control", test_multiplex_control },
! 1347: };
! 1348: int i;
! 1349:
! 1350: for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
! 1351: Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
! 1352: }
! 1353:
! 1354: return TCL_OK;
! 1355: }
! 1356: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>