From: drh <> Date: Sun, 26 Apr 2026 18:35:26 +0000 (+0000) Subject: Improvements to temp-file cleanup on close in the CLI. X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=74006fdcdb78d43d725d002066b6b4d4dafb3a39;p=thirdparty%2Fsqlite.git Improvements to temp-file cleanup on close in the CLI. FossilOrigin-Name: 048c969c34eaaf8c203b996e999a7dbc94c47b4959719af9e6625052520f7135 --- diff --git a/manifest b/manifest index 63c8de6708..121cc8a1ea 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C In\sthe\sCLI,\seliminate\sthe\s2-second\sdelay\sbefore\sgetting\sthe\sprompt\sback\nafter\srunning\sa\s".www"\sor\s".excel".\s\sInstead,\sremember\sthe\sname\sof\sthe\ntemporary\sfile\sthat\sis\sto\sbe\sdeleted\sand\sdelete\sit\slater. -D 2026-04-25T16:17:23.297 +C Improvements\sto\stemp-file\scleanup\son\sclose\sin\sthe\sCLI. +D 2026-04-26T18:35:26.126 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -736,7 +736,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 928ff887f2a7c64275182060d94d06fdddbe32226c569781cf7e7edc6f58d7fd F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 4c05cde130f26991b7411d8c6809e0630625e18078742c963a047b4b9cc01d49 -F src/shell.c.in 0c22dd2c11ff87d4904d990dba632940102012d55a9d221940958ad6afe09506 +F src/shell.c.in 6bf45ed0430cc23686697a6c1fdd756247f3e859d0140454afb884c6a775456d F src/sqlite.h.in 39d2e09114d2bdb7afd998f4a469c8f8cd065f8093835a7d0422f260fc78fb4f F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 9788c301f95370fa30e808861f1d2e6f022a816ddbe2a4f67486784c1b31db2e @@ -2203,8 +2203,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 92cad2cd7284d3a969b713336865007da2b0b2a8eef367d4185802174cbd027a -R d17ad9dd57a963733572b44ccb3c359d +P 4e9ee211ca661fe8978b7f676f33ef962ff5c8177bc7c5ad42b19a2fa4482d90 +R 3dad6c18c2c8aca0242ada41edaef876 U drh -Z 936ee9dc91c7afce31c375f047aeb7da +Z d5ab748eb3952f1319907fdc974e52fb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index b2e522f264..3566ced503 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -4e9ee211ca661fe8978b7f676f33ef962ff5c8177bc7c5ad42b19a2fa4482d90 +048c969c34eaaf8c203b996e999a7dbc94c47b4959719af9e6625052520f7135 diff --git a/src/shell.c.in b/src/shell.c.in index 38e556bcba..117d0262f0 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -663,6 +663,13 @@ static int stdout_tty_width = -1; */ static sqlite3 *globalDb = 0; +/* +** This is a global pointer to the main ShellState variable. This +** exists so that the atexit() callback can access the ShellState +** object to do some cleanup. +*/ +static struct ShellState *globalShellState = 0; + /* ** True if an interrupt (Control-C) has been received. */ @@ -1764,6 +1771,7 @@ static void shellExpandPrompt( sqlite3_free(zRes); } + /************************* BEGIN PERFORMANCE TIMER *****************************/ #if !defined(_WIN32) && !defined(WIN32) && !defined(__minux) #include @@ -2530,6 +2538,11 @@ static void interrupt_handler(int NotUsed){ if( ++seenInterrupt>1 ) cli_exit(1); if( globalDb ) sqlite3_interrupt(globalDb); } +/* No-masking interrupts (SIGTERM or SIGHUP) */ +static void sigterm_handler(int NotUsed){ + UNUSED_PARAMETER(NotUsed); + cli_exit(1); +} /* Try to determine the screen width. Use the default if unable. */ @@ -4850,6 +4863,9 @@ static void shellModuleSchema( #define OPEN_DB_KEEPALIVE 0x001 /* Return after error if true */ #define OPEN_DB_ZIPFILE 0x002 /* Open as ZIP if name matches *.zip */ +/* Forward reference */ +static void shellTempFilenameFunc(sqlite3_context*,int,sqlite3_value**); + /* ** Make sure the database is open. If it is not, then open it. If ** the database fails to open, print an error message and exit. @@ -5022,6 +5038,8 @@ static void open_db(ShellState *p, int openFlags){ p, shellExpandPrompt, 0, 0); sqlite3_create_function(p->db, "shell_prompt_test", 3, SQLITE_UTF8, p, shellExpandPrompt, 0, 0); + sqlite3_create_function(p->db, "shell_temp_filename", 1, SQLITE_UTF8, + p, shellTempFilenameFunc, 0, 0); if( p->openMode==SHELL_OPEN_ZIPFILE ){ @@ -6258,7 +6276,9 @@ static char *shellFilenameFromUri(const char *zFN){ ** ** If unsuccessful on the first attempt and if pRetry!=NULL and pRetry[0] ** is positive, then delay for pRetry[0] milliseconds and try again. On -** a retry, pRetry[0] is set to zero. +** a retry, pRetry[0] is set to zero. Unsuccessful unlinks usually only +** happen on Windows. It is possible (in theory) for unlink() to fail +** on Unix, but it rarely happens. ** ** Return 0 on success and non-zero if unable. */ @@ -6268,9 +6288,11 @@ int shellDeleteFile(const char *zFilename, int *pRetry){ #ifdef _WIN32 wchar_t *z = sqlite3_win32_utf8_to_unicode(zFilename); rc = _wunlink(z); + if( rc && _waccess(z,0)!=0 ) rc = 0; sqlite3_free(z); #else rc = unlink(zFilename); + if( rc && access(zFilename, 0)!=0 ) rc = 0; #endif if( rc && pRetry && pRetry[0] ){ sqlite3_sleep(pRetry[0]); @@ -6283,27 +6305,43 @@ int shellDeleteFile(const char *zFilename, int *pRetry){ } /* -** Try to delete the temporary file (if there is one) with the name -** p->zTempFile and free the memory used to hold the name of the temp file. -** If there are any entries in p->aUnlink[] that are old enough, then -** delete those too, also reclaiming memory. +** This routine does two things: +** +** (1) Delete (unlink) temporary files that are no longer needed. +** (2) Free memory used to hold the names of those temporary files. ** -** If retry is non-zero and a deletion attempt is not successful, -** wait for retry milliseconds and try again before giving up. -** When retry is non-zero, memory is always reclaimed even if the -** file deletion attempt fails. +** Temp filenames are stored in p->zTempName and in the p->aUnlink[] +** array. The p->zTempFile is always subject to immediate deletion. The +** temp files named in p->aUnlink[] are usually not deleted until after +** the timestamp stored in the p->aUnlink[].tm field, except if +** bForce is true, the p->aUnlink[] files will be deleted even if they +** have not reached their expiration, as long as a delay of at least +** nDelay milliseconds occurs first. +** +** If nDelay is non-zero and a deletion attempt is not successful, +** wait for nDelay milliseconds and try again before giving up. Only +** a single wait occurs, even if problems are encountered with multiple +** temp files. Only the first problem seen takes the delay. Thus +** the total delay never exceeds nDelay milliseconds. When nDelay +** is non-zero, memory used to hold the filename is reclaimed regardless +** of whether or not the temporary files were successfully deleted. ** ** If bForce is true, deletion is attempted on p->aUnlink[] files -** even time has not expired. +** even if their time has not expired. However, there is a pause +** of up to nDelay milliseconds before doing the deletion. +** +** When nDelay is zero and the deletion attempt fails, memory used to +** store temporary filesnames is not reclaimed. ** -** When retry is zero and the deletion attempt fails, -** memory is not reclaimed. +** The bForce flag is used only when the process is about to exit. When +** bForce is set, that indicates that this is our last opportunity to +** clean up temporary files. */ -static void clearTempFile(ShellState *p, int retry, int bForce){ - int alwaysFree = retry>0; +static void clearTempFile(ShellState *p, int nDelay, int bForce){ + int alwaysFree = (nDelay>0) || bForce; int rc = 0; - if( p->zTempFile && !p->doXdgOpen ){ - rc = shellDeleteFile(p->zTempFile, &retry); + if( p->zTempFile && (!p->doXdgOpen || bForce) ){ + rc = shellDeleteFile(p->zTempFile, &nDelay); if( rc==0 || alwaysFree ){ sqlite3_free(p->zTempFile); p->zTempFile = 0; @@ -6312,11 +6350,18 @@ static void clearTempFile(ShellState *p, int retry, int bForce){ if( p->nUnlink ){ unsigned int i; for(i=0; inUnlink; i++){ - if( p->aUnlink[i].tm=0) - ){ + sqlite3_int64 tmToGo = p->aUnlink[i].tm - timeOfDay(); + int doDelete = tmToGo<=0; + if( !doDelete && alwaysFree ){ + int tmSleep = nDelay; + if( tmSleep > tmToGo ) tmSleep = (int)tmToGo; + sqlite3_sleep(tmSleep); + nDelay -= tmSleep; + doDelete = 1; + } + if( doDelete ){ char *zFN = p->aUnlink[i].zFN; - rc = shellDeleteFile(zFN, &retry); + rc = shellDeleteFile(zFN, &nDelay); if( rc==0 || alwaysFree ){ sqlite3_free(p->aUnlink[i].zFN); p->nUnlink--; @@ -6362,7 +6407,7 @@ static void newTempFile(ShellState *p, const char *zSuffix){ r /= 36; } zRand[i] = 0; - clearTempFile(p,1000,0); + clearTempFile(p,10,0); sqlite3_free(p->zTempFile); p->zTempFile = 0; zHome = find_home_dir(0); @@ -6371,6 +6416,26 @@ static void newTempFile(ShellState *p, const char *zSuffix){ shell_check_oom(p->zTempFile); } +/* +** SQL function: shell_temp_filename(SUFFIX) +** +** Return a randomly generated temporary filename. +*/ +static void shellTempFilenameFunc( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + ShellState *p = (ShellState*)sqlite3_user_data(pCtx); + const char *zSuffix = (const char*)sqlite3_value_text(apVal[0]); + (void)nVal; + if( zSuffix==0 ) zSuffix = ""; + newTempFile(p, zSuffix); + sqlite3_result_text(pCtx, p->zTempFile, -1, SQLITE_TRANSIENT); + sqlite3_free(p->zTempFile); + p->zTempFile = 0; +} + /* ** The implementation of SQL scalar function fkey_collate_clause(), used ** by the ".lint fkey-indexes" command. This scalar function is always @@ -13285,6 +13350,7 @@ static void main_init(ShellState *p) { #endif sqlite3_config(SQLITE_CONFIG_URI, 1); sqlite3_config(SQLITE_CONFIG_MULTITHREAD); + globalShellState = p; } /* @@ -13320,8 +13386,14 @@ static char *cmdline_option_value(int argc, char **argv, int i){ return argv[i]; } -static void sayAbnormalExit(void){ +/* +** The callback from atexit(). +*/ +static void abnormalExit(void){ if( seenInterrupt ) eputz("Program interrupted.\n"); + if( globalShellState ){ + clearTempFile(globalShellState, 1, 1); + } } /* Routine to output from vfstrace @@ -13439,7 +13511,7 @@ int SQLITE_CDECL main(int argc, char **argv){ stdin_is_interactive = isatty(0); stdout_is_console = isatty(1); #endif - atexit(sayAbnormalExit); + atexit(abnormalExit); #ifdef SQLITE_DEBUG mem_main_enter = sqlite3_memory_used(); #endif @@ -13472,6 +13544,13 @@ int SQLITE_CDECL main(int argc, char **argv){ eputz("No ^C handler.\n"); } #endif +#ifdef SIGTERM + signal(SIGTERM, sigterm_handler); +#endif +#ifdef SIGHUP + signal(SIGHUP, sigterm_handler); +#endif + #if USE_SYSTEM_SQLITE+0!=1 if( cli_strncmp(sqlite3_sourceid(),SQLITE_SOURCE_ID,60)!=0 ){ @@ -14111,7 +14190,8 @@ int SQLITE_CDECL main(int argc, char **argv){ find_home_dir(1); output_reset(&data); data.doXdgOpen = 0; - clearTempFile(&data,1000,1); + clearTempFile(&data,2500,1); + globalShellState = 0; while( data.nModeStack ) modePop(&data); free(data.aModeStack); modeFree(&data.mode);