4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
7 ** May you do good and not evil.
8 ** May you find forgiveness for yourself and forgive others.
9 ** May you share freely, never taking more than you give.
11 *************************************************************************
13 ** This file contains a VFS "shim" - a layer that sits in between the
14 ** pager and the real VFS - that breaks up a very large database file
15 ** into two or more smaller files on disk. This is useful, for example,
16 ** in order to support large, multi-gigabyte databases on older filesystems
17 ** that limit the maximum file size to 2 GiB.
21 ** Compile this source file and link it with your application. Then
22 ** at start-time, invoke the following procedure:
24 ** int sqlite3_multiplex_initialize(
25 ** const char *zOrigVfsName, // The underlying real VFS
26 ** int makeDefault // True to make multiplex the default VFS
29 ** The procedure call above will create and register a new VFS shim named
30 ** "multiplex". The multiplex VFS will use the VFS named by zOrigVfsName to
31 ** do the actual disk I/O. (The zOrigVfsName parameter may be NULL, in
32 ** which case the default VFS at the moment sqlite3_multiplex_initialize()
33 ** is called will be used as the underlying real VFS.)
35 ** If the makeDefault parameter is TRUE then multiplex becomes the new
36 ** default VFS. Otherwise, you can use the multiplex VFS by specifying
37 ** "multiplex" as the 4th parameter to sqlite3_open_v2() or by employing
38 ** URI filenames and adding "vfs=multiplex" as a parameter to the filename
41 ** The multiplex VFS allows databases up to 32 GiB in size. But it splits
42 ** the files up into smaller pieces, so that they will work even on
43 ** filesystems that do not support large files. The default chunk size
44 ** is 2147418112 bytes (which is 64KiB less than 2GiB) but this can be
45 ** changed at compile-time by defining the SQLITE_MULTIPLEX_CHUNK_SIZE
46 ** macro. Use the "chunksize=NNNN" query parameter with a URI filename
47 ** in order to select an alternative chunk size for individual connections
54 #include "test_multiplex.h"
57 #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
59 #include "sqlite3ext.h"
62 ** These should be defined to be the same as the values in
63 ** sqliteInt.h. They are defined separately here so that
64 ** the multiplex VFS shim can be built as a loadable
67 #define UNUSED_PARAMETER(x) (void)(x)
68 #define MAX_PAGE_SIZE 0x10000
69 #define DEFAULT_SECTOR_SIZE 0x1000
72 ** For a build without mutexes, no-op the mutex calls.
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 */
84 /* Maximum chunk number */
85 #define MX_CHUNK_NUMBER 299
87 /* First chunk for rollback journal files */
88 #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
89 #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
92 /************************ Shim Definitions ******************************/
94 #ifndef SQLITE_MULTIPLEX_VFS_NAME
95 # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
98 /* This is the limit on the chunk size. It may be changed by calling
99 ** the xFileControl() interface. It will be rounded up to a
100 ** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.
102 #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
103 # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
106 /* This used to be the default limit on number of chunks, but
107 ** it is no longer enforced. There is currently no limit to the
110 ** May be changed by calling the xFileControl() interface.
112 #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
113 # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
116 /************************ Object Definitions ******************************/
118 /* Forward declaration of all object types */
119 typedef struct multiplexGroup multiplexGroup
;
120 typedef struct multiplexConn multiplexConn
;
123 ** A "multiplex group" is a collection of files that collectively
124 ** makeup a single SQLite DB file. This allows the size of the DB
125 ** to exceed the limits imposed by the file system.
127 ** There is an instance of the following object for each defined multiplex
130 struct multiplexGroup
{
131 struct multiplexReal
{ /* For each chunk */
132 sqlite3_file
*p
; /* Handle for the chunk */
133 char *z
; /* Name of this chunk */
134 } *aReal
; /* list of all chunks */
135 int nReal
; /* Number of chunks */
136 char *zName
; /* Base filename of this group */
137 int nName
; /* Length of base filename */
138 int flags
; /* Flags used for original opening */
139 unsigned int szChunk
; /* Chunk size used for this group */
140 unsigned char bEnabled
; /* TRUE to use Multiplex VFS for this file */
141 unsigned char bTruncate
; /* TRUE to enable truncation of databases */
142 multiplexGroup
*pNext
, *pPrev
; /* Doubly linked list of all group objects */
146 ** An instance of the following object represents each open connection
147 ** to a file that is multiplex'ed. This object is a
148 ** subclass of sqlite3_file. The sqlite3_file object for the underlying
149 ** VFS is appended to this structure.
151 struct multiplexConn
{
152 sqlite3_file base
; /* Base class - must be first */
153 multiplexGroup
*pGroup
; /* The underlying group of files */
156 /************************* Global Variables **********************************/
158 ** All global variables used by this file are containing within the following
159 ** gMultiplex structure.
162 /* The pOrigVfs is the real, original underlying VFS implementation.
163 ** Most operations pass-through to the real VFS. This value is read-only
164 ** during operation. It is only modified at start-time and thus does not
167 sqlite3_vfs
*pOrigVfs
;
169 /* The sThisVfs is the VFS structure used by this shim. It is initialized
170 ** at start-time and thus does not require a mutex
172 sqlite3_vfs sThisVfs
;
174 /* The sIoMethods defines the methods used by sqlite3_file objects
175 ** associated with this shim. It is initialized at start-time and does
176 ** not require a mutex.
178 ** When the underlying VFS is called to open a file, it might return
179 ** either a version 1 or a version 2 sqlite3_file object. This shim
180 ** 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
184 sqlite3_io_methods sIoMethodsV1
;
185 sqlite3_io_methods sIoMethodsV2
;
187 /* True when this shim has been initialized.
191 /* For run-time access any of the other global data structures in this
192 ** shim, the following mutex must be held.
194 sqlite3_mutex
*pMutex
;
196 /* List of multiplexGroup objects.
198 multiplexGroup
*pGroups
;
201 /************************* Utility Routines *********************************/
203 ** Acquire and release the mutex used to serialize access to the
204 ** list of multiplexGroups.
206 static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex
.pMutex
); }
207 static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex
.pMutex
); }
210 ** Compute a string length that is limited to what can be stored in
211 ** lower 30 bits of a 32-bit signed integer.
213 ** The value returned will never be negative. Nor will it ever be greater
214 ** than the actual length of the string. For very long strings (greater
215 ** than 1GiB) the value returned might be less than the true string length.
217 static int multiplexStrlen30(const char *z
){
220 while( *z2
){ z2
++; }
221 return 0x3fffffff & (int)(z2
- z
);
225 ** Generate the file-name for chunk iChunk of the group with base name
226 ** zBase. The file-name is written to buffer zOut before returning. Buffer
227 ** zOut must be allocated by the caller so that it is at least (nBase+5)
228 ** bytes in size, where nBase is the length of zBase, not including the
231 ** If iChunk is 0 (or 400 - the number for the first journal file chunk),
232 ** the output is a copy of the input string. Otherwise, if
233 ** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
234 ** a "." character, then the output is a copy of the input string with the
235 ** three-digit zero-padded decimal representation if iChunk appended to it.
238 ** zBase="test.db", iChunk=4 -> zOut="test.db004"
240 ** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
241 ** a "." character, then everything after the "." is replaced by the
242 ** three-digit representation of iChunk.
244 ** zBase="test.db", iChunk=4 -> zOut="test.004"
246 ** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
247 ** to pass to sqlite3_uri_parameter() and similar.
249 static void multiplexFilename(
250 const char *zBase
, /* Filename for chunk 0 */
251 int nBase
, /* Size of zBase in bytes (without \0) */
252 int flags
, /* Flags used to open file */
253 int iChunk
, /* Chunk to generate filename for */
254 char *zOut
/* Buffer to write generated name to */
257 memcpy(zOut
, zBase
, n
+1);
258 if( iChunk
!=0 && iChunk
<=MX_CHUNK_NUMBER
){
259 #ifdef SQLITE_ENABLE_8_3_NAMES
261 for(i
=n
-1; i
>0 && i
>=n
-4 && zOut
[i
]!='.'; i
--){}
262 if( i
>=n
-4 ) n
= i
+1;
263 if( flags
& SQLITE_OPEN_MAIN_JOURNAL
){
264 /* The extensions on overflow files for main databases are 001, 002,
265 ** 003 and so forth. To avoid name collisions, add 400 to the
266 ** extensions of journal files so that they are 401, 402, 403, ....
268 iChunk
+= SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET
;
269 }else if( flags
& SQLITE_OPEN_WAL
){
270 /* To avoid name collisions, add 700 to the
271 ** extensions of WAL files so that they are 701, 702, 703, ....
273 iChunk
+= SQLITE_MULTIPLEX_WAL_8_3_OFFSET
;
276 sqlite3_snprintf(4,&zOut
[n
],"%03d",iChunk
);
280 assert( zOut
[n
]=='\0' );
284 /* Compute the filename for the iChunk-th chunk
286 static int multiplexSubFilename(multiplexGroup
*pGroup
, int iChunk
){
287 if( iChunk
>=pGroup
->nReal
){
288 struct multiplexReal
*p
;
289 p
= sqlite3_realloc(pGroup
->aReal
, (iChunk
+1)*sizeof(*p
));
293 memset(&p
[pGroup
->nReal
], 0, sizeof(p
[0])*(iChunk
+1-pGroup
->nReal
));
295 pGroup
->nReal
= iChunk
+1;
297 if( pGroup
->zName
&& pGroup
->aReal
[iChunk
].z
==0 ){
299 int n
= pGroup
->nName
;
300 pGroup
->aReal
[iChunk
].z
= z
= sqlite3_malloc( n
+5 );
304 multiplexFilename(pGroup
->zName
, pGroup
->nName
, pGroup
->flags
, iChunk
, z
);
309 /* Translate an sqlite3_file* that is really a multiplexGroup* into
310 ** the sqlite3_file* for the underlying original VFS.
312 ** For chunk 0, the pGroup->flags determines whether or not a new file
313 ** is created if it does not already exist. For chunks 1 and higher, the
314 ** file is created only if createFlag is 1.
316 static sqlite3_file
*multiplexSubOpen(
317 multiplexGroup
*pGroup
, /* The multiplexor group */
318 int iChunk
, /* Which chunk to open. 0==original file */
319 int *rc
, /* Result code in and out */
320 int *pOutFlags
, /* Output flags */
321 int createFlag
/* True to create if iChunk>0 */
323 sqlite3_file
*pSubOpen
= 0;
324 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
326 #ifdef SQLITE_ENABLE_8_3_NAMES
327 /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
328 ** part of a database journal are named db.401, db.402, and so on. A
329 ** database may therefore not grow to larger than 400 chunks. Attempting
330 ** to open chunk 401 indicates the database is full. */
331 if( iChunk
>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET
){
332 sqlite3_log(SQLITE_FULL
, "multiplexed chunk overflow: %s", pGroup
->zName
);
338 *rc
= multiplexSubFilename(pGroup
, iChunk
);
339 if( (*rc
)==SQLITE_OK
&& (pSubOpen
= pGroup
->aReal
[iChunk
].p
)==0 ){
341 flags
= pGroup
->flags
;
343 flags
|= SQLITE_OPEN_CREATE
;
344 }else if( iChunk
==0 ){
346 }else if( pGroup
->aReal
[iChunk
].z
==0 ){
349 *rc
= pOrigVfs
->xAccess(pOrigVfs
, pGroup
->aReal
[iChunk
].z
,
350 SQLITE_ACCESS_EXISTS
, &bExists
);
351 if( *rc
|| !bExists
){
353 sqlite3_log(*rc
, "multiplexor.xAccess failure on %s",
354 pGroup
->aReal
[iChunk
].z
);
358 flags
&= ~SQLITE_OPEN_CREATE
;
360 pSubOpen
= sqlite3_malloc( pOrigVfs
->szOsFile
);
362 *rc
= SQLITE_IOERR_NOMEM
;
365 pGroup
->aReal
[iChunk
].p
= pSubOpen
;
366 *rc
= pOrigVfs
->xOpen(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, pSubOpen
,
368 if( (*rc
)!=SQLITE_OK
){
369 sqlite3_log(*rc
, "multiplexor.xOpen failure on %s",
370 pGroup
->aReal
[iChunk
].z
);
371 sqlite3_free(pSubOpen
);
372 pGroup
->aReal
[iChunk
].p
= 0;
380 ** Return the size, in bytes, of chunk number iChunk. If that chunk
381 ** does not exist, then return 0. This function does not distingish between
382 ** non-existant files and zero-length files.
384 static sqlite3_int64
multiplexSubSize(
385 multiplexGroup
*pGroup
, /* The multiplexor group */
386 int iChunk
, /* Which chunk to open. 0==original file */
387 int *rc
/* Result code in and out */
390 sqlite3_int64 sz
= 0;
393 pSub
= multiplexSubOpen(pGroup
, iChunk
, rc
, NULL
, 0);
394 if( pSub
==0 ) return 0;
395 *rc
= pSub
->pMethods
->xFileSize(pSub
, &sz
);
400 ** This is the implementation of the multiplex_control() SQL function.
402 static void multiplexControlFunc(
403 sqlite3_context
*context
,
408 sqlite3
*db
= sqlite3_context_db_handle(context
);
412 if( !db
|| argc
!=2 ){
416 op
= sqlite3_value_int(argv
[0]);
417 iVal
= sqlite3_value_int(argv
[1]);
418 /* map function op to file_control op */
421 op
= MULTIPLEX_CTRL_ENABLE
;
424 op
= MULTIPLEX_CTRL_SET_CHUNK_SIZE
;
427 op
= MULTIPLEX_CTRL_SET_MAX_CHUNKS
;
430 rc
= SQLITE_NOTFOUND
;
435 rc
= sqlite3_file_control(db
, 0, op
, &iVal
);
437 sqlite3_result_error_code(context
, rc
);
441 ** This is the entry point to register the auto-extension for the
442 ** multiplex_control() function.
444 static int multiplexFuncInit(
447 const sqlite3_api_routines
*pApi
450 rc
= sqlite3_create_function(db
, "multiplex_control", 2, SQLITE_ANY
,
451 0, multiplexControlFunc
, 0, 0);
456 ** Close a single sub-file in the connection group.
458 static void multiplexSubClose(
459 multiplexGroup
*pGroup
,
461 sqlite3_vfs
*pOrigVfs
463 sqlite3_file
*pSubOpen
= pGroup
->aReal
[iChunk
].p
;
465 pSubOpen
->pMethods
->xClose(pSubOpen
);
466 if( pOrigVfs
&& pGroup
->aReal
[iChunk
].z
){
467 pOrigVfs
->xDelete(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, 0);
469 sqlite3_free(pGroup
->aReal
[iChunk
].p
);
471 sqlite3_free(pGroup
->aReal
[iChunk
].z
);
472 memset(&pGroup
->aReal
[iChunk
], 0, sizeof(pGroup
->aReal
[iChunk
]));
476 ** Deallocate memory held by a multiplexGroup
478 static void multiplexFreeComponents(multiplexGroup
*pGroup
){
480 for(i
=0; i
<pGroup
->nReal
; i
++){ multiplexSubClose(pGroup
, i
, 0); }
481 sqlite3_free(pGroup
->aReal
);
487 /************************* VFS Method Wrappers *****************************/
490 ** This is the xOpen method used for the "multiplex" VFS.
492 ** Most of the work is done by the underlying original VFS. This method
493 ** simply links the new file into the appropriate multiplex group if it is a
494 ** file that needs to be tracked.
496 static int multiplexOpen(
497 sqlite3_vfs
*pVfs
, /* The multiplex VFS */
498 const char *zName
, /* Name of file to be opened */
499 sqlite3_file
*pConn
, /* Fill in this file descriptor */
500 int flags
, /* Flags to control the opening */
501 int *pOutFlags
/* Flags showing results of opening */
503 int rc
= SQLITE_OK
; /* Result code */
504 multiplexConn
*pMultiplexOpen
; /* The new multiplex file descriptor */
505 multiplexGroup
*pGroup
= 0; /* Corresponding multiplexGroup object */
506 sqlite3_file
*pSubOpen
= 0; /* Real file descriptor */
507 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
512 UNUSED_PARAMETER(pVfs
);
513 memset(pConn
, 0, pVfs
->szOsFile
);
514 assert( zName
|| (flags
& SQLITE_OPEN_DELETEONCLOSE
) );
516 /* We need to create a group structure and manage
517 ** access to this group of files.
520 pMultiplexOpen
= (multiplexConn
*)pConn
;
523 /* allocate space for group */
524 nName
= zName
? multiplexStrlen30(zName
) : 0;
525 sz
= sizeof(multiplexGroup
) /* multiplexGroup */
526 + nName
+ 1; /* zName */
527 pGroup
= sqlite3_malloc( sz
);
534 const char *zUri
= (flags
& SQLITE_OPEN_URI
) ? zName
: 0;
535 /* assign pointers to extra space allocated */
536 memset(pGroup
, 0, sz
);
537 pMultiplexOpen
->pGroup
= pGroup
;
538 pGroup
->bEnabled
= -1;
539 pGroup
->bTruncate
= sqlite3_uri_boolean(zUri
, "truncate",
540 (flags
& SQLITE_OPEN_MAIN_DB
)==0);
541 pGroup
->szChunk
= (int)sqlite3_uri_int64(zUri
, "chunksize",
542 SQLITE_MULTIPLEX_CHUNK_SIZE
);
543 pGroup
->szChunk
= (pGroup
->szChunk
+0xffff)&~0xffff;
545 char *p
= (char *)&pGroup
[1];
547 memcpy(pGroup
->zName
, zName
, nName
+1);
548 pGroup
->nName
= nName
;
550 if( pGroup
->bEnabled
){
551 /* Make sure that the chunksize is such that the pending byte does not
552 ** falls at the end of a chunk. A region of up to 64K following
553 ** the pending byte is never written, so if the pending byte occurs
554 ** near the end of a chunk, that chunk will be too small. */
555 #ifndef SQLITE_OMIT_WSD
556 extern int sqlite3PendingByte
;
558 int sqlite3PendingByte
= 0x40000000;
560 while( (sqlite3PendingByte
% pGroup
->szChunk
)>=(pGroup
->szChunk
-65536) ){
561 pGroup
->szChunk
+= 65536;
564 pGroup
->flags
= flags
;
565 rc
= multiplexSubFilename(pGroup
, 1);
567 pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, pOutFlags
, 0);
568 if( pSubOpen
==0 && rc
==SQLITE_OK
) rc
= SQLITE_CANTOPEN
;
573 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, &sz
);
574 if( rc
==SQLITE_OK
&& zName
){
577 if( flags
& SQLITE_OPEN_MAIN_JOURNAL
){
578 /* If opening a main journal file and the first chunk is zero
579 ** bytes in size, delete any subsequent chunks from the
583 rc
= pOrigVfs
->xAccess(pOrigVfs
,
584 pGroup
->aReal
[iChunk
].z
, SQLITE_ACCESS_EXISTS
, &bExists
586 if( rc
==SQLITE_OK
&& bExists
){
587 rc
= pOrigVfs
->xDelete(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, 0);
589 rc
= multiplexSubFilename(pGroup
, ++iChunk
);
592 }while( rc
==SQLITE_OK
&& bExists
);
595 /* If the first overflow file exists and if the size of the main file
596 ** is different from the chunk size, that means the chunk size is set
597 ** set incorrectly. So fix it.
599 ** Or, if the first overflow file does not exist and the main file is
600 ** larger than the chunk size, that means the chunk size is too small.
601 ** But we have no way of determining the intended chunk size, so
602 ** just disable the multiplexor all togethre.
604 rc
= pOrigVfs
->xAccess(pOrigVfs
, pGroup
->aReal
[1].z
,
605 SQLITE_ACCESS_EXISTS
, &bExists
);
606 bExists
= multiplexSubSize(pGroup
, 1, &rc
)>0;
607 if( rc
==SQLITE_OK
&& bExists
&& sz
==(sz
&0xffff0000) && sz
>0
608 && sz
!=pGroup
->szChunk
){
609 pGroup
->szChunk
= (int)sz
;
610 }else if( rc
==SQLITE_OK
&& !bExists
&& sz
>pGroup
->szChunk
){
611 pGroup
->bEnabled
= 0;
618 if( pSubOpen
->pMethods
->iVersion
==1 ){
619 pMultiplexOpen
->base
.pMethods
= &gMultiplex
.sIoMethodsV1
;
621 pMultiplexOpen
->base
.pMethods
= &gMultiplex
.sIoMethodsV2
;
623 /* place this group at the head of our list */
624 pGroup
->pNext
= gMultiplex
.pGroups
;
625 if( gMultiplex
.pGroups
) gMultiplex
.pGroups
->pPrev
= pGroup
;
626 gMultiplex
.pGroups
= pGroup
;
628 multiplexFreeComponents(pGroup
);
629 sqlite3_free(pGroup
);
633 sqlite3_free(zToFree
);
638 ** This is the xDelete method used for the "multiplex" VFS.
639 ** It attempts to delete the filename specified.
641 static int multiplexDelete(
642 sqlite3_vfs
*pVfs
, /* The multiplex VFS */
643 const char *zName
, /* Name of file to delete */
647 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
648 rc
= pOrigVfs
->xDelete(pOrigVfs
, zName
, syncDir
);
650 /* If the main chunk was deleted successfully, also delete any subsequent
651 ** chunks - starting with the last (highest numbered).
653 int nName
= (int)strlen(zName
);
655 z
= sqlite3_malloc(nName
+ 5);
657 rc
= SQLITE_IOERR_NOMEM
;
662 multiplexFilename(zName
, nName
, SQLITE_OPEN_MAIN_JOURNAL
, ++iChunk
, z
);
663 rc
= pOrigVfs
->xAccess(pOrigVfs
, z
, SQLITE_ACCESS_EXISTS
, &bExists
);
664 }while( rc
==SQLITE_OK
&& bExists
);
665 while( rc
==SQLITE_OK
&& iChunk
>1 ){
666 multiplexFilename(zName
, nName
, SQLITE_OPEN_MAIN_JOURNAL
, --iChunk
, z
);
667 rc
= pOrigVfs
->xDelete(pOrigVfs
, z
, syncDir
);
672 multiplexFilename(zName
, nName
, SQLITE_OPEN_WAL
, ++iChunk
, z
);
673 rc
= pOrigVfs
->xAccess(pOrigVfs
, z
, SQLITE_ACCESS_EXISTS
, &bExists
);
674 }while( rc
==SQLITE_OK
&& bExists
);
675 while( rc
==SQLITE_OK
&& iChunk
>1 ){
676 multiplexFilename(zName
, nName
, SQLITE_OPEN_WAL
, --iChunk
, z
);
677 rc
= pOrigVfs
->xDelete(pOrigVfs
, z
, syncDir
);
686 static int multiplexAccess(sqlite3_vfs
*a
, const char *b
, int c
, int *d
){
687 return gMultiplex
.pOrigVfs
->xAccess(gMultiplex
.pOrigVfs
, b
, c
, d
);
689 static int multiplexFullPathname(sqlite3_vfs
*a
, const char *b
, int c
, char *d
){
690 return gMultiplex
.pOrigVfs
->xFullPathname(gMultiplex
.pOrigVfs
, b
, c
, d
);
692 static void *multiplexDlOpen(sqlite3_vfs
*a
, const char *b
){
693 return gMultiplex
.pOrigVfs
->xDlOpen(gMultiplex
.pOrigVfs
, b
);
695 static void multiplexDlError(sqlite3_vfs
*a
, int b
, char *c
){
696 gMultiplex
.pOrigVfs
->xDlError(gMultiplex
.pOrigVfs
, b
, c
);
698 static void (*multiplexDlSym(sqlite3_vfs
*a
, void *b
, const char *c
))(void){
699 return gMultiplex
.pOrigVfs
->xDlSym(gMultiplex
.pOrigVfs
, b
, c
);
701 static void multiplexDlClose(sqlite3_vfs
*a
, void *b
){
702 gMultiplex
.pOrigVfs
->xDlClose(gMultiplex
.pOrigVfs
, b
);
704 static int multiplexRandomness(sqlite3_vfs
*a
, int b
, char *c
){
705 return gMultiplex
.pOrigVfs
->xRandomness(gMultiplex
.pOrigVfs
, b
, c
);
707 static int multiplexSleep(sqlite3_vfs
*a
, int b
){
708 return gMultiplex
.pOrigVfs
->xSleep(gMultiplex
.pOrigVfs
, b
);
710 static int multiplexCurrentTime(sqlite3_vfs
*a
, double *b
){
711 return gMultiplex
.pOrigVfs
->xCurrentTime(gMultiplex
.pOrigVfs
, b
);
713 static int multiplexGetLastError(sqlite3_vfs
*a
, int b
, char *c
){
714 return gMultiplex
.pOrigVfs
->xGetLastError(gMultiplex
.pOrigVfs
, b
, c
);
716 static int multiplexCurrentTimeInt64(sqlite3_vfs
*a
, sqlite3_int64
*b
){
717 return gMultiplex
.pOrigVfs
->xCurrentTimeInt64(gMultiplex
.pOrigVfs
, b
);
720 /************************ I/O Method Wrappers *******************************/
722 /* xClose requests get passed through to the original VFS.
723 ** We loop over all open chunk handles and close them.
724 ** The group structure for this file is unlinked from
725 ** our list of groups and freed.
727 static int multiplexClose(sqlite3_file
*pConn
){
728 multiplexConn
*p
= (multiplexConn
*)pConn
;
729 multiplexGroup
*pGroup
= p
->pGroup
;
732 multiplexFreeComponents(pGroup
);
733 /* remove from linked list */
734 if( pGroup
->pNext
) pGroup
->pNext
->pPrev
= pGroup
->pPrev
;
736 pGroup
->pPrev
->pNext
= pGroup
->pNext
;
738 gMultiplex
.pGroups
= pGroup
->pNext
;
740 sqlite3_free(pGroup
);
745 /* Pass xRead requests thru to the original VFS after
746 ** determining the correct chunk to operate on.
747 ** Break up reads across chunk boundaries.
749 static int multiplexRead(
755 multiplexConn
*p
= (multiplexConn
*)pConn
;
756 multiplexGroup
*pGroup
= p
->pGroup
;
759 multiplexEnter(); nMutex
++;
760 if( !pGroup
->bEnabled
){
761 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
762 multiplexLeave(); nMutex
--;
764 rc
= SQLITE_IOERR_READ
;
766 rc
= pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
, iOfst
);
770 int i
= (int)(iOfst
/ pGroup
->szChunk
);
771 sqlite3_file
*pSubOpen
;
772 if( nMutex
==0 ){ multiplexEnter(); nMutex
++; }
773 pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, NULL
, 1);
774 multiplexLeave(); nMutex
--;
776 int extra
= ((int)(iOfst
% pGroup
->szChunk
) + iAmt
) - pGroup
->szChunk
;
777 if( extra
<0 ) extra
= 0;
779 rc
= pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
,
780 iOfst
% pGroup
->szChunk
);
781 if( rc
!=SQLITE_OK
) break;
782 pBuf
= (char *)pBuf
+ iAmt
;
786 rc
= SQLITE_IOERR_READ
;
791 assert( nMutex
==0 || nMutex
==1 );
792 if( nMutex
) multiplexLeave();
796 /* Pass xWrite requests thru to the original VFS after
797 ** determining the correct chunk to operate on.
798 ** Break up writes across chunk boundaries.
800 static int multiplexWrite(
806 multiplexConn
*p
= (multiplexConn
*)pConn
;
807 multiplexGroup
*pGroup
= p
->pGroup
;
810 if( !pGroup
->bEnabled
){
811 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
813 rc
= SQLITE_IOERR_WRITE
;
815 rc
= pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
, iOfst
);
818 while( rc
==SQLITE_OK
&& iAmt
>0 ){
819 int i
= (int)(iOfst
/ pGroup
->szChunk
);
820 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, NULL
, 1);
822 int extra
= ((int)(iOfst
% pGroup
->szChunk
) + iAmt
) -
824 if( extra
<0 ) extra
= 0;
826 rc
= pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
,
827 iOfst
% pGroup
->szChunk
);
828 pBuf
= (char *)pBuf
+ iAmt
;
838 /* Pass xTruncate requests thru to the original VFS after
839 ** determining the correct chunk to operate on. Delete any
840 ** chunks above the truncate mark.
842 static int multiplexTruncate(sqlite3_file
*pConn
, sqlite3_int64 size
){
843 multiplexConn
*p
= (multiplexConn
*)pConn
;
844 multiplexGroup
*pGroup
= p
->pGroup
;
847 if( !pGroup
->bEnabled
){
848 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
850 rc
= SQLITE_IOERR_TRUNCATE
;
852 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
);
856 int iBaseGroup
= (int)(size
/ pGroup
->szChunk
);
857 sqlite3_file
*pSubOpen
;
858 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
859 /* delete the chunks above the truncate limit */
860 for(i
= pGroup
->nReal
-1; i
>iBaseGroup
&& rc
==SQLITE_OK
; i
--){
861 if( pGroup
->bTruncate
){
862 multiplexSubClose(pGroup
, i
, pOrigVfs
);
864 pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, 0, 0);
866 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, 0);
871 pSubOpen
= multiplexSubOpen(pGroup
, iBaseGroup
, &rc
, 0, 0);
873 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
% pGroup
->szChunk
);
876 if( rc
) rc
= SQLITE_IOERR_TRUNCATE
;
882 /* Pass xSync requests through to the original VFS without change
884 static int multiplexSync(sqlite3_file
*pConn
, int flags
){
885 multiplexConn
*p
= (multiplexConn
*)pConn
;
886 multiplexGroup
*pGroup
= p
->pGroup
;
890 for(i
=0; i
<pGroup
->nReal
; i
++){
891 sqlite3_file
*pSubOpen
= pGroup
->aReal
[i
].p
;
893 int rc2
= pSubOpen
->pMethods
->xSync(pSubOpen
, flags
);
894 if( rc2
!=SQLITE_OK
) rc
= rc2
;
901 /* Pass xFileSize requests through to the original VFS.
902 ** Aggregate the size of all the chunks before returning.
904 static int multiplexFileSize(sqlite3_file
*pConn
, sqlite3_int64
*pSize
){
905 multiplexConn
*p
= (multiplexConn
*)pConn
;
906 multiplexGroup
*pGroup
= p
->pGroup
;
910 if( !pGroup
->bEnabled
){
911 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
913 rc
= SQLITE_IOERR_FSTAT
;
915 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, pSize
);
919 for(i
=0; rc
==SQLITE_OK
; i
++){
920 sqlite3_int64 sz
= multiplexSubSize(pGroup
, i
, &rc
);
922 *pSize
= i
*(sqlite3_int64
)pGroup
->szChunk
+ sz
;
929 /* Pass xLock requests through to the original VFS unchanged.
931 static int multiplexLock(sqlite3_file
*pConn
, int lock
){
932 multiplexConn
*p
= (multiplexConn
*)pConn
;
934 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
936 return pSubOpen
->pMethods
->xLock(pSubOpen
, lock
);
941 /* Pass xUnlock requests through to the original VFS unchanged.
943 static int multiplexUnlock(sqlite3_file
*pConn
, int lock
){
944 multiplexConn
*p
= (multiplexConn
*)pConn
;
946 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
948 return pSubOpen
->pMethods
->xUnlock(pSubOpen
, lock
);
950 return SQLITE_IOERR_UNLOCK
;
953 /* Pass xCheckReservedLock requests through to the original VFS unchanged.
955 static int multiplexCheckReservedLock(sqlite3_file
*pConn
, int *pResOut
){
956 multiplexConn
*p
= (multiplexConn
*)pConn
;
958 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
960 return pSubOpen
->pMethods
->xCheckReservedLock(pSubOpen
, pResOut
);
962 return SQLITE_IOERR_CHECKRESERVEDLOCK
;
965 /* Pass xFileControl requests through to the original VFS unchanged,
966 ** except for any MULTIPLEX_CTRL_* requests here.
968 static int multiplexFileControl(sqlite3_file
*pConn
, int op
, void *pArg
){
969 multiplexConn
*p
= (multiplexConn
*)pConn
;
970 multiplexGroup
*pGroup
= p
->pGroup
;
971 int rc
= SQLITE_ERROR
;
972 sqlite3_file
*pSubOpen
;
974 if( !gMultiplex
.isInitialized
) return SQLITE_MISUSE
;
976 case MULTIPLEX_CTRL_ENABLE
:
978 int bEnabled
= *(int *)pArg
;
979 pGroup
->bEnabled
= bEnabled
;
983 case MULTIPLEX_CTRL_SET_CHUNK_SIZE
:
985 unsigned int szChunk
= *(unsigned*)pArg
;
989 /* Round up to nearest multiple of MAX_PAGE_SIZE. */
990 szChunk
= (szChunk
+ (MAX_PAGE_SIZE
-1));
991 szChunk
&= ~(MAX_PAGE_SIZE
-1);
992 pGroup
->szChunk
= szChunk
;
997 case MULTIPLEX_CTRL_SET_MAX_CHUNKS
:
1000 case SQLITE_FCNTL_SIZE_HINT
:
1001 case SQLITE_FCNTL_CHUNK_SIZE
:
1005 case SQLITE_FCNTL_PRAGMA
: {
1006 char **aFcntl
= (char**)pArg
;
1007 if( aFcntl
[1] && sqlite3_stricmp(aFcntl
[1],"multiplex_truncate")==0 ){
1008 if( aFcntl
[2] && aFcntl
[2][0] ){
1009 if( sqlite3_stricmp(aFcntl
[2], "on")==0
1010 || sqlite3_stricmp(aFcntl
[2], "1")==0 ){
1011 pGroup
->bTruncate
= 1;
1013 if( sqlite3_stricmp(aFcntl
[2], "off")==0
1014 || sqlite3_stricmp(aFcntl
[2], "0")==0 ){
1015 pGroup
->bTruncate
= 0;
1018 aFcntl
[0] = sqlite3_mprintf(pGroup
->bTruncate
? "on" : "off");
1022 /* If the multiplexor does not handle the pragma, pass it through
1023 ** into the default case. */
1026 pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
1028 rc
= pSubOpen
->pMethods
->xFileControl(pSubOpen
, op
, pArg
);
1029 if( op
==SQLITE_FCNTL_VFSNAME
&& rc
==SQLITE_OK
){
1030 *(char**)pArg
= sqlite3_mprintf("multiplex/%z", *(char**)pArg
);
1038 /* Pass xSectorSize requests through to the original VFS unchanged.
1040 static int multiplexSectorSize(sqlite3_file
*pConn
){
1041 multiplexConn
*p
= (multiplexConn
*)pConn
;
1043 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1044 if( pSubOpen
&& pSubOpen
->pMethods
->xSectorSize
){
1045 return pSubOpen
->pMethods
->xSectorSize(pSubOpen
);
1047 return DEFAULT_SECTOR_SIZE
;
1050 /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
1052 static int multiplexDeviceCharacteristics(sqlite3_file
*pConn
){
1053 multiplexConn
*p
= (multiplexConn
*)pConn
;
1055 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1057 return pSubOpen
->pMethods
->xDeviceCharacteristics(pSubOpen
);
1062 /* Pass xShmMap requests through to the original VFS unchanged.
1064 static int multiplexShmMap(
1065 sqlite3_file
*pConn
, /* Handle open on database file */
1066 int iRegion
, /* Region to retrieve */
1067 int szRegion
, /* Size of regions */
1068 int bExtend
, /* True to extend file if necessary */
1069 void volatile **pp
/* OUT: Mapped memory */
1071 multiplexConn
*p
= (multiplexConn
*)pConn
;
1073 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1075 return pSubOpen
->pMethods
->xShmMap(pSubOpen
, iRegion
, szRegion
, bExtend
,pp
);
1077 return SQLITE_IOERR
;
1080 /* Pass xShmLock requests through to the original VFS unchanged.
1082 static int multiplexShmLock(
1083 sqlite3_file
*pConn
, /* Database file holding the shared memory */
1084 int ofst
, /* First lock to acquire or release */
1085 int n
, /* Number of locks to acquire or release */
1086 int flags
/* What to do with the lock */
1088 multiplexConn
*p
= (multiplexConn
*)pConn
;
1090 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1092 return pSubOpen
->pMethods
->xShmLock(pSubOpen
, ofst
, n
, flags
);
1097 /* Pass xShmBarrier requests through to the original VFS unchanged.
1099 static void multiplexShmBarrier(sqlite3_file
*pConn
){
1100 multiplexConn
*p
= (multiplexConn
*)pConn
;
1102 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1104 pSubOpen
->pMethods
->xShmBarrier(pSubOpen
);
1108 /* Pass xShmUnmap requests through to the original VFS unchanged.
1110 static int multiplexShmUnmap(sqlite3_file
*pConn
, int deleteFlag
){
1111 multiplexConn
*p
= (multiplexConn
*)pConn
;
1113 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1115 return pSubOpen
->pMethods
->xShmUnmap(pSubOpen
, deleteFlag
);
1120 /************************** Public Interfaces *****************************/
1122 ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
1124 ** Use the VFS named zOrigVfsName as the VFS that does the actual work.
1125 ** Use the default if zOrigVfsName==NULL.
1127 ** The multiplex VFS shim is named "multiplex". It will become the default
1128 ** VFS if makeDefault is non-zero.
1130 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
1133 int sqlite3_multiplex_initialize(const char *zOrigVfsName
, int makeDefault
){
1134 sqlite3_vfs
*pOrigVfs
;
1135 if( gMultiplex
.isInitialized
) return SQLITE_MISUSE
;
1136 pOrigVfs
= sqlite3_vfs_find(zOrigVfsName
);
1137 if( pOrigVfs
==0 ) return SQLITE_ERROR
;
1138 assert( pOrigVfs
!=&gMultiplex
.sThisVfs
);
1139 gMultiplex
.pMutex
= sqlite3_mutex_alloc(SQLITE_MUTEX_FAST
);
1140 if( !gMultiplex
.pMutex
){
1141 return SQLITE_NOMEM
;
1143 gMultiplex
.pGroups
= NULL
;
1144 gMultiplex
.isInitialized
= 1;
1145 gMultiplex
.pOrigVfs
= pOrigVfs
;
1146 gMultiplex
.sThisVfs
= *pOrigVfs
;
1147 gMultiplex
.sThisVfs
.szOsFile
+= sizeof(multiplexConn
);
1148 gMultiplex
.sThisVfs
.zName
= SQLITE_MULTIPLEX_VFS_NAME
;
1149 gMultiplex
.sThisVfs
.xOpen
= multiplexOpen
;
1150 gMultiplex
.sThisVfs
.xDelete
= multiplexDelete
;
1151 gMultiplex
.sThisVfs
.xAccess
= multiplexAccess
;
1152 gMultiplex
.sThisVfs
.xFullPathname
= multiplexFullPathname
;
1153 gMultiplex
.sThisVfs
.xDlOpen
= multiplexDlOpen
;
1154 gMultiplex
.sThisVfs
.xDlError
= multiplexDlError
;
1155 gMultiplex
.sThisVfs
.xDlSym
= multiplexDlSym
;
1156 gMultiplex
.sThisVfs
.xDlClose
= multiplexDlClose
;
1157 gMultiplex
.sThisVfs
.xRandomness
= multiplexRandomness
;
1158 gMultiplex
.sThisVfs
.xSleep
= multiplexSleep
;
1159 gMultiplex
.sThisVfs
.xCurrentTime
= multiplexCurrentTime
;
1160 gMultiplex
.sThisVfs
.xGetLastError
= multiplexGetLastError
;
1161 gMultiplex
.sThisVfs
.xCurrentTimeInt64
= multiplexCurrentTimeInt64
;
1163 gMultiplex
.sIoMethodsV1
.iVersion
= 1;
1164 gMultiplex
.sIoMethodsV1
.xClose
= multiplexClose
;
1165 gMultiplex
.sIoMethodsV1
.xRead
= multiplexRead
;
1166 gMultiplex
.sIoMethodsV1
.xWrite
= multiplexWrite
;
1167 gMultiplex
.sIoMethodsV1
.xTruncate
= multiplexTruncate
;
1168 gMultiplex
.sIoMethodsV1
.xSync
= multiplexSync
;
1169 gMultiplex
.sIoMethodsV1
.xFileSize
= multiplexFileSize
;
1170 gMultiplex
.sIoMethodsV1
.xLock
= multiplexLock
;
1171 gMultiplex
.sIoMethodsV1
.xUnlock
= multiplexUnlock
;
1172 gMultiplex
.sIoMethodsV1
.xCheckReservedLock
= multiplexCheckReservedLock
;
1173 gMultiplex
.sIoMethodsV1
.xFileControl
= multiplexFileControl
;
1174 gMultiplex
.sIoMethodsV1
.xSectorSize
= multiplexSectorSize
;
1175 gMultiplex
.sIoMethodsV1
.xDeviceCharacteristics
=
1176 multiplexDeviceCharacteristics
;
1177 gMultiplex
.sIoMethodsV2
= gMultiplex
.sIoMethodsV1
;
1178 gMultiplex
.sIoMethodsV2
.iVersion
= 2;
1179 gMultiplex
.sIoMethodsV2
.xShmMap
= multiplexShmMap
;
1180 gMultiplex
.sIoMethodsV2
.xShmLock
= multiplexShmLock
;
1181 gMultiplex
.sIoMethodsV2
.xShmBarrier
= multiplexShmBarrier
;
1182 gMultiplex
.sIoMethodsV2
.xShmUnmap
= multiplexShmUnmap
;
1183 sqlite3_vfs_register(&gMultiplex
.sThisVfs
, makeDefault
);
1185 sqlite3_auto_extension((void*)multiplexFuncInit
);
1191 ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
1193 ** All SQLite database connections must be closed before calling this
1196 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
1197 ** shutting down in order to free all remaining multiplex groups.
1199 int sqlite3_multiplex_shutdown(int eForce
){
1201 if( gMultiplex
.isInitialized
==0 ) return SQLITE_MISUSE
;
1202 if( gMultiplex
.pGroups
){
1203 sqlite3_log(SQLITE_MISUSE
, "sqlite3_multiplex_shutdown() called "
1204 "while database connections are still open");
1205 if( !eForce
) return SQLITE_MISUSE
;
1208 gMultiplex
.isInitialized
= 0;
1209 sqlite3_mutex_free(gMultiplex
.pMutex
);
1210 sqlite3_vfs_unregister(&gMultiplex
.sThisVfs
);
1211 memset(&gMultiplex
, 0, sizeof(gMultiplex
));
1215 /***************************** Test Code ***********************************/
1218 extern const char *sqlite3ErrName(int);
1222 ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
1224 static int test_multiplex_initialize(
1228 Tcl_Obj
*CONST objv
[]
1230 const char *zName
; /* Name of new multiplex VFS */
1231 int makeDefault
; /* True to make the new VFS the default */
1232 int rc
; /* Value returned by multiplex_initialize() */
1234 UNUSED_PARAMETER(clientData
);
1236 /* Process arguments */
1238 Tcl_WrongNumArgs(interp
, 1, objv
, "NAME MAKEDEFAULT");
1241 zName
= Tcl_GetString(objv
[1]);
1242 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &makeDefault
) ) return TCL_ERROR
;
1243 if( zName
[0]=='\0' ) zName
= 0;
1245 /* Call sqlite3_multiplex_initialize() */
1246 rc
= sqlite3_multiplex_initialize(zName
, makeDefault
);
1247 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1253 ** tclcmd: sqlite3_multiplex_shutdown
1255 static int test_multiplex_shutdown(
1259 Tcl_Obj
*CONST objv
[]
1261 int rc
; /* Value returned by multiplex_shutdown() */
1263 UNUSED_PARAMETER(clientData
);
1265 if( objc
==2 && strcmp(Tcl_GetString(objv
[1]),"-force")!=0 ){
1268 if( (objc
!=1 && objc
!=2) ){
1269 Tcl_WrongNumArgs(interp
, 1, objv
, "?-force?");
1273 /* Call sqlite3_multiplex_shutdown() */
1274 rc
= sqlite3_multiplex_shutdown(objc
==2);
1275 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1281 ** tclcmd: sqlite3_multiplex_dump
1283 static int test_multiplex_dump(
1287 Tcl_Obj
*CONST objv
[]
1290 Tcl_Obj
*pGroupTerm
;
1291 multiplexGroup
*pGroup
;
1295 UNUSED_PARAMETER(clientData
);
1296 UNUSED_PARAMETER(objc
);
1297 UNUSED_PARAMETER(objv
);
1299 pResult
= Tcl_NewObj();
1301 for(pGroup
=gMultiplex
.pGroups
; pGroup
; pGroup
=pGroup
->pNext
){
1302 pGroupTerm
= Tcl_NewObj();
1304 if( pGroup
->zName
){
1305 pGroup
->zName
[pGroup
->nName
] = '\0';
1306 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1307 Tcl_NewStringObj(pGroup
->zName
, -1));
1309 Tcl_ListObjAppendElement(interp
, pGroupTerm
, Tcl_NewObj());
1311 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1312 Tcl_NewIntObj(pGroup
->nName
));
1313 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1314 Tcl_NewIntObj(pGroup
->flags
));
1316 /* count number of chunks with open handles */
1317 for(i
=0; i
<pGroup
->nReal
; i
++){
1318 if( pGroup
->aReal
[i
].p
!=0 ) nChunks
++;
1320 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1321 Tcl_NewIntObj(nChunks
));
1323 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1324 Tcl_NewIntObj(pGroup
->szChunk
));
1325 Tcl_ListObjAppendElement(interp
, pGroupTerm
,
1326 Tcl_NewIntObj(pGroup
->nReal
));
1328 Tcl_ListObjAppendElement(interp
, pResult
, pGroupTerm
);
1331 Tcl_SetObjResult(interp
, pResult
);
1336 ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
1338 static int test_multiplex_control(
1342 Tcl_Obj
*CONST objv
[]
1344 int rc
; /* Return code from file_control() */
1345 int idx
; /* Index in aSub[] */
1346 Tcl_CmdInfo cmdInfo
; /* Command info structure for HANDLE */
1347 sqlite3
*db
; /* Underlying db handle for HANDLE */
1356 { "enable", MULTIPLEX_CTRL_ENABLE
, 1 },
1357 { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE
, 1 },
1358 { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS
, 1 },
1363 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
1367 if( 0==Tcl_GetCommandInfo(interp
, Tcl_GetString(objv
[1]), &cmdInfo
) ){
1368 Tcl_AppendResult(interp
, "expected database handle, got \"", 0);
1369 Tcl_AppendResult(interp
, Tcl_GetString(objv
[1]), "\"", 0);
1372 db
= *(sqlite3
**)cmdInfo
.objClientData
;
1375 rc
= Tcl_GetIndexFromObjStruct(
1376 interp
, objv
[3], aSub
, sizeof(aSub
[0]), "sub-command", 0, &idx
1378 if( rc
!=TCL_OK
) return rc
;
1380 switch( aSub
[idx
].argtype
){
1382 if( Tcl_GetIntFromObj(interp
, objv
[4], &iValue
) ){
1385 pArg
= (void *)&iValue
;
1388 Tcl_WrongNumArgs(interp
, 4, objv
, "SUB-COMMAND");
1392 rc
= sqlite3_file_control(db
, Tcl_GetString(objv
[2]), aSub
[idx
].op
, pArg
);
1393 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1394 return (rc
==SQLITE_OK
) ? TCL_OK
: TCL_ERROR
;
1398 ** This routine registers the custom TCL commands defined in this
1399 ** module. This should be the only procedure visible from outside
1402 int Sqlitemultiplex_Init(Tcl_Interp
*interp
){
1405 Tcl_ObjCmdProc
*xProc
;
1407 { "sqlite3_multiplex_initialize", test_multiplex_initialize
},
1408 { "sqlite3_multiplex_shutdown", test_multiplex_shutdown
},
1409 { "sqlite3_multiplex_dump", test_multiplex_dump
},
1410 { "sqlite3_multiplex_control", test_multiplex_control
},
1414 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
1415 Tcl_CreateObjCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);