aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/test_vfs.c227
1 files changed, 164 insertions, 63 deletions
diff --git a/src/test_vfs.c b/src/test_vfs.c
index fda44a725..8e7f0ce02 100644
--- a/src/test_vfs.c
+++ b/src/test_vfs.c
@@ -44,27 +44,26 @@ struct Testvfs {
sqlite3_vfs *pParent; /* The VFS to use for file IO */
sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */
Tcl_Interp *interp; /* Interpreter to run script in */
+ Tcl_Obj *pScript; /* Script to execute */
int nScript; /* Number of elements in array apScript */
- Tcl_Obj **apScript; /* Script to execute */
+ Tcl_Obj **apScript; /* Array version of pScript */
TestvfsBuffer *pBuffer; /* List of shared buffers */
int isNoshm;
int mask;
int iIoerrCnt;
int ioerr;
+ int nIoerrFail;
};
/*
-** A shared-memory buffer.
+** The Testvfs.mask variable is set to a combination of the following.
+** If a bit is clear in Testvfs.mask, then calls made by SQLite to the
+** corresponding VFS method is ignored for purposes of:
+**
+** + Simulating IO errors, and
+** + Invoking the Tcl callback script.
*/
-struct TestvfsBuffer {
- char *zFile; /* Associated file name */
- int n; /* Size of allocated buffer in bytes */
- u8 *a; /* Buffer allocated using ckalloc() */
- int nRef; /* Number of references to this object */
- TestvfsBuffer *pNext; /* Next in linked list of all buffers */
-};
-
#define TESTVFS_SHMOPEN_MASK 0x00000001
#define TESTVFS_SHMSIZE_MASK 0x00000002
#define TESTVFS_SHMGET_MASK 0x00000004
@@ -75,9 +74,22 @@ struct TestvfsBuffer {
#define TESTVFS_ALL_MASK 0x0000007F
+/*
+** A shared-memory buffer.
+*/
+struct TestvfsBuffer {
+ char *zFile; /* Associated file name */
+ int n; /* Size of allocated buffer in bytes */
+ u8 *a; /* Buffer allocated using ckalloc() */
+ int nRef; /* Number of references to this object */
+ TestvfsBuffer *pNext; /* Next in linked list of all buffers */
+};
+
#define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent)
+#define TESTVFS_MAX_ARGS 12
+
/*
** Method declarations for TestvfsFile.
@@ -403,6 +415,27 @@ static void tvfsExecTcl(
){
int rc; /* Return code from Tcl_EvalObj() */
int nArg; /* Elements in eval'd list */
+ int nScript;
+ Tcl_Obj ** ap;
+
+ assert( p->pScript );
+
+ if( !p->apScript ){
+ int nByte;
+ int i;
+ if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){
+ Tcl_BackgroundError(p->interp);
+ Tcl_ResetResult(p->interp);
+ return;
+ }
+ p->nScript = nScript;
+ nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *);
+ p->apScript = (Tcl_Obj **)ckalloc(nByte);
+ memset(p->apScript, 0, nByte);
+ for(i=0; i<nScript; i++){
+ p->apScript[i] = ap[i];
+ }
+ }
p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1);
p->apScript[p->nScript+1] = arg1;
@@ -451,6 +484,18 @@ static int tvfsResultCode(Testvfs *p, int *pRc){
return 0;
}
+static int tvfsInjectIoerr(Testvfs *p){
+ int ret = 0;
+ if( p->ioerr ){
+ p->iIoerrCnt--;
+ if( p->iIoerrCnt==0 || (p->iIoerrCnt<0 && p->ioerr==2) ){
+ ret = 1;
+ p->nIoerrFail++;
+ }
+ }
+ return ret;
+}
+
static int tvfsShmOpen(
sqlite3_file *pFileDes
){
@@ -474,16 +519,25 @@ static int tvfsShmOpen(
** script is used as the connection name.
*/
Tcl_ResetResult(p->interp);
- if( p->mask&TESTVFS_SHMOPEN_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0);
+ if( tvfsResultCode(p, &rc) ){
+ if( rc!=SQLITE_OK ) return rc;
+ }else{
+ pId = Tcl_GetObjResult(p->interp);
+ }
}
- if( tvfsResultCode(p, &rc) ){
- if( rc!=SQLITE_OK ) return rc;
+ if( !pId ){
pId = Tcl_NewStringObj("anon", -1);
- }else{
- pId = Tcl_GetObjResult(p->interp);
}
Tcl_IncrRefCount(pId);
+
+ assert( rc==SQLITE_OK );
+ if( p->mask&TESTVFS_SHMOPEN_MASK && tvfsInjectIoerr(p) ){
+ Tcl_DecrRefCount(pId);
+ return SQLITE_IOERR;
+ }
+
pFd->pShmId = pId;
/* Search for a TestvfsBuffer. Create a new one if required. */
@@ -515,12 +569,15 @@ static int tvfsShmSize(
TestvfsFile *pFd = (TestvfsFile *)pFile;
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
- if( p->mask&TESTVFS_SHMSIZE_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMSIZE_MASK ){
tvfsExecTcl(p, "xShmSize",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
);
tvfsResultCode(p, &rc);
}
+ if( rc==SQLITE_OK && p->mask&TESTVFS_SHMSIZE_MASK && tvfsInjectIoerr(p) ){
+ rc = SQLITE_IOERR;
+ }
if( rc==SQLITE_OK ){
tvfsGrowBuffer(pFd, reqSize, pNewSize);
}
@@ -537,12 +594,15 @@ static int tvfsShmGet(
TestvfsFile *pFd = (TestvfsFile *)pFile;
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
- if( p->mask&TESTVFS_SHMGET_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMGET_MASK ){
tvfsExecTcl(p, "xShmGet",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
);
tvfsResultCode(p, &rc);
}
+ if( rc==SQLITE_OK && p->mask&TESTVFS_SHMGET_MASK && tvfsInjectIoerr(p) ){
+ rc = SQLITE_IOERR;
+ }
if( rc==SQLITE_OK ){
tvfsGrowBuffer(pFd, reqMapSize, pMapSize);
*pp = pFd->pShm->a;
@@ -555,7 +615,7 @@ static int tvfsShmRelease(sqlite3_file *pFile){
TestvfsFile *pFd = (TestvfsFile *)pFile;
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
- if( p->mask&TESTVFS_SHMRELEASE_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMRELEASE_MASK ){
tvfsExecTcl(p, "xShmRelease",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
);
@@ -577,7 +637,7 @@ static int tvfsShmLock(
int nLock;
char zLock[80];
- if( p->mask&TESTVFS_SHMLOCK_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMLOCK_MASK ){
sqlite3_snprintf(sizeof(zLock), zLock, "%d %d", ofst, n);
nLock = strlen(zLock);
if( flags & SQLITE_SHM_LOCK ){
@@ -604,7 +664,7 @@ static void tvfsShmBarrier(sqlite3_file *pFile){
TestvfsFile *pFd = (TestvfsFile *)pFile;
Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData);
- if( p->mask&TESTVFS_SHMBARRIER_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
tvfsExecTcl(p, "xShmBarrier",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
);
@@ -625,7 +685,7 @@ static int tvfsShmClose(
assert( (deleteFlag!=0)==(pBuffer->nRef==1) );
#endif
- if( p->mask&TESTVFS_SHMCLOSE_MASK ){
+ if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
tvfsExecTcl(p, "xShmClose",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0
);
@@ -655,8 +715,13 @@ static int testvfs_obj_cmd(
){
Testvfs *p = (Testvfs *)cd;
- static const char *CMD_strs[] = { "shm", "delete", "filter", "ioerr", 0 };
- enum DB_enum { CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR };
+ static const char *CMD_strs[] = {
+ "shm", "delete", "filter", "ioerr", "script", 0
+ };
+ enum DB_enum {
+ CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT
+ };
+
int i;
if( objc<2 ){
@@ -738,22 +803,55 @@ static int testvfs_obj_cmd(
break;
}
+ case CMD_SCRIPT: {
+ if( objc==3 ){
+ int nByte;
+ if( p->pScript ){
+ Tcl_DecrRefCount(p->pScript);
+ ckfree((char *)p->apScript);
+ p->apScript = 0;
+ p->nScript = 0;
+ }
+ Tcl_GetStringFromObj(objv[2], &nByte);
+ if( nByte>0 ){
+ p->pScript = Tcl_DuplicateObj(objv[2]);
+ Tcl_IncrRefCount(p->pScript);
+ }
+ }else if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?SCRIPT?");
+ return TCL_ERROR;
+ }
+
+ Tcl_ResetResult(interp);
+ if( p->pScript ) Tcl_SetObjResult(interp, p->pScript);
+
+ break;
+ }
+
+ /*
+ ** TESTVFS ioerr ?IFAIL PERSIST?
+ **
+ ** Where IFAIL is an integer and PERSIST is boolean.
+ */
case CMD_IOERR: {
- int iRet = ((p->iIoerrCnt<0) ? (1+(p->iIoerrCnt*-1)) : 0);
- if( objc==2 ){
- p->ioerr = 0;
- p->iIoerrCnt = 0;
- }else if( objc==4 ){
+ int iRet = p->nIoerrFail;
+
+ p->nIoerrFail = 0;
+ p->ioerr = 0;
+ p->iIoerrCnt = 0;
+
+ if( objc==4 ){
int iCnt, iPersist;
if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt)
|| TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &iPersist)
){
return TCL_ERROR;
}
- p->ioerr = (iPersist!=0) + 1;
+ p->ioerr = (iCnt>0) + iPersist;
p->iIoerrCnt = iCnt;
- }else{
+ }else if( objc!=2 ){
Tcl_AppendResult(interp, "Bad args", 0);
+ return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
break;
@@ -769,20 +867,21 @@ static int testvfs_obj_cmd(
}
static void testvfs_obj_del(ClientData cd){
- int i;
Testvfs *p = (Testvfs *)cd;
- for(i=0; i<p->nScript; i++){
- Tcl_DecrRefCount(p->apScript[i]);
- }
+ if( p->pScript ) Tcl_DecrRefCount(p->pScript);
sqlite3_vfs_unregister(p->pVfs);
+ ckfree((char *)p->apScript);
ckfree((char *)p->pVfs);
ckfree((char *)p);
}
-#define TESTVFS_MAX_ARGS 12
-
/*
-** Usage: testvfs ?-noshm? VFSNAME SCRIPT
+** Usage: testvfs VFSNAME ?SWITCHES?
+**
+** Switches are:
+**
+** -noshm BOOLEAN (True to omit shm methods. Default false)
+** -default BOOLEAN (True to make the vfs default. Default false)
**
** This command creates two things when it is invoked: an SQLite VFS, and
** a Tcl command. Both are named VFSNAME. The VFS is installed. It is not
@@ -852,41 +951,43 @@ static int testvfs_cmd(
Testvfs *p; /* New object */
sqlite3_vfs *pVfs; /* New VFS */
char *zVfs;
- Tcl_Obj *pScript;
- int nScript; /* Number of elements in list pScript */
- Tcl_Obj **apScript; /* Array of pScript elements */
int nByte; /* Bytes of space to allocate at p */
- int i; /* Counter variable */
+
+ int i;
int isNoshm = 0; /* True if -noshm is passed */
+ int isDefault = 0; /* True if -default is passed */
- if( objc<3 ) goto bad_args;
- if( strcmp(Tcl_GetString(objv[1]), "-noshm")==0 ){
- isNoshm = 1;
- }
- if( objc!=3+isNoshm ) goto bad_args;
- zVfs = Tcl_GetString(objv[isNoshm+1]);
- pScript = objv[isNoshm+2];
+ if( objc<2 || 0!=(objc%2) ) goto bad_args;
+ for(i=2; i<objc; i += 2){
+ int nSwitch;
+ char *zSwitch;
- if( TCL_OK!=Tcl_ListObjGetElements(interp, pScript, &nScript, &apScript) ){
- return TCL_ERROR;
+ zSwitch = Tcl_GetStringFromObj(objv[i], &nSwitch);
+ if( nSwitch>2 && 0==strncmp("-noshm", zSwitch, nSwitch) ){
+ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isNoshm) ){
+ return TCL_ERROR;
+ }
+ }
+ else if( nSwitch>2 && 0==strncmp("-default", zSwitch, nSwitch) ){
+ if( Tcl_GetBooleanFromObj(interp, objv[i+1], &isDefault) ){
+ return TCL_ERROR;
+ }
+ }
+ else{
+ goto bad_args;
+ }
}
- nByte = sizeof(Testvfs)
- + (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *)
- + strlen(zVfs)+1;
+ zVfs = Tcl_GetString(objv[1]);
+ nByte = sizeof(Testvfs) + strlen(zVfs)+1;
p = (Testvfs *)ckalloc(nByte);
memset(p, 0, nByte);
p->pParent = sqlite3_vfs_find(0);
p->interp = interp;
- p->nScript = nScript;
- p->apScript = (Tcl_Obj **)&p[1];
- for(i=0; i<nScript; i++){
- p->apScript[i] = apScript[i];
- Tcl_IncrRefCount(p->apScript[i]);
- }
- p->zName = (char *)&p->apScript[nScript+TESTVFS_MAX_ARGS];
- strcpy(p->zName, zVfs);
+
+ p->zName = (char *)&p[1];
+ memcpy(p->zName, zVfs, strlen(zVfs)+1);
pVfs = (sqlite3_vfs *)ckalloc(sizeof(sqlite3_vfs));
memcpy(pVfs, &tvfs_vfs, sizeof(sqlite3_vfs));
@@ -899,12 +1000,12 @@ static int testvfs_cmd(
p->mask = TESTVFS_ALL_MASK;
Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del);
- sqlite3_vfs_register(pVfs, 0);
+ sqlite3_vfs_register(pVfs, isDefault);
return TCL_OK;
bad_args:
- Tcl_WrongNumArgs(interp, 1, objv, "?-noshm? VFSNAME SCRIPT");
+ Tcl_WrongNumArgs(interp, 1, objv, "VFSNAME ?-noshm BOOL? ?-default BOOL?");
return TCL_ERROR;
}