}
/* Forward declaration */
-static int winGetTempname(int nBuf, char *zBuf);
+static int winGetTempname(sqlite3_vfs *, char **);
#if SQLITE_MAX_MMAP_SIZE>0
static int winMapfile(winFile*, sqlite3_int64);
#endif
return SQLITE_OK;
}
case SQLITE_FCNTL_TEMPFILENAME: {
- char *zTFile = sqlite3MallocZero( pFile->pVfs->mxPathname );
- if( zTFile ){
- winGetTempname(pFile->pVfs->mxPathname, zTFile);
+ char *zTFile = 0;
+ int rc = winGetTempname(pFile->pVfs, &zTFile);
+ if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
- OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
- return SQLITE_OK;
+ OSTRACE(("FCNTL file=%p, rc=%d\n", pFile->h, rc));
+ return rc;
}
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
}
/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
+** This function returns non-zero if the specified UTF-8 string buffer
+** ends with a directory separator character.
+*/
+static int winEndsInDirSep(char *zBuf){
+ if( zBuf ){
+ int nLen = sqlite3Strlen30(zBuf);
+ return nLen>0 && winIsDirSep(zBuf[nLen-1]);
+ }
+ return 0;
+}
+
+/*
+** Create a temporary file name and store the resulting pointer into pzBuf.
+** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
-static int winGetTempname(int nBuf, char *zBuf){
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
- int nTempPath;
- char zTempPath[SQLITE_WIN32_MAX_PATH_BYTES+2];
+ int nBuf, nLen;
+ char *zBuf;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
*/
SimulateIOError( return SQLITE_IOERR );
+ /* Allocate a temporary buffer to store the fully qualified file
+ ** name for the temporary file. If this fails, we cannot continue.
+ */
+ nBuf = pVfs->mxPathname;
+ zBuf = sqlite3MallocZero( nBuf+2 );
+ if( !zBuf ){
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+
+ /* Figure out the effective temporary directory. First, check if one
+ ** has been explicitly set by the application; otherwise, use the one
+ ** configured by the operating system.
+ */
+ assert( nBuf>30 );
if( sqlite3_temp_directory ){
- sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s",
- sqlite3_temp_directory);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory,
+ winEndsInDirSep(sqlite3_temp_directory) ? "" :
+ winGetDirDep());
}
#if !SQLITE_OS_WINRT
else if( osIsNT() ){
char *zMulti;
- WCHAR zWidePath[SQLITE_WIN32_MAX_PATH_CHARS];
- if( osGetTempPathW(SQLITE_WIN32_MAX_PATH_CHARS-30, zWidePath)==0 ){
+ LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) );
+ if( !zWidePath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathW(nBuf, zWidePath)==0 ){
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
return SQLITE_IOERR_GETTEMPPATH;
}
zMulti = winUnicodeToUtf8(zWidePath);
if( zMulti ){
- sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zMulti);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zMulti);
sqlite3_free(zMulti);
+ sqlite3_free(zWidePath);
}else{
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
#ifdef SQLITE_WIN32_HAS_ANSI
else{
char *zUtf8;
- char zMbcsPath[SQLITE_WIN32_MAX_PATH_BYTES];
- if( osGetTempPathA(SQLITE_WIN32_MAX_PATH_BYTES-30, zMbcsPath)==0 ){
+ char *zMbcsPath = sqlite3MallocZero( nBuf );
+ if( !zMbcsPath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM;
+ }
+ if( osGetTempPathA(nBuf, zMbcsPath)==0 ){
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
return SQLITE_IOERR_GETTEMPPATH;
}
zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
if( zUtf8 ){
- sqlite3_snprintf(SQLITE_WIN32_MAX_PATH_BYTES-30, zTempPath, "%s", zUtf8);
+ sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
sqlite3_free(zUtf8);
}else{
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
return SQLITE_IOERR_NOMEM;
}
}
-#else
- else{
- /*
- ** Compiled without ANSI support and the current operating system
- ** is not Windows NT; therefore, just zero the temporary buffer.
- */
- memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
- }
#endif /* SQLITE_WIN32_HAS_ANSI */
-#else
- else{
- /*
- ** Compiled for WinRT and the sqlite3_temp_directory is not set;
- ** therefore, just zero the temporary buffer.
- */
- memset(zTempPath, 0, SQLITE_WIN32_MAX_PATH_BYTES+2);
- }
#endif /* !SQLITE_OS_WINRT */
/* Check that the output buffer is large enough for the temporary file
** name. If it is not, return SQLITE_ERROR.
*/
- nTempPath = sqlite3Strlen30(zTempPath);
+ nLen = sqlite3Strlen30(zBuf);
- if( (nTempPath + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
+ sqlite3_free(zBuf);
OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
return SQLITE_ERROR;
}
- for(i=nTempPath; i>0 && winIsDirSep(zTempPath[i-1]); i--){}
- zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
- sqlite3_snprintf(nBuf-18, zBuf, (nTempPath > 0) ?
- "%s%s" SQLITE_TEMP_FILE_PREFIX : SQLITE_TEMP_FILE_PREFIX,
- zTempPath, winGetDirDep());
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
}
zBuf[j] = 0;
zBuf[j+1] = 0;
+ *pzBuf = zBuf;
OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
return SQLITE_OK;
** Open a file.
*/
static int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
+ sqlite3_vfs *pVfs, /* Used to get maximum path name length */
const char *zName, /* Name of the file (UTF-8) */
sqlite3_file *id, /* Write the SQLite file handle here */
int flags, /* Open mode flags */
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[SQLITE_WIN32_MAX_PATH_BYTES+2]; /* Buffer for temp filename */
+ char *zTmpname = 0; /* For temporary filename, if necessary. */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
*/
if( !zUtf8Name ){
assert( isDelete && !isOpenJournal );
- rc = winGetTempname(SQLITE_WIN32_MAX_PATH_BYTES+2, zTmpname);
+ rc = winGetTempname(pVfs, &zTmpname);
if( rc!=SQLITE_OK ){
OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
return rc;
/* Convert the filename to the system encoding. */
zConverted = winConvertUtf8Filename(zUtf8Name);
if( zConverted==0 ){
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
return SQLITE_IOERR_NOMEM;
}
if( winIsDir(zConverted) ){
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
return SQLITE_CANTOPEN_ISDIR;
}
pFile->lastErrno = lastErrno;
winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
if( isReadWrite && !isExclusive ){
return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY) &
){
osCloseHandle(h);
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
return rc;
}
#endif
{
sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
}
pFile->pMethod = &winIoMethod;
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- assert( pVfs->mxPathname>=SQLITE_WIN32_MAX_PATH_BYTES );
assert( nFull>=pVfs->mxPathname );
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
/*
** for converting the relative path name to an absolute
** one by prepending the data directory and a slash.
*/
- char zOut[SQLITE_WIN32_MAX_PATH_BYTES+1];
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ winLogError(SQLITE_IOERR_NOMEM, 0, "winFullPathname", zRelative);
+ return SQLITE_IOERR_NOMEM;
+ }
if( cygwin_conv_path(CCP_POSIX_TO_WIN_A|CCP_RELATIVE, zRelative, zOut,
- SQLITE_WIN32_MAX_PATH_BYTES+1)<0 ){
+ pVfs->mxPathname+1)<0 ){
winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
zRelative);
+ sqlite3_free(zOut);
return SQLITE_CANTOPEN_FULLPATH;
}
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
sqlite3_data_directory, winGetDirDep(), zOut);
+ sqlite3_free(zOut);
}else{
if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){
winLogError(SQLITE_CANTOPEN_FULLPATH, (DWORD)errno, "cygwin_conv_path",
winGetSystemCall, /* xGetSystemCall */
winNextSystemCall, /* xNextSystemCall */
};
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath", /* zName */
+ 0, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
assert( winSysInfo.dwPageSize>0 );
sqlite3_vfs_register(&winVfs, 1);
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathVfs, 0);
+#endif
+
return SQLITE_OK;
}
CloseHandle(ev);
return TCL_OK;
}
+
+/*
+** exists_win32_path PATH
+**
+** Returns non-zero if the specified path exists, whose fully qualified name
+** may exceed 248 characters if it is prefixed with "\\?\".
+*/
+static int win32_exists_path(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PATH");
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
+ GetFileAttributesW( Tcl_GetUnicode(objv[1]))!=INVALID_FILE_ATTRIBUTES ));
+ return TCL_OK;
+}
+
+/*
+** find_win32_file PATTERN
+**
+** Returns a list of entries in a directory that match the specified pattern,
+** whose fully qualified name may exceed 248 characters if it is prefixed with
+** "\\?\".
+*/
+static int win32_find_file(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ HANDLE hFindFile = INVALID_HANDLE_VALUE;
+ WIN32_FIND_DATAW findData;
+ Tcl_Obj *listObj;
+ DWORD lastErrno;
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "PATTERN");
+ return TCL_ERROR;
+ }
+ hFindFile = FindFirstFileW(Tcl_GetUnicode(objv[1]), &findData);
+ if( hFindFile==INVALID_HANDLE_VALUE ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ listObj = Tcl_NewObj();
+ Tcl_IncrRefCount(listObj);
+ do {
+ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewUnicodeObj(
+ findData.cFileName, -1));
+ Tcl_ListObjAppendElement(interp, listObj, Tcl_NewWideIntObj(
+ findData.dwFileAttributes));
+ } while( FindNextFileW(hFindFile, &findData) );
+ lastErrno = GetLastError();
+ if( lastErrno!=NO_ERROR && lastErrno!=ERROR_NO_MORE_FILES ){
+ FindClose(hFindFile);
+ Tcl_DecrRefCount(listObj);
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ FindClose(hFindFile);
+ Tcl_SetObjResult(interp, listObj);
+ return TCL_OK;
+}
+
+/*
+** delete_win32_file FILENAME
+**
+** Deletes the specified file, whose fully qualified name may exceed 248
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_delete_file(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME");
+ return TCL_ERROR;
+ }
+ if( !DeleteFileW(Tcl_GetUnicode(objv[1])) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+/*
+** make_win32_dir DIRECTORY
+**
+** Creates the specified directory, whose fully qualified name may exceed 248
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_mkdir(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
+ return TCL_ERROR;
+ }
+ if( !CreateDirectoryW(Tcl_GetUnicode(objv[1]), NULL) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
+
+/*
+** remove_win32_dir DIRECTORY
+**
+** Removes the specified directory, whose fully qualified name may exceed 248
+** characters if it is prefixed with "\\?\".
+*/
+static int win32_rmdir(
+ void *clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ if( objc!=2 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DIRECTORY");
+ return TCL_ERROR;
+ }
+ if( !RemoveDirectoryW(Tcl_GetUnicode(objv[1])) ){
+ Tcl_SetObjResult(interp, Tcl_NewWideIntObj(GetLastError()));
+ return TCL_ERROR;
+ }
+ Tcl_ResetResult(interp);
+ return TCL_OK;
+}
#endif
{ "optimization_control", optimization_control,0},
#if SQLITE_OS_WIN
{ "lock_win32_file", win32_file_lock, 0 },
+ { "exists_win32_path", win32_exists_path, 0 },
+ { "find_win32_file", win32_find_file, 0 },
+ { "delete_win32_file", win32_delete_file, 0 },
+ { "make_win32_dir", win32_mkdir, 0 },
+ { "remove_win32_dir", win32_rmdir, 0 },
#endif
{ "tcl_objproc", runAsObjProc, 0 },
--- /dev/null
+# 2013 August 27
+#
+# 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.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library. The
+# focus of this script is testing the file name handling provided
+# by the "win32-longpath" VFS.
+#
+
+if {$tcl_platform(platform)!="windows"} return
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set testprefix win32longpath
+
+proc do_remove_win32_dir {args} {
+ set nRetry [getFileRetries] ;# Maximum number of retries.
+ set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
+
+ foreach dirName $args {
+ # On windows, sometimes even a [remove_win32_dir] can fail just after
+ # a directory is emptied. The cause is usually "tag-alongs" - programs
+ # like anti-virus software, automatic backup tools and various explorer
+ # extensions that keep a file open a little longer than we expect,
+ # causing the delete to fail.
+ #
+ # The solution is to wait a short amount of time before retrying the
+ # removal.
+ #
+ if {$nRetry > 0} {
+ for {set i 0} {$i < $nRetry} {incr i} {
+ set rc [catch {
+ remove_win32_dir $dirName
+ } msg]
+ if {$rc == 0} break
+ if {$nDelay > 0} { after $nDelay }
+ }
+ if {$rc} { error $msg }
+ } else {
+ remove_win32_dir $dirName
+ }
+ }
+}
+
+proc do_delete_win32_file {args} {
+ set nRetry [getFileRetries] ;# Maximum number of retries.
+ set nDelay [getFileRetryDelay] ;# Delay in ms before retrying.
+
+ foreach fileName $args {
+ # On windows, sometimes even a [delete_win32_file] can fail just after
+ # a file is closed. The cause is usually "tag-alongs" - programs like
+ # anti-virus software, automatic backup tools and various explorer
+ # extensions that keep a file open a little longer than we expect,
+ # causing the delete to fail.
+ #
+ # The solution is to wait a short amount of time before retrying the
+ # delete.
+ #
+ if {$nRetry > 0} {
+ for {set i 0} {$i < $nRetry} {incr i} {
+ set rc [catch {
+ delete_win32_file $fileName
+ } msg]
+ if {$rc == 0} break
+ if {$nDelay > 0} { after $nDelay }
+ }
+ if {$rc} { error $msg }
+ } else {
+ delete_win32_file $fileName
+ }
+ }
+}
+
+db close
+set path [file nativename [get_pwd]]
+sqlite3 db [file join $path test.db] -vfs win32-longpath
+
+do_test 1.1 {
+ db eval {
+ BEGIN EXCLUSIVE;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(1);
+ INSERT INTO t1 VALUES(2);
+ INSERT INTO t1 VALUES(3);
+ INSERT INTO t1 VALUES(4);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {1 2 3 4}
+
+set longPath(1) \\\\?\\$path\\[pid]
+make_win32_dir $longPath(1)
+
+set longPath(2) $longPath(1)\\[string repeat X 255]
+make_win32_dir $longPath(2)
+
+set longPath(3) $longPath(2)\\[string repeat Y 255]
+make_win32_dir $longPath(3)
+
+set fileName $longPath(3)\\test.db
+
+do_test 1.2 {
+ list [catch {sqlite3 db2 [string range $fileName 4 end]} msg] $msg
+} {1 {unable to open database file}}
+
+sqlite3 db3 $fileName -vfs win32-longpath
+
+do_test 1.3 {
+ db3 eval {
+ PRAGMA journal_mode = WAL;
+ }
+} {wal}
+
+do_test 1.4 {
+ db3 eval {
+ BEGIN EXCLUSIVE;
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(5);
+ INSERT INTO t1 VALUES(6);
+ INSERT INTO t1 VALUES(7);
+ INSERT INTO t1 VALUES(8);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {5 6 7 8}
+
+db3 close
+# puts " Database exists \{[exists_win32_path $fileName]\}"
+
+sqlite3 db3 $fileName -vfs win32-longpath
+
+do_test 1.5 {
+ db3 eval {
+ BEGIN EXCLUSIVE;
+ INSERT INTO t1 VALUES(9);
+ INSERT INTO t1 VALUES(10);
+ INSERT INTO t1 VALUES(11);
+ INSERT INTO t1 VALUES(12);
+ SELECT x FROM t1 ORDER BY x;
+ COMMIT;
+ }
+} {5 6 7 8 9 10 11 12}
+
+db3 close
+# puts " Database exists \{[exists_win32_path $fileName]\}"
+
+do_delete_win32_file $fileName
+# puts " Files remaining \{[find_win32_file $longPath(3)\\*]\}"
+
+do_remove_win32_dir $longPath(3)
+do_remove_win32_dir $longPath(2)
+do_remove_win32_dir $longPath(1)
+
+finish_test