deprecate cipher_store_pass
[sqlcipher.git] / src / test_multiplex.c
blob5ef1ec18399dd3e7ed6cad648b494a1a171987d4
1 /*
2 ** 2010 October 28
3 **
4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
6 **
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.
19 ** USAGE:
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
27 ** );
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
39 ** URI.
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
48 ** at run-time.
50 #include "sqlite3.h"
51 #include <string.h>
52 #include <assert.h>
53 #include <stdlib.h>
54 #include "test_multiplex.h"
56 #ifndef SQLITE_CORE
57 #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
58 #endif
59 #include "sqlite3ext.h"
61 /*
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
65 ** module.
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"
83 #endif
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
91 #endif
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
95 ** number of chunks.
97 ** May be changed by calling the xFileControl() interface.
99 #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
100 # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
101 #endif
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
115 ** group.
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.
147 static struct {
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
151 ** require a mutex.
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
168 ** for version 2.
170 sqlite3_io_methods sIoMethodsV1;
171 sqlite3_io_methods sIoMethodsV2;
173 /* True when this shim has been initialized.
175 int isInitialized;
176 } gMultiplex;
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){
188 const char *z2 = z;
189 if( z==0 ) return 0;
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
199 ** nul-terminator.
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.
206 ** For example:
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 */
226 int n = nBase;
227 memcpy(zOut, zBase, n+1);
228 if( iChunk!=0 && iChunk<=MX_CHUNK_NUMBER ){
229 #ifdef SQLITE_ENABLE_8_3_NAMES
230 int i;
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;
245 #endif
246 sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
247 n += 3;
250 assert( zOut[n]=='\0' );
251 zOut[n+1] = '\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));
260 if( p==0 ){
261 return SQLITE_NOMEM;
263 memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
264 pGroup->aReal = p;
265 pGroup->nReal = iChunk+1;
267 if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
268 char *z;
269 int n = pGroup->nName;
270 z = sqlite3_malloc64( n+5 );
271 if( z==0 ){
272 return SQLITE_NOMEM;
274 multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
275 pGroup->aReal[iChunk].z = sqlite3_create_filename(z,"","",0,0);
276 sqlite3_free(z);
277 if( pGroup->aReal[iChunk].z==0 ) return SQLITE_NOMEM;
279 return SQLITE_OK;
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);
306 *rc = SQLITE_FULL;
307 return 0;
309 #endif
311 *rc = multiplexSubFilename(pGroup, iChunk);
312 if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
313 int flags, bExists;
314 flags = pGroup->flags;
315 if( createFlag ){
316 flags |= SQLITE_OPEN_CREATE;
317 }else if( iChunk==0 ){
318 /* Fall through */
319 }else if( pGroup->aReal[iChunk].z==0 ){
320 return 0;
321 }else{
322 *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
323 SQLITE_ACCESS_EXISTS, &bExists);
324 if( *rc || !bExists ){
325 if( *rc ){
326 sqlite3_log(*rc, "multiplexor.xAccess failure on %s",
327 pGroup->aReal[iChunk].z);
329 return 0;
331 flags &= ~SQLITE_OPEN_CREATE;
333 pSubOpen = sqlite3_malloc64( pOrigVfs->szOsFile );
334 if( pSubOpen==0 ){
335 *rc = SQLITE_IOERR_NOMEM;
336 return 0;
338 pGroup->aReal[iChunk].p = pSubOpen;
339 *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
340 flags, pOutFlags);
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;
346 return 0;
349 return pSubOpen;
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 */
362 sqlite3_file *pSub;
363 sqlite3_int64 sz = 0;
365 if( *rc ) return 0;
366 pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
367 if( pSub==0 ) return 0;
368 *rc = pSub->pMethods->xFileSize(pSub, &sz);
369 return sz;
373 ** This is the implementation of the multiplex_control() SQL function.
375 static void multiplexControlFunc(
376 sqlite3_context *context,
377 int argc,
378 sqlite3_value **argv
380 int rc = SQLITE_OK;
381 sqlite3 *db = sqlite3_context_db_handle(context);
382 int op = 0;
383 int iVal;
385 if( !db || argc!=2 ){
386 rc = SQLITE_ERROR;
387 }else{
388 /* extract params */
389 op = sqlite3_value_int(argv[0]);
390 iVal = sqlite3_value_int(argv[1]);
391 /* map function op to file_control op */
392 switch( op ){
393 case 1:
394 op = MULTIPLEX_CTRL_ENABLE;
395 break;
396 case 2:
397 op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
398 break;
399 case 3:
400 op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
401 break;
402 default:
403 rc = SQLITE_NOTFOUND;
404 break;
407 if( rc==SQLITE_OK ){
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(
418 sqlite3 *db,
419 char **pzErrMsg,
420 const sqlite3_api_routines *pApi
422 int rc;
423 rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
424 0, multiplexControlFunc, 0, 0);
425 return rc;
429 ** Close a single sub-file in the connection group.
431 static void multiplexSubClose(
432 multiplexGroup *pGroup,
433 int iChunk,
434 sqlite3_vfs *pOrigVfs
436 sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
437 if( pSubOpen ){
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){
452 int i;
453 for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
454 sqlite3_free(pGroup->aReal);
455 pGroup->aReal = 0;
456 pGroup->nReal = 0;
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 */
481 int nName = 0;
482 int sz = 0;
483 char *zToFree = 0;
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;
494 if( rc==SQLITE_OK ){
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 );
500 if( pGroup==0 ){
501 rc = SQLITE_NOMEM;
505 if( rc==SQLITE_OK ){
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;
516 if( zName ){
517 char *p = (char *)&pGroup[1];
518 pGroup->zName = p;
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;
529 #else
530 int sqlite3PendingByte = 0x40000000;
531 #endif
532 while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
533 pGroup->szChunk += 65536;
536 pGroup->flags = (flags & ~SQLITE_OPEN_URI);
537 rc = multiplexSubFilename(pGroup, 1);
538 if( rc==SQLITE_OK ){
539 pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
540 if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
542 if( rc==SQLITE_OK ){
543 sqlite3_int64 sz64;
545 rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz64);
546 if( rc==SQLITE_OK && zName ){
547 int bExists;
548 if( flags & SQLITE_OPEN_SUPER_JOURNAL ){
549 pGroup->bEnabled = 0;
550 }else
551 if( sz64==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
555 ** file-system. */
556 int iChunk = 1;
557 do {
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);
563 if( rc==SQLITE_OK ){
564 rc = multiplexSubFilename(pGroup, ++iChunk);
567 }while( rc==SQLITE_OK && bExists );
569 }else{
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;
592 if( rc==SQLITE_OK ){
593 if( pSubOpen->pMethods->iVersion==1 ){
594 pConn->pMethods = &gMultiplex.sIoMethodsV1;
595 }else{
596 pConn->pMethods = &gMultiplex.sIoMethodsV2;
598 }else{
599 multiplexFreeComponents(pGroup);
600 sqlite3_free(pGroup);
603 sqlite3_free(zToFree);
604 return rc;
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 */
614 int syncDir
616 int rc;
617 sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
618 rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
619 if( rc==SQLITE_OK ){
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);
624 char *z;
625 z = sqlite3_malloc64(nName + 5);
626 if( z==0 ){
627 rc = SQLITE_IOERR_NOMEM;
628 }else{
629 int iChunk = 0;
630 int bExists;
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);
639 if( rc==SQLITE_OK ){
640 iChunk = 0;
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);
651 sqlite3_free(z);
653 return rc;
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);
686 }else{
687 return 0;
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;
704 int rc = SQLITE_OK;
705 multiplexFreeComponents(pGroup);
706 sqlite3_free(pGroup);
707 return rc;
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(
715 sqlite3_file *pConn,
716 void *pBuf,
717 int iAmt,
718 sqlite3_int64 iOfst
720 multiplexConn *p = (multiplexConn*)pConn;
721 multiplexGroup *pGroup = p->pGroup;
722 int rc = SQLITE_OK;
723 if( !pGroup->bEnabled ){
724 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
725 if( pSubOpen==0 ){
726 rc = SQLITE_IOERR_READ;
727 }else{
728 rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
730 }else{
731 while( iAmt > 0 ){
732 int i = (int)(iOfst / pGroup->szChunk);
733 sqlite3_file *pSubOpen;
734 pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
735 if( pSubOpen ){
736 int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
737 if( extra<0 ) extra = 0;
738 iAmt -= extra;
739 rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
740 iOfst % pGroup->szChunk);
741 if( rc!=SQLITE_OK ) break;
742 pBuf = (char *)pBuf + iAmt;
743 iOfst += iAmt;
744 iAmt = extra;
745 }else{
746 rc = SQLITE_IOERR_READ;
747 break;
752 return rc;
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(
760 sqlite3_file *pConn,
761 const void *pBuf,
762 int iAmt,
763 sqlite3_int64 iOfst
765 multiplexConn *p = (multiplexConn*)pConn;
766 multiplexGroup *pGroup = p->pGroup;
767 int rc = SQLITE_OK;
768 if( !pGroup->bEnabled ){
769 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
770 if( pSubOpen==0 ){
771 rc = SQLITE_IOERR_WRITE;
772 }else{
773 rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
775 }else{
776 while( rc==SQLITE_OK && iAmt>0 ){
777 int i = (int)(iOfst / pGroup->szChunk);
778 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
779 if( pSubOpen ){
780 int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
781 pGroup->szChunk;
782 if( extra<0 ) extra = 0;
783 iAmt -= extra;
784 rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
785 iOfst % pGroup->szChunk);
786 pBuf = (char *)pBuf + iAmt;
787 iOfst += iAmt;
788 iAmt = extra;
792 return rc;
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;
802 int rc = SQLITE_OK;
803 if( !pGroup->bEnabled ){
804 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
805 if( pSubOpen==0 ){
806 rc = SQLITE_IOERR_TRUNCATE;
807 }else{
808 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
810 }else{
811 int i;
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);
819 }else{
820 pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
821 if( pSubOpen ){
822 rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
826 if( rc==SQLITE_OK ){
827 pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
828 if( pSubOpen ){
829 rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
832 if( rc ) rc = SQLITE_IOERR_TRUNCATE;
834 return rc;
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;
842 int rc = SQLITE_OK;
843 int i;
844 for(i=0; i<pGroup->nReal; i++){
845 sqlite3_file *pSubOpen = pGroup->aReal[i].p;
846 if( pSubOpen ){
847 int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
848 if( rc2!=SQLITE_OK ) rc = rc2;
851 return rc;
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;
860 int rc = SQLITE_OK;
861 int i;
862 if( !pGroup->bEnabled ){
863 sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
864 if( pSubOpen==0 ){
865 rc = SQLITE_IOERR_FSTAT;
866 }else{
867 rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
869 }else{
870 *pSize = 0;
871 for(i=0; rc==SQLITE_OK; i++){
872 sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
873 if( sz==0 ) break;
874 *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
877 return rc;
880 /* Pass xLock requests through to the original VFS unchanged.
882 static int multiplexLock(sqlite3_file *pConn, int lock){
883 multiplexConn *p = (multiplexConn*)pConn;
884 int rc;
885 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
886 if( pSubOpen ){
887 return pSubOpen->pMethods->xLock(pSubOpen, lock);
889 return SQLITE_BUSY;
892 /* Pass xUnlock requests through to the original VFS unchanged.
894 static int multiplexUnlock(sqlite3_file *pConn, int lock){
895 multiplexConn *p = (multiplexConn*)pConn;
896 int rc;
897 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
898 if( pSubOpen ){
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;
908 int rc;
909 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
910 if( pSubOpen ){
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;
926 switch( op ){
927 case MULTIPLEX_CTRL_ENABLE:
928 if( pArg ) {
929 int bEnabled = *(int *)pArg;
930 pGroup->bEnabled = (unsigned char)bEnabled;
931 rc = SQLITE_OK;
933 break;
934 case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
935 if( pArg ) {
936 unsigned int szChunk = *(unsigned*)pArg;
937 if( szChunk<1 ){
938 rc = SQLITE_MISUSE;
939 }else{
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;
944 rc = SQLITE_OK;
947 break;
948 case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
949 rc = SQLITE_OK;
950 break;
951 case SQLITE_FCNTL_SIZE_HINT:
952 case SQLITE_FCNTL_CHUNK_SIZE:
953 /* no-op these */
954 rc = SQLITE_OK;
955 break;
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
963 ** argument.
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;
970 }else
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");
983 rc = SQLITE_OK;
984 break;
986 /* If the multiplexor does not handle the pragma, pass it through
987 ** into the default case. */
989 default:
990 pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
991 if( pSubOpen ){
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);
997 break;
999 return rc;
1002 /* Pass xSectorSize requests through to the original VFS unchanged.
1004 static int multiplexSectorSize(sqlite3_file *pConn){
1005 multiplexConn *p = (multiplexConn*)pConn;
1006 int rc;
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;
1018 int rc;
1019 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1020 if( pSubOpen ){
1021 return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
1023 return 0;
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;
1036 int rc;
1037 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1038 if( pSubOpen ){
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;
1053 int rc;
1054 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1055 if( pSubOpen ){
1056 return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
1058 return SQLITE_BUSY;
1061 /* Pass xShmBarrier requests through to the original VFS unchanged.
1063 static void multiplexShmBarrier(sqlite3_file *pConn){
1064 multiplexConn *p = (multiplexConn*)pConn;
1065 int rc;
1066 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1067 if( pSubOpen ){
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;
1076 int rc;
1077 sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1078 if( pSubOpen ){
1079 return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
1081 return SQLITE_OK;
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
1095 ** during start-up.
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);
1146 return SQLITE_OK;
1150 ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
1152 ** All SQLite database connections must be closed before calling this
1153 ** routine.
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){
1159 int rc = SQLITE_OK;
1160 if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
1161 gMultiplex.isInitialized = 0;
1162 sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
1163 memset(&gMultiplex, 0, sizeof(gMultiplex));
1164 return rc;
1167 /***************************** Test Code ***********************************/
1168 #ifdef SQLITE_TEST
1169 #if defined(INCLUDE_SQLITE_TCL_H)
1170 # include "sqlite_tcl.h"
1171 #else
1172 # include "tcl.h"
1173 # ifndef SQLITE_TCLAPI
1174 # define SQLITE_TCLAPI
1175 # endif
1176 #endif
1177 extern const char *sqlite3ErrName(int);
1181 ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
1183 static int SQLITE_TCLAPI test_multiplex_initialize(
1184 void * clientData,
1185 Tcl_Interp *interp,
1186 int objc,
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 */
1196 if( objc!=3 ){
1197 Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
1198 return TCL_ERROR;
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);
1208 return TCL_OK;
1212 ** tclcmd: sqlite3_multiplex_shutdown
1214 static int SQLITE_TCLAPI test_multiplex_shutdown(
1215 void * clientData,
1216 Tcl_Interp *interp,
1217 int objc,
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 ){
1225 objc = 3;
1227 if( (objc!=1 && objc!=2) ){
1228 Tcl_WrongNumArgs(interp, 1, objv, "?-force?");
1229 return TCL_ERROR;
1232 /* Call sqlite3_multiplex_shutdown() */
1233 rc = sqlite3_multiplex_shutdown(objc==2);
1234 Tcl_SetResult(interp, (char *)sqlite3ErrName(rc), TCL_STATIC);
1236 return TCL_OK;
1240 ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
1242 static int SQLITE_TCLAPI test_multiplex_control(
1243 ClientData cd,
1244 Tcl_Interp *interp,
1245 int objc,
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 */
1252 int iValue = 0;
1253 void *pArg = 0;
1255 struct SubCommand {
1256 const char *zName;
1257 int op;
1258 int argtype;
1259 } aSub[] = {
1260 { "enable", MULTIPLEX_CTRL_ENABLE, 1 },
1261 { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 },
1262 { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 },
1263 { 0, 0, 0 }
1266 if( objc!=5 ){
1267 Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
1268 return TCL_ERROR;
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);
1274 return TCL_ERROR;
1275 }else{
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 ){
1285 case 1:
1286 if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
1287 return TCL_ERROR;
1289 pArg = (void *)&iValue;
1290 break;
1291 default:
1292 Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
1293 return TCL_ERROR;
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
1304 ** of this module.
1306 int Sqlitemultiplex_Init(Tcl_Interp *interp){
1307 static struct {
1308 char *zName;
1309 Tcl_ObjCmdProc *xProc;
1310 } aCmd[] = {
1311 { "sqlite3_multiplex_initialize", test_multiplex_initialize },
1312 { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
1313 { "sqlite3_multiplex_control", test_multiplex_control },
1315 int i;
1317 for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
1318 Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
1321 return TCL_OK;
1323 #endif