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