Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Changes In Branch multiplex-enhancements Excluding Merge-Ins
This is equivalent to a diff from def98fd23e to 718f1ad7df
2011-04-01
| ||
18:39 | Merge the multiplexer enhancements back into the trunk. (check-in: 2c125710cb user: drh tags: trunk) | |
14:22 | Removed dependency on sqliteInt.h so that multiplex VFS shim can be compiled as loadable module. (Closed-Leaf check-in: 718f1ad7df user: shaneh tags: multiplex-enhancements) | |
2011-03-31
| ||
15:11 | Enable/disable support. (check-in: b3c6d9aa9e user: shaneh tags: multiplex-enhancements) | |
2011-03-15
| ||
18:35 | Fix an out-of-order variable declaration in shell.c. (check-in: 7257084650 user: drh tags: trunk) | |
04:45 | Allow multiplex file names to be preceeded by prefix of the form ":multiplex:chunksize:maxchunks:" Still work to be done, though it compiles and prefixes are ignored. (check-in: cfa4a2f7ea user: shaneh tags: multiplex-enhancements) | |
02:55 | Fix cut-and-paste typo in debugging print statement in winMutexTry(). (check-in: def98fd23e user: shaneh tags: trunk) | |
2011-03-14
| ||
13:54 | Merge the unix-excl VFS into the trunk. This merge also adds the -vfs option to the command-line shell. (check-in: 3934b004e9 user: drh tags: trunk) | |
Changes to src/test_multiplex.c.
︙ | ︙ | |||
18 19 20 21 22 23 24 | ** "chunks" such that the total DB file size may exceed the maximum ** file size of the underlying file system. ** */ #include "sqlite3.h" #include <string.h> #include <assert.h> | > > > > > | > > > > > > > > > > > > | > | > | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | ** "chunks" such that the total DB file size may exceed the maximum ** file size of the underlying file system. ** */ #include "sqlite3.h" #include <string.h> #include <assert.h> #include "test_multiplex.h" #ifndef SQLITE_CORE #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */ #endif #include "sqlite3ext.h" /* ** These should be defined to be the same as the values in ** sqliteInt.h. They are defined seperately here so that ** the multiplex VFS shim can be built as a loadable ** module. */ #define UNUSED_PARAMETER(x) (void)(x) #define MAX_PAGE_SIZE 0x10000 #define DEFAULT_SECTOR_SIZE 0x1000 /* ** For a build without mutexes, no-op the mutex calls. */ #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0 #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8) #define sqlite3_mutex_free(X) #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 ******************************/ #define SQLITE_MULTIPLEX_VFS_NAME "multiplex" /* This is the limit on the chunk size. It may be changed by calling ** the xFileControl() interface. It will be rounded up to a ** multiple of MAX_PAGE_SIZE. We default it here to 1GB. */ #define SQLITE_MULTIPLEX_CHUNK_SIZE (MAX_PAGE_SIZE*16384) /* Default limit on number of chunks. Care should be taken ** so that values for chunks numbers fit in the SQLITE_MULTIPLEX_EXT_FMT ** format specifier. It may be changed by calling ** the xFileControl() interface. */ #define SQLITE_MULTIPLEX_MAX_CHUNKS 32 /* If SQLITE_MULTIPLEX_EXT_OVWR is defined, the ** last SQLITE_MULTIPLEX_EXT_SZ characters of the ** filename will be overwritten, otherwise, the ** multiplex extension is simply appended to the filename. |
︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | */ struct multiplexGroup { sqlite3_file **pReal; /* Handles to each chunk */ char *bOpen; /* array of bools - 0 if chunk not opened */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ }; /* ** An instance of the following object represents each open connection ** to a file that is multiplex'ed. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying | > > > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | */ struct multiplexGroup { sqlite3_file **pReal; /* Handles to each chunk */ char *bOpen; /* array of bools - 0 if chunk not opened */ char *zName; /* Base filename of this group */ int nName; /* Length of base filename */ int flags; /* Flags used for original opening */ int nChunkSize; /* Chunk size used for this group */ int nMaxChunks; /* Max number of chunks for this group */ int bEnabled; /* TRUE to use Multiplex VFS for this file */ multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ }; /* ** An instance of the following object represents each open connection ** to a file that is multiplex'ed. This object is a ** subclass of sqlite3_file. The sqlite3_file object for the underlying |
︙ | ︙ | |||
136 137 138 139 140 141 142 | */ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; | < < < < < > > > > > > > > > > > > > > > | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | */ sqlite3_mutex *pMutex; /* List of multiplexGroup objects. */ multiplexGroup *pGroups; /* Storage for temp file names. Allocated during ** initialization to the max pathname of the underlying VFS. */ char *zName; } gMultiplex; /************************* Utility Routines *********************************/ /* ** Acquire and release the mutex used to serialize access to the ** list of multiplexGroups. */ static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } /* ** Compute a string length that is limited to what can be stored in ** lower 30 bits of a 32-bit signed integer. ** ** The value returned will never be negative. Nor will it ever be greater ** than the actual length of the string. For very long strings (greater ** than 1GiB) the value returned might be less than the true string length. */ int multiplexStrlen30(const char *z){ const char *z2 = z; if( z==0 ) return 0; while( *z2 ){ z2++; } return 0x3fffffff & (int)(z2 - z); } /* Translate an sqlite3_file* that is really a multiplexGroup* into ** the sqlite3_file* for the underlying original VFS. */ static sqlite3_file *multiplexSubOpen(multiplexConn *pConn, int iChunk, int *rc, int *pOutFlags){ multiplexGroup *pGroup = pConn->pGroup; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ if( iChunk<pGroup->nMaxChunks ){ sqlite3_file *pSubOpen = pGroup->pReal[iChunk]; /* Real file descriptor */ if( !pGroup->bOpen[iChunk] ){ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); if( iChunk ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, iChunk); #else |
︙ | ︙ | |||
186 187 188 189 190 191 192 193 194 195 196 197 198 199 | } *rc = SQLITE_OK; return pSubOpen; } *rc = SQLITE_FULL; return NULL; } /************************* VFS Method Wrappers *****************************/ /* ** This is the xOpen method used for the "multiplex" VFS. ** ** Most of the work is done by the underlying original VFS. This method | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | } *rc = SQLITE_OK; return pSubOpen; } *rc = SQLITE_FULL; return NULL; } /* ** This is the implementation of the multiplex_control() SQL function. */ static void multiplexControlFunc( sqlite3_context *context, int argc, sqlite3_value **argv ){ int rc = SQLITE_OK; sqlite3 *db = sqlite3_context_db_handle(context); int op; int iVal; if( !db || argc!=2 ){ rc = SQLITE_ERROR; }else{ /* extract params */ op = sqlite3_value_int(argv[0]); iVal = sqlite3_value_int(argv[1]); /* map function op to file_control op */ switch( op ){ case 1: op = MULTIPLEX_CTRL_ENABLE; break; case 2: op = MULTIPLEX_CTRL_SET_CHUNK_SIZE; break; case 3: op = MULTIPLEX_CTRL_SET_MAX_CHUNKS; break; default: rc = SQLITE_NOTFOUND; break; } } if( rc==SQLITE_OK ){ rc = sqlite3_file_control(db, 0, op, &iVal); } sqlite3_result_error_code(context, rc); } /* ** This is the entry point to register the auto-extension for the ** multiplex_control() function. */ static int multiplexFuncInit( sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi ){ int rc; rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY, 0, multiplexControlFunc, 0, 0); return rc; } /************************* VFS Method Wrappers *****************************/ /* ** This is the xOpen method used for the "multiplex" VFS. ** ** Most of the work is done by the underlying original VFS. This method |
︙ | ︙ | |||
208 209 210 211 212 213 214 | int *pOutFlags /* Flags showing results of opening */ ){ int rc; /* Result code */ multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ | | | | | | | > > > | | > | > > > > > > > > | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 | int *pOutFlags /* Flags showing results of opening */ ){ int rc; /* Result code */ multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */ multiplexGroup *pGroup; /* Corresponding multiplexGroup object */ sqlite3_file *pSubOpen; /* Real file descriptor */ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int nName = multiplexStrlen30(zName); int i; int sz; UNUSED_PARAMETER(pVfs); /* We need to create a group structure and manage ** access to this group of files. */ multiplexEnter(); pMultiplexOpen = (multiplexConn*)pConn; /* allocate space for group */ sz = sizeof(multiplexGroup) /* multiplexGroup */ + (sizeof(sqlite3_file *)*SQLITE_MULTIPLEX_MAX_CHUNKS) /* pReal[] */ + (pOrigVfs->szOsFile*SQLITE_MULTIPLEX_MAX_CHUNKS) /* *pReal */ + SQLITE_MULTIPLEX_MAX_CHUNKS /* bOpen[] */ + nName + 1; /* zName */ #ifndef SQLITE_MULTIPLEX_EXT_OVWR sz += SQLITE_MULTIPLEX_EXT_SZ; assert(nName+SQLITE_MULTIPLEX_EXT_SZ < pOrigVfs->mxPathname); #else assert(nName >= SQLITE_MULTIPLEX_EXT_SZ); assert(nName < pOrigVfs->mxPathname); #endif pGroup = sqlite3_malloc( sz ); if( pGroup==0 ){ rc=SQLITE_NOMEM; }else{ /* assign pointers to extra space allocated */ char *p = (char *)&pGroup[1]; pMultiplexOpen->pGroup = pGroup; memset(pGroup, 0, sz); pGroup->bEnabled = -1; pGroup->nChunkSize = SQLITE_MULTIPLEX_CHUNK_SIZE; pGroup->nMaxChunks = SQLITE_MULTIPLEX_MAX_CHUNKS; pGroup->pReal = (sqlite3_file **)p; p += (sizeof(sqlite3_file *)*pGroup->nMaxChunks); for(i=0; i<pGroup->nMaxChunks; i++){ pGroup->pReal[i] = (sqlite3_file *)p; p += pOrigVfs->szOsFile; } /* bOpen[] vals should all be zero from memset above */ pGroup->bOpen = p; p += pGroup->nMaxChunks; pGroup->zName = p; /* save off base filename, name length, and original open flags */ memcpy(pGroup->zName, zName, nName+1); pGroup->nName = nName; pGroup->flags = flags; pSubOpen = multiplexSubOpen(pMultiplexOpen, 0, &rc, pOutFlags); if( pSubOpen ){ /* if this file is already larger than chunk size, disable ** the multiplex feature. */ sqlite3_int64 sz; int rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( (rc2==SQLITE_OK) && (sz>pGroup->nChunkSize) ){ pGroup->bEnabled = 0; } if( pSubOpen->pMethods->iVersion==1 ){ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; }else{ pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; } /* place this group at the head of our list */ pGroup->pNext = gMultiplex.pGroups; |
︙ | ︙ | |||
284 285 286 287 288 289 290 | static int multiplexDelete( sqlite3_vfs *pVfs, /* The multiplex VFS */ const char *zName, /* Name of file to delete */ int syncDir ){ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int rc = SQLITE_OK; | | | | > > | > > | > | 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | static int multiplexDelete( sqlite3_vfs *pVfs, /* The multiplex VFS */ const char *zName, /* Name of file to delete */ int syncDir ){ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int rc = SQLITE_OK; int nName = multiplexStrlen30(zName); int i; UNUSED_PARAMETER(pVfs); multiplexEnter(); memcpy(gMultiplex.zName, zName, nName+1); for(i=0; i<SQLITE_MULTIPLEX_MAX_CHUNKS; i++){ int rc2; int exists = 0; if( i ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif } rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); if( rc2==SQLITE_OK && exists){ /* if it exists, delete it */ rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, syncDir); if( rc2!=SQLITE_OK ) rc = rc2; }else{ /* stop at first "gap" */ break; |
︙ | ︙ | |||
363 364 365 366 367 368 369 | static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); /* close any open handles */ | | | 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | static int multiplexClose(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); /* close any open handles */ for(i=0; i<pGroup->nMaxChunks; i++){ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; int rc2 = pSubOpen->pMethods->xClose(pSubOpen); if( rc2!=SQLITE_OK ) rc = rc2; pGroup->bOpen[i] = 0; } } |
︙ | ︙ | |||
394 395 396 397 398 399 400 401 402 | static int multiplexRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; int rc = SQLITE_OK; multiplexEnter(); | > > > > > | | | | | | | | | | | | | | | > > > > > > | | | | | | | | | | | | | | | > > > > > > | | | | < | | | | | | | | | | | > > | > > | | | | | | | | | > | | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | static int multiplexRead( sqlite3_file *pConn, void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_READ : pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst); }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); if( rc!=SQLITE_OK ) break; pBuf = (char *)pBuf + iAmt; iOfst += iAmt; iAmt = extra; }else{ rc = SQLITE_IOERR_READ; break; } } } multiplexLeave(); return rc; } /* Pass xWrite requests thru to the original VFS after ** determining the correct chunk to operate on. ** Break up writes across chunk boundaries. */ static int multiplexWrite( sqlite3_file *pConn, const void *pBuf, int iAmt, sqlite3_int64 iOfst ){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_WRITE : pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst); }else{ while( iAmt > 0 ){ int i = (int)(iOfst / pGroup->nChunkSize); sqlite3_file *pSubOpen = multiplexSubOpen(p, i, &rc, NULL); if( pSubOpen ){ int extra = ((int)(iOfst % pGroup->nChunkSize) + iAmt) - pGroup->nChunkSize; if( extra<0 ) extra = 0; iAmt -= extra; rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst % pGroup->nChunkSize); if( rc!=SQLITE_OK ) break; pBuf = (char *)pBuf + iAmt; iOfst += iAmt; iAmt = extra; }else{ rc = SQLITE_IOERR_WRITE; break; } } } multiplexLeave(); return rc; } /* Pass xTruncate requests thru to the original VFS after ** determining the correct chunk to operate on. Delete any ** chunks above the truncate mark. */ static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_TRUNCATE : pSubOpen->pMethods->xTruncate(pSubOpen, size); }else{ int rc2; int i; sqlite3_file *pSubOpen; sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); /* delete the chunks above the truncate limit */ for(i=(int)(size / pGroup->nChunkSize)+1; i<pGroup->nMaxChunks; i++){ /* close any open chunks before deleting them */ if( pGroup->bOpen[i] ){ pSubOpen = pGroup->pReal[i]; rc2 = pSubOpen->pMethods->xClose(pSubOpen); if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; pGroup->bOpen[i] = 0; } #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif rc2 = pOrigVfs->xDelete(pOrigVfs, gMultiplex.zName, 0); if( rc2!=SQLITE_OK ) rc = SQLITE_IOERR_TRUNCATE; } pSubOpen = multiplexSubOpen(p, (int)(size / pGroup->nChunkSize), &rc2, NULL); if( pSubOpen ){ rc2 = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->nChunkSize); if( rc2!=SQLITE_OK ) rc = rc2; }else{ rc = SQLITE_IOERR_TRUNCATE; } } multiplexLeave(); return rc; } /* Pass xSync requests through to the original VFS without change */ static int multiplexSync(sqlite3_file *pConn, int flags){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int i; multiplexEnter(); for(i=0; i<pGroup->nMaxChunks; i++){ /* if we don't have it open, we don't need to sync it */ if( pGroup->bOpen[i] ){ sqlite3_file *pSubOpen = pGroup->pReal[i]; int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); if( rc2!=SQLITE_OK ) rc = rc2; } } |
︙ | ︙ | |||
523 524 525 526 527 528 529 | static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int rc2; int i; multiplexEnter(); | > > > > | | | | | | | | | | | | > > | > > | | > | | | | | | | | | | | | | | | | | | | | | > | 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 | static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_OK; int rc2; int i; multiplexEnter(); if( !pGroup->bEnabled ){ sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); rc = ( !pSubOpen ) ? SQLITE_IOERR_FSTAT : pSubOpen->pMethods->xFileSize(pSubOpen, pSize); }else{ *pSize = 0; for(i=0; i<pGroup->nMaxChunks; i++){ sqlite3_file *pSubOpen = NULL; /* if not opened already, check to see if the chunk exists */ if( pGroup->bOpen[i] ){ pSubOpen = pGroup->pReal[i]; }else{ sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */ int exists = 0; memcpy(gMultiplex.zName, pGroup->zName, pGroup->nName+1); if( i ){ #ifdef SQLITE_MULTIPLEX_EXT_OVWR sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName-SQLITE_MULTIPLEX_EXT_SZ, SQLITE_MULTIPLEX_EXT_FMT, i); #else sqlite3_snprintf(SQLITE_MULTIPLEX_EXT_SZ+1, gMultiplex.zName+pGroup->nName, SQLITE_MULTIPLEX_EXT_FMT, i); #endif } rc2 = pOrigVfs->xAccess(pOrigVfs, gMultiplex.zName, SQLITE_ACCESS_EXISTS, &exists); if( rc2==SQLITE_OK && exists){ /* if it exists, open it */ pSubOpen = multiplexSubOpen(p, i, &rc, NULL); }else{ /* stop at first "gap" */ break; } } if( pSubOpen ){ sqlite3_int64 sz; rc2 = pSubOpen->pMethods->xFileSize(pSubOpen, &sz); if( rc2!=SQLITE_OK ){ rc = rc2; }else{ if( sz>pGroup->nChunkSize ){ rc = SQLITE_IOERR_FSTAT; } *pSize += sz; } }else{ break; } } } multiplexLeave(); return rc; } /* Pass xLock requests through to the original VFS unchanged. |
︙ | ︙ | |||
604 605 606 607 608 609 610 | sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } return SQLITE_IOERR_CHECKRESERVEDLOCK; } | | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | | | | > > | | | 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 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 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 | sqlite3_file *pSubOpen = multiplexSubOpen(p, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut); } return SQLITE_IOERR_CHECKRESERVEDLOCK; } /* Pass xFileControl requests through to the original VFS unchanged, ** except for any MULTIPLEX_CTRL_* requests here. */ static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){ multiplexConn *p = (multiplexConn*)pConn; multiplexGroup *pGroup = p->pGroup; int rc = SQLITE_ERROR; sqlite3_file *pSubOpen; if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; switch( op ){ case MULTIPLEX_CTRL_ENABLE: if( pArg ) { int bEnabled = *(int *)pArg; pGroup->bEnabled = bEnabled; rc = SQLITE_OK; } break; case MULTIPLEX_CTRL_SET_CHUNK_SIZE: if( pArg ) { int nChunkSize = *(int *)pArg; if( nChunkSize<1 ){ rc = SQLITE_MISUSE; }else{ /* Round up to nearest multiple of MAX_PAGE_SIZE. */ nChunkSize = (nChunkSize + (MAX_PAGE_SIZE-1)); nChunkSize &= ~(MAX_PAGE_SIZE-1); pGroup->nChunkSize = nChunkSize; rc = SQLITE_OK; } } break; case MULTIPLEX_CTRL_SET_MAX_CHUNKS: if( pArg ) { int nMaxChunks = *(int *)pArg; if(( nMaxChunks<1 ) || ( nMaxChunks>SQLITE_MULTIPLEX_MAX_CHUNKS )){ rc = SQLITE_MISUSE; }else{ pGroup->nMaxChunks = nMaxChunks; rc = SQLITE_OK; } } break; case SQLITE_FCNTL_SIZE_HINT: case SQLITE_FCNTL_CHUNK_SIZE: /* no-op these */ rc = SQLITE_OK; break; default: pSubOpen = multiplexSubOpen(p, 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, 0, &rc, NULL); if( pSubOpen ){ return pSubOpen->pMethods->xSectorSize(pSubOpen); } return DEFAULT_SECTOR_SIZE; } /* Pass xDeviceCharacteristics requests through to the original VFS unchanged. */ static int multiplexDeviceCharacteristics(sqlite3_file *pConn){ multiplexConn *p = (multiplexConn*)pConn; int rc; |
︙ | ︙ | |||
702 703 704 705 706 707 708 | return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } /************************** Public Interfaces *****************************/ /* | | > | | | 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 | return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag); } return SQLITE_OK; } /************************** Public Interfaces *****************************/ /* ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() ** ** Use the VFS named zOrigVfsName as the VFS that does the actual work. ** Use the default if zOrigVfsName==NULL. ** ** The multiplex VFS shim is named "multiplex". It will become the default ** VFS if makeDefault is non-zero. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once ** during start-up. */ |
︙ | ︙ | |||
727 728 729 730 731 732 733 | return SQLITE_NOMEM; } gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); if( !gMultiplex.zName ){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } | < < | | 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 | return SQLITE_NOMEM; } gMultiplex.zName = sqlite3_malloc(pOrigVfs->mxPathname); if( !gMultiplex.zName ){ sqlite3_mutex_free(gMultiplex.pMutex); return SQLITE_NOMEM; } gMultiplex.pGroups = NULL; gMultiplex.isInitialized = 1; gMultiplex.pOrigVfs = pOrigVfs; gMultiplex.sThisVfs = *pOrigVfs; gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; gMultiplex.sThisVfs.xOpen = multiplexOpen; gMultiplex.sThisVfs.xDelete = multiplexDelete; gMultiplex.sThisVfs.xAccess = multiplexAccess; gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; gMultiplex.sThisVfs.xDlError = multiplexDlError; gMultiplex.sThisVfs.xDlSym = multiplexDlSym; |
︙ | ︙ | |||
769 770 771 772 773 774 775 776 777 778 779 | gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; gMultiplex.sIoMethodsV2.iVersion = 2; gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); return SQLITE_OK; } /* | > > > | < < < < < < < < < < < < < < < < < < < < < < | 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 | gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; gMultiplex.sIoMethodsV2.iVersion = 2; gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); sqlite3_auto_extension((void*)multiplexFuncInit); return SQLITE_OK; } /* ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() ** ** All SQLite database connections must be closed before calling this ** routine. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ int sqlite3_multiplex_shutdown(void){ if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; if( gMultiplex.pGroups ) return SQLITE_MISUSE; gMultiplex.isInitialized = 0; sqlite3_free(gMultiplex.zName); sqlite3_mutex_free(gMultiplex.pMutex); sqlite3_vfs_unregister(&gMultiplex.sThisVfs); memset(&gMultiplex, 0, sizeof(gMultiplex)); return SQLITE_OK; } /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST #include <tcl.h> extern const char *sqlite3TestErrorName(int); /* ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT */ static int test_multiplex_initialize( |
︙ | ︙ | |||
876 877 878 879 880 881 882 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 | /* Call sqlite3_multiplex_shutdown() */ rc = sqlite3_multiplex_shutdown(); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** tclcmd: sqlite3_multiplex_dump */ static int test_multiplex_dump( void * clientData, Tcl_Interp *interp, int objc, |
︙ | ︙ | |||
939 940 941 942 943 944 945 | Tcl_NewStringObj(pGroup->zName, -1)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nName)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->flags)); /* count number of chunks with open handles */ | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < > | 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 | Tcl_NewStringObj(pGroup->zName, -1)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nName)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->flags)); /* count number of chunks with open handles */ for(i=0; i<pGroup->nMaxChunks; i++){ if( pGroup->bOpen[i] ) nChunks++; } Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(nChunks)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nChunkSize)); Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewIntObj(pGroup->nMaxChunks)); Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); } multiplexLeave(); Tcl_SetObjResult(interp, pResult); return TCL_OK; } /* ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? */ static int test_multiplex_control( ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; /* Return code from file_control() */ int idx; /* Index in aSub[] */ Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ sqlite3 *db; /* Underlying db handle for HANDLE */ int iValue = 0; void *pArg = 0; struct SubCommand { const char *zName; int op; int argtype; } aSub[] = { { "enable", MULTIPLEX_CTRL_ENABLE, 1 }, { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 }, { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 }, { 0, 0, 0 } }; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE"); return TCL_ERROR; } if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ Tcl_AppendResult(interp, "expected database handle, got \"", 0); Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0); return TCL_ERROR; }else{ db = *(sqlite3 **)cmdInfo.objClientData; } rc = Tcl_GetIndexFromObjStruct( interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx ); if( rc!=TCL_OK ) return rc; switch( aSub[idx].argtype ){ case 1: if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){ return TCL_ERROR; } pArg = (void *)&iValue; break; default: Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND"); return TCL_ERROR; } rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg); Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC); return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR; } /* ** This routine registers the custom TCL commands defined in this ** module. This should be the only procedure visible from outside ** of this module. */ int Sqlitemultiplex_Init(Tcl_Interp *interp){ static struct { char *zName; Tcl_ObjCmdProc *xProc; } aCmd[] = { { "sqlite3_multiplex_initialize", test_multiplex_initialize }, { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, { "sqlite3_multiplex_dump", test_multiplex_dump }, { "sqlite3_multiplex_control", test_multiplex_control }, }; int i; for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); } return TCL_OK; } #endif |
Added src/test_multiplex.h.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | /* ** 2011 March 18 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** ** This file contains a VFS "shim" - a layer that sits in between the ** pager and the real VFS. ** ** This particular shim enforces a multiplex system on DB files. ** This shim shards/partitions a single DB file into smaller ** "chunks" such that the total DB file size may exceed the maximum ** file size of the underlying file system. ** */ #ifndef _TEST_MULTIPLEX_H #define _TEST_MULTIPLEX_H /* ** CAPI: File-control Operations Supported by Multiplex VFS ** ** Values interpreted by the xFileControl method of a Multiplex VFS db file-handle. ** ** MULTIPLEX_CTRL_ENABLE: ** This file control is used to enable or disable the multiplex ** shim. ** ** MULTIPLEX_CTRL_SET_CHUNK_SIZE: ** This file control is used to set the maximum allowed chunk ** size for a multiplex file set. The chunk size should be ** a multiple of SQLITE_MAX_PAGE_SIZE, and will be rounded up ** if not. ** ** MULTIPLEX_CTRL_SET_MAX_CHUNKS: ** This file control is used to set the maximum number of chunks ** allowed to be used for a mutliplex file set. */ #define MULTIPLEX_CTRL_ENABLE 214014 #define MULTIPLEX_CTRL_SET_CHUNK_SIZE 214015 #define MULTIPLEX_CTRL_SET_MAX_CHUNKS 214016 /* ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize() ** ** Use the VFS named zOrigVfsName as the VFS that does the actual work. ** Use the default if zOrigVfsName==NULL. ** ** The multiplex VFS shim is named "multiplex". It will become the default ** VFS if makeDefault is non-zero. ** ** An auto-extension is registered which will make the function ** multiplex_control() available to database connections. This ** function gives access to the xFileControl interface of the ** multiplex VFS shim. ** ** SELECT multiplex_control(<op>,<val>); ** ** <op>=1 MULTIPLEX_CTRL_ENABLE ** <val>=0 disable ** <val>=1 enable ** ** <op>=2 MULTIPLEX_CTRL_SET_CHUNK_SIZE ** <val> int, chunk size ** ** <op>=3 MULTIPLEX_CTRL_SET_MAX_CHUNKS ** <val> int, max chunks ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once ** during start-up. */ extern int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault); /* ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() ** ** All SQLite database connections must be closed before calling this ** routine. ** ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while ** shutting down in order to free all remaining multiplex groups. */ extern int sqlite3_multiplex_shutdown(void); #endif |
Changes to test/multiplex.test.
︙ | ︙ | |||
10 11 12 13 14 15 16 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl | | | | | | > > > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | #*********************************************************************** # set testdir [file dirname $argv0] source $testdir/tester.tcl source $testdir/malloc_common.tcl set g_chunk_size [ expr ($::SQLITE_MAX_PAGE_SIZE*16384) ] set g_max_chunks 32 # This handles appending the chunk number # to the end of the filename. if # SQLITE_MULTIPLEX_EXT_OVWR is defined, then # it overwrites the last 2 bytes of the # file name with the chunk number. proc multiplex_name {name chunk} { if {$chunk==0} { return $name } set num [format "%02d" $chunk] ifcapable {multiplex_ext_overwrite} { set name [string range $name 0 [expr [string length $name]-2-1]] } return $name$num } # This saves off the parameters and calls the # underlying sqlite3_multiplex_control() API. proc multiplex_set {db name chunk_size max_chunks} { global g_chunk_size global g_max_chunks set g_chunk_size [ expr (($chunk_size+($::SQLITE_MAX_PAGE_SIZE-1)) & ~($::SQLITE_MAX_PAGE_SIZE-1)) ] set g_max_chunks $max_chunks set rc [catch {sqlite3_multiplex_control $db $name chunk_size $chunk_size} msg] if { $rc==0 } { set rc [catch {sqlite3_multiplex_control $db $name max_chunks $max_chunks} msg] } list $msg } # This attempts to delete the base file and # and files with the chunk extension. proc multiplex_delete {name} { global g_max_chunks for {set i 0} {$i<$g_max_chunks} {incr i} { |
︙ | ︙ | |||
66 67 68 69 70 71 72 | do_test multiplex-1.4 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 } {SQLITE_OK} do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK} | > | | > > > > > > > > | > > | > > > > > > > | > > > > > > > | > > > > > > | > > > > > > | > > > | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | do_test multiplex-1.4 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.5 { sqlite3_multiplex_initialize "" 0 } {SQLITE_OK} do_test multiplex-1.6 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.7 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.8 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.9.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.9.2 { sqlite3 db test.db } {} do_test multiplex-1.9.3 { multiplex_set db main 32768 16 } {SQLITE_OK} do_test multiplex-1.9.4 { multiplex_set db main 32768 -1 } {SQLITE_MISUSE} do_test multiplex-1.9.5 { multiplex_set db main -1 16 } {SQLITE_MISUSE} do_test multiplex-1.9.6 { multiplex_set db main 31 16 } {SQLITE_OK} do_test multiplex-1.9.7 { multiplex_set db main 32768 100 } {SQLITE_MISUSE} do_test multiplex-1.9.8 { multiplex_set db main 1073741824 1 } {SQLITE_OK} do_test multiplex-1.9.9 { db close } {} do_test multiplex-1.9.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.10.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.10.2 { sqlite3 db test.db } {} do_test multiplex-1.10.3 { lindex [ catchsql { SELECT multiplex_control(2, 32768); } ] 0 } {0} do_test multiplex-1.10.4 { lindex [ catchsql { SELECT multiplex_control(3, -1); } ] 0 } {1} do_test multiplex-1.10.5 { lindex [ catchsql { SELECT multiplex_control(2, -1); } ] 0 } {1} do_test multiplex-1.10.6 { lindex [ catchsql { SELECT multiplex_control(2, 31); } ] 0 } {0} do_test multiplex-1.10.7 { lindex [ catchsql { SELECT multiplex_control(3, 100); } ] 0 } {1} do_test multiplex-1.10.8 { lindex [ catchsql { SELECT multiplex_control(2, 1073741824); } ] 0 } {0} do_test multiplex-1.10.9 { db close } {} do_test multiplex-1.10.10 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.11.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.11.2 { sqlite3 db test.db } {} do_test multiplex-1.11.3 { sqlite3_multiplex_control db main enable 0 } {SQLITE_OK} do_test multiplex-1.11.4 { sqlite3_multiplex_control db main enable 1 } {SQLITE_OK} do_test multiplex-1.11.5 { sqlite3_multiplex_control db main enable -1 } {SQLITE_OK} do_test multiplex-1.11.6 { db close } {} do_test multiplex-1.11.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.12.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.12.2 { sqlite3 db test.db } {} do_test multiplex-1.12.3 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} do_test multiplex-1.12.4 { lindex [ catchsql { SELECT multiplex_control(1, 1); } ] 0 } {0} do_test multiplex-1.12.5 { lindex [ catchsql { SELECT multiplex_control(1, -1); } ] 0 } {0} do_test multiplex-1.12.6 { db close } {} do_test multiplex-1.12.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-1.13.1 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-1.13.2 { sqlite3 db test.db } {} do_test multiplex-1.13.3 { lindex [ catchsql { SELECT multiplex_control(-1, 0); } ] 0 } {1} do_test multiplex-1.13.4 { lindex [ catchsql { SELECT multiplex_control(4, 1); } ] 0 } {1} do_test multiplex-1.13.6 { db close } {} do_test multiplex-1.13.7 { sqlite3_multiplex_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Some simple warm-body tests with a single database file in rollback # mode: # # multiplex-2.1.*: Test simple writing to a multiplex file. # # multiplex-2.2.*: More writing. # # multiplex-2.3.*: Open and close a second db. # # multiplex-2.4.*: Try to shutdown the multiplex system before closing the db # file. Check that this fails and the multiplex system still works # afterwards. Then close the database and successfully shut # down the multiplex system. # # multiplex-2.5.*: More reading/writing. # # multiplex-2.6.*: More reading/writing with varying small chunk sizes, as # well as varying journal mode. # # multiplex-2.7.*: Disable/enable tests. # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 do_test multiplex-2.1.2 { sqlite3 db test.db execsql { PRAGMA page_size=1024; PRAGMA auto_vacuum=OFF; PRAGMA journal_mode=DELETE; |
︙ | ︙ | |||
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | } {} do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} do_test multiplex-2.3.1 { sqlite3 db2 test2.db db2 close } {} do_test multiplex-2.4.1 { sqlite3_multiplex_shutdown } {SQLITE_MISUSE} do_test multiplex-2.4.2 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168} do_test multiplex-2.4.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-2.5.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 | > > | < | > > | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | } {} do_test multiplex-2.2.3 { file size [multiplex_name test.db 0] } {6144} do_test multiplex-2.3.1 { sqlite3 db2 test2.db db2 close } {} do_test multiplex-2.4.1 { sqlite3_multiplex_shutdown } {SQLITE_MISUSE} do_test multiplex-2.4.2 { execsql { INSERT INTO t1 VALUES(3, randomblob(1100)) } } {} do_test multiplex-2.4.4 { file size [multiplex_name test.db 0] } {7168} do_test multiplex-2.4.99 { db close sqlite3_multiplex_shutdown } {SQLITE_OK} do_test multiplex-2.5.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 sqlite3 db test.db multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-2.5.2 { execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a PRIMARY KEY, b); } } {delete} do_test multiplex-2.5.3 { execsql { INSERT INTO t1 VALUES(1, 'one'); INSERT INTO t1 VALUES(2, randomblob(4000)); INSERT INTO t1 VALUES(3, 'three'); INSERT INTO t1 VALUES(4, randomblob(4000)); INSERT INTO t1 VALUES(5, 'five'); INSERT INTO t1 VALUES(6, randomblob($g_chunk_size)); INSERT INTO t1 VALUES(7, randomblob($g_chunk_size)); } } {} do_test multiplex-2.5.4 { db eval {SELECT * FROM t1 WHERE a=1} } {1 one} |
︙ | ︙ | |||
201 202 203 204 205 206 207 | set all_journal_modes {delete persist truncate memory off} foreach jmode $all_journal_modes { for {set sz 151} {$sz<8000} {set sz [expr $sz+419]} { do_test multiplex-2.6.1.$sz.$jmode { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 | > | < | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | set all_journal_modes {delete persist truncate memory off} foreach jmode $all_journal_modes { for {set sz 151} {$sz<8000} {set sz [expr $sz+419]} { do_test multiplex-2.6.1.$sz.$jmode { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 sqlite3 db test.db multiplex_set db main $sz 32 } {SQLITE_OK} do_test multiplex-2.6.2.$sz.$jmode { db eval { PRAGMA page_size = 1024; PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode |
︙ | ︙ | |||
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | db close sqlite3_multiplex_shutdown } {SQLITE_OK} } } #------------------------------------------------------------------------- # Try some tests with more than one connection to a database file. Still # in rollback mode. # # multiplex-3.1.*: Two connections to a single database file. # # multiplex-3.2.*: Two connections to each of several database files (that # are in the same multiplex group). # do_test multiplex-3.1.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 | > > > > > > > > > > > > > > > > > > > > > > > > > > | < | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | db close sqlite3_multiplex_shutdown } {SQLITE_OK} } } do_test multiplex-2.7.1 { multiplex_delete test.db } {} do_test multiplex-2.7.2 { sqlite3_multiplex_initialize "" 1 } {SQLITE_OK} do_test multiplex-2.7.3 { sqlite3 db test.db } {} do_test multiplex-2.7.4 { lindex [ catchsql { SELECT multiplex_control(2, 65536); } ] 0 } {0} do_test multiplex-2.7.5 { lindex [ catchsql { SELECT multiplex_control(1, 0); } ] 0 } {0} do_test multiplex-2.7.6 { execsql { CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, randomblob(1000)); } } {} # verify only one file, and file size is less than chunks size do_test multiplex-2.7.7 { expr ([file size [multiplex_name test.db 0]] < 65536) } {1} do_test multiplex-2.7.8 { file exists [multiplex_name test.db 1] } {0} do_test multiplex-2.7.9 { execsql { INSERT INTO t1 VALUES(2, randomblob(65536)); } } {} # verify only one file, and file size exceeds chunks size do_test multiplex-2.7.10 { expr ([file size [multiplex_name test.db 0]] > 65536) } {1} do_test multiplex-2.7.11 { file exists [multiplex_name test.db 1] } {0} do_test multiplex-2.7.12 { db close } {} do_test multiplex-2.7.13 { sqlite3_multiplex_shutdown } {SQLITE_OK} #------------------------------------------------------------------------- # Try some tests with more than one connection to a database file. Still # in rollback mode. # # multiplex-3.1.*: Two connections to a single database file. # # multiplex-3.2.*: Two connections to each of several database files (that # are in the same multiplex group). # do_test multiplex-3.1.1 { multiplex_delete test.db sqlite3_multiplex_initialize "" 1 sqlite3 db test.db multiplex_set db main 32768 16 } {SQLITE_OK} do_test multiplex-3.1.2 { execsql { PRAGMA page_size = 1024; PRAGMA journal_mode = delete; PRAGMA auto_vacuum = off; CREATE TABLE t1(a PRIMARY KEY, b); INSERT INTO t1 VALUES(1, 'one'); } |
︙ | ︙ | |||
337 338 339 340 341 342 343 | foreach db {db1a db2a db2b db1b} { catch { $db close } } } {} #------------------------------------------------------------------------- # sqlite3_multiplex_initialize "" 1 | | | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 | foreach db {db1a db2a db2b db1b} { catch { $db close } } } {} #------------------------------------------------------------------------- # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 # Return a list of all currently defined multiplexs. proc multiplex_list {} { set allq {} foreach q [sqlite3_multiplex_dump] { lappend allq [lindex $q 0] } |
︙ | ︙ | |||
399 400 401 402 403 404 405 | #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # sqlite3_multiplex_initialize "" 1 | | | 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 | #------------------------------------------------------------------------- # The following tests test that the multiplex VFS handles malloc and IO # errors. # sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 do_faultsim_test multiplex-5.1 -prep { catch {db close} } -body { sqlite3 db test2.db } do_faultsim_test multiplex-5.2 -prep { |
︙ | ︙ | |||
444 445 446 447 448 449 450 | } {1 {unable to open database file}} catch { file delete test.db } do_faultsim_test multiplex-5.5 -prep { catch { sqlite3_multiplex_shutdown } } -body { sqlite3_multiplex_initialize "" 1 | | | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 | } {1 {unable to open database file}} catch { file delete test.db } do_faultsim_test multiplex-5.5 -prep { catch { sqlite3_multiplex_shutdown } } -body { sqlite3_multiplex_initialize "" 1 multiplex_set db main 32768 16 } # test that mismatch filesize is detected # # Do not run this test if $::G(perm:presql) is set. If it is set, then the # expected IO error will occur within the Tcl [sqlite3] wrapper, not within # the first SQL statement executed below. This breaks the test case. |
︙ | ︙ | |||
469 470 471 472 473 474 475 | PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode do_test multiplex-5.6.2.$jmode { execsql { CREATE TABLE t1(a, b); | | | | | | < > | | | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 | PRAGMA auto_vacuum = off; } db eval "PRAGMA journal_mode = $jmode;" } $jmode do_test multiplex-5.6.2.$jmode { execsql { CREATE TABLE t1(a, b); INSERT INTO t1 VALUES(1, randomblob(15000)); INSERT INTO t1 VALUES(2, randomblob(15000)); INSERT INTO t1 VALUES(3, randomblob(15000)); INSERT INTO t1 VALUES(4, randomblob(15000)); INSERT INTO t1 VALUES(5, randomblob(15000)); } db close sqlite3_multiplex_initialize "" 1 sqlite3 db test.db multiplex_set db main 4096 16 } {SQLITE_OK} do_test multiplex-5.6.3.$jmode { catchsql { INSERT INTO t1 VALUES(6, randomblob(15000)); } } {1 {disk I/O error}} do_test multiplex-5.6.4.$jmode { db close } {} } } catch { sqlite3_multiplex_shutdown } finish_test |