From 9a09a3caed06458db632ef2e0d0f14bfdda6a4fa Mon Sep 17 00:00:00 2001 From: tpoindex Date: Mon, 20 Dec 2004 19:01:32 +0000 Subject: [PATCH] Add PRAGMA 'temp_store_directory'. Added os_*.c function sqlite3OsIsDirWritable(), split pragma.c changeTempStorage() function into invalidateTempStorage(). (CVS 2171) FossilOrigin-Name: 772e22cbd69463be41c2e73b4fd4eb33946193c4 --- Makefile.in | 3 ++ manifest | 30 ++++++++--------- manifest.uuid | 2 +- src/os.h | 1 + src/os_mac.c | 16 +++++++++ src/os_unix.c | 16 ++++++++- src/os_win.c | 17 +++++++++- src/pragma.c | 88 ++++++++++++++++++++++++++++++++++++++++++++---- src/sqlite.h.in | 11 +++--- test/pragma.test | 77 +++++++++++++++++++++++++++++++++++++++++- www/pragma.tcl | 43 +++++++++++++++++++++-- 11 files changed, 270 insertions(+), 34 deletions(-) diff --git a/Makefile.in b/Makefile.in index a533a2c4d8..4ad6d84d4f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -513,6 +513,9 @@ index.html: $(TOP)/www/index.tcl last_change lang.html: $(TOP)/www/lang.tcl tclsh $(TOP)/www/lang.tcl >lang.html +pragma.html: $(TOP)/www/pragma.tcl + tclsh $(TOP)/www/pragma.tcl >pragma.html + lockingv3.html: $(TOP)/www/lockingv3.tcl tclsh $(TOP)/www/lockingv3.tcl >lockingv3.html diff --git a/manifest b/manifest index da3e269803..1effda3b28 100644 --- a/manifest +++ b/manifest @@ -1,6 +1,6 @@ -C The\soptimizer\snow\suses\sonly\sthe\sindex\sand\signores\sthe\stable\sif\sit\scan\sget\naway\swith\sdoing\sso,\sthus\ssaving\sa\ssingle\sBTree\ssearch\sper\srow\sof\sresult.\nThis\scould\spotentially\sdouble\sthe\sspeed\sof\scertain\squeries.\s\sThe\ncode\spasses\sall\sregression\stests\sbut\snew\stests\sto\sexercise\sthe\snew\nfunctionality\sare\syet\sto\sbe\sadded.\s(CVS\s2170) -D 2004-12-19T00:11:35 -F Makefile.in da09f379b80c8cd78d78abaa0f32ca90a124e884 +C Add\sPRAGMA\s'temp_store_directory'.\s\sAdded\sos_*.c\sfunction\nsqlite3OsIsDirWritable(),\ssplit\spragma.c\schangeTempStorage()\sfunction\sinto\ninvalidateTempStorage().\s(CVS\s2171) +D 2004-12-20T19:01:32 +F Makefile.in 02a184d734a2b4bbbc1ecc2e3ef504fcb13de069 F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 F VERSION 342b6d5fde93b6d45023e2fee0163dda6464b9d6 @@ -43,25 +43,25 @@ F src/insert.c 0b9077c6752530e9919a8c84375cfa2c4652260a F src/legacy.c d58ea507bce885298a2c8c3cbb0f4bff5d47830b F src/main.c fc383dc9cf03847b96e5ed9942696467725cfdfd F src/md5.c 7ae1c39044b95de2f62e066f47bb1deb880a1070 -F src/os.h 38258df2db895499b6e2957dbf17f25e0df71667 +F src/os.h c92a675533c75fea0f53559f34c7b91c5afe1a9d F src/os_common.h 0e7f428ba0a6c40a61bc56c4e96f493231301b73 -F src/os_mac.c 7367dab0c44ab0b2c4337e73ac6f6f97f171c2cb +F src/os_mac.c e2a35e96bdf57a113ae1c446532e3c0924d3d046 F src/os_mac.h 608fdf39eafa1ce25fc8cb223b8b0a073341d4da F src/os_test.c 91e5f22dd89491e5e1554820e715805f43fa4ece F src/os_test.h 6a26a4978492e4bbdbf385554958418ff02db162 -F src/os_unix.c 5824b22ba41fe9d514ef9169aac1b5fde73af229 +F src/os_unix.c f3835451ffa69072ea88f30cfd6f3ed12b728cfa F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13 -F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb +F src/os_win.c 39525c414e57ca3f18d860d40d6d38df85689522 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b F src/pager.c 7b4dc9a94228efde924f1d9f4b7751f332da4587 F src/pager.h 9eba8c53dd91eae7f3f90743b2ee242da02a9862 F src/parse.y ceba179b9703657180963568f54b0e75f33e36e1 -F src/pragma.c d6406e12c9eac353b3a026b50d41e4fd561afcc2 +F src/pragma.c 639a7e7ef0999211aafa1f3d474ecc7241033cb9 F src/printf.c 3d20b21cfecadacecac3fb7274e746cb81d3d357 F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c ac6610b4b2c5bd5ffc46536b760dacc420119dac F src/shell.c e8f4f486cbf6e60d81173146ac8a6522c930fa51 -F src/sqlite.h.in fa75850f412808afd38fddc1fd6456f4efc6fb97 +F src/sqlite.h.in 0d5e48e506845b74a845c9470e01d3f472b59611 F src/sqliteInt.h a922cfd13711c68538684619fb15a4d262b12b9d F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 3a4044ef609565c8cc51e887d8b96933ba9f3b5c @@ -161,7 +161,7 @@ F test/pager.test 394455707a079804e8a4e431d12edce831a065f0 F test/pager2.test 49c0f57c7da0b060f0486b85fdd074025caa694e F test/pager3.test 647f696a9cf7409df00a1e0047c2eb55585a1b85 F test/pagesize.test 1b826d1608fd86d2303aa895b5586052ad07eba1 -F test/pragma.test 726167cc97de01445f645c6d7e52e427db9ce46f +F test/pragma.test 18b3f99853ff694211c2c69567b0a9426e2a5e7a F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57 F test/progress.test 5ddba78cb6011fba36093973cfb3ac473b8fb96a x F test/quick.test 91e5b8ae6663dc9e3e754b271f0384f0cae706e6 @@ -254,7 +254,7 @@ F www/nulls.tcl ec35193f92485b87b90a994a01d0171b58823fcf F www/oldnews.tcl 7aa4478e64631859770a5fe4b413919ba6ee8a08 F www/omitted.tcl 7bd62b6f0f53b60c5360895b16b3af8407bbca03 F www/opcode.tcl dafa030a5a3cc24a2f9fd4cfbfb7d7323d2151b0 -F www/pragma.tcl 39c4a2be847538360c5d3e234f40a11f2eb08916 +F www/pragma.tcl 8c6b5662875bccde826324fe0e37edda39b870f4 F www/quickstart.tcl 6f6f694b6139be2d967b1492eb9a6bdf7058aa60 F www/speed.tcl de99c82c4729a10b6733463636f15473c4ec95bc F www/sqlite.tcl b51fd15f0531a54874de785a9efba323eecd5975 @@ -263,7 +263,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc F www/vdbe.tcl 095f106d93875c94b47367384ebc870517431618 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P 9b86993ff721b577b920c7c67fb41d3d4355fe88 -R 2476c11934030eade9ca4e863deb7247 -U drh -Z 352407e4634c619fec9ef3761d4b80dd +P e5aa489453bf31126da6473ef93c89ec27935cde +R 80852dba2ab3f55be612ddeeffa02604 +U tpoindex +Z 5d44d728827caef4bb2a5da252002b6d diff --git a/manifest.uuid b/manifest.uuid index 1d30fe31ae..a08b47c486 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -e5aa489453bf31126da6473ef93c89ec27935cde \ No newline at end of file +772e22cbd69463be41c2e73b4fd4eb33946193c4 \ No newline at end of file diff --git a/src/os.h b/src/os.h index 9bad88e161..ad14584f1b 100644 --- a/src/os.h +++ b/src/os.h @@ -176,6 +176,7 @@ int sqlite3OsOpenReadOnly(const char*, OsFile*); int sqlite3OsOpenDirectory(const char*, OsFile*); int sqlite3OsSyncDirectory(const char*); int sqlite3OsTempFileName(char*); +int sqlite3OsIsDirWritable(char*); int sqlite3OsClose(OsFile*); int sqlite3OsRead(OsFile*, void*, int amt); int sqlite3OsWrite(OsFile*, const void*, int amt); diff --git a/src/os_mac.c b/src/os_mac.c index f84c168d4a..1e2de7bb07 100644 --- a/src/os_mac.c +++ b/src/os_mac.c @@ -272,6 +272,22 @@ int sqlite3OsTempFileName(char *zBuf){ return SQLITE_OK; } +/* +** If the following global variable points to a string which is the +** name of a directory, then that directory will be used to store +** temporary files. +*/ +char *sqlite3_temp_directory = 0; + +/* +** Check that a given pathname is a directory and is writable +** Just return false, as module is deprecated. +*/ +int sqlite3OsIsDirWritable(char *zBuf){ + return 0; +} + + /* ** Close a file. */ diff --git a/src/os_unix.c b/src/os_unix.c index 94fca70199..4d17c632d3 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -574,7 +574,7 @@ int sqlite3OsOpenDirectory( ** name of a directory, then that directory will be used to store ** temporary files. */ -const char *sqlite3_temp_directory = 0; +char *sqlite3_temp_directory = 0; /* ** Create a temporary file name in zBuf. zBuf must be big enough to @@ -616,6 +616,20 @@ int sqlite3OsTempFileName(char *zBuf){ return SQLITE_OK; } +/* +** Check that a given pathname is a directory and is writable +** +*/ +int sqlite3OsIsDirWritable(char *zBuf){ + struct stat buf; + if( zBuf==0 ) return 0; + if( strlen(zBuf)==0 ) return 0; + if( stat(zBuf, &buf) ) return 0; + if( !S_ISDIR(buf.st_mode) ) return 0; + if( access(zBuf, 07) ) return 0; + return 1; +} + /* ** Read data from a file into a buffer. Return SQLITE_OK if all ** bytes were read successfully and SQLITE_IOERR if anything goes diff --git a/src/os_win.c b/src/os_win.c index f6e3e3ea83..732b796563 100644 --- a/src/os_win.c +++ b/src/os_win.c @@ -202,7 +202,7 @@ int sqlite3OsOpenDirectory( ** name of a directory, then that directory will be used to store ** temporary files. */ -const char *sqlite3_temp_directory = 0; +char *sqlite3_temp_directory = 0; /* ** Create a temporary file name in zBuf. zBuf must be big enough to @@ -409,6 +409,21 @@ static int unlockReadLock(OsFile *id){ return res; } +/* +** Check that a given pathname is a directory and is writable +** +*/ +int sqlite3OsIsDirWritable(char *zBuf){ + int fileAttr; + if(! zBuf ) return 0; + if(! isNT() && strlen(zBuf) > MAX_PATH ) return 0; + fileAttr = GetFileAttributesA(zBuf); + if( fileAttr == 0xffffffff ) return 0; + if( (fileAttr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY ) return 0; + return 1; +} + + /* ** Lock the file with the lock specified by parameter locktype - one ** of the following: diff --git a/src/pragma.c b/src/pragma.c index 3d094d3854..5589e9317d 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.79 2004/11/22 19:12:21 drh Exp $ +** $Id: pragma.c,v 1.80 2004/12/20 19:01:33 tpoindex Exp $ */ #include "sqliteInt.h" #include @@ -79,14 +79,11 @@ static int getTempStore(const char *z){ } /* -** If the TEMP database is open, close it and mark the database schema -** as needing reloading. This must be done when using the TEMP_STORE -** or DEFAULT_TEMP_STORE pragmas. +** Invalidate temp storage, either when the temp storage is changed +** from default, or when 'file' and the temp_store_directory has changed */ -static int changeTempStorage(Parse *pParse, const char *zStorageType){ - int ts = getTempStore(zStorageType); +static int invalidateTempStorage(Parse *pParse){ sqlite3 *db = pParse->db; - if( db->temp_store==ts ) return SQLITE_OK; if( db->aDb[1].pBt!=0 ){ if( db->flags & SQLITE_InTrans ){ sqlite3ErrorMsg(pParse, "temporary storage cannot be changed " @@ -97,6 +94,21 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){ db->aDb[1].pBt = 0; sqlite3ResetInternalSchema(db, 0); } + return SQLITE_OK; +} + +/* +** If the TEMP database is open, close it and mark the database schema +** as needing reloading. This must be done when using the TEMP_STORE +** or DEFAULT_TEMP_STORE pragmas. +*/ +static int changeTempStorage(Parse *pParse, const char *zStorageType){ + int ts = getTempStore(zStorageType); + sqlite3 *db = pParse->db; + if( db->temp_store==ts ) return SQLITE_OK; + if( invalidateTempStorage( pParse ) != SQLITE_OK ){ + return SQLITE_ERROR; + } db->temp_store = ts; return SQLITE_OK; } @@ -342,6 +354,68 @@ void sqlite3Pragma( } }else + /* + ** PRAGMA temp_store_directory + ** PRAGMA temp_store_directory = ""|"directory_name" + ** + ** Return or set the local value of the temp_store_directory flag. Changing + ** the value sets a specific directory to be used for temporary files. + ** Setting to a null string reverts to the default temporary directory search. + ** If temporary directory is changed, then invalidateTempStorage. + ** + */ + if( sqlite3StrICmp(zLeft, "temp_store_directory")==0 ){ + if( !zRight ){ + if( sqlite3_temp_directory ){ + sqlite3VdbeSetNumCols(v, 1); + sqlite3VdbeSetColName(v, 0, "temp_store_directory", P3_STATIC); + sqlite3VdbeOp3(v, OP_String8, 0, 0, sqlite3_temp_directory, 0); + sqlite3VdbeAddOp(v, OP_Callback, 1, 0); + } + }else{ + if( strlen(zRight)==0 ){ + /* empty path, set to default. allows os_{unix,win}.c to choose directory */ + if( sqlite3_temp_directory ){ + /* previous temp_store_directory defined, free and invalidate */ + sqlite3FreeX(sqlite3_temp_directory); + if( db->temp_store==1 ) { + /* temp storage is "file", so invalidate temp */ + invalidateTempStorage( pParse ); + } + } + sqlite3_temp_directory = 0; + }else{ + /* check if previous directory defined, free and alloc if needed */ + if( sqlite3_temp_directory ){ + if( strlen(sqlite3_temp_directory) < strlen(zRight) + 1){ + sqlite3FreeX(sqlite3_temp_directory); + sqlite3_temp_directory = sqlite3Malloc( strlen(zRight) + 1 ); + if( sqlite3_temp_directory==0 ){ + goto pragma_out; + } + sqlite3_temp_directory[0] = '\0'; + } + }else{ + sqlite3_temp_directory = sqlite3Malloc( strlen(zRight) + 1 ); + if( sqlite3_temp_directory==0 ){ + goto pragma_out; + } + sqlite3_temp_directory[0] = '\0'; + } + /* check that directory exists and is writable */ + if( sqlite3OsIsDirWritable( zRight ) ){ + strcpy(sqlite3_temp_directory, zRight); + if( db->temp_store==1 ) { + /* temp storage is "file", so invalidate temp */ + invalidateTempStorage( pParse ); + } + }else{ + sqlite3ErrorMsg(pParse, "not a directory, or not writable"); + } + } + } + }else + /* ** PRAGMA [database.]synchronous ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL diff --git a/src/sqlite.h.in b/src/sqlite.h.in index d4633bbd83..3c5f1f2e5e 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -12,7 +12,7 @@ ** This header file defines the interface that the SQLite library ** presents to client programs. ** -** @(#) $Id: sqlite.h.in,v 1.125 2004/12/07 02:14:51 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.126 2004/12/20 19:01:33 tpoindex Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1155,17 +1155,16 @@ int sqlite3_rekey( ); /* -** If the following global variable is made to point to a constant +** If the following global variable is made to point to a ** string which is the name of a directory, then all temporary files ** created by SQLite will be placed in that directory. If this variable ** is NULL pointer, then SQLite does a search for an appropriate temporary ** file directory. ** -** This variable should only be changed when there are no open databases. -** Once sqlite3_open() has been called, this variable should not be changed -** until all database connections are closed. +** Once sqlite3_open() has been called, changing this variable will invalidate the +** current temporary database, if any. */ -extern const char *sqlite3_temp_directory; +extern char *sqlite3_temp_directory; #ifdef __cplusplus } /* End of the 'extern "C"' block */ diff --git a/test/pragma.test b/test/pragma.test index 0d47da55aa..5bbf178cf3 100644 --- a/test/pragma.test +++ b/test/pragma.test @@ -12,7 +12,7 @@ # # This file implements tests for the PRAGMA command. # -# $Id: pragma.test,v 1.27 2004/11/23 11:16:42 danielk1977 Exp $ +# $Id: pragma.test,v 1.28 2004/12/20 19:01:33 tpoindex Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -25,6 +25,7 @@ source $testdir/tester.tcl # pragma-4.*: Test cache_size and default_cache_size on attached db. # pragma-5.*: Test that pragma synchronous may not be used inside of a # transaction. +# pragma-9.*: Test temp_store and temp_store_directory. # # Delete the preexisting database to avoid the special setup @@ -586,6 +587,80 @@ do_test pragma-8.2.15 { } } {-450} + +# Test temp_store and temp_store_directory pragmas +# + +do_test pragma-9.1 { + db close + sqlite3 db test.db + execsql { + PRAGMA temp_store; + } +} {0} +do_test pragma-9.2 { + execsql { + PRAGMA temp_store=file; + PRAGMA temp_store; + } +} {1} +do_test pragma-9.3 { + execsql { + PRAGMA temp_store=memory; + PRAGMA temp_store; + } +} {2} +do_test pragma-9.4 { + execsql { + PRAGMA temp_store_directory; + } +} {} +do_test pragma-9.5 { + execsql " \ + PRAGMA temp_store_directory='[pwd]'; \ + " +} {} +do_test pragma-9.6 { + execsql { + PRAGMA temp_store_directory; + } +} [pwd] +do_test pragma-9.7 { + set result "" + catch { + execsql { + PRAGMA temp_store_directory='/NON/EXISTENT/PATH/FOOBAR'; + } + } result + set result +} {not a directory, or not writable} +do_test pragma-9.8 { + execsql { + PRAGMA temp_store_directory=''; + } +} {} +do_test pragma-9.9 { + execsql { + PRAGMA temp_store_directory; + PRAGMA temp_store=FILE; + CREATE TEMP TABLE temp_store_directory_test(a integer); + INSERT INTO temp_store_directory_test values (2); + SELECT * FROM temp_store_directory_test; + } +} {2} +do_test pragma-9.10 { + set result "" + catch { + execsql " \ + PRAGMA temp_store_directory='[pwd]'; \ + SELECT * FROM temp_store_directory_test; + " + } result + set result +} {no such table: temp_store_directory_test} + + + } ; # ifcapable schema_version finish_test diff --git a/www/pragma.tcl b/www/pragma.tcl index 47cd61ad89..3df1d0f9c4 100644 --- a/www/pragma.tcl +++ b/www/pragma.tcl @@ -1,7 +1,7 @@ # # Run this Tcl script to generate the pragma.html file. # -set rcsid {$Id: pragma.tcl,v 1.6 2004/11/20 08:17:18 danielk1977 Exp $} +set rcsid {$Id: pragma.tcl,v 1.7 2004/12/20 19:01:34 tpoindex Exp $} source common.tcl header {Pragma statements supported by SQLite} @@ -211,12 +211,51 @@ puts { is closed and reopened. For additional information on the temp_store flag, see the description of the default_temp_store pragma. Note that it is possible for - the library compile-time options to override this setting.

+ the library compile-time options to override this setting. See + PRAGMA temp_store_directory + for further temporary storage options when FILE is specified.

When the temp_store setting is changed, all existing temporary tables, indices, triggers, and viewers are immediately deleted.

+ + +
  • PRAGMA temp_store_directory; +
    PRAGMA temp_store_directory = 'directory-name';

    +

    Query or change the setting of the "temp_store_directory" flag affecting + the database for the duration of the current database connection. + The temp_store_directory flag reverts to its default value when the database + is closed and reopened. Setting temp_store_directory allows control of the + placement of temporary files created by SQLite when PRAGMA + temp_store is FILE (1), + or when the compile time default temporary store is FILE. + Otherwise, when the temp_store (or default) setting is + MEMORY (2), setting temp_store_directory has no effect.

    + +

    When the temp_store_directory setting is changed, all existing temporary + tables, indices, triggers, and viewers are immediately deleted. In + practice, temp_store_directory should be set immediately after the + database is opened.

    + +

    The value directory-name should be enclosed in single quotes. + To revert the directory to the default, set the directory-name to + a null string, e.g., PRAGMA temp_store_directory = ''. An + error is raised if directory-name is not found or is not + writable.

    + +

    The default directory for temporary files depends on the OS. For + Unix/Linux/OSX, the default is the is the first writable directory found + in the list of: /var/tmp, /usr/tmp, /tmp, and + current-directory. For Windows NT, the default + directory is determined by Windows, generally + C:\Documents and Settings\user-name\Local Settings\Temp\. + Temporary files created by SQLite are unlinked immediately after + opening, so that the operating system can automatically delete the + files when the SQLite process exits. Thus, temporary files are not + normally visible through ls or dir commands.

    + +
  • }