From 69b2232d6e2cfe4a5d1cd25d48af015ee1faac7b Mon Sep 17 00:00:00 2001 From: drh Date: Sat, 3 Dec 2011 00:13:06 +0000 Subject: [PATCH] Add the sqlite3_quota_fflush() interface. Enhance sqlite3_quota_remove() so that it can remove entire directories. FossilOrigin-Name: abcb65af4cdd192beaccdbc2109ad45b9e7f9d00 --- manifest | 16 ++++----- manifest.uuid | 2 +- src/test_quota.c | 91 ++++++++++++++++++++++++++++++++++++++++++++---- src/test_quota.h | 41 ++++++++++++++++++---- test/quota2.test | 64 +++++++++++++++++++++++++++++++--- 5 files changed, 189 insertions(+), 25 deletions(-) diff --git a/manifest b/manifest index 5de01fa78e..e49d82bcc0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C One\sminor\sdocumentation\senhancement. -D 2011-12-02T15:31:07.596 +C Add\sthe\ssqlite3_quota_fflush()\sinterface.\s\sEnhance\ssqlite3_quota_remove()\nso\sthat\sit\scan\sremove\sentire\sdirectories. +D 2011-12-03T00:13:06.592 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -220,8 +220,8 @@ F src/test_mutex.c a6bd7b9cf6e19d989e31392b06ac8d189f0d573e F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec F src/test_osinst.c 62b0b8ef21ce754cc94e17bb42377ed8795dba32 F src/test_pcache.c a5cd24730cb43c5b18629043314548c9169abb00 -F src/test_quota.c 38c23c0fcbc92e0f8fe6fc0a76e66680cdb1b5cb -F src/test_quota.h c9e778a1ace38cd5be936e99a3d757477e2d1d4f +F src/test_quota.c 2e6191cbfc6ae978330a0d0ffcc3fb81b7059e68 +F src/test_quota.h 9b3c75a79e8c3c6a9d3846b73435bebcd550ba12 F src/test_rtree.c 6d06306e29946dc36f528a3a2cdc3add794656f1 F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0 F src/test_server.c 2f99eb2837dfa06a4aacf24af24c6affdf66a84f @@ -636,7 +636,7 @@ F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc F test/quick.test 1681febc928d686362d50057c642f77a02c62e57 F test/quota-glob.test 32901e9eed6705d68ca3faee2a06b73b57cb3c26 F test/quota.test 46e6571b45c3c58ac131cc38f7d600aa9f75974d -F test/quota2.test b7169f613adf64c22a5336ca607d3b54d7b2ed5a +F test/quota2.test 562b27570d1e0d0606c3769b648e23c72fa3859b F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6 F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459 F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a @@ -979,7 +979,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P fa71896089538589fb7015d6507e22961e72233b -R a322cd5996330a42d9cdf5afaa24ab27 +P 8cfd3575c8d9f5361c5276d6b83aba47606975a3 +R 28ca02cd727421756b639f8157eb6391 U drh -Z 9258d38c087b789fa674695734308523 +Z 84dc1ce8a56d57cb5251e17d63b686ff diff --git a/manifest.uuid b/manifest.uuid index b9b86d16df..83ef24af72 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8cfd3575c8d9f5361c5276d6b83aba47606975a3 \ No newline at end of file +abcb65af4cdd192beaccdbc2109ad45b9e7f9d00 \ No newline at end of file diff --git a/src/test_quota.c b/src/test_quota.c index 479cf0f0d8..c073b47d28 100644 --- a/src/test_quota.c +++ b/src/test_quota.c @@ -552,7 +552,10 @@ static int quotaClose(sqlite3_file *pConn){ pFile->nRef--; if( pFile->nRef==0 ){ quotaGroup *pGroup = pFile->pGroup; - if( pFile->deleteOnClose ) quotaRemoveFile(pFile); + if( pFile->deleteOnClose ){ + gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); + quotaRemoveFile(pFile); + } quotaGroupDeref(pGroup); } quotaLeave(); @@ -1041,7 +1044,10 @@ int sqlite3_quota_fclose(quota_FILE *p){ pFile->nRef--; if( pFile->nRef==0 ){ quotaGroup *pGroup = pFile->pGroup; - if( pFile->deleteOnClose ) quotaRemoveFile(pFile); + if( pFile->deleteOnClose ){ + gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); + quotaRemoveFile(pFile); + } quotaGroupDeref(pGroup); } quotaLeave(); @@ -1050,6 +1056,13 @@ int sqlite3_quota_fclose(quota_FILE *p){ return rc; } +/* +** Flush memory buffers for a quota_FILE to disk. +*/ +int sqlite3_quota_fflush(quota_FILE *p){ + return fflush(p->f); +} + /* ** Seek on a quota_FILE stream. */ @@ -1072,14 +1085,57 @@ long sqlite3_quota_ftell(quota_FILE *p){ } /* -** Remove a file. Update quotas accordingly. +** Remove a managed file. Update quotas accordingly. */ int sqlite3_quota_remove(const char *zFilename){ - int rc = remove(zFilename); - sqlite3_quota_file(zFilename); + char *zFull; /* Full pathname for zFilename */ + int nFull; /* Number of bytes in zFilename */ + int rc; /* Result code */ + quotaGroup *pGroup; /* Group containing zFilename */ + quotaFile *pFile; /* A file in the group */ + quotaFile *pNextFile; /* next file in the group */ + int diff; /* Difference between filenames */ + char c; /* First character past end of pattern */ + + zFull = sqlite3_malloc(gQuota.sThisVfs.mxPathname + 1); + if( zFull==0 ) return SQLITE_NOMEM; + rc = gQuota.pOrigVfs->xFullPathname(gQuota.pOrigVfs, zFilename, + gQuota.sThisVfs.mxPathname+1, zFull); + if( rc ){ + sqlite3_free(zFull); + return rc; + } + + /* Figure out the length of the full pathname. If the name ends with + ** / (or \ on windows) then remove the trailing /. + */ + nFull = strlen(zFull); + if( nFull>0 && (zFull[nFull-1]=='/' || zFull[nFull-1]=='\\') ){ + nFull--; + zFull[nFull] = 0; + } + + quotaEnter(); + pGroup = quotaGroupFind(zFull); + if( pGroup ){ + for(pFile=pGroup->pFiles; pFile && rc==SQLITE_OK; pFile=pNextFile){ + pNextFile = pFile->pNext; + diff = memcmp(zFull, pFile->zFilename, nFull); + if( diff==0 && ((c = pFile->zFilename[nFull])==0 || c=='/' || c=='\\') ){ + if( pFile->nRef ){ + pFile->deleteOnClose = 1; + }else{ + rc = gQuota.pOrigVfs->xDelete(gQuota.pOrigVfs, pFile->zFilename, 0); + quotaRemoveFile(pFile); + quotaGroupDeref(pGroup); + } + } + } + } + quotaLeave(); + sqlite3_free(zFull); return rc; } - /***************************** Test Code ***********************************/ #ifdef SQLITE_TEST @@ -1445,6 +1501,28 @@ static int test_quota_fclose( return TCL_OK; } +/* +** tclcmd: sqlite3_quota_fflush HANDLE +*/ +static int test_quota_fflush( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + quota_FILE *p; + int rc; + + if( objc!=2 ){ + Tcl_WrongNumArgs(interp, 1, objv, "HANDLE"); + return TCL_ERROR; + } + p = sqlite3TestTextToPtr(Tcl_GetString(objv[1])); + rc = sqlite3_quota_fflush(p); + Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); + return TCL_OK; +} + /* ** tclcmd: sqlite3_quota_fseek HANDLE OFFSET WHENCE */ @@ -1589,6 +1667,7 @@ int Sqlitequota_Init(Tcl_Interp *interp){ { "sqlite3_quota_fread", test_quota_fread }, { "sqlite3_quota_fwrite", test_quota_fwrite }, { "sqlite3_quota_fclose", test_quota_fclose }, + { "sqlite3_quota_fflush", test_quota_fflush }, { "sqlite3_quota_fseek", test_quota_fseek }, { "sqlite3_quota_rewind", test_quota_rewind }, { "sqlite3_quota_ftell", test_quota_ftell }, diff --git a/src/test_quota.h b/src/test_quota.h index 6df423f5d1..dbcb2c185c 100644 --- a/src/test_quota.h +++ b/src/test_quota.h @@ -101,6 +101,10 @@ int sqlite3_quota_shutdown(void); ** database connections if those connections are to participate in the ** quota group. Creating a quota group does not affect database connections ** that are already open. +** +** The patterns that define the various quota groups should be distinct. +** If the same filename matches more than one quota group pattern, then +** the behavior of this package is undefined. */ int sqlite3_quota_set( const char *zPattern, /* The filename pattern */ @@ -116,14 +120,20 @@ int sqlite3_quota_set( ); /* -** Bring the named file under quota management. Or if it is already under -** management, update its size. +** Bring the named file under quota management, assuming its name matches +** the glob pattern of some quota group. Or if it is already under +** management, update its size. If zFilename does not match the glob +** pattern of any quota group, this routine is a no-op. */ int sqlite3_quota_file(const char *zFilename); /* ** The following object serves the same role as FILE in the standard C ** library. It represents an open connection to a file on disk for I/O. +** +** A single quota_FILE should not be used by two or more threads at the +** same time. Multiple threads can be using different quota_FILE objects +** simultaneously, but not the same quota_FILE object. */ typedef struct quota_FILE quota_FILE; @@ -141,6 +151,13 @@ quota_FILE *sqlite3_quota_fopen(const char *zFilename, const char *zMode); size_t sqlite3_quota_fread(void*, size_t, size_t, quota_FILE*); size_t sqlite3_quota_fwrite(void*, size_t, size_t, quota_FILE*); +/* +** Flush all written content held in memory buffers out to disk. +** This is the equivalent of fflush() in the standard library - not +** an fsync(). +*/ +int sqlite3_quota_fflush(quota_FILE*); + /* ** Close a quota_FILE object and free all associated resources. The ** file remains under quota management. @@ -156,11 +173,23 @@ void sqlite3_quota_rewind(quota_FILE*); long sqlite3_quota_ftell(quota_FILE*); /* -** Delete a file from the disk. If that file is under quota management, -** then adjust quotas accordingly. +** Delete a file from the disk, if that file is under quota management. +** Adjust quotas accordingly. ** -** The file being deleted must not be open for reading or writing or as -** a database when it is deleted. +** If zFilename is the name of a directory that matches one of the +** quota glob patterns, then all files under quota management that +** are contained within that directory are deleted. +** +** A standard SQLite result code is returned (SQLITE_OK, SQLITE_NOMEM, etc.) +** When deleting a directory of files, if the deletion of any one +** file fails (for example due to an I/O error), then this routine +** returns immediately, with the error code, and does not try to +** delete any of the other files in the specified directory. +** +** All files are removed from quota management and deleted from disk. +** However, no attempt is made to remove empty directories. +** +** This routine is a no-op for files that are not under quota management. */ int sqlite3_quota_remove(const char *zFilename); diff --git a/test/quota2.test b/test/quota2.test index d408cda9ca..4b9daca2a2 100644 --- a/test/quota2.test +++ b/test/quota2.test @@ -17,8 +17,10 @@ source $testdir/malloc_common.tcl db close sqlite3_quota_initialize "" 1 -foreach dir {quota2a quota2b quota2c} { +foreach dir {quota2a/x1 quota2a/x2 quota2a quota2b quota2c} { file delete -force $dir +} +foreach dir {quota2a quota2a/x1 quota2a/x2 quota2b quota2c} { file mkdir $dir } @@ -109,11 +111,11 @@ do_test quota2-1.10 { do_test quota2-1.11 { standard_path [sqlite3_quota_dump] } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 1 0}}} -do_test quota1-1.12 { +do_test quota2-1.12 { sqlite3_quota_fclose $::h1 standard_path [sqlite3_quota_dump] } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 4000 {PWD/quota2a/xyz.txt 4000 0 0}}} -do_test quota1-1.13 { +do_test quota2-1.13 { sqlite3_quota_remove quota2a/xyz.txt standard_path [sqlite3_quota_dump] } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}} @@ -162,11 +164,65 @@ do_test quota2-2.10 { do_test quota2-2.11 { standard_path [sqlite3_quota_dump] } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}} -do_test quota1-2.12 { +do_test quota2-2.12 { sqlite3_quota_fclose $::h1 standard_path [sqlite3_quota_dump] } {{*/quota2b/* 5000 0} {*/quota2a/* 4000 0}} +do_test quota2-3.1 { + sqlite3_quota_set */quota2b/* 0 quota_check + set ::h1 [sqlite3_quota_fopen quota2a/x1/a.txt a] + sqlite3_quota_fwrite $::h1 10 10 $bigtext +} {10} +do_test quota2-3.2 { + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}} +do_test quota2-3.3 { + sqlite3_quota_fflush $::h1 + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 1 0}}} +do_test quota2-3.4 { + sqlite3_quota_fclose $::h1 + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 100 {PWD/quota2a/x1/a.txt 100 0 0}}} +do_test quota2-3.5 { + set ::h2 [sqlite3_quota_fopen quota2a/x2/b.txt a] + sqlite3_quota_fwrite $::h2 10 20 $bigtext + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 300 {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}} +do_test quota2-3.6 { + set ::h3 [sqlite3_quota_fopen quota2a/x1/c.txt a] + sqlite3_quota_fwrite $::h3 10 50 $bigtext + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 800 {PWD/quota2a/x1/c.txt 500 1 0} {PWD/quota2a/x2/b.txt 200 1 0} {PWD/quota2a/x1/a.txt 100 0 0}}} +do_test quota2-3.7 { + file exists quota2a/x1/a.txt +} {1} +do_test quota2-3.8 { + file exists quota2a/x2/b.txt +} {1} +do_test quota2-3.9 { + file exists quota2a/x1/c.txt +} {1} +do_test quota2-3.10 { + sqlite3_quota_remove quota2a/x1 + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 700 {PWD/quota2a/x1/c.txt 500 1 1} {PWD/quota2a/x2/b.txt 200 1 0}}} +do_test quota2-3.11 { + sqlite3_quota_fclose $::h2 + sqlite3_quota_fclose $::h3 + standard_path [sqlite3_quota_dump] +} {{*/quota2a/* 4000 200 {PWD/quota2a/x2/b.txt 200 0 0}}} +do_test quota2-3.12 { + file exists quota2a/x1/a.txt +} {0} +do_test quota2-3.13 { + file exists quota2a/x2/b.txt +} {1} +do_test quota2-3.14 { + file exists quota2a/x1/c.txt +} {0} + catch { sqlite3_quota_shutdown } catch { unset quota_request_ok } finish_test