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
71 /* Maximum chunk number */
72 #define MX_CHUNK_NUMBER 299
74 /* First chunk for rollback journal files */
75 #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
76 #define SQLITE_MULTIPLEX_WAL_8_3_OFFSET 700
79 /************************ Shim Definitions ******************************/
81 #ifndef SQLITE_MULTIPLEX_VFS_NAME
82 # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
85 /* This is the limit on the chunk size. It may be changed by calling
86 ** the xFileControl() interface. It will be rounded up to a
87 ** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.
89 #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
90 # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
93 /* This used to be the default limit on number of chunks, but
94 ** it is no longer enforced. There is currently no limit to the
97 ** May be changed by calling the xFileControl() interface.
99 #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
100 # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
103 /************************ Object Definitions ******************************/
105 /* Forward declaration of all object types */
106 typedef struct multiplexGroup multiplexGroup
;
107 typedef struct multiplexConn multiplexConn
;
110 ** A "multiplex group" is a collection of files that collectively
111 ** makeup a single SQLite DB file. This allows the size of the DB
112 ** to exceed the limits imposed by the file system.
114 ** There is an instance of the following object for each defined multiplex
117 struct multiplexGroup
{
118 struct multiplexReal
{ /* For each chunk */
119 sqlite3_file
*p
; /* Handle for the chunk */
120 char *z
; /* Name of this chunk */
121 } *aReal
; /* list of all chunks */
122 int nReal
; /* Number of chunks */
123 char *zName
; /* Base filename of this group */
124 int nName
; /* Length of base filename */
125 int flags
; /* Flags used for original opening */
126 unsigned int szChunk
; /* Chunk size used for this group */
127 unsigned char bEnabled
; /* TRUE to use Multiplex VFS for this file */
128 unsigned char bTruncate
; /* TRUE to enable truncation of databases */
132 ** An instance of the following object represents each open connection
133 ** to a file that is multiplex'ed. This object is a
134 ** subclass of sqlite3_file. The sqlite3_file object for the underlying
135 ** VFS is appended to this structure.
137 struct multiplexConn
{
138 sqlite3_file base
; /* Base class - must be first */
139 multiplexGroup
*pGroup
; /* The underlying group of files */
142 /************************* Global Variables **********************************/
144 ** All global variables used by this file are containing within the following
145 ** gMultiplex structure.
148 /* The pOrigVfs is the real, original underlying VFS implementation.
149 ** Most operations pass-through to the real VFS. This value is read-only
150 ** during operation. It is only modified at start-time and thus does not
153 sqlite3_vfs
*pOrigVfs
;
155 /* The sThisVfs is the VFS structure used by this shim. It is initialized
156 ** at start-time and thus does not require a mutex
158 sqlite3_vfs sThisVfs
;
160 /* The sIoMethods defines the methods used by sqlite3_file objects
161 ** associated with this shim. It is initialized at start-time and does
162 ** not require a mutex.
164 ** When the underlying VFS is called to open a file, it might return
165 ** either a version 1 or a version 2 sqlite3_file object. This shim
166 ** has to create a wrapper sqlite3_file of the same version. Hence
167 ** there are two I/O method structures, one for version 1 and the other
170 sqlite3_io_methods sIoMethodsV1
;
171 sqlite3_io_methods sIoMethodsV2
;
173 /* True when this shim has been initialized.
178 /************************* Utility Routines *********************************/
180 ** Compute a string length that is limited to what can be stored in
181 ** lower 30 bits of a 32-bit signed integer.
183 ** The value returned will never be negative. Nor will it ever be greater
184 ** than the actual length of the string. For very long strings (greater
185 ** than 1GiB) the value returned might be less than the true string length.
187 static int multiplexStrlen30(const char *z
){
190 while( *z2
){ z2
++; }
191 return 0x3fffffff & (int)(z2
- z
);
195 ** Generate the file-name for chunk iChunk of the group with base name
196 ** zBase. The file-name is written to buffer zOut before returning. Buffer
197 ** zOut must be allocated by the caller so that it is at least (nBase+5)
198 ** bytes in size, where nBase is the length of zBase, not including the
201 ** If iChunk is 0 (or 400 - the number for the first journal file chunk),
202 ** the output is a copy of the input string. Otherwise, if
203 ** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
204 ** a "." character, then the output is a copy of the input string with the
205 ** three-digit zero-padded decimal representation if iChunk appended to it.
208 ** zBase="test.db", iChunk=4 -> zOut="test.db004"
210 ** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
211 ** a "." character, then everything after the "." is replaced by the
212 ** three-digit representation of iChunk.
214 ** zBase="test.db", iChunk=4 -> zOut="test.004"
216 ** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
217 ** to pass to sqlite3_uri_parameter() and similar.
219 static void multiplexFilename(
220 const char *zBase
, /* Filename for chunk 0 */
221 int nBase
, /* Size of zBase in bytes (without \0) */
222 int flags
, /* Flags used to open file */
223 int iChunk
, /* Chunk to generate filename for */
224 char *zOut
/* Buffer to write generated name to */
227 memcpy(zOut
, zBase
, n
+1);
228 if( iChunk
!=0 && iChunk
<=MX_CHUNK_NUMBER
){
229 #ifdef SQLITE_ENABLE_8_3_NAMES
231 for(i
=n
-1; i
>0 && i
>=n
-4 && zOut
[i
]!='.'; i
--){}
232 if( i
>=n
-4 ) n
= i
+1;
233 if( flags
& SQLITE_OPEN_MAIN_JOURNAL
){
234 /* The extensions on overflow files for main databases are 001, 002,
235 ** 003 and so forth. To avoid name collisions, add 400 to the
236 ** extensions of journal files so that they are 401, 402, 403, ....
238 iChunk
+= SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET
;
239 }else if( flags
& SQLITE_OPEN_WAL
){
240 /* To avoid name collisions, add 700 to the
241 ** extensions of WAL files so that they are 701, 702, 703, ....
243 iChunk
+= SQLITE_MULTIPLEX_WAL_8_3_OFFSET
;
246 sqlite3_snprintf(4,&zOut
[n
],"%03d",iChunk
);
250 assert( zOut
[n
]=='\0' );
254 /* Compute the filename for the iChunk-th chunk
256 static int multiplexSubFilename(multiplexGroup
*pGroup
, int iChunk
){
257 if( iChunk
>=pGroup
->nReal
){
258 struct multiplexReal
*p
;
259 p
= sqlite3_realloc64(pGroup
->aReal
, (iChunk
+1)*sizeof(*p
));
263 memset(&p
[pGroup
->nReal
], 0, sizeof(p
[0])*(iChunk
+1-pGroup
->nReal
));
265 pGroup
->nReal
= iChunk
+1;
267 if( pGroup
->zName
&& pGroup
->aReal
[iChunk
].z
==0 ){
269 int n
= pGroup
->nName
;
270 z
= sqlite3_malloc64( n
+5 );
274 multiplexFilename(pGroup
->zName
, pGroup
->nName
, pGroup
->flags
, iChunk
, z
);
275 pGroup
->aReal
[iChunk
].z
= sqlite3_create_filename(z
,"","",0,0);
277 if( pGroup
->aReal
[iChunk
].z
==0 ) return SQLITE_NOMEM
;
282 /* Translate an sqlite3_file* that is really a multiplexGroup* into
283 ** the sqlite3_file* for the underlying original VFS.
285 ** For chunk 0, the pGroup->flags determines whether or not a new file
286 ** is created if it does not already exist. For chunks 1 and higher, the
287 ** file is created only if createFlag is 1.
289 static sqlite3_file
*multiplexSubOpen(
290 multiplexGroup
*pGroup
, /* The multiplexor group */
291 int iChunk
, /* Which chunk to open. 0==original file */
292 int *rc
, /* Result code in and out */
293 int *pOutFlags
, /* Output flags */
294 int createFlag
/* True to create if iChunk>0 */
296 sqlite3_file
*pSubOpen
= 0;
297 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
299 #ifdef SQLITE_ENABLE_8_3_NAMES
300 /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
301 ** part of a database journal are named db.401, db.402, and so on. A
302 ** database may therefore not grow to larger than 400 chunks. Attempting
303 ** to open chunk 401 indicates the database is full. */
304 if( iChunk
>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET
){
305 sqlite3_log(SQLITE_FULL
, "multiplexed chunk overflow: %s", pGroup
->zName
);
311 *rc
= multiplexSubFilename(pGroup
, iChunk
);
312 if( (*rc
)==SQLITE_OK
&& (pSubOpen
= pGroup
->aReal
[iChunk
].p
)==0 ){
314 flags
= pGroup
->flags
;
316 flags
|= SQLITE_OPEN_CREATE
;
317 }else if( iChunk
==0 ){
319 }else if( pGroup
->aReal
[iChunk
].z
==0 ){
322 *rc
= pOrigVfs
->xAccess(pOrigVfs
, pGroup
->aReal
[iChunk
].z
,
323 SQLITE_ACCESS_EXISTS
, &bExists
);
324 if( *rc
|| !bExists
){
326 sqlite3_log(*rc
, "multiplexor.xAccess failure on %s",
327 pGroup
->aReal
[iChunk
].z
);
331 flags
&= ~SQLITE_OPEN_CREATE
;
333 pSubOpen
= sqlite3_malloc64( pOrigVfs
->szOsFile
);
335 *rc
= SQLITE_IOERR_NOMEM
;
338 pGroup
->aReal
[iChunk
].p
= pSubOpen
;
339 *rc
= pOrigVfs
->xOpen(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, pSubOpen
,
341 if( (*rc
)!=SQLITE_OK
){
342 sqlite3_log(*rc
, "multiplexor.xOpen failure on %s",
343 pGroup
->aReal
[iChunk
].z
);
344 sqlite3_free(pSubOpen
);
345 pGroup
->aReal
[iChunk
].p
= 0;
353 ** Return the size, in bytes, of chunk number iChunk. If that chunk
354 ** does not exist, then return 0. This function does not distingish between
355 ** non-existant files and zero-length files.
357 static sqlite3_int64
multiplexSubSize(
358 multiplexGroup
*pGroup
, /* The multiplexor group */
359 int iChunk
, /* Which chunk to open. 0==original file */
360 int *rc
/* Result code in and out */
363 sqlite3_int64 sz
= 0;
366 pSub
= multiplexSubOpen(pGroup
, iChunk
, rc
, NULL
, 0);
367 if( pSub
==0 ) return 0;
368 *rc
= pSub
->pMethods
->xFileSize(pSub
, &sz
);
373 ** This is the implementation of the multiplex_control() SQL function.
375 static void multiplexControlFunc(
376 sqlite3_context
*context
,
381 sqlite3
*db
= sqlite3_context_db_handle(context
);
385 if( !db
|| argc
!=2 ){
389 op
= sqlite3_value_int(argv
[0]);
390 iVal
= sqlite3_value_int(argv
[1]);
391 /* map function op to file_control op */
394 op
= MULTIPLEX_CTRL_ENABLE
;
397 op
= MULTIPLEX_CTRL_SET_CHUNK_SIZE
;
400 op
= MULTIPLEX_CTRL_SET_MAX_CHUNKS
;
403 rc
= SQLITE_NOTFOUND
;
408 rc
= sqlite3_file_control(db
, 0, op
, &iVal
);
410 sqlite3_result_error_code(context
, rc
);
414 ** This is the entry point to register the auto-extension for the
415 ** multiplex_control() function.
417 static int multiplexFuncInit(
420 const sqlite3_api_routines
*pApi
423 rc
= sqlite3_create_function(db
, "multiplex_control", 2, SQLITE_ANY
,
424 0, multiplexControlFunc
, 0, 0);
429 ** Close a single sub-file in the connection group.
431 static void multiplexSubClose(
432 multiplexGroup
*pGroup
,
434 sqlite3_vfs
*pOrigVfs
436 sqlite3_file
*pSubOpen
= pGroup
->aReal
[iChunk
].p
;
438 pSubOpen
->pMethods
->xClose(pSubOpen
);
439 if( pOrigVfs
&& pGroup
->aReal
[iChunk
].z
){
440 pOrigVfs
->xDelete(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, 0);
442 sqlite3_free(pGroup
->aReal
[iChunk
].p
);
444 sqlite3_free_filename(pGroup
->aReal
[iChunk
].z
);
445 memset(&pGroup
->aReal
[iChunk
], 0, sizeof(pGroup
->aReal
[iChunk
]));
449 ** Deallocate memory held by a multiplexGroup
451 static void multiplexFreeComponents(multiplexGroup
*pGroup
){
453 for(i
=0; i
<pGroup
->nReal
; i
++){ multiplexSubClose(pGroup
, i
, 0); }
454 sqlite3_free(pGroup
->aReal
);
460 /************************* VFS Method Wrappers *****************************/
463 ** This is the xOpen method used for the "multiplex" VFS.
465 ** Most of the work is done by the underlying original VFS. This method
466 ** simply links the new file into the appropriate multiplex group if it is a
467 ** file that needs to be tracked.
469 static int multiplexOpen(
470 sqlite3_vfs
*pVfs
, /* The multiplex VFS */
471 const char *zName
, /* Name of file to be opened */
472 sqlite3_file
*pConn
, /* Fill in this file descriptor */
473 int flags
, /* Flags to control the opening */
474 int *pOutFlags
/* Flags showing results of opening */
476 int rc
= SQLITE_OK
; /* Result code */
477 multiplexConn
*pMultiplexOpen
; /* The new multiplex file descriptor */
478 multiplexGroup
*pGroup
= 0; /* Corresponding multiplexGroup object */
479 sqlite3_file
*pSubOpen
= 0; /* Real file descriptor */
480 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
485 UNUSED_PARAMETER(pVfs
);
486 memset(pConn
, 0, pVfs
->szOsFile
);
487 assert( zName
|| (flags
& SQLITE_OPEN_DELETEONCLOSE
) );
489 /* We need to create a group structure and manage
490 ** access to this group of files.
492 pMultiplexOpen
= (multiplexConn
*)pConn
;
495 /* allocate space for group */
496 nName
= zName
? multiplexStrlen30(zName
) : 0;
497 sz
= sizeof(multiplexGroup
) /* multiplexGroup */
498 + nName
+ 1; /* zName */
499 pGroup
= sqlite3_malloc64( sz
);
506 const char *zUri
= (flags
& SQLITE_OPEN_URI
) ? zName
: 0;
507 /* assign pointers to extra space allocated */
508 memset(pGroup
, 0, sz
);
509 pMultiplexOpen
->pGroup
= pGroup
;
510 pGroup
->bEnabled
= (unsigned char)-1;
511 pGroup
->bTruncate
= (unsigned char)sqlite3_uri_boolean(zUri
, "truncate",
512 (flags
& SQLITE_OPEN_MAIN_DB
)==0);
513 pGroup
->szChunk
= (int)sqlite3_uri_int64(zUri
, "chunksize",
514 SQLITE_MULTIPLEX_CHUNK_SIZE
);
515 pGroup
->szChunk
= (pGroup
->szChunk
+0xffff)&~0xffff;
517 char *p
= (char *)&pGroup
[1];
519 memcpy(pGroup
->zName
, zName
, nName
+1);
520 pGroup
->nName
= nName
;
522 if( pGroup
->bEnabled
){
523 /* Make sure that the chunksize is such that the pending byte does not
524 ** falls at the end of a chunk. A region of up to 64K following
525 ** the pending byte is never written, so if the pending byte occurs
526 ** near the end of a chunk, that chunk will be too small. */
527 #ifndef SQLITE_OMIT_WSD
528 extern int sqlite3PendingByte
;
530 int sqlite3PendingByte
= 0x40000000;
532 while( (sqlite3PendingByte
% pGroup
->szChunk
)>=(pGroup
->szChunk
-65536) ){
533 pGroup
->szChunk
+= 65536;
536 pGroup
->flags
= (flags
& ~SQLITE_OPEN_URI
);
537 rc
= multiplexSubFilename(pGroup
, 1);
539 pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, pOutFlags
, 0);
540 if( pSubOpen
==0 && rc
==SQLITE_OK
) rc
= SQLITE_CANTOPEN
;
545 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, &sz64
);
546 if( rc
==SQLITE_OK
&& zName
){
548 if( flags
& SQLITE_OPEN_SUPER_JOURNAL
){
549 pGroup
->bEnabled
= 0;
552 if( flags
& SQLITE_OPEN_MAIN_JOURNAL
){
553 /* If opening a main journal file and the first chunk is zero
554 ** bytes in size, delete any subsequent chunks from the
558 rc
= pOrigVfs
->xAccess(pOrigVfs
,
559 pGroup
->aReal
[iChunk
].z
, SQLITE_ACCESS_EXISTS
, &bExists
561 if( rc
==SQLITE_OK
&& bExists
){
562 rc
= pOrigVfs
->xDelete(pOrigVfs
, pGroup
->aReal
[iChunk
].z
, 0);
564 rc
= multiplexSubFilename(pGroup
, ++iChunk
);
567 }while( rc
==SQLITE_OK
&& bExists
);
570 /* If the first overflow file exists and if the size of the main file
571 ** is different from the chunk size, that means the chunk size is set
572 ** set incorrectly. So fix it.
574 ** Or, if the first overflow file does not exist and the main file is
575 ** larger than the chunk size, that means the chunk size is too small.
576 ** But we have no way of determining the intended chunk size, so
577 ** just disable the multiplexor all togethre.
579 rc
= pOrigVfs
->xAccess(pOrigVfs
, pGroup
->aReal
[1].z
,
580 SQLITE_ACCESS_EXISTS
, &bExists
);
581 bExists
= multiplexSubSize(pGroup
, 1, &rc
)>0;
582 if( rc
==SQLITE_OK
&& bExists
&& sz64
==(sz64
&0xffff0000) && sz64
>0
583 && sz64
!=pGroup
->szChunk
){
584 pGroup
->szChunk
= (int)sz64
;
585 }else if( rc
==SQLITE_OK
&& !bExists
&& sz64
>pGroup
->szChunk
){
586 pGroup
->bEnabled
= 0;
593 if( pSubOpen
->pMethods
->iVersion
==1 ){
594 pConn
->pMethods
= &gMultiplex
.sIoMethodsV1
;
596 pConn
->pMethods
= &gMultiplex
.sIoMethodsV2
;
599 multiplexFreeComponents(pGroup
);
600 sqlite3_free(pGroup
);
603 sqlite3_free(zToFree
);
608 ** This is the xDelete method used for the "multiplex" VFS.
609 ** It attempts to delete the filename specified.
611 static int multiplexDelete(
612 sqlite3_vfs
*pVfs
, /* The multiplex VFS */
613 const char *zName
, /* Name of file to delete */
617 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
618 rc
= pOrigVfs
->xDelete(pOrigVfs
, zName
, syncDir
);
620 /* If the main chunk was deleted successfully, also delete any subsequent
621 ** chunks - starting with the last (highest numbered).
623 int nName
= (int)strlen(zName
);
625 z
= sqlite3_malloc64(nName
+ 5);
627 rc
= SQLITE_IOERR_NOMEM
;
632 multiplexFilename(zName
, nName
, SQLITE_OPEN_MAIN_JOURNAL
, ++iChunk
, z
);
633 rc
= pOrigVfs
->xAccess(pOrigVfs
, z
, SQLITE_ACCESS_EXISTS
, &bExists
);
634 }while( rc
==SQLITE_OK
&& bExists
);
635 while( rc
==SQLITE_OK
&& iChunk
>1 ){
636 multiplexFilename(zName
, nName
, SQLITE_OPEN_MAIN_JOURNAL
, --iChunk
, z
);
637 rc
= pOrigVfs
->xDelete(pOrigVfs
, z
, syncDir
);
642 multiplexFilename(zName
, nName
, SQLITE_OPEN_WAL
, ++iChunk
, z
);
643 rc
= pOrigVfs
->xAccess(pOrigVfs
, z
, SQLITE_ACCESS_EXISTS
, &bExists
);
644 }while( rc
==SQLITE_OK
&& bExists
);
645 while( rc
==SQLITE_OK
&& iChunk
>1 ){
646 multiplexFilename(zName
, nName
, SQLITE_OPEN_WAL
, --iChunk
, z
);
647 rc
= pOrigVfs
->xDelete(pOrigVfs
, z
, syncDir
);
656 static int multiplexAccess(sqlite3_vfs
*a
, const char *b
, int c
, int *d
){
657 return gMultiplex
.pOrigVfs
->xAccess(gMultiplex
.pOrigVfs
, b
, c
, d
);
659 static int multiplexFullPathname(sqlite3_vfs
*a
, const char *b
, int c
, char *d
){
660 return gMultiplex
.pOrigVfs
->xFullPathname(gMultiplex
.pOrigVfs
, b
, c
, d
);
662 static void *multiplexDlOpen(sqlite3_vfs
*a
, const char *b
){
663 return gMultiplex
.pOrigVfs
->xDlOpen(gMultiplex
.pOrigVfs
, b
);
665 static void multiplexDlError(sqlite3_vfs
*a
, int b
, char *c
){
666 gMultiplex
.pOrigVfs
->xDlError(gMultiplex
.pOrigVfs
, b
, c
);
668 static void (*multiplexDlSym(sqlite3_vfs
*a
, void *b
, const char *c
))(void){
669 return gMultiplex
.pOrigVfs
->xDlSym(gMultiplex
.pOrigVfs
, b
, c
);
671 static void multiplexDlClose(sqlite3_vfs
*a
, void *b
){
672 gMultiplex
.pOrigVfs
->xDlClose(gMultiplex
.pOrigVfs
, b
);
674 static int multiplexRandomness(sqlite3_vfs
*a
, int b
, char *c
){
675 return gMultiplex
.pOrigVfs
->xRandomness(gMultiplex
.pOrigVfs
, b
, c
);
677 static int multiplexSleep(sqlite3_vfs
*a
, int b
){
678 return gMultiplex
.pOrigVfs
->xSleep(gMultiplex
.pOrigVfs
, b
);
680 static int multiplexCurrentTime(sqlite3_vfs
*a
, double *b
){
681 return gMultiplex
.pOrigVfs
->xCurrentTime(gMultiplex
.pOrigVfs
, b
);
683 static int multiplexGetLastError(sqlite3_vfs
*a
, int b
, char *c
){
684 if( gMultiplex
.pOrigVfs
->xGetLastError
){
685 return gMultiplex
.pOrigVfs
->xGetLastError(gMultiplex
.pOrigVfs
, b
, c
);
690 static int multiplexCurrentTimeInt64(sqlite3_vfs
*a
, sqlite3_int64
*b
){
691 return gMultiplex
.pOrigVfs
->xCurrentTimeInt64(gMultiplex
.pOrigVfs
, b
);
694 /************************ I/O Method Wrappers *******************************/
696 /* xClose requests get passed through to the original VFS.
697 ** We loop over all open chunk handles and close them.
698 ** The group structure for this file is unlinked from
699 ** our list of groups and freed.
701 static int multiplexClose(sqlite3_file
*pConn
){
702 multiplexConn
*p
= (multiplexConn
*)pConn
;
703 multiplexGroup
*pGroup
= p
->pGroup
;
705 multiplexFreeComponents(pGroup
);
706 sqlite3_free(pGroup
);
710 /* Pass xRead requests thru to the original VFS after
711 ** determining the correct chunk to operate on.
712 ** Break up reads across chunk boundaries.
714 static int multiplexRead(
720 multiplexConn
*p
= (multiplexConn
*)pConn
;
721 multiplexGroup
*pGroup
= p
->pGroup
;
723 if( !pGroup
->bEnabled
){
724 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
726 rc
= SQLITE_IOERR_READ
;
728 rc
= pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
, iOfst
);
732 int i
= (int)(iOfst
/ pGroup
->szChunk
);
733 sqlite3_file
*pSubOpen
;
734 pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, NULL
, 1);
736 int extra
= ((int)(iOfst
% pGroup
->szChunk
) + iAmt
) - pGroup
->szChunk
;
737 if( extra
<0 ) extra
= 0;
739 rc
= pSubOpen
->pMethods
->xRead(pSubOpen
, pBuf
, iAmt
,
740 iOfst
% pGroup
->szChunk
);
741 if( rc
!=SQLITE_OK
) break;
742 pBuf
= (char *)pBuf
+ iAmt
;
746 rc
= SQLITE_IOERR_READ
;
755 /* Pass xWrite requests thru to the original VFS after
756 ** determining the correct chunk to operate on.
757 ** Break up writes across chunk boundaries.
759 static int multiplexWrite(
765 multiplexConn
*p
= (multiplexConn
*)pConn
;
766 multiplexGroup
*pGroup
= p
->pGroup
;
768 if( !pGroup
->bEnabled
){
769 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
771 rc
= SQLITE_IOERR_WRITE
;
773 rc
= pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
, iOfst
);
776 while( rc
==SQLITE_OK
&& iAmt
>0 ){
777 int i
= (int)(iOfst
/ pGroup
->szChunk
);
778 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, NULL
, 1);
780 int extra
= ((int)(iOfst
% pGroup
->szChunk
) + iAmt
) -
782 if( extra
<0 ) extra
= 0;
784 rc
= pSubOpen
->pMethods
->xWrite(pSubOpen
, pBuf
, iAmt
,
785 iOfst
% pGroup
->szChunk
);
786 pBuf
= (char *)pBuf
+ iAmt
;
795 /* Pass xTruncate requests thru to the original VFS after
796 ** determining the correct chunk to operate on. Delete any
797 ** chunks above the truncate mark.
799 static int multiplexTruncate(sqlite3_file
*pConn
, sqlite3_int64 size
){
800 multiplexConn
*p
= (multiplexConn
*)pConn
;
801 multiplexGroup
*pGroup
= p
->pGroup
;
803 if( !pGroup
->bEnabled
){
804 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
806 rc
= SQLITE_IOERR_TRUNCATE
;
808 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
);
812 int iBaseGroup
= (int)(size
/ pGroup
->szChunk
);
813 sqlite3_file
*pSubOpen
;
814 sqlite3_vfs
*pOrigVfs
= gMultiplex
.pOrigVfs
; /* Real VFS */
815 /* delete the chunks above the truncate limit */
816 for(i
= pGroup
->nReal
-1; i
>iBaseGroup
&& rc
==SQLITE_OK
; i
--){
817 if( pGroup
->bTruncate
){
818 multiplexSubClose(pGroup
, i
, pOrigVfs
);
820 pSubOpen
= multiplexSubOpen(pGroup
, i
, &rc
, 0, 0);
822 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, 0);
827 pSubOpen
= multiplexSubOpen(pGroup
, iBaseGroup
, &rc
, 0, 0);
829 rc
= pSubOpen
->pMethods
->xTruncate(pSubOpen
, size
% pGroup
->szChunk
);
832 if( rc
) rc
= SQLITE_IOERR_TRUNCATE
;
837 /* Pass xSync requests through to the original VFS without change
839 static int multiplexSync(sqlite3_file
*pConn
, int flags
){
840 multiplexConn
*p
= (multiplexConn
*)pConn
;
841 multiplexGroup
*pGroup
= p
->pGroup
;
844 for(i
=0; i
<pGroup
->nReal
; i
++){
845 sqlite3_file
*pSubOpen
= pGroup
->aReal
[i
].p
;
847 int rc2
= pSubOpen
->pMethods
->xSync(pSubOpen
, flags
);
848 if( rc2
!=SQLITE_OK
) rc
= rc2
;
854 /* Pass xFileSize requests through to the original VFS.
855 ** Aggregate the size of all the chunks before returning.
857 static int multiplexFileSize(sqlite3_file
*pConn
, sqlite3_int64
*pSize
){
858 multiplexConn
*p
= (multiplexConn
*)pConn
;
859 multiplexGroup
*pGroup
= p
->pGroup
;
862 if( !pGroup
->bEnabled
){
863 sqlite3_file
*pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
865 rc
= SQLITE_IOERR_FSTAT
;
867 rc
= pSubOpen
->pMethods
->xFileSize(pSubOpen
, pSize
);
871 for(i
=0; rc
==SQLITE_OK
; i
++){
872 sqlite3_int64 sz
= multiplexSubSize(pGroup
, i
, &rc
);
874 *pSize
= i
*(sqlite3_int64
)pGroup
->szChunk
+ sz
;
880 /* Pass xLock requests through to the original VFS unchanged.
882 static int multiplexLock(sqlite3_file
*pConn
, int lock
){
883 multiplexConn
*p
= (multiplexConn
*)pConn
;
885 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
887 return pSubOpen
->pMethods
->xLock(pSubOpen
, lock
);
892 /* Pass xUnlock requests through to the original VFS unchanged.
894 static int multiplexUnlock(sqlite3_file
*pConn
, int lock
){
895 multiplexConn
*p
= (multiplexConn
*)pConn
;
897 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
899 return pSubOpen
->pMethods
->xUnlock(pSubOpen
, lock
);
901 return SQLITE_IOERR_UNLOCK
;
904 /* Pass xCheckReservedLock requests through to the original VFS unchanged.
906 static int multiplexCheckReservedLock(sqlite3_file
*pConn
, int *pResOut
){
907 multiplexConn
*p
= (multiplexConn
*)pConn
;
909 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
911 return pSubOpen
->pMethods
->xCheckReservedLock(pSubOpen
, pResOut
);
913 return SQLITE_IOERR_CHECKRESERVEDLOCK
;
916 /* Pass xFileControl requests through to the original VFS unchanged,
917 ** except for any MULTIPLEX_CTRL_* requests here.
919 static int multiplexFileControl(sqlite3_file
*pConn
, int op
, void *pArg
){
920 multiplexConn
*p
= (multiplexConn
*)pConn
;
921 multiplexGroup
*pGroup
= p
->pGroup
;
922 int rc
= SQLITE_ERROR
;
923 sqlite3_file
*pSubOpen
;
925 if( !gMultiplex
.isInitialized
) return SQLITE_MISUSE
;
927 case MULTIPLEX_CTRL_ENABLE
:
929 int bEnabled
= *(int *)pArg
;
930 pGroup
->bEnabled
= (unsigned char)bEnabled
;
934 case MULTIPLEX_CTRL_SET_CHUNK_SIZE
:
936 unsigned int szChunk
= *(unsigned*)pArg
;
940 /* Round up to nearest multiple of MAX_PAGE_SIZE. */
941 szChunk
= (szChunk
+ (MAX_PAGE_SIZE
-1));
942 szChunk
&= ~(MAX_PAGE_SIZE
-1);
943 pGroup
->szChunk
= szChunk
;
948 case MULTIPLEX_CTRL_SET_MAX_CHUNKS
:
951 case SQLITE_FCNTL_SIZE_HINT
:
952 case SQLITE_FCNTL_CHUNK_SIZE
:
956 case SQLITE_FCNTL_PRAGMA
: {
957 char **aFcntl
= (char**)pArg
;
959 ** EVIDENCE-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA
960 ** file control is an array of pointers to strings (char**) in which the
961 ** second element of the array is the name of the pragma and the third
962 ** element is the argument to the pragma or NULL if the pragma has no
965 if( aFcntl
[1] && sqlite3_stricmp(aFcntl
[1],"multiplex_truncate")==0 ){
966 if( aFcntl
[2] && aFcntl
[2][0] ){
967 if( sqlite3_stricmp(aFcntl
[2], "on")==0
968 || sqlite3_stricmp(aFcntl
[2], "1")==0 ){
969 pGroup
->bTruncate
= 1;
971 if( sqlite3_stricmp(aFcntl
[2], "off")==0
972 || sqlite3_stricmp(aFcntl
[2], "0")==0 ){
973 pGroup
->bTruncate
= 0;
976 /* EVIDENCE-OF: R-27806-26076 The handler for an SQLITE_FCNTL_PRAGMA
977 ** file control can optionally make the first element of the char**
978 ** argument point to a string obtained from sqlite3_mprintf() or the
979 ** equivalent and that string will become the result of the pragma
980 ** or the error message if the pragma fails.
982 aFcntl
[0] = sqlite3_mprintf(pGroup
->bTruncate
? "on" : "off");
986 /* If the multiplexor does not handle the pragma, pass it through
987 ** into the default case. */
990 pSubOpen
= multiplexSubOpen(pGroup
, 0, &rc
, NULL
, 0);
992 rc
= pSubOpen
->pMethods
->xFileControl(pSubOpen
, op
, pArg
);
993 if( op
==SQLITE_FCNTL_VFSNAME
&& rc
==SQLITE_OK
){
994 *(char**)pArg
= sqlite3_mprintf("multiplex/%z", *(char**)pArg
);
1002 /* Pass xSectorSize requests through to the original VFS unchanged.
1004 static int multiplexSectorSize(sqlite3_file
*pConn
){
1005 multiplexConn
*p
= (multiplexConn
*)pConn
;
1007 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1008 if( pSubOpen
&& pSubOpen
->pMethods
->xSectorSize
){
1009 return pSubOpen
->pMethods
->xSectorSize(pSubOpen
);
1011 return DEFAULT_SECTOR_SIZE
;
1014 /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
1016 static int multiplexDeviceCharacteristics(sqlite3_file
*pConn
){
1017 multiplexConn
*p
= (multiplexConn
*)pConn
;
1019 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1021 return pSubOpen
->pMethods
->xDeviceCharacteristics(pSubOpen
);
1026 /* Pass xShmMap requests through to the original VFS unchanged.
1028 static int multiplexShmMap(
1029 sqlite3_file
*pConn
, /* Handle open on database file */
1030 int iRegion
, /* Region to retrieve */
1031 int szRegion
, /* Size of regions */
1032 int bExtend
, /* True to extend file if necessary */
1033 void volatile **pp
/* OUT: Mapped memory */
1035 multiplexConn
*p
= (multiplexConn
*)pConn
;
1037 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1039 return pSubOpen
->pMethods
->xShmMap(pSubOpen
, iRegion
, szRegion
, bExtend
,pp
);
1041 return SQLITE_IOERR
;
1044 /* Pass xShmLock requests through to the original VFS unchanged.
1046 static int multiplexShmLock(
1047 sqlite3_file
*pConn
, /* Database file holding the shared memory */
1048 int ofst
, /* First lock to acquire or release */
1049 int n
, /* Number of locks to acquire or release */
1050 int flags
/* What to do with the lock */
1052 multiplexConn
*p
= (multiplexConn
*)pConn
;
1054 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1056 return pSubOpen
->pMethods
->xShmLock(pSubOpen
, ofst
, n
, flags
);
1061 /* Pass xShmBarrier requests through to the original VFS unchanged.
1063 static void multiplexShmBarrier(sqlite3_file
*pConn
){
1064 multiplexConn
*p
= (multiplexConn
*)pConn
;
1066 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1068 pSubOpen
->pMethods
->xShmBarrier(pSubOpen
);
1072 /* Pass xShmUnmap requests through to the original VFS unchanged.
1074 static int multiplexShmUnmap(sqlite3_file
*pConn
, int deleteFlag
){
1075 multiplexConn
*p
= (multiplexConn
*)pConn
;
1077 sqlite3_file
*pSubOpen
= multiplexSubOpen(p
->pGroup
, 0, &rc
, NULL
, 0);
1079 return pSubOpen
->pMethods
->xShmUnmap(pSubOpen
, deleteFlag
);
1084 /************************** Public Interfaces *****************************/
1086 ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
1088 ** Use the VFS named zOrigVfsName as the VFS that does the actual work.
1089 ** Use the default if zOrigVfsName==NULL.
1091 ** The multiplex VFS shim is named "multiplex". It will become the default
1092 ** VFS if makeDefault is non-zero.
1094 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
1097 int sqlite3_multiplex_initialize(const char *zOrigVfsName
, int makeDefault
){
1098 sqlite3_vfs
*pOrigVfs
;
1099 if( gMultiplex
.isInitialized
) return SQLITE_MISUSE
;
1100 pOrigVfs
= sqlite3_vfs_find(zOrigVfsName
);
1101 if( pOrigVfs
==0 ) return SQLITE_ERROR
;
1102 assert( pOrigVfs
!=&gMultiplex
.sThisVfs
);
1103 gMultiplex
.isInitialized
= 1;
1104 gMultiplex
.pOrigVfs
= pOrigVfs
;
1105 gMultiplex
.sThisVfs
= *pOrigVfs
;
1106 gMultiplex
.sThisVfs
.szOsFile
+= sizeof(multiplexConn
);
1107 gMultiplex
.sThisVfs
.zName
= SQLITE_MULTIPLEX_VFS_NAME
;
1108 gMultiplex
.sThisVfs
.xOpen
= multiplexOpen
;
1109 gMultiplex
.sThisVfs
.xDelete
= multiplexDelete
;
1110 gMultiplex
.sThisVfs
.xAccess
= multiplexAccess
;
1111 gMultiplex
.sThisVfs
.xFullPathname
= multiplexFullPathname
;
1112 gMultiplex
.sThisVfs
.xDlOpen
= multiplexDlOpen
;
1113 gMultiplex
.sThisVfs
.xDlError
= multiplexDlError
;
1114 gMultiplex
.sThisVfs
.xDlSym
= multiplexDlSym
;
1115 gMultiplex
.sThisVfs
.xDlClose
= multiplexDlClose
;
1116 gMultiplex
.sThisVfs
.xRandomness
= multiplexRandomness
;
1117 gMultiplex
.sThisVfs
.xSleep
= multiplexSleep
;
1118 gMultiplex
.sThisVfs
.xCurrentTime
= multiplexCurrentTime
;
1119 gMultiplex
.sThisVfs
.xGetLastError
= multiplexGetLastError
;
1120 gMultiplex
.sThisVfs
.xCurrentTimeInt64
= multiplexCurrentTimeInt64
;
1122 gMultiplex
.sIoMethodsV1
.iVersion
= 1;
1123 gMultiplex
.sIoMethodsV1
.xClose
= multiplexClose
;
1124 gMultiplex
.sIoMethodsV1
.xRead
= multiplexRead
;
1125 gMultiplex
.sIoMethodsV1
.xWrite
= multiplexWrite
;
1126 gMultiplex
.sIoMethodsV1
.xTruncate
= multiplexTruncate
;
1127 gMultiplex
.sIoMethodsV1
.xSync
= multiplexSync
;
1128 gMultiplex
.sIoMethodsV1
.xFileSize
= multiplexFileSize
;
1129 gMultiplex
.sIoMethodsV1
.xLock
= multiplexLock
;
1130 gMultiplex
.sIoMethodsV1
.xUnlock
= multiplexUnlock
;
1131 gMultiplex
.sIoMethodsV1
.xCheckReservedLock
= multiplexCheckReservedLock
;
1132 gMultiplex
.sIoMethodsV1
.xFileControl
= multiplexFileControl
;
1133 gMultiplex
.sIoMethodsV1
.xSectorSize
= multiplexSectorSize
;
1134 gMultiplex
.sIoMethodsV1
.xDeviceCharacteristics
=
1135 multiplexDeviceCharacteristics
;
1136 gMultiplex
.sIoMethodsV2
= gMultiplex
.sIoMethodsV1
;
1137 gMultiplex
.sIoMethodsV2
.iVersion
= 2;
1138 gMultiplex
.sIoMethodsV2
.xShmMap
= multiplexShmMap
;
1139 gMultiplex
.sIoMethodsV2
.xShmLock
= multiplexShmLock
;
1140 gMultiplex
.sIoMethodsV2
.xShmBarrier
= multiplexShmBarrier
;
1141 gMultiplex
.sIoMethodsV2
.xShmUnmap
= multiplexShmUnmap
;
1142 sqlite3_vfs_register(&gMultiplex
.sThisVfs
, makeDefault
);
1144 sqlite3_auto_extension((void(*)(void))multiplexFuncInit
);
1150 ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
1152 ** All SQLite database connections must be closed before calling this
1155 ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
1156 ** shutting down in order to free all remaining multiplex groups.
1158 int sqlite3_multiplex_shutdown(int eForce
){
1160 if( gMultiplex
.isInitialized
==0 ) return SQLITE_MISUSE
;
1161 gMultiplex
.isInitialized
= 0;
1162 sqlite3_vfs_unregister(&gMultiplex
.sThisVfs
);
1163 memset(&gMultiplex
, 0, sizeof(gMultiplex
));
1167 /***************************** Test Code ***********************************/
1169 #if defined(INCLUDE_SQLITE_TCL_H)
1170 # include "sqlite_tcl.h"
1173 # ifndef SQLITE_TCLAPI
1174 # define SQLITE_TCLAPI
1177 extern const char *sqlite3ErrName(int);
1181 ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
1183 static int SQLITE_TCLAPI
test_multiplex_initialize(
1187 Tcl_Obj
*CONST objv
[]
1189 const char *zName
; /* Name of new multiplex VFS */
1190 int makeDefault
; /* True to make the new VFS the default */
1191 int rc
; /* Value returned by multiplex_initialize() */
1193 UNUSED_PARAMETER(clientData
);
1195 /* Process arguments */
1197 Tcl_WrongNumArgs(interp
, 1, objv
, "NAME MAKEDEFAULT");
1200 zName
= Tcl_GetString(objv
[1]);
1201 if( Tcl_GetBooleanFromObj(interp
, objv
[2], &makeDefault
) ) return TCL_ERROR
;
1202 if( zName
[0]=='\0' ) zName
= 0;
1204 /* Call sqlite3_multiplex_initialize() */
1205 rc
= sqlite3_multiplex_initialize(zName
, makeDefault
);
1206 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1212 ** tclcmd: sqlite3_multiplex_shutdown
1214 static int SQLITE_TCLAPI
test_multiplex_shutdown(
1218 Tcl_Obj
*CONST objv
[]
1220 int rc
; /* Value returned by multiplex_shutdown() */
1222 UNUSED_PARAMETER(clientData
);
1224 if( objc
==2 && strcmp(Tcl_GetString(objv
[1]),"-force")!=0 ){
1227 if( (objc
!=1 && objc
!=2) ){
1228 Tcl_WrongNumArgs(interp
, 1, objv
, "?-force?");
1232 /* Call sqlite3_multiplex_shutdown() */
1233 rc
= sqlite3_multiplex_shutdown(objc
==2);
1234 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1240 ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
1242 static int SQLITE_TCLAPI
test_multiplex_control(
1246 Tcl_Obj
*CONST objv
[]
1248 int rc
; /* Return code from file_control() */
1249 int idx
; /* Index in aSub[] */
1250 Tcl_CmdInfo cmdInfo
; /* Command info structure for HANDLE */
1251 sqlite3
*db
; /* Underlying db handle for HANDLE */
1260 { "enable", MULTIPLEX_CTRL_ENABLE
, 1 },
1261 { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE
, 1 },
1262 { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS
, 1 },
1267 Tcl_WrongNumArgs(interp
, 1, objv
, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
1271 if( 0==Tcl_GetCommandInfo(interp
, Tcl_GetString(objv
[1]), &cmdInfo
) ){
1272 Tcl_AppendResult(interp
, "expected database handle, got \"", 0);
1273 Tcl_AppendResult(interp
, Tcl_GetString(objv
[1]), "\"", 0);
1276 db
= *(sqlite3
**)cmdInfo
.objClientData
;
1279 rc
= Tcl_GetIndexFromObjStruct(
1280 interp
, objv
[3], aSub
, sizeof(aSub
[0]), "sub-command", 0, &idx
1282 if( rc
!=TCL_OK
) return rc
;
1284 switch( aSub
[idx
].argtype
){
1286 if( Tcl_GetIntFromObj(interp
, objv
[4], &iValue
) ){
1289 pArg
= (void *)&iValue
;
1292 Tcl_WrongNumArgs(interp
, 4, objv
, "SUB-COMMAND");
1296 rc
= sqlite3_file_control(db
, Tcl_GetString(objv
[2]), aSub
[idx
].op
, pArg
);
1297 Tcl_SetResult(interp
, (char *)sqlite3ErrName(rc
), TCL_STATIC
);
1298 return (rc
==SQLITE_OK
) ? TCL_OK
: TCL_ERROR
;
1302 ** This routine registers the custom TCL commands defined in this
1303 ** module. This should be the only procedure visible from outside
1306 int Sqlitemultiplex_Init(Tcl_Interp
*interp
){
1309 Tcl_ObjCmdProc
*xProc
;
1311 { "sqlite3_multiplex_initialize", test_multiplex_initialize
},
1312 { "sqlite3_multiplex_shutdown", test_multiplex_shutdown
},
1313 { "sqlite3_multiplex_control", test_multiplex_control
},
1317 for(i
=0; i
<sizeof(aCmd
)/sizeof(aCmd
[0]); i
++){
1318 Tcl_CreateObjCommand(interp
, aCmd
[i
].zName
, aCmd
[i
].xProc
, 0, 0);