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 |