Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/sqlite/sqlite-src-3100200/src/test_multiplex.c

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

Powered by Google App Engine
This is Rietveld 408576698