blob: d5e8ea1faf936b399118a8a840958ea04f92db9a [file] [log] [blame]
danc7991bd2010-05-05 19:04:59 +00001/*
2** 2010 May 05
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*/
14#if SQLITE_TEST /* This file is used for testing only */
15
16#include "sqlite3.h"
17#include "sqliteInt.h"
18
danc7991bd2010-05-05 19:04:59 +000019typedef struct Testvfs Testvfs;
20typedef struct TestvfsShm TestvfsShm;
21typedef struct TestvfsBuffer TestvfsBuffer;
dan7fd555a2010-05-13 06:19:37 +000022typedef struct TestvfsFile TestvfsFile;
23
24/*
25** An open file handle.
26*/
27struct TestvfsFile {
28 sqlite3_file base; /* Base class. Must be first */
29 sqlite3_vfs *pVfs; /* The VFS */
30 const char *zFilename; /* Filename as passed to xOpen() */
31 sqlite3_file *pReal; /* The real, underlying file descriptor */
32 Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */
33 TestvfsBuffer *pShm; /* Shared memory buffer */
drhd9e5c4f2010-05-12 18:01:39 +000034};
35
danc7991bd2010-05-05 19:04:59 +000036
37/*
38** An instance of this structure is allocated for each VFS created. The
39** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite
40** is set to point to it.
41*/
42struct Testvfs {
43 char *zName; /* Name of this VFS */
44 sqlite3_vfs *pParent; /* The VFS to use for file IO */
45 sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */
46 Tcl_Interp *interp; /* Interpreter to run script in */
47 int nScript; /* Number of elements in array apScript */
48 Tcl_Obj **apScript; /* Script to execute */
49 TestvfsBuffer *pBuffer; /* List of shared buffers */
dan7fd555a2010-05-13 06:19:37 +000050 int isNoshm;
danc7991bd2010-05-05 19:04:59 +000051};
52
53/*
54** A shared-memory buffer.
55*/
56struct TestvfsBuffer {
57 char *zFile; /* Associated file name */
58 int n; /* Size of allocated buffer in bytes */
59 u8 *a; /* Buffer allocated using ckalloc() */
60 int nRef; /* Number of references to this object */
61 TestvfsBuffer *pNext; /* Next in linked list of all buffers */
62};
63
danc7991bd2010-05-05 19:04:59 +000064
65#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
66
67
68/*
dan7fd555a2010-05-13 06:19:37 +000069** Method declarations for TestvfsFile.
danc7991bd2010-05-05 19:04:59 +000070*/
71static int tvfsClose(sqlite3_file*);
72static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
73static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst);
74static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size);
75static int tvfsSync(sqlite3_file*, int flags);
76static int tvfsFileSize(sqlite3_file*, sqlite3_int64 *pSize);
77static int tvfsLock(sqlite3_file*, int);
78static int tvfsUnlock(sqlite3_file*, int);
79static int tvfsCheckReservedLock(sqlite3_file*, int *);
80static int tvfsFileControl(sqlite3_file*, int op, void *pArg);
81static int tvfsSectorSize(sqlite3_file*);
82static int tvfsDeviceCharacteristics(sqlite3_file*);
83
84/*
85** Method declarations for tvfs_vfs.
86*/
87static int tvfsOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *);
88static int tvfsDelete(sqlite3_vfs*, const char *zName, int syncDir);
89static int tvfsAccess(sqlite3_vfs*, const char *zName, int flags, int *);
90static int tvfsFullPathname(sqlite3_vfs*, const char *zName, int, char *zOut);
91#ifndef SQLITE_OMIT_LOAD_EXTENSION
92static void *tvfsDlOpen(sqlite3_vfs*, const char *zFilename);
93static void tvfsDlError(sqlite3_vfs*, int nByte, char *zErrMsg);
94static void (*tvfsDlSym(sqlite3_vfs*,void*, const char *zSymbol))(void);
95static void tvfsDlClose(sqlite3_vfs*, void*);
96#endif /* SQLITE_OMIT_LOAD_EXTENSION */
97static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut);
98static int tvfsSleep(sqlite3_vfs*, int microseconds);
99static int tvfsCurrentTime(sqlite3_vfs*, double*);
100
drhd9e5c4f2010-05-12 18:01:39 +0000101static int tvfsShmOpen(sqlite3_file*);
102static int tvfsShmSize(sqlite3_file*, int , int *);
drh5939f442010-05-18 13:27:12 +0000103static int tvfsShmGet(sqlite3_file*, int , int *, volatile void **);
drhd9e5c4f2010-05-12 18:01:39 +0000104static int tvfsShmRelease(sqlite3_file*);
drh73b64e42010-05-30 19:55:15 +0000105static int tvfsShmLock(sqlite3_file*, int , int, int);
drh286a2882010-05-20 23:51:06 +0000106static void tvfsShmBarrier(sqlite3_file*);
drhd9e5c4f2010-05-12 18:01:39 +0000107static int tvfsShmClose(sqlite3_file*, int);
danc7991bd2010-05-05 19:04:59 +0000108
109static sqlite3_io_methods tvfs_io_methods = {
drhd9e5c4f2010-05-12 18:01:39 +0000110 2, /* iVersion */
danc7991bd2010-05-05 19:04:59 +0000111 tvfsClose, /* xClose */
112 tvfsRead, /* xRead */
113 tvfsWrite, /* xWrite */
114 tvfsTruncate, /* xTruncate */
115 tvfsSync, /* xSync */
116 tvfsFileSize, /* xFileSize */
117 tvfsLock, /* xLock */
118 tvfsUnlock, /* xUnlock */
119 tvfsCheckReservedLock, /* xCheckReservedLock */
120 tvfsFileControl, /* xFileControl */
121 tvfsSectorSize, /* xSectorSize */
drhd9e5c4f2010-05-12 18:01:39 +0000122 tvfsDeviceCharacteristics, /* xDeviceCharacteristics */
123 tvfsShmOpen, /* xShmOpen */
124 tvfsShmSize, /* xShmSize */
125 tvfsShmGet, /* xShmGet */
126 tvfsShmRelease, /* xShmRelease */
127 tvfsShmLock, /* xShmLock */
drh286a2882010-05-20 23:51:06 +0000128 tvfsShmBarrier, /* xShmBarrier */
drhd9e5c4f2010-05-12 18:01:39 +0000129 tvfsShmClose /* xShmClose */
danc7991bd2010-05-05 19:04:59 +0000130};
131
132/*
133** Close an tvfs-file.
134*/
135static int tvfsClose(sqlite3_file *pFile){
dan7fd555a2010-05-13 06:19:37 +0000136 TestvfsFile *p = (TestvfsFile *)pFile;
137 if( p->pShmId ){
138 Tcl_DecrRefCount(p->pShmId);
139 p->pShmId = 0;
140 }
141 if( pFile->pMethods ){
142 ckfree((char *)pFile->pMethods);
143 }
danc7991bd2010-05-05 19:04:59 +0000144 return sqlite3OsClose(p->pReal);
145}
146
147/*
148** Read data from an tvfs-file.
149*/
150static int tvfsRead(
151 sqlite3_file *pFile,
152 void *zBuf,
153 int iAmt,
154 sqlite_int64 iOfst
155){
dan7fd555a2010-05-13 06:19:37 +0000156 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000157 return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
158}
159
160/*
161** Write data to an tvfs-file.
162*/
163static int tvfsWrite(
164 sqlite3_file *pFile,
165 const void *zBuf,
166 int iAmt,
167 sqlite_int64 iOfst
168){
dan7fd555a2010-05-13 06:19:37 +0000169 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000170 return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
171}
172
173/*
174** Truncate an tvfs-file.
175*/
176static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
dan7fd555a2010-05-13 06:19:37 +0000177 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000178 return sqlite3OsTruncate(p->pReal, size);
179}
180
181/*
182** Sync an tvfs-file.
183*/
184static int tvfsSync(sqlite3_file *pFile, int flags){
dan7fd555a2010-05-13 06:19:37 +0000185 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000186 return sqlite3OsSync(p->pReal, flags);
187}
188
189/*
190** Return the current file-size of an tvfs-file.
191*/
192static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
dan7fd555a2010-05-13 06:19:37 +0000193 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000194 return sqlite3OsFileSize(p->pReal, pSize);
195}
196
197/*
198** Lock an tvfs-file.
199*/
200static int tvfsLock(sqlite3_file *pFile, int eLock){
dan7fd555a2010-05-13 06:19:37 +0000201 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000202 return sqlite3OsLock(p->pReal, eLock);
203}
204
205/*
206** Unlock an tvfs-file.
207*/
208static int tvfsUnlock(sqlite3_file *pFile, int eLock){
dan7fd555a2010-05-13 06:19:37 +0000209 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000210 return sqlite3OsUnlock(p->pReal, eLock);
211}
212
213/*
214** Check if another file-handle holds a RESERVED lock on an tvfs-file.
215*/
216static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
dan7fd555a2010-05-13 06:19:37 +0000217 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000218 return sqlite3OsCheckReservedLock(p->pReal, pResOut);
219}
220
221/*
222** File control method. For custom operations on an tvfs-file.
223*/
224static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
dan7fd555a2010-05-13 06:19:37 +0000225 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000226 return sqlite3OsFileControl(p->pReal, op, pArg);
227}
228
229/*
230** Return the sector-size in bytes for an tvfs-file.
231*/
232static int tvfsSectorSize(sqlite3_file *pFile){
dan7fd555a2010-05-13 06:19:37 +0000233 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000234 return sqlite3OsSectorSize(p->pReal);
235}
236
237/*
238** Return the device characteristic flags supported by an tvfs-file.
239*/
240static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
dan7fd555a2010-05-13 06:19:37 +0000241 TestvfsFile *p = (TestvfsFile *)pFile;
danc7991bd2010-05-05 19:04:59 +0000242 return sqlite3OsDeviceCharacteristics(p->pReal);
243}
244
245/*
246** Open an tvfs file handle.
247*/
248static int tvfsOpen(
249 sqlite3_vfs *pVfs,
250 const char *zName,
251 sqlite3_file *pFile,
252 int flags,
253 int *pOutFlags
254){
255 int rc;
dan7fd555a2010-05-13 06:19:37 +0000256 TestvfsFile *p = (TestvfsFile *)pFile;
drhd9e5c4f2010-05-12 18:01:39 +0000257 p->pShm = 0;
dan7fd555a2010-05-13 06:19:37 +0000258 p->pShmId = 0;
drhd9e5c4f2010-05-12 18:01:39 +0000259 p->zFilename = zName;
260 p->pVfs = pVfs;
danc7991bd2010-05-05 19:04:59 +0000261 p->pReal = (sqlite3_file *)&p[1];
262 rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, p->pReal, flags, pOutFlags);
263 if( p->pReal->pMethods ){
dan7fd555a2010-05-13 06:19:37 +0000264 sqlite3_io_methods *pMethods;
265 pMethods = (sqlite3_io_methods *)ckalloc(sizeof(sqlite3_io_methods));
266 memcpy(pMethods, &tvfs_io_methods, sizeof(sqlite3_io_methods));
267 if( ((Testvfs *)pVfs->pAppData)->isNoshm ){
268 pMethods->xShmOpen = 0;
269 pMethods->xShmGet = 0;
270 pMethods->xShmSize = 0;
271 pMethods->xShmRelease = 0;
272 pMethods->xShmClose = 0;
273 pMethods->xShmLock = 0;
drh286a2882010-05-20 23:51:06 +0000274 pMethods->xShmBarrier = 0;
dan7fd555a2010-05-13 06:19:37 +0000275 }
276 pFile->pMethods = pMethods;
danc7991bd2010-05-05 19:04:59 +0000277 }
dan7fd555a2010-05-13 06:19:37 +0000278
danc7991bd2010-05-05 19:04:59 +0000279 return rc;
280}
281
282/*
283** Delete the file located at zPath. If the dirSync argument is true,
284** ensure the file-system modifications are synced to disk before
285** returning.
286*/
287static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
288 return sqlite3OsDelete(PARENTVFS(pVfs), zPath, dirSync);
289}
290
291/*
292** Test for access permissions. Return true if the requested permission
293** is available, or false otherwise.
294*/
295static int tvfsAccess(
296 sqlite3_vfs *pVfs,
297 const char *zPath,
298 int flags,
299 int *pResOut
300){
301 return sqlite3OsAccess(PARENTVFS(pVfs), zPath, flags, pResOut);
302}
303
304/*
305** Populate buffer zOut with the full canonical pathname corresponding
306** to the pathname in zPath. zOut is guaranteed to point to a buffer
307** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
308*/
309static int tvfsFullPathname(
310 sqlite3_vfs *pVfs,
311 const char *zPath,
312 int nOut,
313 char *zOut
314){
315 return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut);
316}
317
318#ifndef SQLITE_OMIT_LOAD_EXTENSION
319/*
320** Open the dynamic library located at zPath and return a handle.
321*/
322static void *tvfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
323 return sqlite3OsDlOpen(PARENTVFS(pVfs), zPath);
324}
325
326/*
327** Populate the buffer zErrMsg (size nByte bytes) with a human readable
328** utf-8 string describing the most recent error encountered associated
329** with dynamic libraries.
330*/
331static void tvfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
332 sqlite3OsDlError(PARENTVFS(pVfs), nByte, zErrMsg);
333}
334
335/*
336** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
337*/
338static void (*tvfsDlSym(sqlite3_vfs *pVfs, void *p, const char *zSym))(void){
339 return sqlite3OsDlSym(PARENTVFS(pVfs), p, zSym);
340}
341
342/*
343** Close the dynamic library handle pHandle.
344*/
345static void tvfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
346 sqlite3OsDlClose(PARENTVFS(pVfs), pHandle);
347}
348#endif /* SQLITE_OMIT_LOAD_EXTENSION */
349
350/*
351** Populate the buffer pointed to by zBufOut with nByte bytes of
352** random data.
353*/
354static int tvfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
355 return sqlite3OsRandomness(PARENTVFS(pVfs), nByte, zBufOut);
356}
357
358/*
359** Sleep for nMicro microseconds. Return the number of microseconds
360** actually slept.
361*/
362static int tvfsSleep(sqlite3_vfs *pVfs, int nMicro){
363 return sqlite3OsSleep(PARENTVFS(pVfs), nMicro);
364}
365
366/*
367** Return the current time as a Julian Day number in *pTimeOut.
368*/
369static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
370 return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
371}
372
dan7fd555a2010-05-13 06:19:37 +0000373static void tvfsGrowBuffer(TestvfsFile *pFd, int reqSize, int *pNewSize){
374 TestvfsBuffer *pBuffer = pFd->pShm;
danc7991bd2010-05-05 19:04:59 +0000375 if( reqSize>pBuffer->n ){
376 pBuffer->a = (u8 *)ckrealloc((char *)pBuffer->a, reqSize);
dan8f6097c2010-05-06 07:43:58 +0000377 memset(&pBuffer->a[pBuffer->n], 0x55, reqSize-pBuffer->n);
danc7991bd2010-05-05 19:04:59 +0000378 pBuffer->n = reqSize;
379 }
380 *pNewSize = pBuffer->n;
381}
382
383static void tvfsExecTcl(
384 Testvfs *p,
385 const char *zMethod,
386 Tcl_Obj *arg1,
387 Tcl_Obj *arg2,
388 Tcl_Obj *arg3
389){
390 int rc; /* Return code from Tcl_EvalObj() */
391 int nArg; /* Elements in eval'd list */
392
393 p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
394 p->apScript[p->nScript+1] = arg1;
395 p->apScript[p->nScript+2] = arg2;
396 p->apScript[p->nScript+3] = arg3;
397
398 for(nArg=p->nScript; p->apScript[nArg]; nArg++){
399 Tcl_IncrRefCount(p->apScript[nArg]);
400 }
401
402 rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL);
403 if( rc!=TCL_OK ){
404 Tcl_BackgroundError(p->interp);
405 Tcl_ResetResult(p->interp);
406 }
407
408 for(nArg=p->nScript; p->apScript[nArg]; nArg++){
409 Tcl_DecrRefCount(p->apScript[nArg]);
410 p->apScript[nArg] = 0;
411 }
412}
413
414static int tvfsResultCode(Testvfs *p, int *pRc){
415 struct errcode {
416 int eCode;
417 const char *zCode;
418 } aCode[] = {
419 { SQLITE_OK, "SQLITE_OK" },
420 { SQLITE_ERROR, "SQLITE_ERROR" },
421 { SQLITE_IOERR, "SQLITE_IOERR" },
422 { SQLITE_LOCKED, "SQLITE_LOCKED" },
danff6dfc72010-05-06 12:15:48 +0000423 { SQLITE_BUSY, "SQLITE_BUSY" },
danc7991bd2010-05-05 19:04:59 +0000424 };
425
426 const char *z;
427 int i;
428
429 z = Tcl_GetStringResult(p->interp);
430 for(i=0; i<ArraySize(aCode); i++){
431 if( 0==strcmp(z, aCode[i].zCode) ){
432 *pRc = aCode[i].eCode;
433 return 1;
434 }
435 }
436
437 return 0;
438}
439
440static int tvfsShmOpen(
drhd9e5c4f2010-05-12 18:01:39 +0000441 sqlite3_file *pFileDes
danc7991bd2010-05-05 19:04:59 +0000442){
dan7fd555a2010-05-13 06:19:37 +0000443 Testvfs *p;
danc7991bd2010-05-05 19:04:59 +0000444 int rc = SQLITE_OK; /* Return code */
445 Tcl_Obj *pId = 0; /* Id for this connection */
446 TestvfsBuffer *pBuffer; /* Buffer to open connection to */
dan7fd555a2010-05-13 06:19:37 +0000447 TestvfsFile *pFd; /* The testvfs file structure */
drhd9e5c4f2010-05-12 18:01:39 +0000448
dan7fd555a2010-05-13 06:19:37 +0000449 pFd = (TestvfsFile*)pFileDes;
450 p = (Testvfs *)pFd->pVfs->pAppData;
451 assert( pFd->pShmId==0 && pFd->pShm==0 );
danc7991bd2010-05-05 19:04:59 +0000452
453 /* Evaluate the Tcl script:
454 **
455 ** SCRIPT xShmOpen FILENAME
456 **
457 ** If the script returns an SQLite error code other than SQLITE_OK, an
458 ** error is returned to the caller. If it returns SQLITE_OK, the new
459 ** connection is named "anon". Otherwise, the value returned by the
460 ** script is used as the connection name.
461 */
drhd9e5c4f2010-05-12 18:01:39 +0000462 tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
danc7991bd2010-05-05 19:04:59 +0000463 if( tvfsResultCode(p, &rc) ){
464 if( rc!=SQLITE_OK ) return rc;
465 pId = Tcl_NewStringObj("anon", -1);
466 }else{
467 pId = Tcl_GetObjResult(p->interp);
468 }
469 Tcl_IncrRefCount(pId);
dan7fd555a2010-05-13 06:19:37 +0000470 pFd->pShmId = pId;
danc7991bd2010-05-05 19:04:59 +0000471
472 /* Search for a TestvfsBuffer. Create a new one if required. */
473 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
dan7fd555a2010-05-13 06:19:37 +0000474 if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break;
danc7991bd2010-05-05 19:04:59 +0000475 }
476 if( !pBuffer ){
dan7fd555a2010-05-13 06:19:37 +0000477 int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1;
danc7991bd2010-05-05 19:04:59 +0000478 pBuffer = (TestvfsBuffer *)ckalloc(nByte);
479 memset(pBuffer, 0, nByte);
480 pBuffer->zFile = (char *)&pBuffer[1];
dan7fd555a2010-05-13 06:19:37 +0000481 strcpy(pBuffer->zFile, pFd->zFilename);
danc7991bd2010-05-05 19:04:59 +0000482 pBuffer->pNext = p->pBuffer;
483 p->pBuffer = pBuffer;
484 }
485
486 /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */
487 pBuffer->nRef++;
dan7fd555a2010-05-13 06:19:37 +0000488 pFd->pShm = pBuffer;
danc7991bd2010-05-05 19:04:59 +0000489 return SQLITE_OK;
490}
491
492static int tvfsShmSize(
dan7fd555a2010-05-13 06:19:37 +0000493 sqlite3_file *pFile,
danc7991bd2010-05-05 19:04:59 +0000494 int reqSize,
495 int *pNewSize
496){
497 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000498 TestvfsFile *pFd = (TestvfsFile *)pFile;
499 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
danc7991bd2010-05-05 19:04:59 +0000500
danc7991bd2010-05-05 19:04:59 +0000501 tvfsExecTcl(p, "xShmSize",
dan7fd555a2010-05-13 06:19:37 +0000502 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
danc7991bd2010-05-05 19:04:59 +0000503 );
504 tvfsResultCode(p, &rc);
dan8f6097c2010-05-06 07:43:58 +0000505 if( rc==SQLITE_OK ){
dan7fd555a2010-05-13 06:19:37 +0000506 tvfsGrowBuffer(pFd, reqSize, pNewSize);
dan8f6097c2010-05-06 07:43:58 +0000507 }
danc7991bd2010-05-05 19:04:59 +0000508 return rc;
509}
510
511static int tvfsShmGet(
dan7fd555a2010-05-13 06:19:37 +0000512 sqlite3_file *pFile,
danc7991bd2010-05-05 19:04:59 +0000513 int reqMapSize,
514 int *pMapSize,
drh5939f442010-05-18 13:27:12 +0000515 volatile void **pp
danc7991bd2010-05-05 19:04:59 +0000516){
517 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000518 TestvfsFile *pFd = (TestvfsFile *)pFile;
519 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
danc7991bd2010-05-05 19:04:59 +0000520
danc7991bd2010-05-05 19:04:59 +0000521 tvfsExecTcl(p, "xShmGet",
dan7fd555a2010-05-13 06:19:37 +0000522 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
danc7991bd2010-05-05 19:04:59 +0000523 );
524 tvfsResultCode(p, &rc);
dand41a29a2010-05-06 15:56:28 +0000525 if( rc==SQLITE_OK ){
dan7fd555a2010-05-13 06:19:37 +0000526 tvfsGrowBuffer(pFd, reqMapSize, pMapSize);
527 *pp = pFd->pShm->a;
dand41a29a2010-05-06 15:56:28 +0000528 }
danc7991bd2010-05-05 19:04:59 +0000529 return rc;
530}
531
dan7fd555a2010-05-13 06:19:37 +0000532static int tvfsShmRelease(sqlite3_file *pFile){
danc7991bd2010-05-05 19:04:59 +0000533 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000534 TestvfsFile *pFd = (TestvfsFile *)pFile;
535 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
danc7991bd2010-05-05 19:04:59 +0000536
537 tvfsExecTcl(p, "xShmRelease",
dan7fd555a2010-05-13 06:19:37 +0000538 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
danc7991bd2010-05-05 19:04:59 +0000539 );
540 tvfsResultCode(p, &rc);
541
542 return rc;
543}
544
545static int tvfsShmLock(
dan7fd555a2010-05-13 06:19:37 +0000546 sqlite3_file *pFile,
drh73b64e42010-05-30 19:55:15 +0000547 int ofst,
548 int n,
549 int flags
danc7991bd2010-05-05 19:04:59 +0000550){
551 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000552 TestvfsFile *pFd = (TestvfsFile *)pFile;
553 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
drh73b64e42010-05-30 19:55:15 +0000554 int nLock;
555 char zLock[80];
danc7991bd2010-05-05 19:04:59 +0000556
drh73b64e42010-05-30 19:55:15 +0000557 sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
558 nLock = strlen(zLock);
559 if( flags & SQLITE_SHM_LOCK ){
560 strcpy(&zLock[nLock], " lock");
561 }else{
562 strcpy(&zLock[nLock], " unlock");
563 }
564 nLock += strlen(&zLock[nLock]);
565 if( flags & SQLITE_SHM_SHARED ){
566 strcpy(&zLock[nLock], " shared");
567 }else{
568 strcpy(&zLock[nLock], " exclusive");
danc7991bd2010-05-05 19:04:59 +0000569 }
570 tvfsExecTcl(p, "xShmLock",
dan7fd555a2010-05-13 06:19:37 +0000571 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
danc7991bd2010-05-05 19:04:59 +0000572 Tcl_NewStringObj(zLock, -1)
573 );
574 tvfsResultCode(p, &rc);
danc7991bd2010-05-05 19:04:59 +0000575 return rc;
576}
577
drh286a2882010-05-20 23:51:06 +0000578static void tvfsShmBarrier(sqlite3_file *pFile){
579 int rc = SQLITE_OK;
580 TestvfsFile *pFd = (TestvfsFile *)pFile;
581 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
582
583 tvfsExecTcl(p, "xShmBarrier",
584 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
585 );
586 tvfsResultCode(p, &rc);
587}
588
danc7991bd2010-05-05 19:04:59 +0000589static int tvfsShmClose(
dan7fd555a2010-05-13 06:19:37 +0000590 sqlite3_file *pFile,
danc7991bd2010-05-05 19:04:59 +0000591 int deleteFlag
592){
593 int rc = SQLITE_OK;
dan7fd555a2010-05-13 06:19:37 +0000594 TestvfsFile *pFd = (TestvfsFile *)pFile;
595 Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
596 TestvfsBuffer *pBuffer = pFd->pShm;
danc7991bd2010-05-05 19:04:59 +0000597
dan7fd555a2010-05-13 06:19:37 +0000598 assert( pFd->pShmId && pFd->pShm );
dan8f6097c2010-05-06 07:43:58 +0000599#if 0
danc7991bd2010-05-05 19:04:59 +0000600 assert( (deleteFlag!=0)==(pBuffer->nRef==1) );
dan8f6097c2010-05-06 07:43:58 +0000601#endif
danc7991bd2010-05-05 19:04:59 +0000602
603 tvfsExecTcl(p, "xShmClose",
dan7fd555a2010-05-13 06:19:37 +0000604 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
danc7991bd2010-05-05 19:04:59 +0000605 );
606 tvfsResultCode(p, &rc);
607
608 pBuffer->nRef--;
609 if( pBuffer->nRef==0 ){
610 TestvfsBuffer **pp;
611 for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext));
612 *pp = (*pp)->pNext;
613 ckfree((char *)pBuffer->a);
614 ckfree((char *)pBuffer);
615 }
dan7fd555a2010-05-13 06:19:37 +0000616 Tcl_DecrRefCount(pFd->pShmId);
617 pFd->pShmId = 0;
618 pFd->pShm = 0;
danc7991bd2010-05-05 19:04:59 +0000619
620 return rc;
621}
622
623static int testvfs_obj_cmd(
624 ClientData cd,
625 Tcl_Interp *interp,
626 int objc,
627 Tcl_Obj *CONST objv[]
628){
629 Testvfs *p = (Testvfs *)cd;
630
631 static const char *CMD_strs[] = { "shm", "delete", 0 };
632 enum DB_enum { CMD_SHM, CMD_DELETE };
633 int i;
634
635 if( objc<2 ){
636 Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
637 return TCL_ERROR;
638 }
639 if( Tcl_GetIndexFromObj(interp, objv[1], CMD_strs, "subcommand", 0, &i) ){
640 return TCL_ERROR;
641 }
642 Tcl_ResetResult(interp);
643
644 switch( (enum DB_enum)i ){
645 case CMD_SHM: {
646 TestvfsBuffer *pBuffer;
647 char *zName;
648 if( objc!=3 && objc!=4 ){
649 Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?");
650 return TCL_ERROR;
651 }
652 zName = Tcl_GetString(objv[2]);
653 for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){
654 if( 0==strcmp(pBuffer->zFile, zName) ) break;
655 }
656 if( !pBuffer ){
657 Tcl_AppendResult(interp, "no such file: ", zName, 0);
658 return TCL_ERROR;
659 }
660 if( objc==4 ){
661 int n;
662 u8 *a = Tcl_GetByteArrayFromObj(objv[3], &n);
663 pBuffer->a = (u8 *)ckrealloc((char *)pBuffer->a, n);
664 pBuffer->n = n;
665 memcpy(pBuffer->a, a, n);
666 }
667 Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(pBuffer->a, pBuffer->n));
668 break;
669 }
670 case CMD_DELETE: {
671 Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
672 break;
673 }
674 }
675
676 return TCL_OK;
677}
678
679static void testvfs_obj_del(ClientData cd){
680 int i;
681 Testvfs *p = (Testvfs *)cd;
682 for(i=0; i<p->nScript; i++){
683 Tcl_DecrRefCount(p->apScript[i]);
684 }
685 sqlite3_vfs_unregister(p->pVfs);
686 ckfree((char *)p->pVfs);
687 ckfree((char *)p);
688}
689
690#define TESTVFS_MAX_ARGS 12
691
692/*
dan576bc322010-05-06 18:04:50 +0000693** Usage: testvfs ?-noshm? VFSNAME SCRIPT
danc7991bd2010-05-05 19:04:59 +0000694**
695** This command creates two things when it is invoked: an SQLite VFS, and
696** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
697** installed as the default VFS.
698**
699** The VFS passes all file I/O calls through to the underlying VFS.
700**
701** Whenever one of the xShmSize, xShmGet or xShmRelease methods of the VFS
702** are invoked, the SCRIPT is executed as follows:
703**
704** SCRIPT xShmSize FILENAME ID
705** SCRIPT xShmGet FILENAME ID
706** SCRIPT xShmRelease FILENAME ID
707**
708** The value returned by the invocation of SCRIPT above is interpreted as
709** an SQLite error code and returned to SQLite. Either a symbolic
710** "SQLITE_OK" or numeric "0" value may be returned.
711**
712** The contents of the shared-memory buffer associated with a given file
713** may be read and set using the following command:
714**
715** VFSNAME shm FILENAME ?NEWVALUE?
716**
717** When the xShmLock method is invoked by SQLite, the following script is
718** run:
719**
720** SCRIPT xShmLock FILENAME ID LOCK
721**
drh73b64e42010-05-30 19:55:15 +0000722** where LOCK is of the form "OFFSET NBYTE lock/unlock shared/exclusive"
danc7991bd2010-05-05 19:04:59 +0000723*/
724static int testvfs_cmd(
725 ClientData cd,
726 Tcl_Interp *interp,
727 int objc,
728 Tcl_Obj *CONST objv[]
729){
danc7991bd2010-05-05 19:04:59 +0000730 static sqlite3_vfs tvfs_vfs = {
731 2, /* iVersion */
dan7fd555a2010-05-13 06:19:37 +0000732 sizeof(TestvfsFile), /* szOsFile */
danc7991bd2010-05-05 19:04:59 +0000733 0, /* mxPathname */
734 0, /* pNext */
735 0, /* zName */
736 0, /* pAppData */
737 tvfsOpen, /* xOpen */
738 tvfsDelete, /* xDelete */
739 tvfsAccess, /* xAccess */
740 tvfsFullPathname, /* xFullPathname */
741#ifndef SQLITE_OMIT_LOAD_EXTENSION
742 tvfsDlOpen, /* xDlOpen */
743 tvfsDlError, /* xDlError */
744 tvfsDlSym, /* xDlSym */
745 tvfsDlClose, /* xDlClose */
746#else
747 0, /* xDlOpen */
748 0, /* xDlError */
749 0, /* xDlSym */
750 0, /* xDlClose */
751#endif /* SQLITE_OMIT_LOAD_EXTENSION */
752 tvfsRandomness, /* xRandomness */
753 tvfsSleep, /* xSleep */
754 tvfsCurrentTime, /* xCurrentTime */
755 0, /* xGetLastError */
danc7991bd2010-05-05 19:04:59 +0000756 0,
757 0,
758 };
759
760 Testvfs *p; /* New object */
761 sqlite3_vfs *pVfs; /* New VFS */
762 char *zVfs;
763 Tcl_Obj *pScript;
764 int nScript; /* Number of elements in list pScript */
765 Tcl_Obj **apScript; /* Array of pScript elements */
766 int nByte; /* Bytes of space to allocate at p */
767 int i; /* Counter variable */
dan576bc322010-05-06 18:04:50 +0000768 int isNoshm = 0; /* True if -noshm is passed */
danc7991bd2010-05-05 19:04:59 +0000769
dan576bc322010-05-06 18:04:50 +0000770 if( objc<3 ) goto bad_args;
771 if( strcmp(Tcl_GetString(objv[1]), "-noshm")==0 ){
772 isNoshm = 1;
danc7991bd2010-05-05 19:04:59 +0000773 }
dan576bc322010-05-06 18:04:50 +0000774 if( objc!=3+isNoshm ) goto bad_args;
775 zVfs = Tcl_GetString(objv[isNoshm+1]);
776 pScript = objv[isNoshm+2];
danc7991bd2010-05-05 19:04:59 +0000777
778 if( TCL_OK!=Tcl_ListObjGetElements(interp, pScript, &nScript, &apScript) ){
779 return TCL_ERROR;
780 }
781
782 nByte = sizeof(Testvfs)
783 + (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *)
784 + strlen(zVfs)+1;
785 p = (Testvfs *)ckalloc(nByte);
786 memset(p, 0, nByte);
787
788 p->pParent = sqlite3_vfs_find(0);
789 p->interp = interp;
790 p->nScript = nScript;
791 p->apScript = (Tcl_Obj **)&p[1];
792 for(i=0; i<nScript; i++){
793 p->apScript[i] = apScript[i];
794 Tcl_IncrRefCount(p->apScript[i]);
795 }
796 p->zName = (char *)&p->apScript[nScript+TESTVFS_MAX_ARGS];
797 strcpy(p->zName, zVfs);
798
799 pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
800 memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
801 pVfs->pAppData = (void *)p;
802 pVfs->zName = p->zName;
803 pVfs->mxPathname = p->pParent->mxPathname;
804 pVfs->szOsFile += p->pParent->szOsFile;
dan8f6097c2010-05-06 07:43:58 +0000805 p->pVfs = pVfs;
dan7fd555a2010-05-13 06:19:37 +0000806 p->isNoshm = isNoshm;
danc7991bd2010-05-05 19:04:59 +0000807
808 Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
809 sqlite3_vfs_register(pVfs, 0);
810
811 return TCL_OK;
dan576bc322010-05-06 18:04:50 +0000812
813 bad_args:
814 Tcl_WrongNumArgs(interp, 1, objv, "?-noshm? VFSNAME SCRIPT");
815 return TCL_ERROR;
danc7991bd2010-05-05 19:04:59 +0000816}
817
818int Sqlitetestvfs_Init(Tcl_Interp *interp){
dan7fd555a2010-05-13 06:19:37 +0000819 Tcl_CreateObjCommand(interp, "testvfs", testvfs_cmd, 0, 0);
danc7991bd2010-05-05 19:04:59 +0000820 return TCL_OK;
821}
822
823#endif