Annotation of embedaddon/sqlite3/src/test_multiplex.c, revision 1.1.1.1
1.1 misho 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.
10: **
11: *************************************************************************
12: **
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.
18: **
19: ** USAGE:
20: **
21: ** Compile this source file and link it with your application. Then
22: ** at start-time, invoke the following procedure:
23: **
24: ** int sqlite3_multiplex_initialize(
25: ** const char *zOrigVfsName, // The underlying real VFS
26: ** int makeDefault // True to make multiplex the default VFS
27: ** );
28: **
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.)
34: **
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.
40: **
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.
49: */
50: #include "sqlite3.h"
51: #include <string.h>
52: #include <assert.h>
53: #include <stdlib.h>
54: #include "test_multiplex.h"
55:
56: #ifndef SQLITE_CORE
57: #define SQLITE_CORE 1 /* Disable the API redefinition in sqlite3ext.h */
58: #endif
59: #include "sqlite3ext.h"
60:
61: /*
62: ** These should be defined to be the same as the values in
63: ** sqliteInt.h. They are defined seperately here so that
64: ** the multiplex VFS shim can be built as a loadable
65: ** module.
66: */
67: #define UNUSED_PARAMETER(x) (void)(x)
68: #define MAX_PAGE_SIZE 0x10000
69: #define DEFAULT_SECTOR_SIZE 0x1000
70:
71: /*
72: ** For a build without mutexes, no-op the mutex calls.
73: */
74: #if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE==0
75: #define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
76: #define sqlite3_mutex_free(X)
77: #define sqlite3_mutex_enter(X)
78: #define sqlite3_mutex_try(X) SQLITE_OK
79: #define sqlite3_mutex_leave(X)
80: #define sqlite3_mutex_held(X) ((void)(X),1)
81: #define sqlite3_mutex_notheld(X) ((void)(X),1)
82: #endif /* SQLITE_THREADSAFE==0 */
83:
84: /* First chunk for rollback journal files */
85: #define SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET 400
86:
87:
88: /************************ Shim Definitions ******************************/
89:
90: #ifndef SQLITE_MULTIPLEX_VFS_NAME
91: # define SQLITE_MULTIPLEX_VFS_NAME "multiplex"
92: #endif
93:
94: /* This is the limit on the chunk size. It may be changed by calling
95: ** the xFileControl() interface. It will be rounded up to a
96: ** multiple of MAX_PAGE_SIZE. We default it here to 2GiB less 64KiB.
97: */
98: #ifndef SQLITE_MULTIPLEX_CHUNK_SIZE
99: # define SQLITE_MULTIPLEX_CHUNK_SIZE 2147418112
100: #endif
101:
102: /* This used to be the default limit on number of chunks, but
103: ** it is no longer enforced. There is currently no limit to the
104: ** number of chunks.
105: **
106: ** May be changed by calling the xFileControl() interface.
107: */
108: #ifndef SQLITE_MULTIPLEX_MAX_CHUNKS
109: # define SQLITE_MULTIPLEX_MAX_CHUNKS 12
110: #endif
111:
112: /************************ Object Definitions ******************************/
113:
114: /* Forward declaration of all object types */
115: typedef struct multiplexGroup multiplexGroup;
116: typedef struct multiplexConn multiplexConn;
117:
118: /*
119: ** A "multiplex group" is a collection of files that collectively
120: ** makeup a single SQLite DB file. This allows the size of the DB
121: ** to exceed the limits imposed by the file system.
122: **
123: ** There is an instance of the following object for each defined multiplex
124: ** group.
125: */
126: struct multiplexGroup {
127: struct multiplexReal { /* For each chunk */
128: sqlite3_file *p; /* Handle for the chunk */
129: char *z; /* Name of this chunk */
130: } *aReal; /* list of all chunks */
131: int nReal; /* Number of chunks */
132: char *zName; /* Base filename of this group */
133: int nName; /* Length of base filename */
134: int flags; /* Flags used for original opening */
135: unsigned int szChunk; /* Chunk size used for this group */
136: unsigned char bEnabled; /* TRUE to use Multiplex VFS for this file */
137: unsigned char bTruncate; /* TRUE to enable truncation of databases */
138: multiplexGroup *pNext, *pPrev; /* Doubly linked list of all group objects */
139: };
140:
141: /*
142: ** An instance of the following object represents each open connection
143: ** to a file that is multiplex'ed. This object is a
144: ** subclass of sqlite3_file. The sqlite3_file object for the underlying
145: ** VFS is appended to this structure.
146: */
147: struct multiplexConn {
148: sqlite3_file base; /* Base class - must be first */
149: multiplexGroup *pGroup; /* The underlying group of files */
150: };
151:
152: /************************* Global Variables **********************************/
153: /*
154: ** All global variables used by this file are containing within the following
155: ** gMultiplex structure.
156: */
157: static struct {
158: /* The pOrigVfs is the real, original underlying VFS implementation.
159: ** Most operations pass-through to the real VFS. This value is read-only
160: ** during operation. It is only modified at start-time and thus does not
161: ** require a mutex.
162: */
163: sqlite3_vfs *pOrigVfs;
164:
165: /* The sThisVfs is the VFS structure used by this shim. It is initialized
166: ** at start-time and thus does not require a mutex
167: */
168: sqlite3_vfs sThisVfs;
169:
170: /* The sIoMethods defines the methods used by sqlite3_file objects
171: ** associated with this shim. It is initialized at start-time and does
172: ** not require a mutex.
173: **
174: ** When the underlying VFS is called to open a file, it might return
175: ** either a version 1 or a version 2 sqlite3_file object. This shim
176: ** has to create a wrapper sqlite3_file of the same version. Hence
177: ** there are two I/O method structures, one for version 1 and the other
178: ** for version 2.
179: */
180: sqlite3_io_methods sIoMethodsV1;
181: sqlite3_io_methods sIoMethodsV2;
182:
183: /* True when this shim has been initialized.
184: */
185: int isInitialized;
186:
187: /* For run-time access any of the other global data structures in this
188: ** shim, the following mutex must be held.
189: */
190: sqlite3_mutex *pMutex;
191:
192: /* List of multiplexGroup objects.
193: */
194: multiplexGroup *pGroups;
195: } gMultiplex;
196:
197: /************************* Utility Routines *********************************/
198: /*
199: ** Acquire and release the mutex used to serialize access to the
200: ** list of multiplexGroups.
201: */
202: static void multiplexEnter(void){ sqlite3_mutex_enter(gMultiplex.pMutex); }
203: static void multiplexLeave(void){ sqlite3_mutex_leave(gMultiplex.pMutex); }
204:
205: /*
206: ** Compute a string length that is limited to what can be stored in
207: ** lower 30 bits of a 32-bit signed integer.
208: **
209: ** The value returned will never be negative. Nor will it ever be greater
210: ** than the actual length of the string. For very long strings (greater
211: ** than 1GiB) the value returned might be less than the true string length.
212: */
213: static int multiplexStrlen30(const char *z){
214: const char *z2 = z;
215: if( z==0 ) return 0;
216: while( *z2 ){ z2++; }
217: return 0x3fffffff & (int)(z2 - z);
218: }
219:
220: /*
221: ** Generate the file-name for chunk iChunk of the group with base name
222: ** zBase. The file-name is written to buffer zOut before returning. Buffer
223: ** zOut must be allocated by the caller so that it is at least (nBase+5)
224: ** bytes in size, where nBase is the length of zBase, not including the
225: ** nul-terminator.
226: **
227: ** If iChunk is 0 (or 400 - the number for the first journal file chunk),
228: ** the output is a copy of the input string. Otherwise, if
229: ** SQLITE_ENABLE_8_3_NAMES is not defined or the input buffer does not contain
230: ** a "." character, then the output is a copy of the input string with the
231: ** three-digit zero-padded decimal representation if iChunk appended to it.
232: ** For example:
233: **
234: ** zBase="test.db", iChunk=4 -> zOut="test.db004"
235: **
236: ** Or, if SQLITE_ENABLE_8_3_NAMES is defined and the input buffer contains
237: ** a "." character, then everything after the "." is replaced by the
238: ** three-digit representation of iChunk.
239: **
240: ** zBase="test.db", iChunk=4 -> zOut="test.004"
241: **
242: ** The output buffer string is terminated by 2 0x00 bytes. This makes it safe
243: ** to pass to sqlite3_uri_parameter() and similar.
244: */
245: static void multiplexFilename(
246: const char *zBase, /* Filename for chunk 0 */
247: int nBase, /* Size of zBase in bytes (without \0) */
248: int flags, /* Flags used to open file */
249: int iChunk, /* Chunk to generate filename for */
250: char *zOut /* Buffer to write generated name to */
251: ){
252: int n = nBase;
253: memcpy(zOut, zBase, n+1);
254: if( iChunk!=0 && iChunk!=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
255: #ifdef SQLITE_ENABLE_8_3_NAMES
256: int i;
257: for(i=n-1; i>0 && i>=n-4 && zOut[i]!='.'; i--){}
258: if( i>=n-4 ) n = i+1;
259: if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
260: /* The extensions on overflow files for main databases are 001, 002,
261: ** 003 and so forth. To avoid name collisions, add 400 to the
262: ** extensions of journal files so that they are 401, 402, 403, ....
263: */
264: iChunk += SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET;
265: }
266: #endif
267: sqlite3_snprintf(4,&zOut[n],"%03d",iChunk);
268: n += 3;
269: }
270:
271: assert( zOut[n]=='\0' );
272: zOut[n+1] = '\0';
273: }
274:
275: /* Compute the filename for the iChunk-th chunk
276: */
277: static int multiplexSubFilename(multiplexGroup *pGroup, int iChunk){
278: if( iChunk>=pGroup->nReal ){
279: struct multiplexReal *p;
280: p = sqlite3_realloc(pGroup->aReal, (iChunk+1)*sizeof(*p));
281: if( p==0 ){
282: return SQLITE_NOMEM;
283: }
284: memset(&p[pGroup->nReal], 0, sizeof(p[0])*(iChunk+1-pGroup->nReal));
285: pGroup->aReal = p;
286: pGroup->nReal = iChunk+1;
287: }
288: if( pGroup->zName && pGroup->aReal[iChunk].z==0 ){
289: char *z;
290: int n = pGroup->nName;
291: pGroup->aReal[iChunk].z = z = sqlite3_malloc( n+5 );
292: if( z==0 ){
293: return SQLITE_NOMEM;
294: }
295: multiplexFilename(pGroup->zName, pGroup->nName, pGroup->flags, iChunk, z);
296: }
297: return SQLITE_OK;
298: }
299:
300: /* Translate an sqlite3_file* that is really a multiplexGroup* into
301: ** the sqlite3_file* for the underlying original VFS.
302: **
303: ** For chunk 0, the pGroup->flags determines whether or not a new file
304: ** is created if it does not already exist. For chunks 1 and higher, the
305: ** file is created only if createFlag is 1.
306: */
307: static sqlite3_file *multiplexSubOpen(
308: multiplexGroup *pGroup, /* The multiplexor group */
309: int iChunk, /* Which chunk to open. 0==original file */
310: int *rc, /* Result code in and out */
311: int *pOutFlags, /* Output flags */
312: int createFlag /* True to create if iChunk>0 */
313: ){
314: sqlite3_file *pSubOpen = 0;
315: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
316:
317: #ifdef SQLITE_ENABLE_8_3_NAMES
318: /* If JOURNAL_8_3_OFFSET is set to (say) 400, then any overflow files are
319: ** part of a database journal are named db.401, db.402, and so on. A
320: ** database may therefore not grow to larger than 400 chunks. Attempting
321: ** to open chunk 401 indicates the database is full. */
322: if( iChunk>=SQLITE_MULTIPLEX_JOURNAL_8_3_OFFSET ){
323: *rc = SQLITE_FULL;
324: return 0;
325: }
326: #endif
327:
328: *rc = multiplexSubFilename(pGroup, iChunk);
329: if( (*rc)==SQLITE_OK && (pSubOpen = pGroup->aReal[iChunk].p)==0 ){
330: int flags, bExists;
331: flags = pGroup->flags;
332: if( createFlag ){
333: flags |= SQLITE_OPEN_CREATE;
334: }else if( iChunk==0 ){
335: /* Fall through */
336: }else if( pGroup->aReal[iChunk].z==0 ){
337: return 0;
338: }else{
339: *rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[iChunk].z,
340: SQLITE_ACCESS_EXISTS, &bExists);
341: if( *rc || !bExists ) return 0;
342: flags &= ~SQLITE_OPEN_CREATE;
343: }
344: pSubOpen = sqlite3_malloc( pOrigVfs->szOsFile );
345: if( pSubOpen==0 ){
346: *rc = SQLITE_IOERR_NOMEM;
347: return 0;
348: }
349: pGroup->aReal[iChunk].p = pSubOpen;
350: *rc = pOrigVfs->xOpen(pOrigVfs, pGroup->aReal[iChunk].z, pSubOpen,
351: flags, pOutFlags);
352: if( (*rc)!=SQLITE_OK ){
353: sqlite3_free(pSubOpen);
354: pGroup->aReal[iChunk].p = 0;
355: return 0;
356: }
357: }
358: return pSubOpen;
359: }
360:
361: /*
362: ** Return the size, in bytes, of chunk number iChunk. If that chunk
363: ** does not exist, then return 0. This function does not distingish between
364: ** non-existant files and zero-length files.
365: */
366: static sqlite3_int64 multiplexSubSize(
367: multiplexGroup *pGroup, /* The multiplexor group */
368: int iChunk, /* Which chunk to open. 0==original file */
369: int *rc /* Result code in and out */
370: ){
371: sqlite3_file *pSub;
372: sqlite3_int64 sz = 0;
373:
374: if( *rc ) return 0;
375: pSub = multiplexSubOpen(pGroup, iChunk, rc, NULL, 0);
376: if( pSub==0 ) return 0;
377: *rc = pSub->pMethods->xFileSize(pSub, &sz);
378: return sz;
379: }
380:
381: /*
382: ** This is the implementation of the multiplex_control() SQL function.
383: */
384: static void multiplexControlFunc(
385: sqlite3_context *context,
386: int argc,
387: sqlite3_value **argv
388: ){
389: int rc = SQLITE_OK;
390: sqlite3 *db = sqlite3_context_db_handle(context);
391: int op;
392: int iVal;
393:
394: if( !db || argc!=2 ){
395: rc = SQLITE_ERROR;
396: }else{
397: /* extract params */
398: op = sqlite3_value_int(argv[0]);
399: iVal = sqlite3_value_int(argv[1]);
400: /* map function op to file_control op */
401: switch( op ){
402: case 1:
403: op = MULTIPLEX_CTRL_ENABLE;
404: break;
405: case 2:
406: op = MULTIPLEX_CTRL_SET_CHUNK_SIZE;
407: break;
408: case 3:
409: op = MULTIPLEX_CTRL_SET_MAX_CHUNKS;
410: break;
411: default:
412: rc = SQLITE_NOTFOUND;
413: break;
414: }
415: }
416: if( rc==SQLITE_OK ){
417: rc = sqlite3_file_control(db, 0, op, &iVal);
418: }
419: sqlite3_result_error_code(context, rc);
420: }
421:
422: /*
423: ** This is the entry point to register the auto-extension for the
424: ** multiplex_control() function.
425: */
426: static int multiplexFuncInit(
427: sqlite3 *db,
428: char **pzErrMsg,
429: const sqlite3_api_routines *pApi
430: ){
431: int rc;
432: rc = sqlite3_create_function(db, "multiplex_control", 2, SQLITE_ANY,
433: 0, multiplexControlFunc, 0, 0);
434: return rc;
435: }
436:
437: /*
438: ** Close a single sub-file in the connection group.
439: */
440: static void multiplexSubClose(
441: multiplexGroup *pGroup,
442: int iChunk,
443: sqlite3_vfs *pOrigVfs
444: ){
445: sqlite3_file *pSubOpen = pGroup->aReal[iChunk].p;
446: if( pSubOpen ){
447: pSubOpen->pMethods->xClose(pSubOpen);
448: if( pOrigVfs && pGroup->aReal[iChunk].z ){
449: pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
450: }
451: sqlite3_free(pGroup->aReal[iChunk].p);
452: }
453: sqlite3_free(pGroup->aReal[iChunk].z);
454: memset(&pGroup->aReal[iChunk], 0, sizeof(pGroup->aReal[iChunk]));
455: }
456:
457: /*
458: ** Deallocate memory held by a multiplexGroup
459: */
460: static void multiplexFreeComponents(multiplexGroup *pGroup){
461: int i;
462: for(i=0; i<pGroup->nReal; i++){ multiplexSubClose(pGroup, i, 0); }
463: sqlite3_free(pGroup->aReal);
464: pGroup->aReal = 0;
465: pGroup->nReal = 0;
466: }
467:
468:
469: /************************* VFS Method Wrappers *****************************/
470:
471: /*
472: ** This is the xOpen method used for the "multiplex" VFS.
473: **
474: ** Most of the work is done by the underlying original VFS. This method
475: ** simply links the new file into the appropriate multiplex group if it is a
476: ** file that needs to be tracked.
477: */
478: static int multiplexOpen(
479: sqlite3_vfs *pVfs, /* The multiplex VFS */
480: const char *zName, /* Name of file to be opened */
481: sqlite3_file *pConn, /* Fill in this file descriptor */
482: int flags, /* Flags to control the opening */
483: int *pOutFlags /* Flags showing results of opening */
484: ){
485: int rc = SQLITE_OK; /* Result code */
486: multiplexConn *pMultiplexOpen; /* The new multiplex file descriptor */
487: multiplexGroup *pGroup; /* Corresponding multiplexGroup object */
488: sqlite3_file *pSubOpen = 0; /* Real file descriptor */
489: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
490: int nName;
491: int sz;
492: char *zToFree = 0;
493:
494: UNUSED_PARAMETER(pVfs);
495: memset(pConn, 0, pVfs->szOsFile);
496: assert( zName || (flags & SQLITE_OPEN_DELETEONCLOSE) );
497:
498: /* We need to create a group structure and manage
499: ** access to this group of files.
500: */
501: multiplexEnter();
502: pMultiplexOpen = (multiplexConn*)pConn;
503:
504: if( rc==SQLITE_OK ){
505: /* allocate space for group */
506: nName = zName ? multiplexStrlen30(zName) : 0;
507: sz = sizeof(multiplexGroup) /* multiplexGroup */
508: + nName + 1; /* zName */
509: pGroup = sqlite3_malloc( sz );
510: if( pGroup==0 ){
511: rc = SQLITE_NOMEM;
512: }
513: }
514:
515: if( rc==SQLITE_OK ){
516: const char *zUri = (flags & SQLITE_OPEN_URI) ? zName : 0;
517: /* assign pointers to extra space allocated */
518: memset(pGroup, 0, sz);
519: pMultiplexOpen->pGroup = pGroup;
520: pGroup->bEnabled = -1;
521: pGroup->bTruncate = sqlite3_uri_boolean(zUri, "truncate",
522: (flags & SQLITE_OPEN_MAIN_DB)==0);
523: pGroup->szChunk = sqlite3_uri_int64(zUri, "chunksize",
524: SQLITE_MULTIPLEX_CHUNK_SIZE);
525: pGroup->szChunk = (pGroup->szChunk+0xffff)&~0xffff;
526: if( zName ){
527: char *p = (char *)&pGroup[1];
528: pGroup->zName = p;
529: memcpy(pGroup->zName, zName, nName+1);
530: pGroup->nName = nName;
531: }
532: if( pGroup->bEnabled ){
533: /* Make sure that the chunksize is such that the pending byte does not
534: ** falls at the end of a chunk. A region of up to 64K following
535: ** the pending byte is never written, so if the pending byte occurs
536: ** near the end of a chunk, that chunk will be too small. */
537: #ifndef SQLITE_OMIT_WSD
538: extern int sqlite3PendingByte;
539: #else
540: int sqlite3PendingByte = 0x40000000;
541: #endif
542: while( (sqlite3PendingByte % pGroup->szChunk)>=(pGroup->szChunk-65536) ){
543: pGroup->szChunk += 65536;
544: }
545: }
546: pGroup->flags = flags;
547: rc = multiplexSubFilename(pGroup, 1);
548: if( rc==SQLITE_OK ){
549: pSubOpen = multiplexSubOpen(pGroup, 0, &rc, pOutFlags, 0);
550: if( pSubOpen==0 && rc==SQLITE_OK ) rc = SQLITE_CANTOPEN;
551: }
552: if( rc==SQLITE_OK ){
553: sqlite3_int64 sz;
554:
555: rc = pSubOpen->pMethods->xFileSize(pSubOpen, &sz);
556: if( rc==SQLITE_OK && zName ){
557: int bExists;
558: if( sz==0 ){
559: if( flags & SQLITE_OPEN_MAIN_JOURNAL ){
560: /* If opening a main journal file and the first chunk is zero
561: ** bytes in size, delete any subsequent chunks from the
562: ** file-system. */
563: int iChunk = 1;
564: do {
565: rc = pOrigVfs->xAccess(pOrigVfs,
566: pGroup->aReal[iChunk].z, SQLITE_ACCESS_EXISTS, &bExists
567: );
568: if( rc==SQLITE_OK && bExists ){
569: rc = pOrigVfs->xDelete(pOrigVfs, pGroup->aReal[iChunk].z, 0);
570: if( rc==SQLITE_OK ){
571: rc = multiplexSubFilename(pGroup, ++iChunk);
572: }
573: }
574: }while( rc==SQLITE_OK && bExists );
575: }
576: }else{
577: /* If the first overflow file exists and if the size of the main file
578: ** is different from the chunk size, that means the chunk size is set
579: ** set incorrectly. So fix it.
580: **
581: ** Or, if the first overflow file does not exist and the main file is
582: ** larger than the chunk size, that means the chunk size is too small.
583: ** But we have no way of determining the intended chunk size, so
584: ** just disable the multiplexor all togethre.
585: */
586: rc = pOrigVfs->xAccess(pOrigVfs, pGroup->aReal[1].z,
587: SQLITE_ACCESS_EXISTS, &bExists);
588: bExists = multiplexSubSize(pGroup, 1, &rc)>0;
589: if( rc==SQLITE_OK && bExists && sz==(sz&0xffff0000) && sz>0
590: && sz!=pGroup->szChunk ){
591: pGroup->szChunk = sz;
592: }else if( rc==SQLITE_OK && !bExists && sz>pGroup->szChunk ){
593: pGroup->bEnabled = 0;
594: }
595: }
596: }
597: }
598:
599: if( rc==SQLITE_OK ){
600: if( pSubOpen->pMethods->iVersion==1 ){
601: pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV1;
602: }else{
603: pMultiplexOpen->base.pMethods = &gMultiplex.sIoMethodsV2;
604: }
605: /* place this group at the head of our list */
606: pGroup->pNext = gMultiplex.pGroups;
607: if( gMultiplex.pGroups ) gMultiplex.pGroups->pPrev = pGroup;
608: gMultiplex.pGroups = pGroup;
609: }else{
610: multiplexFreeComponents(pGroup);
611: sqlite3_free(pGroup);
612: }
613: }
614: multiplexLeave();
615: sqlite3_free(zToFree);
616: return rc;
617: }
618:
619: /*
620: ** This is the xDelete method used for the "multiplex" VFS.
621: ** It attempts to delete the filename specified.
622: */
623: static int multiplexDelete(
624: sqlite3_vfs *pVfs, /* The multiplex VFS */
625: const char *zName, /* Name of file to delete */
626: int syncDir
627: ){
628: int rc;
629: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
630: rc = pOrigVfs->xDelete(pOrigVfs, zName, syncDir);
631: if( rc==SQLITE_OK ){
632: /* If the main chunk was deleted successfully, also delete any subsequent
633: ** chunks - starting with the last (highest numbered).
634: */
635: int nName = strlen(zName);
636: char *z;
637: z = sqlite3_malloc(nName + 5);
638: if( z==0 ){
639: rc = SQLITE_IOERR_NOMEM;
640: }else{
641: int iChunk = 0;
642: int bExists;
643: do{
644: multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, ++iChunk, z);
645: rc = pOrigVfs->xAccess(pOrigVfs, z, SQLITE_ACCESS_EXISTS, &bExists);
646: }while( rc==SQLITE_OK && bExists );
647: while( rc==SQLITE_OK && iChunk>1 ){
648: multiplexFilename(zName, nName, SQLITE_OPEN_MAIN_JOURNAL, --iChunk, z);
649: rc = pOrigVfs->xDelete(pOrigVfs, z, syncDir);
650: }
651: }
652: sqlite3_free(z);
653: }
654: return rc;
655: }
656:
657: static int multiplexAccess(sqlite3_vfs *a, const char *b, int c, int *d){
658: return gMultiplex.pOrigVfs->xAccess(gMultiplex.pOrigVfs, b, c, d);
659: }
660: static int multiplexFullPathname(sqlite3_vfs *a, const char *b, int c, char *d){
661: return gMultiplex.pOrigVfs->xFullPathname(gMultiplex.pOrigVfs, b, c, d);
662: }
663: static void *multiplexDlOpen(sqlite3_vfs *a, const char *b){
664: return gMultiplex.pOrigVfs->xDlOpen(gMultiplex.pOrigVfs, b);
665: }
666: static void multiplexDlError(sqlite3_vfs *a, int b, char *c){
667: gMultiplex.pOrigVfs->xDlError(gMultiplex.pOrigVfs, b, c);
668: }
669: static void (*multiplexDlSym(sqlite3_vfs *a, void *b, const char *c))(void){
670: return gMultiplex.pOrigVfs->xDlSym(gMultiplex.pOrigVfs, b, c);
671: }
672: static void multiplexDlClose(sqlite3_vfs *a, void *b){
673: gMultiplex.pOrigVfs->xDlClose(gMultiplex.pOrigVfs, b);
674: }
675: static int multiplexRandomness(sqlite3_vfs *a, int b, char *c){
676: return gMultiplex.pOrigVfs->xRandomness(gMultiplex.pOrigVfs, b, c);
677: }
678: static int multiplexSleep(sqlite3_vfs *a, int b){
679: return gMultiplex.pOrigVfs->xSleep(gMultiplex.pOrigVfs, b);
680: }
681: static int multiplexCurrentTime(sqlite3_vfs *a, double *b){
682: return gMultiplex.pOrigVfs->xCurrentTime(gMultiplex.pOrigVfs, b);
683: }
684: static int multiplexGetLastError(sqlite3_vfs *a, int b, char *c){
685: return gMultiplex.pOrigVfs->xGetLastError(gMultiplex.pOrigVfs, b, c);
686: }
687: static int multiplexCurrentTimeInt64(sqlite3_vfs *a, sqlite3_int64 *b){
688: return gMultiplex.pOrigVfs->xCurrentTimeInt64(gMultiplex.pOrigVfs, b);
689: }
690:
691: /************************ I/O Method Wrappers *******************************/
692:
693: /* xClose requests get passed through to the original VFS.
694: ** We loop over all open chunk handles and close them.
695: ** The group structure for this file is unlinked from
696: ** our list of groups and freed.
697: */
698: static int multiplexClose(sqlite3_file *pConn){
699: multiplexConn *p = (multiplexConn*)pConn;
700: multiplexGroup *pGroup = p->pGroup;
701: int rc = SQLITE_OK;
702: multiplexEnter();
703: multiplexFreeComponents(pGroup);
704: /* remove from linked list */
705: if( pGroup->pNext ) pGroup->pNext->pPrev = pGroup->pPrev;
706: if( pGroup->pPrev ){
707: pGroup->pPrev->pNext = pGroup->pNext;
708: }else{
709: gMultiplex.pGroups = pGroup->pNext;
710: }
711: sqlite3_free(pGroup);
712: multiplexLeave();
713: return rc;
714: }
715:
716: /* Pass xRead requests thru to the original VFS after
717: ** determining the correct chunk to operate on.
718: ** Break up reads across chunk boundaries.
719: */
720: static int multiplexRead(
721: sqlite3_file *pConn,
722: void *pBuf,
723: int iAmt,
724: sqlite3_int64 iOfst
725: ){
726: multiplexConn *p = (multiplexConn*)pConn;
727: multiplexGroup *pGroup = p->pGroup;
728: int rc = SQLITE_OK;
729: multiplexEnter();
730: if( !pGroup->bEnabled ){
731: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
732: if( pSubOpen==0 ){
733: rc = SQLITE_IOERR_READ;
734: }else{
735: rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt, iOfst);
736: }
737: }else{
738: while( iAmt > 0 ){
739: int i = (int)(iOfst / pGroup->szChunk);
740: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
741: if( pSubOpen ){
742: int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) - pGroup->szChunk;
743: if( extra<0 ) extra = 0;
744: iAmt -= extra;
745: rc = pSubOpen->pMethods->xRead(pSubOpen, pBuf, iAmt,
746: iOfst % pGroup->szChunk);
747: if( rc!=SQLITE_OK ) break;
748: pBuf = (char *)pBuf + iAmt;
749: iOfst += iAmt;
750: iAmt = extra;
751: }else{
752: rc = SQLITE_IOERR_READ;
753: break;
754: }
755: }
756: }
757: multiplexLeave();
758: return rc;
759: }
760:
761: /* Pass xWrite requests thru to the original VFS after
762: ** determining the correct chunk to operate on.
763: ** Break up writes across chunk boundaries.
764: */
765: static int multiplexWrite(
766: sqlite3_file *pConn,
767: const void *pBuf,
768: int iAmt,
769: sqlite3_int64 iOfst
770: ){
771: multiplexConn *p = (multiplexConn*)pConn;
772: multiplexGroup *pGroup = p->pGroup;
773: int rc = SQLITE_OK;
774: multiplexEnter();
775: if( !pGroup->bEnabled ){
776: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
777: if( pSubOpen==0 ){
778: rc = SQLITE_IOERR_WRITE;
779: }else{
780: rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt, iOfst);
781: }
782: }else{
783: while( rc==SQLITE_OK && iAmt>0 ){
784: int i = (int)(iOfst / pGroup->szChunk);
785: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, i, &rc, NULL, 1);
786: if( pSubOpen ){
787: int extra = ((int)(iOfst % pGroup->szChunk) + iAmt) -
788: pGroup->szChunk;
789: if( extra<0 ) extra = 0;
790: iAmt -= extra;
791: rc = pSubOpen->pMethods->xWrite(pSubOpen, pBuf, iAmt,
792: iOfst % pGroup->szChunk);
793: pBuf = (char *)pBuf + iAmt;
794: iOfst += iAmt;
795: iAmt = extra;
796: }
797: }
798: }
799: multiplexLeave();
800: return rc;
801: }
802:
803: /* Pass xTruncate requests thru to the original VFS after
804: ** determining the correct chunk to operate on. Delete any
805: ** chunks above the truncate mark.
806: */
807: static int multiplexTruncate(sqlite3_file *pConn, sqlite3_int64 size){
808: multiplexConn *p = (multiplexConn*)pConn;
809: multiplexGroup *pGroup = p->pGroup;
810: int rc = SQLITE_OK;
811: multiplexEnter();
812: if( !pGroup->bEnabled ){
813: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
814: if( pSubOpen==0 ){
815: rc = SQLITE_IOERR_TRUNCATE;
816: }else{
817: rc = pSubOpen->pMethods->xTruncate(pSubOpen, size);
818: }
819: }else{
820: int i;
821: int iBaseGroup = (int)(size / pGroup->szChunk);
822: sqlite3_file *pSubOpen;
823: sqlite3_vfs *pOrigVfs = gMultiplex.pOrigVfs; /* Real VFS */
824: /* delete the chunks above the truncate limit */
825: for(i = pGroup->nReal-1; i>iBaseGroup && rc==SQLITE_OK; i--){
826: if( pGroup->bTruncate ){
827: multiplexSubClose(pGroup, i, pOrigVfs);
828: }else{
829: pSubOpen = multiplexSubOpen(pGroup, i, &rc, 0, 0);
830: if( pSubOpen ){
831: rc = pSubOpen->pMethods->xTruncate(pSubOpen, 0);
832: }
833: }
834: }
835: if( rc==SQLITE_OK ){
836: pSubOpen = multiplexSubOpen(pGroup, iBaseGroup, &rc, 0, 0);
837: if( pSubOpen ){
838: rc = pSubOpen->pMethods->xTruncate(pSubOpen, size % pGroup->szChunk);
839: }
840: }
841: if( rc ) rc = SQLITE_IOERR_TRUNCATE;
842: }
843: multiplexLeave();
844: return rc;
845: }
846:
847: /* Pass xSync requests through to the original VFS without change
848: */
849: static int multiplexSync(sqlite3_file *pConn, int flags){
850: multiplexConn *p = (multiplexConn*)pConn;
851: multiplexGroup *pGroup = p->pGroup;
852: int rc = SQLITE_OK;
853: int i;
854: multiplexEnter();
855: for(i=0; i<pGroup->nReal; i++){
856: sqlite3_file *pSubOpen = pGroup->aReal[i].p;
857: if( pSubOpen ){
858: int rc2 = pSubOpen->pMethods->xSync(pSubOpen, flags);
859: if( rc2!=SQLITE_OK ) rc = rc2;
860: }
861: }
862: multiplexLeave();
863: return rc;
864: }
865:
866: /* Pass xFileSize requests through to the original VFS.
867: ** Aggregate the size of all the chunks before returning.
868: */
869: static int multiplexFileSize(sqlite3_file *pConn, sqlite3_int64 *pSize){
870: multiplexConn *p = (multiplexConn*)pConn;
871: multiplexGroup *pGroup = p->pGroup;
872: int rc = SQLITE_OK;
873: int i;
874: multiplexEnter();
875: if( !pGroup->bEnabled ){
876: sqlite3_file *pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
877: if( pSubOpen==0 ){
878: rc = SQLITE_IOERR_FSTAT;
879: }else{
880: rc = pSubOpen->pMethods->xFileSize(pSubOpen, pSize);
881: }
882: }else{
883: *pSize = 0;
884: for(i=0; rc==SQLITE_OK; i++){
885: sqlite3_int64 sz = multiplexSubSize(pGroup, i, &rc);
886: if( sz==0 ) break;
887: *pSize = i*(sqlite3_int64)pGroup->szChunk + sz;
888: }
889: }
890: multiplexLeave();
891: return rc;
892: }
893:
894: /* Pass xLock requests through to the original VFS unchanged.
895: */
896: static int multiplexLock(sqlite3_file *pConn, int lock){
897: multiplexConn *p = (multiplexConn*)pConn;
898: int rc;
899: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
900: if( pSubOpen ){
901: return pSubOpen->pMethods->xLock(pSubOpen, lock);
902: }
903: return SQLITE_BUSY;
904: }
905:
906: /* Pass xUnlock requests through to the original VFS unchanged.
907: */
908: static int multiplexUnlock(sqlite3_file *pConn, int lock){
909: multiplexConn *p = (multiplexConn*)pConn;
910: int rc;
911: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
912: if( pSubOpen ){
913: return pSubOpen->pMethods->xUnlock(pSubOpen, lock);
914: }
915: return SQLITE_IOERR_UNLOCK;
916: }
917:
918: /* Pass xCheckReservedLock requests through to the original VFS unchanged.
919: */
920: static int multiplexCheckReservedLock(sqlite3_file *pConn, int *pResOut){
921: multiplexConn *p = (multiplexConn*)pConn;
922: int rc;
923: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
924: if( pSubOpen ){
925: return pSubOpen->pMethods->xCheckReservedLock(pSubOpen, pResOut);
926: }
927: return SQLITE_IOERR_CHECKRESERVEDLOCK;
928: }
929:
930: /* Pass xFileControl requests through to the original VFS unchanged,
931: ** except for any MULTIPLEX_CTRL_* requests here.
932: */
933: static int multiplexFileControl(sqlite3_file *pConn, int op, void *pArg){
934: multiplexConn *p = (multiplexConn*)pConn;
935: multiplexGroup *pGroup = p->pGroup;
936: int rc = SQLITE_ERROR;
937: sqlite3_file *pSubOpen;
938:
939: if( !gMultiplex.isInitialized ) return SQLITE_MISUSE;
940: switch( op ){
941: case MULTIPLEX_CTRL_ENABLE:
942: if( pArg ) {
943: int bEnabled = *(int *)pArg;
944: pGroup->bEnabled = bEnabled;
945: rc = SQLITE_OK;
946: }
947: break;
948: case MULTIPLEX_CTRL_SET_CHUNK_SIZE:
949: if( pArg ) {
950: unsigned int szChunk = *(unsigned*)pArg;
951: if( szChunk<1 ){
952: rc = SQLITE_MISUSE;
953: }else{
954: /* Round up to nearest multiple of MAX_PAGE_SIZE. */
955: szChunk = (szChunk + (MAX_PAGE_SIZE-1));
956: szChunk &= ~(MAX_PAGE_SIZE-1);
957: pGroup->szChunk = szChunk;
958: rc = SQLITE_OK;
959: }
960: }
961: break;
962: case MULTIPLEX_CTRL_SET_MAX_CHUNKS:
963: rc = SQLITE_OK;
964: break;
965: case SQLITE_FCNTL_SIZE_HINT:
966: case SQLITE_FCNTL_CHUNK_SIZE:
967: /* no-op these */
968: rc = SQLITE_OK;
969: break;
970: default:
971: pSubOpen = multiplexSubOpen(pGroup, 0, &rc, NULL, 0);
972: if( pSubOpen ){
973: rc = pSubOpen->pMethods->xFileControl(pSubOpen, op, pArg);
974: if( op==SQLITE_FCNTL_VFSNAME && rc==SQLITE_OK ){
975: *(char**)pArg = sqlite3_mprintf("multiplex/%z", *(char**)pArg);
976: }
977: }
978: break;
979: }
980: return rc;
981: }
982:
983: /* Pass xSectorSize requests through to the original VFS unchanged.
984: */
985: static int multiplexSectorSize(sqlite3_file *pConn){
986: multiplexConn *p = (multiplexConn*)pConn;
987: int rc;
988: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
989: if( pSubOpen && pSubOpen->pMethods->xSectorSize ){
990: return pSubOpen->pMethods->xSectorSize(pSubOpen);
991: }
992: return DEFAULT_SECTOR_SIZE;
993: }
994:
995: /* Pass xDeviceCharacteristics requests through to the original VFS unchanged.
996: */
997: static int multiplexDeviceCharacteristics(sqlite3_file *pConn){
998: multiplexConn *p = (multiplexConn*)pConn;
999: int rc;
1000: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1001: if( pSubOpen ){
1002: return pSubOpen->pMethods->xDeviceCharacteristics(pSubOpen);
1003: }
1004: return 0;
1005: }
1006:
1007: /* Pass xShmMap requests through to the original VFS unchanged.
1008: */
1009: static int multiplexShmMap(
1010: sqlite3_file *pConn, /* Handle open on database file */
1011: int iRegion, /* Region to retrieve */
1012: int szRegion, /* Size of regions */
1013: int bExtend, /* True to extend file if necessary */
1014: void volatile **pp /* OUT: Mapped memory */
1015: ){
1016: multiplexConn *p = (multiplexConn*)pConn;
1017: int rc;
1018: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1019: if( pSubOpen ){
1020: return pSubOpen->pMethods->xShmMap(pSubOpen, iRegion, szRegion, bExtend,pp);
1021: }
1022: return SQLITE_IOERR;
1023: }
1024:
1025: /* Pass xShmLock requests through to the original VFS unchanged.
1026: */
1027: static int multiplexShmLock(
1028: sqlite3_file *pConn, /* Database file holding the shared memory */
1029: int ofst, /* First lock to acquire or release */
1030: int n, /* Number of locks to acquire or release */
1031: int flags /* What to do with the lock */
1032: ){
1033: multiplexConn *p = (multiplexConn*)pConn;
1034: int rc;
1035: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1036: if( pSubOpen ){
1037: return pSubOpen->pMethods->xShmLock(pSubOpen, ofst, n, flags);
1038: }
1039: return SQLITE_BUSY;
1040: }
1041:
1042: /* Pass xShmBarrier requests through to the original VFS unchanged.
1043: */
1044: static void multiplexShmBarrier(sqlite3_file *pConn){
1045: multiplexConn *p = (multiplexConn*)pConn;
1046: int rc;
1047: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1048: if( pSubOpen ){
1049: pSubOpen->pMethods->xShmBarrier(pSubOpen);
1050: }
1051: }
1052:
1053: /* Pass xShmUnmap requests through to the original VFS unchanged.
1054: */
1055: static int multiplexShmUnmap(sqlite3_file *pConn, int deleteFlag){
1056: multiplexConn *p = (multiplexConn*)pConn;
1057: int rc;
1058: sqlite3_file *pSubOpen = multiplexSubOpen(p->pGroup, 0, &rc, NULL, 0);
1059: if( pSubOpen ){
1060: return pSubOpen->pMethods->xShmUnmap(pSubOpen, deleteFlag);
1061: }
1062: return SQLITE_OK;
1063: }
1064:
1065: /************************** Public Interfaces *****************************/
1066: /*
1067: ** CAPI: Initialize the multiplex VFS shim - sqlite3_multiplex_initialize()
1068: **
1069: ** Use the VFS named zOrigVfsName as the VFS that does the actual work.
1070: ** Use the default if zOrigVfsName==NULL.
1071: **
1072: ** The multiplex VFS shim is named "multiplex". It will become the default
1073: ** VFS if makeDefault is non-zero.
1074: **
1075: ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once
1076: ** during start-up.
1077: */
1078: int sqlite3_multiplex_initialize(const char *zOrigVfsName, int makeDefault){
1079: sqlite3_vfs *pOrigVfs;
1080: if( gMultiplex.isInitialized ) return SQLITE_MISUSE;
1081: pOrigVfs = sqlite3_vfs_find(zOrigVfsName);
1082: if( pOrigVfs==0 ) return SQLITE_ERROR;
1083: assert( pOrigVfs!=&gMultiplex.sThisVfs );
1084: gMultiplex.pMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
1085: if( !gMultiplex.pMutex ){
1086: return SQLITE_NOMEM;
1087: }
1088: gMultiplex.pGroups = NULL;
1089: gMultiplex.isInitialized = 1;
1090: gMultiplex.pOrigVfs = pOrigVfs;
1091: gMultiplex.sThisVfs = *pOrigVfs;
1092: gMultiplex.sThisVfs.szOsFile += sizeof(multiplexConn);
1093: gMultiplex.sThisVfs.zName = SQLITE_MULTIPLEX_VFS_NAME;
1094: gMultiplex.sThisVfs.xOpen = multiplexOpen;
1095: gMultiplex.sThisVfs.xDelete = multiplexDelete;
1096: gMultiplex.sThisVfs.xAccess = multiplexAccess;
1097: gMultiplex.sThisVfs.xFullPathname = multiplexFullPathname;
1098: gMultiplex.sThisVfs.xDlOpen = multiplexDlOpen;
1099: gMultiplex.sThisVfs.xDlError = multiplexDlError;
1100: gMultiplex.sThisVfs.xDlSym = multiplexDlSym;
1101: gMultiplex.sThisVfs.xDlClose = multiplexDlClose;
1102: gMultiplex.sThisVfs.xRandomness = multiplexRandomness;
1103: gMultiplex.sThisVfs.xSleep = multiplexSleep;
1104: gMultiplex.sThisVfs.xCurrentTime = multiplexCurrentTime;
1105: gMultiplex.sThisVfs.xGetLastError = multiplexGetLastError;
1106: gMultiplex.sThisVfs.xCurrentTimeInt64 = multiplexCurrentTimeInt64;
1107:
1108: gMultiplex.sIoMethodsV1.iVersion = 1;
1109: gMultiplex.sIoMethodsV1.xClose = multiplexClose;
1110: gMultiplex.sIoMethodsV1.xRead = multiplexRead;
1111: gMultiplex.sIoMethodsV1.xWrite = multiplexWrite;
1112: gMultiplex.sIoMethodsV1.xTruncate = multiplexTruncate;
1113: gMultiplex.sIoMethodsV1.xSync = multiplexSync;
1114: gMultiplex.sIoMethodsV1.xFileSize = multiplexFileSize;
1115: gMultiplex.sIoMethodsV1.xLock = multiplexLock;
1116: gMultiplex.sIoMethodsV1.xUnlock = multiplexUnlock;
1117: gMultiplex.sIoMethodsV1.xCheckReservedLock = multiplexCheckReservedLock;
1118: gMultiplex.sIoMethodsV1.xFileControl = multiplexFileControl;
1119: gMultiplex.sIoMethodsV1.xSectorSize = multiplexSectorSize;
1120: gMultiplex.sIoMethodsV1.xDeviceCharacteristics =
1121: multiplexDeviceCharacteristics;
1122: gMultiplex.sIoMethodsV2 = gMultiplex.sIoMethodsV1;
1123: gMultiplex.sIoMethodsV2.iVersion = 2;
1124: gMultiplex.sIoMethodsV2.xShmMap = multiplexShmMap;
1125: gMultiplex.sIoMethodsV2.xShmLock = multiplexShmLock;
1126: gMultiplex.sIoMethodsV2.xShmBarrier = multiplexShmBarrier;
1127: gMultiplex.sIoMethodsV2.xShmUnmap = multiplexShmUnmap;
1128: sqlite3_vfs_register(&gMultiplex.sThisVfs, makeDefault);
1129:
1130: sqlite3_auto_extension((void*)multiplexFuncInit);
1131:
1132: return SQLITE_OK;
1133: }
1134:
1135: /*
1136: ** CAPI: Shutdown the multiplex system - sqlite3_multiplex_shutdown()
1137: **
1138: ** All SQLite database connections must be closed before calling this
1139: ** routine.
1140: **
1141: ** THIS ROUTINE IS NOT THREADSAFE. Call this routine exactly once while
1142: ** shutting down in order to free all remaining multiplex groups.
1143: */
1144: int sqlite3_multiplex_shutdown(void){
1145: if( gMultiplex.isInitialized==0 ) return SQLITE_MISUSE;
1146: if( gMultiplex.pGroups ) return SQLITE_MISUSE;
1147: gMultiplex.isInitialized = 0;
1148: sqlite3_mutex_free(gMultiplex.pMutex);
1149: sqlite3_vfs_unregister(&gMultiplex.sThisVfs);
1150: memset(&gMultiplex, 0, sizeof(gMultiplex));
1151: return SQLITE_OK;
1152: }
1153:
1154: /***************************** Test Code ***********************************/
1155: #ifdef SQLITE_TEST
1156: #include <tcl.h>
1157: extern const char *sqlite3TestErrorName(int);
1158:
1159:
1160: /*
1161: ** tclcmd: sqlite3_multiplex_initialize NAME MAKEDEFAULT
1162: */
1163: static int test_multiplex_initialize(
1164: void * clientData,
1165: Tcl_Interp *interp,
1166: int objc,
1167: Tcl_Obj *CONST objv[]
1168: ){
1169: const char *zName; /* Name of new multiplex VFS */
1170: int makeDefault; /* True to make the new VFS the default */
1171: int rc; /* Value returned by multiplex_initialize() */
1172:
1173: UNUSED_PARAMETER(clientData);
1174:
1175: /* Process arguments */
1176: if( objc!=3 ){
1177: Tcl_WrongNumArgs(interp, 1, objv, "NAME MAKEDEFAULT");
1178: return TCL_ERROR;
1179: }
1180: zName = Tcl_GetString(objv[1]);
1181: if( Tcl_GetBooleanFromObj(interp, objv[2], &makeDefault) ) return TCL_ERROR;
1182: if( zName[0]=='\0' ) zName = 0;
1183:
1184: /* Call sqlite3_multiplex_initialize() */
1185: rc = sqlite3_multiplex_initialize(zName, makeDefault);
1186: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1187:
1188: return TCL_OK;
1189: }
1190:
1191: /*
1192: ** tclcmd: sqlite3_multiplex_shutdown
1193: */
1194: static int test_multiplex_shutdown(
1195: void * clientData,
1196: Tcl_Interp *interp,
1197: int objc,
1198: Tcl_Obj *CONST objv[]
1199: ){
1200: int rc; /* Value returned by multiplex_shutdown() */
1201:
1202: UNUSED_PARAMETER(clientData);
1203:
1204: if( objc!=1 ){
1205: Tcl_WrongNumArgs(interp, 1, objv, "");
1206: return TCL_ERROR;
1207: }
1208:
1209: /* Call sqlite3_multiplex_shutdown() */
1210: rc = sqlite3_multiplex_shutdown();
1211: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1212:
1213: return TCL_OK;
1214: }
1215:
1216: /*
1217: ** tclcmd: sqlite3_multiplex_dump
1218: */
1219: static int test_multiplex_dump(
1220: void * clientData,
1221: Tcl_Interp *interp,
1222: int objc,
1223: Tcl_Obj *CONST objv[]
1224: ){
1225: Tcl_Obj *pResult;
1226: Tcl_Obj *pGroupTerm;
1227: multiplexGroup *pGroup;
1228: int i;
1229: int nChunks = 0;
1230:
1231: UNUSED_PARAMETER(clientData);
1232: UNUSED_PARAMETER(objc);
1233: UNUSED_PARAMETER(objv);
1234:
1235: pResult = Tcl_NewObj();
1236: multiplexEnter();
1237: for(pGroup=gMultiplex.pGroups; pGroup; pGroup=pGroup->pNext){
1238: pGroupTerm = Tcl_NewObj();
1239:
1240: if( pGroup->zName ){
1241: pGroup->zName[pGroup->nName] = '\0';
1242: Tcl_ListObjAppendElement(interp, pGroupTerm,
1243: Tcl_NewStringObj(pGroup->zName, -1));
1244: }else{
1245: Tcl_ListObjAppendElement(interp, pGroupTerm, Tcl_NewObj());
1246: }
1247: Tcl_ListObjAppendElement(interp, pGroupTerm,
1248: Tcl_NewIntObj(pGroup->nName));
1249: Tcl_ListObjAppendElement(interp, pGroupTerm,
1250: Tcl_NewIntObj(pGroup->flags));
1251:
1252: /* count number of chunks with open handles */
1253: for(i=0; i<pGroup->nReal; i++){
1254: if( pGroup->aReal[i].p!=0 ) nChunks++;
1255: }
1256: Tcl_ListObjAppendElement(interp, pGroupTerm,
1257: Tcl_NewIntObj(nChunks));
1258:
1259: Tcl_ListObjAppendElement(interp, pGroupTerm,
1260: Tcl_NewIntObj(pGroup->szChunk));
1261: Tcl_ListObjAppendElement(interp, pGroupTerm,
1262: Tcl_NewIntObj(pGroup->nReal));
1263:
1264: Tcl_ListObjAppendElement(interp, pResult, pGroupTerm);
1265: }
1266: multiplexLeave();
1267: Tcl_SetObjResult(interp, pResult);
1268: return TCL_OK;
1269: }
1270:
1271: /*
1272: ** Tclcmd: test_multiplex_control HANDLE DBNAME SUB-COMMAND ?INT-VALUE?
1273: */
1274: static int test_multiplex_control(
1275: ClientData cd,
1276: Tcl_Interp *interp,
1277: int objc,
1278: Tcl_Obj *CONST objv[]
1279: ){
1280: int rc; /* Return code from file_control() */
1281: int idx; /* Index in aSub[] */
1282: Tcl_CmdInfo cmdInfo; /* Command info structure for HANDLE */
1283: sqlite3 *db; /* Underlying db handle for HANDLE */
1284: int iValue = 0;
1285: void *pArg = 0;
1286:
1287: struct SubCommand {
1288: const char *zName;
1289: int op;
1290: int argtype;
1291: } aSub[] = {
1292: { "enable", MULTIPLEX_CTRL_ENABLE, 1 },
1293: { "chunk_size", MULTIPLEX_CTRL_SET_CHUNK_SIZE, 1 },
1294: { "max_chunks", MULTIPLEX_CTRL_SET_MAX_CHUNKS, 1 },
1295: { 0, 0, 0 }
1296: };
1297:
1298: if( objc!=5 ){
1299: Tcl_WrongNumArgs(interp, 1, objv, "HANDLE DBNAME SUB-COMMAND INT-VALUE");
1300: return TCL_ERROR;
1301: }
1302:
1303: if( 0==Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){
1304: Tcl_AppendResult(interp, "expected database handle, got \"", 0);
1305: Tcl_AppendResult(interp, Tcl_GetString(objv[1]), "\"", 0);
1306: return TCL_ERROR;
1307: }else{
1308: db = *(sqlite3 **)cmdInfo.objClientData;
1309: }
1310:
1311: rc = Tcl_GetIndexFromObjStruct(
1312: interp, objv[3], aSub, sizeof(aSub[0]), "sub-command", 0, &idx
1313: );
1314: if( rc!=TCL_OK ) return rc;
1315:
1316: switch( aSub[idx].argtype ){
1317: case 1:
1318: if( Tcl_GetIntFromObj(interp, objv[4], &iValue) ){
1319: return TCL_ERROR;
1320: }
1321: pArg = (void *)&iValue;
1322: break;
1323: default:
1324: Tcl_WrongNumArgs(interp, 4, objv, "SUB-COMMAND");
1325: return TCL_ERROR;
1326: }
1327:
1328: rc = sqlite3_file_control(db, Tcl_GetString(objv[2]), aSub[idx].op, pArg);
1329: Tcl_SetResult(interp, (char *)sqlite3TestErrorName(rc), TCL_STATIC);
1330: return (rc==SQLITE_OK) ? TCL_OK : TCL_ERROR;
1331: }
1332:
1333: /*
1334: ** This routine registers the custom TCL commands defined in this
1335: ** module. This should be the only procedure visible from outside
1336: ** of this module.
1337: */
1338: int Sqlitemultiplex_Init(Tcl_Interp *interp){
1339: static struct {
1340: char *zName;
1341: Tcl_ObjCmdProc *xProc;
1342: } aCmd[] = {
1343: { "sqlite3_multiplex_initialize", test_multiplex_initialize },
1344: { "sqlite3_multiplex_shutdown", test_multiplex_shutdown },
1345: { "sqlite3_multiplex_dump", test_multiplex_dump },
1346: { "sqlite3_multiplex_control", test_multiplex_control },
1347: };
1348: int i;
1349:
1350: for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
1351: Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xProc, 0, 0);
1352: }
1353:
1354: return TCL_OK;
1355: }
1356: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>