aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backup.c4
-rw-r--r--src/btree.c17
-rw-r--r--src/btree.h2
-rw-r--r--src/build.c2
-rw-r--r--src/os_unix.c20
-rw-r--r--src/sqlite.h.in8
-rw-r--r--src/tclsqlite.c3
-rw-r--r--src/test_syscall.c642
-rw-r--r--src/test_vfstrace.c11
-rw-r--r--src/utf.c8
-rw-r--r--src/vdbe.c19
-rw-r--r--src/vdbeaux.c4
-rw-r--r--src/vtab.c2
13 files changed, 695 insertions, 47 deletions
diff --git a/src/backup.c b/src/backup.c
index 5d8ea7f3f..82be9635b 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -488,7 +488,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Finish committing the transaction to the destination database. */
if( SQLITE_OK==rc
- && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest))
+ && SQLITE_OK==(rc = sqlite3BtreeCommitPhaseTwo(p->pDest, 0))
){
rc = SQLITE_DONE;
}
@@ -502,7 +502,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
if( bCloseTrans ){
TESTONLY( int rc2 );
TESTONLY( rc2 = ) sqlite3BtreeCommitPhaseOne(p->pSrc, 0);
- TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc);
+ TESTONLY( rc2 |= ) sqlite3BtreeCommitPhaseTwo(p->pSrc, 0);
assert( rc2==SQLITE_OK );
}
diff --git a/src/btree.c b/src/btree.c
index 33d746067..088c555fc 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3160,10 +3160,21 @@ static void btreeEndTransaction(Btree *p){
** the rollback journal (which causes the transaction to commit) and
** drop locks.
**
+** Normally, if an error occurs while the pager layer is attempting to
+** finalize the underlying journal file, this function returns an error and
+** the upper layer will attempt a rollback. However, if the second argument
+** is non-zero then this b-tree transaction is part of a multi-file
+** transaction. In this case, the transaction has already been committed
+** (by deleting a master journal file) and the caller will ignore this
+** functions return code. So, even if an error occurs in the pager layer,
+** reset the b-tree objects internal state to indicate that the write
+** transaction has been closed. This is quite safe, as the pager will have
+** transitioned to the error state.
+**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
-int sqlite3BtreeCommitPhaseTwo(Btree *p){
+int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
if( p->inTrans==TRANS_NONE ) return SQLITE_OK;
sqlite3BtreeEnter(p);
@@ -3178,7 +3189,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->inTransaction==TRANS_WRITE );
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK && bCleanup==0 ){
sqlite3BtreeLeave(p);
return rc;
}
@@ -3198,7 +3209,7 @@ int sqlite3BtreeCommit(Btree *p){
sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeCommitPhaseTwo(p);
+ rc = sqlite3BtreeCommitPhaseTwo(p, 0);
}
sqlite3BtreeLeave(p);
return rc;
diff --git a/src/btree.h b/src/btree.h
index 6886dd944..468723b33 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -87,7 +87,7 @@ int sqlite3BtreeSetAutoVacuum(Btree *, int);
int sqlite3BtreeGetAutoVacuum(Btree *);
int sqlite3BtreeBeginTrans(Btree*,int);
int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
-int sqlite3BtreeCommitPhaseTwo(Btree*);
+int sqlite3BtreeCommitPhaseTwo(Btree*, int);
int sqlite3BtreeCommit(Btree*);
int sqlite3BtreeRollback(Btree*);
int sqlite3BtreeBeginStmt(Btree*,int);
diff --git a/src/build.c b/src/build.c
index e7c56c9ab..bf3273fc8 100644
--- a/src/build.c
+++ b/src/build.c
@@ -368,7 +368,7 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
len = sqlite3Strlen30(zIdxName);
pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0);
- if( pIndex ){
+ if( ALWAYS(pIndex) ){
if( pIndex->pTable->pIndex==pIndex ){
pIndex->pTable->pIndex = pIndex->pNext;
}else{
diff --git a/src/os_unix.c b/src/os_unix.c
index f4e689fa9..d958b6fc9 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -371,7 +371,7 @@ static struct unix_syscall {
#else
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osFallocate ((int(*)(int,off_t,off_t)aSyscall[15].pCurrent)
+#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
}; /* End of the overrideable system calls */
@@ -444,18 +444,16 @@ static sqlite3_syscall_ptr unixGetSystemCall(
** system call.
*/
static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
- unsigned int i;
+ int i = -1;
UNUSED_PARAMETER(p);
- if( zName==0 ){
- i = -1;
- }else{
- for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0])-1; i++){
- if( strcmp(zName, aSyscall[0].zName)==0 ) break;
+ if( zName ){
+ for(i=0; i<ArraySize(aSyscall)-1; i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break;
}
}
- for(i++; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
- if( aSyscall[0].pCurrent!=0 ) return aSyscall[0].zName;
+ for(i++; i<ArraySize(aSyscall); i++){
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
}
return 0;
}
@@ -3380,8 +3378,8 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
int rc;
do{
- rc = osFallocate(pFile->.h, buf.st_size, nSize-buf.st_size;
- }while( rc<0 && errno=EINTR );
+ rc = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size);
+ }while( rc<0 && errno==EINTR );
if( rc ) return SQLITE_IOERR_WRITE;
#else
/* If the OS does not have posix_fallocate(), fake it. First use
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index f3393da01..42ce94be2 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -2975,7 +2975,9 @@ int sqlite3_column_count(sqlite3_stmt *pStmt);
** column number. ^The leftmost column is number 0.
**
** ^The returned string pointer is valid until either the [prepared statement]
-** is destroyed by [sqlite3_finalize()] or until the next call to
+** is destroyed by [sqlite3_finalize()] or until the statement is automatically
+** reprepared by the first call to [sqlite3_step()] for a particular run
+** or until the next call to
** sqlite3_column_name() or sqlite3_column_name16() on the same column.
**
** ^If sqlite3_malloc() fails during the processing of either routine
@@ -3001,7 +3003,9 @@ const void *sqlite3_column_name16(sqlite3_stmt*, int N);
** the database name, the _table_ routines return the table name, and
** the origin_ routines return the column name.
** ^The returned string is valid until the [prepared statement] is destroyed
-** using [sqlite3_finalize()] or until the same information is requested
+** using [sqlite3_finalize()] or until the statement is automatically
+** reprepared by the first call to [sqlite3_step()] for a particular run
+** or until the same information is requested
** again in a different encoding.
**
** ^The names returned are the original un-aliased names of the
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 2d945be53..93c511837 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -3719,10 +3719,10 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
extern int SqliteSuperlock_Init(Tcl_Interp*);
+ extern int SqlitetestSyscall_Init(Tcl_Interp*);
#ifdef SQLITE_ENABLE_SESSION
extern int TestSession_Init(Tcl_Interp*);
#endif
-
#ifdef SQLITE_ENABLE_ZIPVFS
extern int Zipvfs_Init(Tcl_Interp*);
Zipvfs_Init(interp);
@@ -3759,6 +3759,7 @@ static void init_all(Tcl_Interp *interp){
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
SqliteSuperlock_Init(interp);
+ SqlitetestSyscall_Init(interp);
#ifdef SQLITE_ENABLE_SESSION
TestSession_Init(interp);
#endif
diff --git a/src/test_syscall.c b/src/test_syscall.c
new file mode 100644
index 000000000..61508ec45
--- /dev/null
+++ b/src/test_syscall.c
@@ -0,0 +1,642 @@
+/*
+** 2011 March 28
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** The code in this file implements a Tcl interface used to test error
+** handling in the os_unix.c module. Wrapper functions that support fault
+** injection are registered as the low-level OS functions using the
+** xSetSystemCall() method of the VFS. The Tcl interface is as follows:
+**
+**
+** test_syscall install LIST
+** Install wrapper functions for all system calls in argument LIST.
+** LIST must be a list consisting of zero or more of the following
+** literal values:
+**
+** open close access getcwd stat fstat
+** ftruncate fcntl read pread pread64 write
+** pwrite pwrite64 fchmod fallocate
+**
+** test_syscall uninstall
+** Uninstall all wrapper functions.
+**
+** test_syscall fault ?COUNT PERSIST?
+** If [test_syscall fault] is invoked without the two arguments, fault
+** injection is disabled. Otherwise, fault injection is configured to
+** cause a failure on the COUNT'th next call to a system call with a
+** wrapper function installed. A COUNT value of 1 means fail the next
+** system call.
+**
+** Argument PERSIST is interpreted as a boolean. If true, the all
+** system calls following the initial failure also fail. Otherwise, only
+** the single transient failure is injected.
+**
+** test_syscall errno CALL ERRNO
+** Set the value that the global "errno" is set to following a fault
+** in call CALL. Argument CALL must be one of the system call names
+** listed above (under [test_syscall install]). ERRNO is a symbolic
+** name (i.e. "EACCES"). Not all errno codes are supported. Add extra
+** to the aErrno table in function test_syscall_errno() below as
+** required.
+**
+** test_syscall reset ?SYSTEM-CALL?
+** With no argument, this is an alias for the [uninstall] command. However,
+** this command uses a VFS call of the form:
+**
+** xSetSystemCall(pVfs, 0, 0);
+**
+** To restore the default system calls. The [uninstall] command restores
+** each system call individually by calling (i.e.):
+**
+** xSetSystemCall(pVfs, "open", 0);
+**
+** With an argument, this command attempts to reset the system call named
+** by the parameter using the same method as [uninstall].
+**
+** test_syscall exists SYSTEM-CALL
+** Return true if the named system call exists. Or false otherwise.
+**
+** test_syscall list
+** Return a list of all system calls. The list is constructed using
+** the xNextSystemCall() VFS method.
+*/
+
+#include "sqlite3.h"
+#include "tcl.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef SQLITE_OS_UNIX
+
+/* From test1.c */
+extern const char *sqlite3TestErrorName(int);
+
+#include <sys/types.h>
+#include <errno.h>
+
+static struct TestSyscallGlobal {
+ int bPersist; /* 1 for persistent errors, 0 for transient */
+ int nCount; /* Fail after this many more calls */
+ int nFail; /* Number of failures that have occurred */
+} gSyscall = { 0, 0 };
+
+static int ts_open(const char *, int, int);
+static int ts_close(int fd);
+static int ts_access(const char *zPath, int mode);
+static char *ts_getcwd(char *zPath, size_t nPath);
+static int ts_stat(const char *zPath, struct stat *p);
+static int ts_fstat(int fd, struct stat *p);
+static int ts_ftruncate(int fd, off_t n);
+static int ts_fcntl(int fd, int cmd, ... );
+static int ts_read(int fd, void *aBuf, size_t nBuf);
+static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off);
+static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off);
+static int ts_write(int fd, const void *aBuf, size_t nBuf);
+static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off);
+static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off);
+static int ts_fchmod(int fd, mode_t mode);
+static int ts_fallocate(int fd, off_t off, off_t len);
+
+
+struct TestSyscallArray {
+ const char *zName;
+ sqlite3_syscall_ptr xTest;
+ sqlite3_syscall_ptr xOrig;
+ int default_errno; /* Default value for errno following errors */
+ int custom_errno; /* Current value for errno if error */
+} aSyscall[] = {
+ /* 0 */ { "open", (sqlite3_syscall_ptr)ts_open, 0, EACCES, 0 },
+ /* 1 */ { "close", (sqlite3_syscall_ptr)ts_close, 0, 0, 0 },
+ /* 2 */ { "access", (sqlite3_syscall_ptr)ts_access, 0, 0, 0 },
+ /* 3 */ { "getcwd", (sqlite3_syscall_ptr)ts_getcwd, 0, 0, 0 },
+ /* 4 */ { "stat", (sqlite3_syscall_ptr)ts_stat, 0, 0, 0 },
+ /* 5 */ { "fstat", (sqlite3_syscall_ptr)ts_fstat, 0, 0, 0 },
+ /* 6 */ { "ftruncate", (sqlite3_syscall_ptr)ts_ftruncate, 0, EIO, 0 },
+ /* 7 */ { "fcntl", (sqlite3_syscall_ptr)ts_fcntl, 0, 0, 0 },
+ /* 8 */ { "read", (sqlite3_syscall_ptr)ts_read, 0, 0, 0 },
+ /* 9 */ { "pread", (sqlite3_syscall_ptr)ts_pread, 0, 0, 0 },
+ /* 10 */ { "pread64", (sqlite3_syscall_ptr)ts_pread64, 0, 0, 0 },
+ /* 11 */ { "write", (sqlite3_syscall_ptr)ts_write, 0, 0, 0 },
+ /* 12 */ { "pwrite", (sqlite3_syscall_ptr)ts_pwrite, 0, 0, 0 },
+ /* 13 */ { "pwrite64", (sqlite3_syscall_ptr)ts_pwrite64, 0, 0, 0 },
+ /* 14 */ { "fchmod", (sqlite3_syscall_ptr)ts_fchmod, 0, 0, 0 },
+ /* 15 */ { "fallocate", (sqlite3_syscall_ptr)ts_fallocate, 0, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+#define orig_open ((int(*)(const char *, int, int))aSyscall[0].xOrig)
+#define orig_close ((int(*)(int))aSyscall[1].xOrig)
+#define orig_access ((int(*)(const char*,int))aSyscall[2].xOrig)
+#define orig_getcwd ((char*(*)(char*,size_t))aSyscall[3].xOrig)
+#define orig_stat ((int(*)(const char*,struct stat*))aSyscall[4].xOrig)
+#define orig_fstat ((int(*)(int,struct stat*))aSyscall[5].xOrig)
+#define orig_ftruncate ((int(*)(int,off_t))aSyscall[6].xOrig)
+#define orig_fcntl ((int(*)(int,int,...))aSyscall[7].xOrig)
+#define orig_read ((ssize_t(*)(int,void*,size_t))aSyscall[8].xOrig)
+#define orig_pread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].xOrig)
+#define orig_pread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].xOrig)
+#define orig_write ((ssize_t(*)(int,const void*,size_t))aSyscall[11].xOrig)
+#define orig_pwrite ((ssize_t(*)(int,const void*,size_t,off_t))\
+ aSyscall[12].xOrig)
+#define orig_pwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
+ aSyscall[13].xOrig)
+#define orig_fchmod ((int(*)(int,mode_t))aSyscall[14].xOrig)
+#define orig_fallocate ((int(*)(int,off_t,off_t))aSyscall[15].xOrig)
+
+/*
+** This function is called exactly once from within each invocation of a
+** system call wrapper in this file. It returns 1 if the function should
+** fail, or 0 if it should succeed.
+*/
+static int tsIsFail(void){
+ gSyscall.nCount--;
+ if( gSyscall.nCount==0 || (gSyscall.nFail && gSyscall.bPersist) ){
+ gSyscall.nFail++;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** Return the current error-number value for function zFunc. zFunc must be
+** the name of a system call in the aSyscall[] table.
+**
+** Usually, the current error-number is the value that errno should be set
+** to if the named system call fails. The exception is "fallocate". See
+** comments above the implementation of ts_fallocate() for details.
+*/
+static int tsErrno(const char *zFunc){
+ int i;
+ int nFunc = strlen(zFunc);
+ for(i=0; aSyscall[i].zName; i++){
+ if( strlen(aSyscall[i].zName)!=nFunc ) continue;
+ if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
+ return aSyscall[i].custom_errno;
+ }
+
+ assert(0);
+ return 0;
+}
+
+/*
+** A wrapper around tsIsFail(). If tsIsFail() returns non-zero, set the
+** value of errno before returning.
+*/
+static int tsIsFailErrno(const char *zFunc){
+ if( tsIsFail() ){
+ errno = tsErrno(zFunc);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+** A wrapper around open().
+*/
+static int ts_open(const char *zFile, int flags, int mode){
+ if( tsIsFailErrno("open") ){
+ return -1;
+ }
+ return orig_open(zFile, flags, mode);
+}
+
+/*
+** A wrapper around close().
+*/
+static int ts_close(int fd){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_close(fd);
+}
+
+/*
+** A wrapper around access().
+*/
+static int ts_access(const char *zPath, int mode){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_access(zPath, mode);
+}
+
+/*
+** A wrapper around getcwd().
+*/
+static char *ts_getcwd(char *zPath, size_t nPath){
+ if( tsIsFail() ){
+ return NULL;
+ }
+ return orig_getcwd(zPath, nPath);
+}
+
+/*
+** A wrapper around stat().
+*/
+static int ts_stat(const char *zPath, struct stat *p){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_stat(zPath, p);
+}
+
+/*
+** A wrapper around fstat().
+*/
+static int ts_fstat(int fd, struct stat *p){
+ if( tsIsFailErrno("fstat") ){
+ return -1;
+ }
+ return orig_fstat(fd, p);
+}
+
+/*
+** A wrapper around ftruncate().
+*/
+static int ts_ftruncate(int fd, off_t n){
+ if( tsIsFailErrno("ftruncate") ){
+ return -1;
+ }
+ return orig_ftruncate(fd, n);
+}
+
+/*
+** A wrapper around fcntl().
+*/
+static int ts_fcntl(int fd, int cmd, ... ){
+ va_list ap;
+ void *pArg;
+ if( tsIsFail() ){
+ return -1;
+ }
+ va_start(ap, cmd);
+ pArg = va_arg(ap, void *);
+ return orig_fcntl(fd, cmd, pArg);
+}
+
+/*
+** A wrapper around read().
+*/
+static int ts_read(int fd, void *aBuf, size_t nBuf){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_read(fd, aBuf, nBuf);
+}
+
+/*
+** A wrapper around pread().
+*/
+static int ts_pread(int fd, void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_pread(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around pread64().
+*/
+static int ts_pread64(int fd, void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_pread64(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around write().
+*/
+static int ts_write(int fd, const void *aBuf, size_t nBuf){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_write(fd, aBuf, nBuf);
+}
+
+/*
+** A wrapper around pwrite().
+*/
+static int ts_pwrite(int fd, const void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_pwrite(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around pwrite64().
+*/
+static int ts_pwrite64(int fd, const void *aBuf, size_t nBuf, off_t off){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_pwrite64(fd, aBuf, nBuf, off);
+}
+
+/*
+** A wrapper around fchmod().
+*/
+static int ts_fchmod(int fd, mode_t mode){
+ if( tsIsFail() ){
+ return -1;
+ }
+ return orig_fchmod(fd, mode);
+}
+
+/*
+** A wrapper around fallocate().
+**
+** SQLite assumes that the fallocate() function is compatible with
+** posix_fallocate(). According to the Linux man page (2009-09-30):
+**
+** posix_fallocate() returns zero on success, or an error number on
+** failure. Note that errno is not set.
+*/
+static int ts_fallocate(int fd, off_t off, off_t len){
+ if( tsIsFail() ){
+ return tsErrno("fallocate");
+ }
+ return orig_fallocate(fd, off, len);
+}
+
+static int test_syscall_install(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int nElem;
+ int i;
+ Tcl_Obj **apElem;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL-LIST");
+ return TCL_ERROR;
+ }
+ if( Tcl_ListObjGetElements(interp, objv[2], &nElem, &apElem) ){
+ return TCL_ERROR;
+ }
+ pVfs = sqlite3_vfs_find(0);
+
+ for(i=0; i<nElem; i++){
+ int iCall;
+ int rc = Tcl_GetIndexFromObjStruct(interp,
+ apElem[i], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
+ );
+ if( rc ) return rc;
+ if( aSyscall[iCall].xOrig==0 ){
+ aSyscall[iCall].xOrig = pVfs->xGetSystemCall(pVfs, aSyscall[iCall].zName);
+ pVfs->xSetSystemCall(pVfs, aSyscall[iCall].zName, aSyscall[iCall].xTest);
+ }
+ aSyscall[iCall].custom_errno = aSyscall[iCall].default_errno;
+ }
+
+ return TCL_OK;
+}
+
+static int test_syscall_uninstall(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int i;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ for(i=0; aSyscall[i].zName; i++){
+ if( aSyscall[i].xOrig ){
+ pVfs->xSetSystemCall(pVfs, aSyscall[i].zName, 0);
+ aSyscall[i].xOrig = 0;
+ }
+ }
+ return TCL_OK;
+}
+
+static int test_syscall_reset(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ int i;
+ int rc;
+
+ if( objc!=2 && objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ if( objc==2 ){
+ rc = pVfs->xSetSystemCall(pVfs, 0, 0);
+ for(i=0; aSyscall[i].zName; i++) aSyscall[i].xOrig = 0;
+ }else{
+ int nFunc;
+ char *zFunc = Tcl_GetStringFromObj(objv[2], &nFunc);
+ rc = pVfs->xSetSystemCall(pVfs, Tcl_GetString(objv[2]), 0);
+ for(i=0; rc==SQLITE_OK && aSyscall[i].zName; i++){
+ if( strlen(aSyscall[i].zName)!=nFunc ) continue;
+ if( memcmp(aSyscall[i].zName, zFunc, nFunc) ) continue;
+ aSyscall[i].xOrig = 0;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3TestErrorName(rc), -1));
+ return TCL_ERROR;
+ }
+
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+static int test_syscall_exists(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ sqlite3_vfs *pVfs;
+ sqlite3_syscall_ptr x;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ x = pVfs->xGetSystemCall(pVfs, Tcl_GetString(objv[2]));
+
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(x!=0));
+ return TCL_OK;
+}
+
+static int test_syscall_fault(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int nCount = 0;
+ int bPersist = 0;
+
+ if( objc!=2 && objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?COUNT PERSIST?");
+ return TCL_ERROR;
+ }
+
+ if( objc==4 ){
+ if( Tcl_GetIntFromObj(interp, objv[2], &nCount)
+ || Tcl_GetBooleanFromObj(interp, objv[3], &bPersist)
+ ){
+ return TCL_ERROR;
+ }
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(gSyscall.nFail));
+ gSyscall.nCount = nCount;
+ gSyscall.bPersist = bPersist;
+ gSyscall.nFail = 0;
+ return TCL_OK;
+}
+
+static int test_syscall_errno(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int iCall;
+ int iErrno;
+ int rc;
+
+ struct Errno {
+ const char *z;
+ int i;
+ } aErrno[] = {
+ { "EACCES", EACCES },
+ { "EINTR", EINTR },
+ { "EIO", EIO },
+ { "EOVERFLOW", EOVERFLOW },
+ { "ENOMEM", ENOMEM },
+ { 0, 0 }
+ };
+
+ if( objc!=4 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "SYSCALL ERRNO");
+ return TCL_ERROR;
+ }
+
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[2], aSyscall, sizeof(aSyscall[0]), "system-call", 0, &iCall
+ );
+ if( rc!=TCL_OK ) return rc;
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[3], aErrno, sizeof(aErrno[0]), "errno", 0, &iErrno
+ );
+ if( rc!=TCL_OK ) return rc;
+
+ aSyscall[iCall].custom_errno = aErrno[iErrno].i;
+ return TCL_OK;
+}
+
+static int test_syscall_list(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ const char *zSys;
+ sqlite3_vfs *pVfs;
+ Tcl_Obj *pList;
+
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "");
+ return TCL_ERROR;
+ }
+
+ pVfs = sqlite3_vfs_find(0);
+ pList = Tcl_NewObj();
+ Tcl_IncrRefCount(pList);
+ for(zSys = pVfs->xNextSystemCall(pVfs, 0);
+ zSys!=0;
+ zSys = pVfs->xNextSystemCall(pVfs, zSys)
+ ){
+ Tcl_ListObjAppendElement(interp, pList, Tcl_NewStringObj(zSys, -1));
+ }
+
+ Tcl_SetObjResult(interp, pList);
+ Tcl_DecrRefCount(pList);
+ return TCL_OK;
+}
+
+static int test_syscall(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ struct SyscallCmd {
+ const char *zName;
+ Tcl_ObjCmdProc *xCmd;
+ } aCmd[] = {
+ { "fault", test_syscall_fault },
+ { "install", test_syscall_install },
+ { "uninstall", test_syscall_uninstall },
+ { "reset", test_syscall_reset },
+ { "errno", test_syscall_errno },
+ { "exists", test_syscall_exists },
+ { "list", test_syscall_list },
+ { 0, 0 }
+ };
+ int iCmd;
+ int rc;
+
+ if( objc<2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "SUB-COMMAND ...");
+ return TCL_ERROR;
+ }
+ rc = Tcl_GetIndexFromObjStruct(interp,
+ objv[1], aCmd, sizeof(aCmd[0]), "sub-command", 0, &iCmd
+ );
+ if( rc!=TCL_OK ) return rc;
+ return aCmd[iCmd].xCmd(clientData, interp, objc, objv);
+}
+
+int SqlitetestSyscall_Init(Tcl_Interp *interp){
+ struct SyscallCmd {
+ const char *zName;
+ Tcl_ObjCmdProc *xCmd;
+ } aCmd[] = {
+ { "test_syscall", test_syscall},
+ };
+ int i;
+
+ for(i=0; i<sizeof(aCmd)/sizeof(aCmd[0]); i++){
+ Tcl_CreateObjCommand(interp, aCmd[i].zName, aCmd[i].xCmd, 0, 0);
+ }
+ return TCL_OK;
+}
+#else
+int SqlitetestSyscall_Init(Tcl_Interp *interp){
+ return TCL_OK;
+}
+#endif
+
diff --git a/src/test_vfstrace.c b/src/test_vfstrace.c
index 24bd0afae..073eab631 100644
--- a/src/test_vfstrace.c
+++ b/src/test_vfstrace.c
@@ -77,8 +77,8 @@ static int vfstraceSleep(sqlite3_vfs*, int microseconds);
static int vfstraceCurrentTime(sqlite3_vfs*, double*);
static int vfstraceGetLastError(sqlite3_vfs*, int, char*);
static int vfstraceCurrentTimeInt64(sqlite3_vfs*, sqlite3_int64*);
-static int vfstraceSetSystemCall(sqlite3_vfs*, const char *zName, void *pFunc);
-static void *vfstraceGetSystemCall(sqlite3_vfs*, const char *zName);
+static int vfstraceSetSystemCall(sqlite3_vfs*,const char*, sqlite3_syscall_ptr);
+static sqlite3_syscall_ptr vfstraceGetSystemCall(sqlite3_vfs*, const char *);
static const char *vfstraceNextSystemCall(sqlite3_vfs*, const char *zName);
/*
@@ -682,13 +682,16 @@ static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
static int vfstraceSetSystemCall(
sqlite3_vfs *pVfs,
const char *zName,
- void *pFunc
+ sqlite3_syscall_ptr pFunc
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xSetSystemCall(pRoot, zName, pFunc);
}
-static void *vfstraceGetSystemCall(sqlite3_vfs *pVfs, const char *zName){
+static sqlite3_syscall_ptr vfstraceGetSystemCall(
+ sqlite3_vfs *pVfs,
+ const char *zName
+){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xGetSystemCall(pRoot, zName);
diff --git a/src/utf.c b/src/utf.c
index 8312cf933..95182694d 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -167,7 +167,7 @@ int sqlite3Utf8Read(
const unsigned char *zIn, /* First byte of UTF-8 character */
const unsigned char **pzNext /* Write first byte past UTF-8 char here */
){
- int c;
+ unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
@@ -410,15 +410,15 @@ int sqlite3Utf8CharLen(const char *zIn, int nByte){
** This has the effect of making sure that the string is well-formed
** UTF-8. Miscoded characters are removed.
**
-** The translation is done in-place (since it is impossible for the
-** correct UTF-8 encoding to be longer than a malformed encoding).
+** The translation is done in-place and aborted if the output
+** overruns the input.
*/
int sqlite3Utf8To8(unsigned char *zIn){
unsigned char *zOut = zIn;
unsigned char *zStart = zIn;
u32 c;
- while( zIn[0] ){
+ while( zIn[0] && zOut<=zIn ){
c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
if( c!=0xfffd ){
WRITE_UTF8(zOut, c);
diff --git a/src/vdbe.c b/src/vdbe.c
index de979b1cb..cd8c1ec69 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -4646,14 +4646,10 @@ case OP_CreateTable: { /* out2-prerelease */
break;
}
-/* Opcode: ParseSchema P1 P2 * P4 *
+/* Opcode: ParseSchema P1 * * P4 *
**
** Read and parse all entries from the SQLITE_MASTER table of database P1
-** that match the WHERE clause P4. P2 is the "force" flag. Always do
-** the parsing if P2 is true. If P2 is false, then this routine is a
-** no-op if the schema is not currently loaded. In other words, if P2
-** is false, the SQLITE_MASTER table is only parsed if the rest of the
-** schema is already loaded into the symbol table.
+** that match the WHERE clause P4.
**
** This opcode invokes the parser to create a new virtual machine,
** then runs the new virtual machine. It is thus a re-entrant opcode.
@@ -4667,14 +4663,7 @@ case OP_ParseSchema: {
iDb = pOp->p1;
assert( iDb>=0 && iDb<db->nDb );
- /* If pOp->p2 is 0, then this opcode is being executed to read a
- ** single row, for example the row corresponding to a new index
- ** created by this VDBE, from the sqlite_master table. It only
- ** does this if the corresponding in-memory schema is currently
- ** loaded. Otherwise, the new index definition can be loaded along
- ** with the rest of the schema when it is required.
- **
- ** Although the mutex on the BtShared object that corresponds to
+ /* Although the mutex on the BtShared object that corresponds to
** database iDb (the database containing the sqlite_master table
** read by this instruction) is currently held, it is necessary to
** obtain the mutexes on all attached databases before checking if
@@ -4690,7 +4679,7 @@ case OP_ParseSchema: {
*/
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
sqlite3BtreeEnterAll(db);
- if( pOp->p2 || DbHasProperty(db, iDb, DB_SchemaLoaded) ){
+ if( ALWAYS(DbHasProperty(db, iDb, DB_SchemaLoaded)) ){
zMaster = SCHEMA_TABLE(iDb);
initData.db = db;
initData.iDb = pOp->p1;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 28acaea31..839feb905 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1704,7 +1704,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- rc = sqlite3BtreeCommitPhaseTwo(pBt);
+ rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
if( rc==SQLITE_OK ){
@@ -1836,7 +1836,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- sqlite3BtreeCommitPhaseTwo(pBt);
+ sqlite3BtreeCommitPhaseTwo(pBt, 1);
}
}
sqlite3EndBenignMalloc();
diff --git a/src/vtab.c b/src/vtab.c
index e460ee59e..b9f1e6f34 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -372,7 +372,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
- sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 1, 0, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
}