| OLD | NEW |
| 1 /* | 1 /* |
| 2 ** 2010 October 28 | 2 ** 2010 October 28 |
| 3 ** | 3 ** |
| 4 ** The author disclaims copyright to this source code. In place of | 4 ** The author disclaims copyright to this source code. In place of |
| 5 ** a legal notice, here is a blessing: | 5 ** a legal notice, here is a blessing: |
| 6 ** | 6 ** |
| 7 ** May you do good and not evil. | 7 ** May you do good and not evil. |
| 8 ** May you find forgiveness for yourself and forgive others. | 8 ** May you find forgiveness for yourself and forgive others. |
| 9 ** May you share freely, never taking more than you give. | 9 ** May you share freely, never taking more than you give. |
| 10 ** | 10 ** |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 61 /* | 61 /* |
| 62 ** These should be defined to be the same as the values in | 62 ** These should be defined to be the same as the values in |
| 63 ** sqliteInt.h. They are defined separately here so that | 63 ** sqliteInt.h. They are defined separately here so that |
| 64 ** the multiplex VFS shim can be built as a loadable | 64 ** the multiplex VFS shim can be built as a loadable |
| 65 ** module. | 65 ** module. |
| 66 */ | 66 */ |
| 67 #define UNUSED_PARAMETER(x) (void)(x) | 67 #define UNUSED_PARAMETER(x) (void)(x) |
| 68 #define MAX_PAGE_SIZE 0x10000 | 68 #define MAX_PAGE_SIZE 0x10000 |
| 69 #define DEFAULT_SECTOR_SIZE 0x1000 | 69 #define DEFAULT_SECTOR_SIZE 0x1000 |
| 70 | 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 /* Maximum chunk number */ | 71 /* Maximum chunk number */ |
| 85 #define MX_CHUNK_NUMBER 299 | 72 #define MX_CHUNK_NUMBER 299 |
| 86 | 73 |
| 87 /* First chunk for rollback journal files */ | 74 /* First chunk for rollback journal files */ |
| 88 #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400 | 75 #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400 |
| 89 #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700 | 76 #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700 |
| 90 | 77 |
| 91 | 78 |
| 92 /************************ Shim Definitions ******************************/ | 79 /************************ Shim Definitions ******************************/ |
| 93 | 80 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 sqlite3_file *p; /* Handle for the chunk */ | 119 sqlite3_file *p; /* Handle for the chunk */ |
| 133 char *z; /* Name of this chunk */ | 120 char *z; /* Name of this chunk */ |
| 134 } *aReal; /* list of all chunks */ | 121 } *aReal; /* list of all chunks */ |
| 135 int nReal; /* Number of chunks */ | 122 int nReal; /* Number of chunks */ |
| 136 char *zName; /* Base filename of this group */ | 123 char *zName; /* Base filename of this group */ |
| 137 int nName; /* Length of base filename */ | 124 int nName; /* Length of base filename */ |
| 138 int flags; /* Flags used for original opening */ | 125 int flags; /* Flags used for original opening */ |
| 139 unsigned int szChunk; /* Chunk size used for this group */ | 126 unsigned int szChunk; /* Chunk size used for this group */ |
| 140 unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */ | 127 unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */ |
| 141 unsigned char bTruncate; /* TRUE to enable truncation of databases */ | 128 unsigned char bTruncate; /* TRUE to enable truncation of databases */ |
| 142 multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */ | |
| 143 }; | 129 }; |
| 144 | 130 |
| 145 /* | 131 /* |
| 146 ** An instance of the following object represents each open connection | 132 ** An instance of the following object represents each open connection |
| 147 ** to a file that is multiplex'ed. This object is a | 133 ** to a file that is multiplex'ed. This object is a |
| 148 ** subclass of sqlite3_file. The sqlite3_file object for the underlying | 134 ** subclass of sqlite3_file. The sqlite3_file object for the underlying |
| 149 ** VFS is appended to this structure. | 135 ** VFS is appended to this structure. |
| 150 */ | 136 */ |
| 151 struct multiplexConn { | 137 struct multiplexConn { |
| 152 sqlite3_file base; /* Base class - must be first */ | 138 sqlite3_file base; /* Base class - must be first */ |
| (...skipping 27 matching lines...) Expand all Loading... |
| 180 ** has to create a wrapper sqlite3_file of the same version. Hence | 166 ** has to create a wrapper sqlite3_file of the same version. Hence |
| 181 ** there are two I/O method structures, one for version 1 and the other | 167 ** there are two I/O method structures, one for version 1 and the other |
| 182 ** for version 2. | 168 ** for version 2. |
| 183 */ | 169 */ |
| 184 sqlite3_io_methods sIoMethodsV1; | 170 sqlite3_io_methods sIoMethodsV1; |
| 185 sqlite3_io_methods sIoMethodsV2; | 171 sqlite3_io_methods sIoMethodsV2; |
| 186 | 172 |
| 187 /* True when this shim has been initialized. | 173 /* True when this shim has been initialized. |
| 188 */ | 174 */ |
| 189 int isInitialized; | 175 int isInitialized; |
| 190 | |
| 191 /* For run-time access any of the other global data structures in this | |
| 192 ** shim, the following mutex must be held. In practice, all this mutex | |
| 193 ** protects is add/remove operations to/from the linked list of group objects | |
| 194 ** starting at pGroups below. More specifically, it protects the value of | |
| 195 ** pGroups itself, and the pNext/pPrev fields of each multiplexGroup | |
| 196 ** structure. */ | |
| 197 sqlite3_mutex *pMutex; | |
| 198 | |
| 199 /* List of multiplexGroup objects. | |
| 200 */ | |
| 201 multiplexGroup *pGroups; | |
| 202 } gMultiplex; | 176 } gMultiplex; |
| 203 | 177 |
| 204 /************************* Utility Routines *********************************/ | 178 /************************* Utility Routines *********************************/ |
| 205 /* | 179 /* |
| 206 ** Acquire and release the mutex used to serialize access to the | |
| 207 ** list of multiplexGroups. | |
| 208 */ | |
| 209 static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); } | |
| 210 static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); } | |
| 211 | |
| 212 /* | |
| 213 ** Compute a string length that is limited to what can be stored in | 180 ** Compute a string length that is limited to what can be stored in |
| 214 ** lower 30 bits of a 32-bit signed integer. | 181 ** lower 30 bits of a 32-bit signed integer. |
| 215 ** | 182 ** |
| 216 ** The value returned will never be negative. Nor will it ever be greater | 183 ** The value returned will never be negative. Nor will it ever be greater |
| 217 ** than the actual length of the string. For very long strings (greater | 184 ** than the actual length of the string. For very long strings (greater |
| 218 ** than 1GiB) the value returned might be less than the true string length. | 185 ** than 1GiB) the value returned might be less than the true string length. |
| 219 */ | 186 */ |
| 220 static int multiplexStrlen30(const char *z){ | 187 static int multiplexStrlen30(const char *z){ |
| 221 const char *z2 = z; | 188 const char *z2 = z; |
| 222 if( z==0 ) return 0; | 189 if( z==0 ) return 0; |
| (...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 512 int sz = 0; | 479 int sz = 0; |
| 513 char *zToFree = 0; | 480 char *zToFree = 0; |
| 514 | 481 |
| 515 UNUSED_PARAMETER(pVfs); | 482 UNUSED_PARAMETER(pVfs); |
| 516 memset(pConn, 0, pVfs->szOsFile); | 483 memset(pConn, 0, pVfs->szOsFile); |
| 517 assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) ); | 484 assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) ); |
| 518 | 485 |
| 519 /* We need to create a group structure and manage | 486 /* We need to create a group structure and manage |
| 520 ** access to this group of files. | 487 ** access to this group of files. |
| 521 */ | 488 */ |
| 522 multiplexEnter(); | |
| 523 pMultiplexOpen = (multiplexConn*)pConn; | 489 pMultiplexOpen = (multiplexConn*)pConn; |
| 524 | 490 |
| 525 if( rc==SQLITE_OK ){ | 491 if( rc==SQLITE_OK ){ |
| 526 /* allocate space for group */ | 492 /* allocate space for group */ |
| 527 nName = zName ? multiplexStrlen30(zName) : 0; | 493 nName = zName ? multiplexStrlen30(zName) : 0; |
| 528 sz = sizeof(multiplexGroup) /* multiplexGroup */ | 494 sz = sizeof(multiplexGroup) /* multiplexGroup */ |
| 529 + nName + 1; /* zName */ | 495 + nName + 1; /* zName */ |
| 530 pGroup = sqlite3_malloc64( sz ); | 496 pGroup = sqlite3_malloc64( sz ); |
| 531 if( pGroup==0 ){ | 497 if( pGroup==0 ){ |
| 532 rc = SQLITE_NOMEM; | 498 rc = SQLITE_NOMEM; |
| 533 } | 499 } |
| 534 } | 500 } |
| 535 | 501 |
| 536 if( rc==SQLITE_OK ){ | 502 if( rc==SQLITE_OK ){ |
| 537 const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0; | 503 const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0; |
| 538 /* assign pointers to extra space allocated */ | 504 /* assign pointers to extra space allocated */ |
| 539 memset(pGroup, 0, sz); | 505 memset(pGroup, 0, sz); |
| 540 pMultiplexOpen->pGroup = pGroup; | 506 pMultiplexOpen->pGroup = pGroup; |
| 541 pGroup->bEnabled = (unsigned char)-1; | 507 pGroup->bEnabled = (unsigned char)-1; |
| 542 pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate", | 508 pGroup->bTruncate = (unsigned char)sqlite3_uri_boolean(zUri, "truncate", |
| 543 (flags & SQLITE_OPEN_MAIN_DB)==0); | 509 (flags & SQLITE_OPEN_MAIN_DB)==0); |
| 544 pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize", | 510 pGroup->szChunk = (int)sqlite3_uri_int64(zUri, "chunksize", |
| 545 SQLITE_MULTIPLEX_CHUNK_SIZE); | 511 SQLITE_MULTIPLEX_CHUNK_SIZE); |
| 546 pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff; | 512 pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff; |
| 547 if( zName ){ | 513 if( zName ){ |
| 548 char *p = (char *)&pGroup[1]; | 514 char *p = (char *)&pGroup[1]; |
| 549 pGroup->zName = p; | 515 pGroup->zName = p; |
| 550 memcpy(pGroup->zName, zName, nName+1); | 516 memcpy(pGroup->zName, zName, nName+1); |
| 551 pGroup->nName = nName; | 517 pGroup->nName = nName; |
| 552 } | 518 } |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 619 } | 585 } |
| 620 } | 586 } |
| 621 } | 587 } |
| 622 | 588 |
| 623 if( rc==SQLITE_OK ){ | 589 if( rc==SQLITE_OK ){ |
| 624 if( pSubOpen->pMethods->iVersion==1 ){ | 590 if( pSubOpen->pMethods->iVersion==1 ){ |
| 625 pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; | 591 pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1; |
| 626 }else{ | 592 }else{ |
| 627 pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; | 593 pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2; |
| 628 } | 594 } |
| 629 /* place this group at the head of our list */ | |
| 630 pGroup->pNext = gMultiplex.pGroups; | |
| 631 if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup; | |
| 632 gMultiplex.pGroups = pGroup; | |
| 633 }else{ | 595 }else{ |
| 634 multiplexFreeComponents(pGroup); | 596 multiplexFreeComponents(pGroup); |
| 635 sqlite3_free(pGroup); | 597 sqlite3_free(pGroup); |
| 636 } | 598 } |
| 637 } | 599 } |
| 638 multiplexLeave(); | |
| 639 sqlite3_free(zToFree); | 600 sqlite3_free(zToFree); |
| 640 return rc; | 601 return rc; |
| 641 } | 602 } |
| 642 | 603 |
| 643 /* | 604 /* |
| 644 ** This is the xDelete method used for the "multiplex" VFS. | 605 ** This is the xDelete method used for the "multiplex" VFS. |
| 645 ** It attempts to delete the filename specified. | 606 ** It attempts to delete the filename specified. |
| 646 */ | 607 */ |
| 647 static int multiplexDelete( | 608 static int multiplexDelete( |
| 648 sqlite3_vfs *pVfs, /* The multiplex VFS */ | 609 sqlite3_vfs *pVfs, /* The multiplex VFS */ |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 710 static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){ | 671 static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){ |
| 711 return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c); | 672 return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c); |
| 712 } | 673 } |
| 713 static int multiplexSleep(sqlite3_vfs *a, int b){ | 674 static int multiplexSleep(sqlite3_vfs *a, int b){ |
| 714 return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b); | 675 return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b); |
| 715 } | 676 } |
| 716 static int multiplexCurrentTime(sqlite3_vfs *a, double *b){ | 677 static int multiplexCurrentTime(sqlite3_vfs *a, double *b){ |
| 717 return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b); | 678 return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b); |
| 718 } | 679 } |
| 719 static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){ | 680 static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){ |
| 720 return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c); | 681 if( gMultiplex.pOrigVfs->xGetLastError ){ |
| 682 return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c); |
| 683 }else{ |
| 684 return 0; |
| 685 } |
| 721 } | 686 } |
| 722 static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){ | 687 static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){ |
| 723 return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b); | 688 return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b); |
| 724 } | 689 } |
| 725 | 690 |
| 726 /************************ I/O Method Wrappers *******************************/ | 691 /************************ I/O Method Wrappers *******************************/ |
| 727 | 692 |
| 728 /* xClose requests get passed through to the original VFS. | 693 /* xClose requests get passed through to the original VFS. |
| 729 ** We loop over all open chunk handles and close them. | 694 ** We loop over all open chunk handles and close them. |
| 730 ** The group structure for this file is unlinked from | 695 ** The group structure for this file is unlinked from |
| 731 ** our list of groups and freed. | 696 ** our list of groups and freed. |
| 732 */ | 697 */ |
| 733 static int multiplexClose(sqlite3_file *pConn){ | 698 static int multiplexClose(sqlite3_file *pConn){ |
| 734 multiplexConn *p = (multiplexConn*)pConn; | 699 multiplexConn *p = (multiplexConn*)pConn; |
| 735 multiplexGroup *pGroup = p->pGroup; | 700 multiplexGroup *pGroup = p->pGroup; |
| 736 int rc = SQLITE_OK; | 701 int rc = SQLITE_OK; |
| 737 multiplexEnter(); | |
| 738 multiplexFreeComponents(pGroup); | 702 multiplexFreeComponents(pGroup); |
| 739 /* remove from linked list */ | |
| 740 if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev; | |
| 741 if( pGroup->pPrev ){ | |
| 742 pGroup->pPrev->pNext = pGroup->pNext; | |
| 743 }else{ | |
| 744 gMultiplex.pGroups = pGroup->pNext; | |
| 745 } | |
| 746 sqlite3_free(pGroup); | 703 sqlite3_free(pGroup); |
| 747 multiplexLeave(); | |
| 748 return rc; | 704 return rc; |
| 749 } | 705 } |
| 750 | 706 |
| 751 /* Pass xRead requests thru to the original VFS after | 707 /* Pass xRead requests thru to the original VFS after |
| 752 ** determining the correct chunk to operate on. | 708 ** determining the correct chunk to operate on. |
| 753 ** Break up reads across chunk boundaries. | 709 ** Break up reads across chunk boundaries. |
| 754 */ | 710 */ |
| 755 static int multiplexRead( | 711 static int multiplexRead( |
| 756 sqlite3_file *pConn, | 712 sqlite3_file *pConn, |
| 757 void *pBuf, | 713 void *pBuf, |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 834 } | 790 } |
| 835 | 791 |
| 836 /* Pass xTruncate requests thru to the original VFS after | 792 /* Pass xTruncate requests thru to the original VFS after |
| 837 ** determining the correct chunk to operate on. Delete any | 793 ** determining the correct chunk to operate on. Delete any |
| 838 ** chunks above the truncate mark. | 794 ** chunks above the truncate mark. |
| 839 */ | 795 */ |
| 840 static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ | 796 static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){ |
| 841 multiplexConn *p = (multiplexConn*)pConn; | 797 multiplexConn *p = (multiplexConn*)pConn; |
| 842 multiplexGroup *pGroup = p->pGroup; | 798 multiplexGroup *pGroup = p->pGroup; |
| 843 int rc = SQLITE_OK; | 799 int rc = SQLITE_OK; |
| 844 multiplexEnter(); | |
| 845 if( !pGroup->bEnabled ){ | 800 if( !pGroup->bEnabled ){ |
| 846 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); | 801 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); |
| 847 if( pSubOpen==0 ){ | 802 if( pSubOpen==0 ){ |
| 848 rc = SQLITE_IOERR_TRUNCATE; | 803 rc = SQLITE_IOERR_TRUNCATE; |
| 849 }else{ | 804 }else{ |
| 850 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); | 805 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size); |
| 851 } | 806 } |
| 852 }else{ | 807 }else{ |
| 853 int i; | 808 int i; |
| 854 int iBaseGroup = (int)(size / pGroup->szChunk); | 809 int iBaseGroup = (int)(size / pGroup->szChunk); |
| (...skipping 11 matching lines...) Expand all Loading... |
| 866 } | 821 } |
| 867 } | 822 } |
| 868 if( rc==SQLITE_OK ){ | 823 if( rc==SQLITE_OK ){ |
| 869 pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0); | 824 pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0); |
| 870 if( pSubOpen ){ | 825 if( pSubOpen ){ |
| 871 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk); | 826 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk); |
| 872 } | 827 } |
| 873 } | 828 } |
| 874 if( rc ) rc = SQLITE_IOERR_TRUNCATE; | 829 if( rc ) rc = SQLITE_IOERR_TRUNCATE; |
| 875 } | 830 } |
| 876 multiplexLeave(); | |
| 877 return rc; | 831 return rc; |
| 878 } | 832 } |
| 879 | 833 |
| 880 /* Pass xSync requests through to the original VFS without change | 834 /* Pass xSync requests through to the original VFS without change |
| 881 */ | 835 */ |
| 882 static int multiplexSync(sqlite3_file *pConn, int flags){ | 836 static int multiplexSync(sqlite3_file *pConn, int flags){ |
| 883 multiplexConn *p = (multiplexConn*)pConn; | 837 multiplexConn *p = (multiplexConn*)pConn; |
| 884 multiplexGroup *pGroup = p->pGroup; | 838 multiplexGroup *pGroup = p->pGroup; |
| 885 int rc = SQLITE_OK; | 839 int rc = SQLITE_OK; |
| 886 int i; | 840 int i; |
| 887 multiplexEnter(); | |
| 888 for(i=0; i<pGroup->nReal; i++){ | 841 for(i=0; i<pGroup->nReal; i++){ |
| 889 sqlite3_file *pSubOpen = pGroup->aReal[i].p; | 842 sqlite3_file *pSubOpen = pGroup->aReal[i].p; |
| 890 if( pSubOpen ){ | 843 if( pSubOpen ){ |
| 891 int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); | 844 int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags); |
| 892 if( rc2!=SQLITE_OK ) rc = rc2; | 845 if( rc2!=SQLITE_OK ) rc = rc2; |
| 893 } | 846 } |
| 894 } | 847 } |
| 895 multiplexLeave(); | |
| 896 return rc; | 848 return rc; |
| 897 } | 849 } |
| 898 | 850 |
| 899 /* Pass xFileSize requests through to the original VFS. | 851 /* Pass xFileSize requests through to the original VFS. |
| 900 ** Aggregate the size of all the chunks before returning. | 852 ** Aggregate the size of all the chunks before returning. |
| 901 */ | 853 */ |
| 902 static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ | 854 static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){ |
| 903 multiplexConn *p = (multiplexConn*)pConn; | 855 multiplexConn *p = (multiplexConn*)pConn; |
| 904 multiplexGroup *pGroup = p->pGroup; | 856 multiplexGroup *pGroup = p->pGroup; |
| 905 int rc = SQLITE_OK; | 857 int rc = SQLITE_OK; |
| 906 int i; | 858 int i; |
| 907 multiplexEnter(); | |
| 908 if( !pGroup->bEnabled ){ | 859 if( !pGroup->bEnabled ){ |
| 909 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); | 860 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0); |
| 910 if( pSubOpen==0 ){ | 861 if( pSubOpen==0 ){ |
| 911 rc = SQLITE_IOERR_FSTAT; | 862 rc = SQLITE_IOERR_FSTAT; |
| 912 }else{ | 863 }else{ |
| 913 rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize); | 864 rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize); |
| 914 } | 865 } |
| 915 }else{ | 866 }else{ |
| 916 *pSize = 0; | 867 *pSize = 0; |
| 917 for(i=0; rc==SQLITE_OK; i++){ | 868 for(i=0; rc==SQLITE_OK; i++){ |
| 918 sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc); | 869 sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc); |
| 919 if( sz==0 ) break; | 870 if( sz==0 ) break; |
| 920 *pSize = i*(sqlite3_int64)pGroup->szChunk + sz; | 871 *pSize = i*(sqlite3_int64)pGroup->szChunk + sz; |
| 921 } | 872 } |
| 922 } | 873 } |
| 923 multiplexLeave(); | |
| 924 return rc; | 874 return rc; |
| 925 } | 875 } |
| 926 | 876 |
| 927 /* Pass xLock requests through to the original VFS unchanged. | 877 /* Pass xLock requests through to the original VFS unchanged. |
| 928 */ | 878 */ |
| 929 static int multiplexLock(sqlite3_file *pConn, int lock){ | 879 static int multiplexLock(sqlite3_file *pConn, int lock){ |
| 930 multiplexConn *p = (multiplexConn*)pConn; | 880 multiplexConn *p = (multiplexConn*)pConn; |
| 931 int rc; | 881 int rc; |
| 932 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0); | 882 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0); |
| 933 if( pSubOpen ){ | 883 if( pSubOpen ){ |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 967 multiplexConn *p = (multiplexConn*)pConn; | 917 multiplexConn *p = (multiplexConn*)pConn; |
| 968 multiplexGroup *pGroup = p->pGroup; | 918 multiplexGroup *pGroup = p->pGroup; |
| 969 int rc = SQLITE_ERROR; | 919 int rc = SQLITE_ERROR; |
| 970 sqlite3_file *pSubOpen; | 920 sqlite3_file *pSubOpen; |
| 971 | 921 |
| 972 if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; | 922 if( !gMultiplex.isInitialized ) return SQLITE_MISUSE; |
| 973 switch( op ){ | 923 switch( op ){ |
| 974 case MULTIPLEX_CTRL_ENABLE: | 924 case MULTIPLEX_CTRL_ENABLE: |
| 975 if( pArg ) { | 925 if( pArg ) { |
| 976 int bEnabled = *(int *)pArg; | 926 int bEnabled = *(int *)pArg; |
| 977 pGroup->bEnabled = bEnabled; | 927 pGroup->bEnabled = (unsigned char)bEnabled; |
| 978 rc = SQLITE_OK; | 928 rc = SQLITE_OK; |
| 979 } | 929 } |
| 980 break; | 930 break; |
| 981 case MULTIPLEX_CTRL_SET_CHUNK_SIZE: | 931 case MULTIPLEX_CTRL_SET_CHUNK_SIZE: |
| 982 if( pArg ) { | 932 if( pArg ) { |
| 983 unsigned int szChunk = *(unsigned*)pArg; | 933 unsigned int szChunk = *(unsigned*)pArg; |
| 984 if( szChunk<1 ){ | 934 if( szChunk<1 ){ |
| 985 rc = SQLITE_MISUSE; | 935 rc = SQLITE_MISUSE; |
| 986 }else{ | 936 }else{ |
| 987 /* Round up to nearest multiple of MAX_PAGE_SIZE. */ | 937 /* Round up to nearest multiple of MAX_PAGE_SIZE. */ |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1140 ** | 1090 ** |
| 1141 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once | 1091 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once |
| 1142 ** during start-up. | 1092 ** during start-up. |
| 1143 */ | 1093 */ |
| 1144 int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ | 1094 int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){ |
| 1145 sqlite3_vfs *pOrigVfs; | 1095 sqlite3_vfs *pOrigVfs; |
| 1146 if( gMultiplex.isInitialized ) return SQLITE_MISUSE; | 1096 if( gMultiplex.isInitialized ) return SQLITE_MISUSE; |
| 1147 pOrigVfs = sqlite3_vfs_find(zOrigVfsName); | 1097 pOrigVfs = sqlite3_vfs_find(zOrigVfsName); |
| 1148 if( pOrigVfs==0 ) return SQLITE_ERROR; | 1098 if( pOrigVfs==0 ) return SQLITE_ERROR; |
| 1149 assert( pOrigVfs!=&gMultiplex.sThisVfs ); | 1099 assert( pOrigVfs!=&gMultiplex.sThisVfs ); |
| 1150 gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); | |
| 1151 if( !gMultiplex.pMutex ){ | |
| 1152 return SQLITE_NOMEM; | |
| 1153 } | |
| 1154 gMultiplex.pGroups = NULL; | |
| 1155 gMultiplex.isInitialized = 1; | 1100 gMultiplex.isInitialized = 1; |
| 1156 gMultiplex.pOrigVfs = pOrigVfs; | 1101 gMultiplex.pOrigVfs = pOrigVfs; |
| 1157 gMultiplex.sThisVfs = *pOrigVfs; | 1102 gMultiplex.sThisVfs = *pOrigVfs; |
| 1158 gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); | 1103 gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn); |
| 1159 gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; | 1104 gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME; |
| 1160 gMultiplex.sThisVfs.xOpen = multiplexOpen; | 1105 gMultiplex.sThisVfs.xOpen = multiplexOpen; |
| 1161 gMultiplex.sThisVfs.xDelete = multiplexDelete; | 1106 gMultiplex.sThisVfs.xDelete = multiplexDelete; |
| 1162 gMultiplex.sThisVfs.xAccess = multiplexAccess; | 1107 gMultiplex.sThisVfs.xAccess = multiplexAccess; |
| 1163 gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; | 1108 gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname; |
| 1164 gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; | 1109 gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1186 gMultiplex.sIoMethodsV1.xDeviceCharacteristics = | 1131 gMultiplex.sIoMethodsV1.xDeviceCharacteristics = |
| 1187 multiplexDeviceCharacteristics; | 1132 multiplexDeviceCharacteristics; |
| 1188 gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; | 1133 gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1; |
| 1189 gMultiplex.sIoMethodsV2.iVersion = 2; | 1134 gMultiplex.sIoMethodsV2.iVersion = 2; |
| 1190 gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; | 1135 gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap; |
| 1191 gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; | 1136 gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock; |
| 1192 gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; | 1137 gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier; |
| 1193 gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; | 1138 gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap; |
| 1194 sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); | 1139 sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault); |
| 1195 | 1140 |
| 1196 sqlite3_auto_extension((void*)multiplexFuncInit); | 1141 sqlite3_auto_extension((void(*)(void))multiplexFuncInit); |
| 1197 | 1142 |
| 1198 return SQLITE_OK; | 1143 return SQLITE_OK; |
| 1199 } | 1144 } |
| 1200 | 1145 |
| 1201 /* | 1146 /* |
| 1202 ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() | 1147 ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown() |
| 1203 ** | 1148 ** |
| 1204 ** All SQLite database connections must be closed before calling this | 1149 ** All SQLite database connections must be closed before calling this |
| 1205 ** routine. | 1150 ** routine. |
| 1206 ** | 1151 ** |
| 1207 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while | 1152 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while |
| 1208 ** shutting down in order to free all remaining multiplex groups. | 1153 ** shutting down in order to free all remaining multiplex groups. |
| 1209 */ | 1154 */ |
| 1210 int sqlite3_multiplex_shutdown(int eForce){ | 1155 int sqlite3_multiplex_shutdown(int eForce){ |
| 1211 int rc = SQLITE_OK; | 1156 int rc = SQLITE_OK; |
| 1212 if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; | 1157 if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE; |
| 1213 if( gMultiplex.pGroups ){ | |
| 1214 sqlite3_log(SQLITE_MISUSE, "sqlite3_multiplex_shutdown() called " | |
| 1215 "while database connections are still open"); | |
| 1216 if( !eForce ) return SQLITE_MISUSE; | |
| 1217 rc = SQLITE_MISUSE; | |
| 1218 } | |
| 1219 gMultiplex.isInitialized = 0; | 1158 gMultiplex.isInitialized = 0; |
| 1220 sqlite3_mutex_free(gMultiplex.pMutex); | |
| 1221 sqlite3_vfs_unregister(&gMultiplex.sThisVfs); | 1159 sqlite3_vfs_unregister(&gMultiplex.sThisVfs); |
| 1222 memset(&gMultiplex, 0, sizeof(gMultiplex)); | 1160 memset(&gMultiplex, 0, sizeof(gMultiplex)); |
| 1223 return rc; | 1161 return rc; |
| 1224 } | 1162 } |
| 1225 | 1163 |
| 1226 /***************************** Test Code ***********************************/ | 1164 /***************************** Test Code ***********************************/ |
| 1227 #ifdef SQLITE_TEST | 1165 #ifdef SQLITE_TEST |
| 1228 #include <tcl.h> | 1166 #if defined(INCLUDE_SQLITE_TCL_H) |
| 1167 # include "sqlite_tcl.h" |
| 1168 #else |
| 1169 # include "tcl.h" |
| 1170 # ifndef SQLITE_TCLAPI |
| 1171 # define SQLITE_TCLAPI |
| 1172 # endif |
| 1173 #endif |
| 1229 extern const char *sqlite3ErrName(int); | 1174 extern const char *sqlite3ErrName(int); |
| 1230 | 1175 |
| 1231 | 1176 |
| 1232 /* | 1177 /* |
| 1233 ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT | 1178 ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT |
| 1234 */ | 1179 */ |
| 1235 static int test_multiplex_initialize( | 1180 static int SQLITE_TCLAPI test_multiplex_initialize( |
| 1236 void * clientData, | 1181 void * clientData, |
| 1237 Tcl_Interp *interp, | 1182 Tcl_Interp *interp, |
| 1238 int objc, | 1183 int objc, |
| 1239 Tcl_Obj *CONST objv[] | 1184 Tcl_Obj *CONST objv[] |
| 1240 ){ | 1185 ){ |
| 1241 const char *zName; /* Name of new multiplex VFS */ | 1186 const char *zName; /* Name of new multiplex VFS */ |
| 1242 int makeDefault; /* True to make the new VFS the default */ | 1187 int makeDefault; /* True to make the new VFS the default */ |
| 1243 int rc; /* Value returned by multiplex_initialize() */ | 1188 int rc; /* Value returned by multiplex_initialize() */ |
| 1244 | 1189 |
| 1245 UNUSED_PARAMETER(clientData); | 1190 UNUSED_PARAMETER(clientData); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1256 /* Call sqlite3_multiplex_initialize() */ | 1201 /* Call sqlite3_multiplex_initialize() */ |
| 1257 rc = sqlite3_multiplex_initialize(zName, makeDefault); | 1202 rc = sqlite3_multiplex_initialize(zName, makeDefault); |
| 1258 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 1203 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); |
| 1259 | 1204 |
| 1260 return TCL_OK; | 1205 return TCL_OK; |
| 1261 } | 1206 } |
| 1262 | 1207 |
| 1263 /* | 1208 /* |
| 1264 ** tclcmd: sqlite3_multiplex_shutdown | 1209 ** tclcmd: sqlite3_multiplex_shutdown |
| 1265 */ | 1210 */ |
| 1266 static int test_multiplex_shutdown( | 1211 static int SQLITE_TCLAPI test_multiplex_shutdown( |
| 1267 void * clientData, | 1212 void * clientData, |
| 1268 Tcl_Interp *interp, | 1213 Tcl_Interp *interp, |
| 1269 int objc, | 1214 int objc, |
| 1270 Tcl_Obj *CONST objv[] | 1215 Tcl_Obj *CONST objv[] |
| 1271 ){ | 1216 ){ |
| 1272 int rc; /* Value returned by multiplex_shutdown() */ | 1217 int rc; /* Value returned by multiplex_shutdown() */ |
| 1273 | 1218 |
| 1274 UNUSED_PARAMETER(clientData); | 1219 UNUSED_PARAMETER(clientData); |
| 1275 | 1220 |
| 1276 if( objc==2 && strcmp(Tcl_GetString(objv[1]),"-force")!=0 ){ | 1221 if( objc==2 && strcmp(Tcl_GetString(objv[1]),"-force")!=0 ){ |
| 1277 objc = 3; | 1222 objc = 3; |
| 1278 } | 1223 } |
| 1279 if( (objc!=1 && objc!=2) ){ | 1224 if( (objc!=1 && objc!=2) ){ |
| 1280 Tcl_WrongNumArgs(interp, 1, objv, "?-force?"); | 1225 Tcl_WrongNumArgs(interp, 1, objv, "?-force?"); |
| 1281 return TCL_ERROR; | 1226 return TCL_ERROR; |
| 1282 } | 1227 } |
| 1283 | 1228 |
| 1284 /* Call sqlite3_multiplex_shutdown() */ | 1229 /* Call sqlite3_multiplex_shutdown() */ |
| 1285 rc = sqlite3_multiplex_shutdown(objc==2); | 1230 rc = sqlite3_multiplex_shutdown(objc==2); |
| 1286 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); | 1231 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC); |
| 1287 | 1232 |
| 1288 return TCL_OK; | 1233 return TCL_OK; |
| 1289 } | 1234 } |
| 1290 | 1235 |
| 1291 /* | 1236 /* |
| 1292 ** tclcmd: sqlite3_multiplex_dump | |
| 1293 */ | |
| 1294 static int test_multiplex_dump( | |
| 1295 void * clientData, | |
| 1296 Tcl_Interp *interp, | |
| 1297 int objc, | |
| 1298 Tcl_Obj *CONST objv[] | |
| 1299 ){ | |
| 1300 Tcl_Obj *pResult; | |
| 1301 Tcl_Obj *pGroupTerm; | |
| 1302 multiplexGroup *pGroup; | |
| 1303 int i; | |
| 1304 int nChunks = 0; | |
| 1305 | |
| 1306 UNUSED_PARAMETER(clientData); | |
| 1307 UNUSED_PARAMETER(objc); | |
| 1308 UNUSED_PARAMETER(objv); | |
| 1309 | |
| 1310 pResult = Tcl_NewObj(); | |
| 1311 multiplexEnter(); | |
| 1312 for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){ | |
| 1313 pGroupTerm = Tcl_NewObj(); | |
| 1314 | |
| 1315 if( pGroup->zName ){ | |
| 1316 pGroup->zName[pGroup->nName] = '\0'; | |
| 1317 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1318 Tcl_NewStringObj(pGroup->zName, -1)); | |
| 1319 }else{ | |
| 1320 Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj()); | |
| 1321 } | |
| 1322 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1323 Tcl_NewIntObj(pGroup->nName)); | |
| 1324 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1325 Tcl_NewIntObj(pGroup->flags)); | |
| 1326 | |
| 1327 /* count number of chunks with open handles */ | |
| 1328 for(i=0; i<pGroup->nReal; i++){ | |
| 1329 if( pGroup->aReal[i].p!=0 ) nChunks++; | |
| 1330 } | |
| 1331 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1332 Tcl_NewIntObj(nChunks)); | |
| 1333 | |
| 1334 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1335 Tcl_NewIntObj(pGroup->szChunk)); | |
| 1336 Tcl_ListObjAppendElement(interp, pGroupTerm, | |
| 1337 Tcl_NewIntObj(pGroup->nReal)); | |
| 1338 | |
| 1339 Tcl_ListObjAppendElement(interp, pResult, pGroupTerm); | |
| 1340 } | |
| 1341 multiplexLeave(); | |
| 1342 Tcl_SetObjResult(interp, pResult); | |
| 1343 return TCL_OK; | |
| 1344 } | |
| 1345 | |
| 1346 /* | |
| 1347 ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? | 1237 ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE? |
| 1348 */ | 1238 */ |
| 1349 static int test_multiplex_control( | 1239 static int SQLITE_TCLAPI test_multiplex_control( |
| 1350 ClientData cd, | 1240 ClientData cd, |
| 1351 Tcl_Interp *interp, | 1241 Tcl_Interp *interp, |
| 1352 int objc, | 1242 int objc, |
| 1353 Tcl_Obj *CONST objv[] | 1243 Tcl_Obj *CONST objv[] |
| 1354 ){ | 1244 ){ |
| 1355 int rc; /* Return code from file_control() */ | 1245 int rc; /* Return code from file_control() */ |
| 1356 int idx; /* Index in aSub[] */ | 1246 int idx; /* Index in aSub[] */ |
| 1357 Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ | 1247 Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */ |
| 1358 sqlite3 *db; /* Underlying db handle for HANDLE */ | 1248 sqlite3 *db; /* Underlying db handle for HANDLE */ |
| 1359 int iValue = 0; | 1249 int iValue = 0; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1410 ** module. This should be the only procedure visible from outside | 1300 ** module. This should be the only procedure visible from outside |
| 1411 ** of this module. | 1301 ** of this module. |
| 1412 */ | 1302 */ |
| 1413 int Sqlitemultiplex_Init(Tcl_Interp *interp){ | 1303 int Sqlitemultiplex_Init(Tcl_Interp *interp){ |
| 1414 static struct { | 1304 static struct { |
| 1415 char *zName; | 1305 char *zName; |
| 1416 Tcl_ObjCmdProc *xProc; | 1306 Tcl_ObjCmdProc *xProc; |
| 1417 } aCmd[] = { | 1307 } aCmd[] = { |
| 1418 { "sqlite3_multiplex_initialize", test_multiplex_initialize }, | 1308 { "sqlite3_multiplex_initialize", test_multiplex_initialize }, |
| 1419 { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, | 1309 { "sqlite3_multiplex_shutdown", test_multiplex_shutdown }, |
| 1420 { "sqlite3_multiplex_dump", test_multiplex_dump }, | |
| 1421 { "sqlite3_multiplex_control", test_multiplex_control }, | 1310 { "sqlite3_multiplex_control", test_multiplex_control }, |
| 1422 }; | 1311 }; |
| 1423 int i; | 1312 int i; |
| 1424 | 1313 |
| 1425 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ | 1314 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){ |
| 1426 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); | 1315 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0); |
| 1427 } | 1316 } |
| 1428 | 1317 |
| 1429 return TCL_OK; | 1318 return TCL_OK; |
| 1430 } | 1319 } |
| 1431 #endif | 1320 #endif |
| OLD | NEW |