Index: src/os_win.c
==================================================================
--- src/os_win.c
+++ src/os_win.c
@@ -2922,12 +2922,20 @@
** around problems caused by indexing and/or anti-virus software on
** Windows systems.
** If you are using this code as a model for alternative VFSes, do not
** copy this retry logic. It is a hack intended for Windows only.
*/
+ lastErrno = osGetLastError();
OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
pFile->h, cnt, res));
+ if( lastErrno==ERROR_INVALID_HANDLE ){
+ pFile->lastErrno = lastErrno;
+ rc = SQLITE_IOERR_LOCK;
+ OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
+ pFile->h, cnt, sqlite3ErrName(rc)));
+ return rc;
+ }
if( cnt ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
if( !res ){
lastErrno = osGetLastError();
@@ -3167,10 +3175,21 @@
a[1] = winIoerrRetryDelay;
}
OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
+#ifdef SQLITE_TEST
+ case SQLITE_FCNTL_WIN32_SET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ HANDLE hOldFile = pFile->h;
+ pFile->h = *phFile;
+ *phFile = hOldFile;
+ OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
+ hOldFile, pFile->h));
+ return SQLITE_OK;
+ }
+#endif
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
Index: src/sqlite.h.in
==================================================================
--- src/sqlite.h.in
+++ src/sqlite.h.in
@@ -940,10 +940,16 @@
** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
** pointer to an integer and it writes a boolean into that integer depending
** on whether or not the file has been renamed, moved, or deleted since it
** was first opened.
**
+**
[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
+** opcode causes the xFileControl method to swap the file handle with the one
+** pointed to by the pArg argument. This capability is used during testing
+** and only needs to be supported when SQLITE_TEST is defined.
+**
**
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
#define SQLITE_SET_LOCKPROXYFILE 3
@@ -963,10 +969,11 @@
#define SQLITE_FCNTL_MMAP_SIZE 18
#define SQLITE_FCNTL_TRACE 19
#define SQLITE_FCNTL_HAS_MOVED 20
#define SQLITE_FCNTL_SYNC 21
#define SQLITE_FCNTL_COMMIT_PHASETWO 22
+#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
/*
** CAPI3REF: Mutex Handle
**
** The mutex module within SQLite defines [sqlite3_mutex] to be an
Index: src/test1.c
==================================================================
--- src/test1.c
+++ src/test1.c
@@ -114,10 +114,20 @@
}else{
*ppDb = (sqlite3*)sqlite3TestTextToPtr(zA);
}
return TCL_OK;
}
+
+#if SQLITE_OS_WIN
+/*
+** Decode a Win32 HANDLE object.
+*/
+int getWin32Handle(Tcl_Interp *interp, const char *zA, LPHANDLE phFile){
+ *phFile = (HANDLE)sqlite3TestTextToPtr(zA);
+ return TCL_OK;
+}
+#endif
extern const char *sqlite3ErrName(int);
#define t1ErrorName sqlite3ErrName
/*
@@ -5208,10 +5218,11 @@
}
#endif
return TCL_OK;
}
+#if SQLITE_OS_WIN
/*
** tclcmd: file_control_win32_av_retry DB NRETRY DELAY
**
** This TCL command runs the sqlite3_file_control interface with
** the SQLITE_FCNTL_WIN32_AV_RETRY opcode.
@@ -5241,10 +5252,46 @@
sqlite3_snprintf(sizeof(z), z, "%d %d %d", rc, a[0], a[1]);
Tcl_AppendResult(interp, z, (char*)0);
return TCL_OK;
}
+/*
+** tclcmd: file_control_win32_set_handle DB HANDLE
+**
+** This TCL command runs the sqlite3_file_control interface with
+** the SQLITE_FCNTL_WIN32_SET_HANDLE opcode.
+*/
+static int file_control_win32_set_handle(
+ ClientData clientData, /* Pointer to sqlite3_enable_XXX function */
+ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
+ int objc, /* Number of arguments */
+ Tcl_Obj *CONST objv[] /* Command arguments */
+){
+ sqlite3 *db;
+ int rc;
+ HANDLE hFile = NULL;
+ char z[100];
+
+ if( objc!=3 ){
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ Tcl_GetStringFromObj(objv[0], 0), " DB HANDLE", 0);
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
+ return TCL_ERROR;
+ }
+ if( getWin32Handle(interp, Tcl_GetString(objv[2]), &hFile) ){
+ return TCL_ERROR;
+ }
+ rc = sqlite3_file_control(db, NULL, SQLITE_FCNTL_WIN32_SET_HANDLE,
+ (void*)&hFile);
+ sqlite3_snprintf(sizeof(z), z, "%d %p", rc, (void*)hFile);
+ Tcl_AppendResult(interp, z, (char*)0);
+ return TCL_OK;
+}
+#endif
+
/*
** tclcmd: file_control_persist_wal DB PERSIST-FLAG
**
** This TCL command runs the sqlite3_file_control interface with
** the SQLITE_FCNTL_PERSIST_WAL opcode.
@@ -6470,11 +6517,14 @@
{ "file_control_test", file_control_test, 0 },
{ "file_control_lasterrno_test", file_control_lasterrno_test, 0 },
{ "file_control_lockproxy_test", file_control_lockproxy_test, 0 },
{ "file_control_chunksize_test", file_control_chunksize_test, 0 },
{ "file_control_sizehint_test", file_control_sizehint_test, 0 },
+#if SQLITE_OS_WIN
{ "file_control_win32_av_retry", file_control_win32_av_retry, 0 },
+ { "file_control_win32_set_handle", file_control_win32_set_handle, 0 },
+#endif
{ "file_control_persist_wal", file_control_persist_wal, 0 },
{ "file_control_powersafe_overwrite",file_control_powersafe_overwrite,0},
{ "file_control_vfsname", file_control_vfsname, 0 },
{ "file_control_tempfilename", file_control_tempfilename, 0 },
{ "sqlite3_vfs_list", vfs_list, 0 },
Index: test/win32lock.test
==================================================================
--- test/win32lock.test
+++ test/win32lock.test
@@ -22,11 +22,11 @@
db close
sqlite3_shutdown
test_sqlite3_log xLog
proc xLog {error_code msg} {
- lappend ::log $msg
+ lappend ::log $msg
}
sqlite3 db test.db
db eval {PRAGMA mmap_size=0}
do_test win32lock-1.1 {
@@ -125,10 +125,56 @@
sqlite3_sleep 10
}
file_control_win32_av_retry db 10 25
sqlite3_test_control_pending_byte $old_pending_byte
+db close
+forcedelete test.db
+
+sqlite3 db test.db
+sqlite3 db2 test.db
+
+do_test win32lock-3.0 {
+ db eval {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ }
+} {}
+
+do_test win32lock-3.1 {
+ db eval {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(4);
+ }
+} {}
+
+do_test win32lock-3.2 {
+ catchsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(5);
+ COMMIT;
+ } db2
+} {1 {database is locked}}
+
+do_test win32lock-3.3 {
+ db eval {
+ COMMIT;
+ }
+} {}
+
+do_test win32lock-3.4 {
+ set handle [lindex [file_control_win32_set_handle db 0] end]
+ list [catchsql {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(6);
+ COMMIT;
+ }] [file_control_win32_set_handle db $handle] [sqlite3_extended_errcode db]
+} {{1 {disk I/O error}} {0 0} SQLITE_IOERR_LOCK}
+
+db2 close
db close
sqlite3_shutdown
-test_sqlite3_log
+test_sqlite3_log
sqlite3_initialize
finish_test