From: larrybr Date: Mon, 24 Jan 2022 07:11:25 +0000 (+0000) Subject: Take CLI input redirect recursion limit from trunk X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=63255fda5c505af3724494b41e377fd020bc80ed;p=thirdparty%2Fsqlite.git Take CLI input redirect recursion limit from trunk FossilOrigin-Name: 5e7020441514d1febdd6d97d6c5be4429f65310e9bcde2eb50c071aa9459a333 --- 63255fda5c505af3724494b41e377fd020bc80ed diff --cc manifest index 5275464dff,4e48b7001b..9d76dc6f89 --- a/manifest +++ b/manifest @@@ -1,5 -1,5 +1,5 @@@ - C Add\sCLI\s.x\scommand,\smake\sstring\sredirection\swork - D 2022-01-24T04:21:20.922 -C Limit\sCLI\sinput\sredirect\snesting -D 2022-01-24T06:36:16.156 ++C Take\sCLI\sinput\sredirect\srecursion\slimit\sfrom\strunk ++D 2022-01-24T07:11:25.201 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@@ -553,7 -553,7 +553,7 @@@ F src/random.c 097dc8b31b8fba5a9aca1697 F src/resolve.c 359bc0e445d427583d2ab6110433a5dc777f64a0ecdf8d24826d8b475233ead9 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92 F src/select.c ab5717255420972e69b9b9ce4d1c4730fe82cfbdc14b7743e389a8bdb79ca027 - F src/shell.c.in 3308232026d5b984fcbe6386915dae00d786de06013dfb398d25b1996eec2c0a -F src/shell.c.in e80a140e92e342e2f92d405a77155c8e3a67c9b1d0bdbacb92885960cd4fc8f2 ++F src/shell.c.in a97789adf08a352a98e8de2e225253e02b06535301c7e8c0d430fee15828ceaa F src/sqlite.h.in 31c2c8d737814369bd3b71f3849c4a97ef7ede0aa3ce976ecb11632fa5f1f863 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 5d54cf13d3406d8eb65d921a0d3c349de6126b732e695e79ecd4830ce86b4f8a @@@ -1388,7 -1388,7 +1388,7 @@@ F test/sharedlock.test 5ede3c37439067c4 F test/shell1.test 70f46b5d07776a107335c3c2c9cbd0431d44637bfeae1f6b9ded5e33b4c7c0bf F test/shell2.test f00a0501c00583cbc46f7510e1d713366326b2b3e63d06d15937284171a8787c F test/shell3.test cb4b835a901742c9719437a89171172ecc4a8823ad97349af8e4e841e6f82566 - F test/shell4.test 3ed6c4b42fd695efcbc25d69ef759dbb15855ca8e52ba6c5ee076f8b435f48be -F test/shell4.test 8427e08751d4b16100fadb29f109cc1b8cce5c3858bdf34837c6e3b35fbbfee7 ++F test/shell4.test d817597bb7f11f97ea86e4ccad0480c21183bcdc2083cd9de05a11303d9ee577 F test/shell5.test b85069bfcf3159b225228629ab2c3e69aa923d098fea8ea074b5dcd743522e2c F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3 F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f @@@ -1941,8 -1941,8 +1941,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 - P 073ded4d185d0133b07b4548dd78f4ad5eb17ce1f5c457d89fee23281420bbff - R f62f785dd4d4b36cb5a7d1dd62c6a2c1 -P e4caf1e3932b1bd0dea072df7fc9458aed98c84ea397b6948b89292603949c41 -R 3ff059f17b79f35a7110343334a8c695 ++P 2f2f1aaed691ba31ba70577012f785aae1f4a53ac7582bf30d26fbbec1eb3f3c 7a073931752d16ba71f1a606091461e427ca5ccf4d135d3c5141bfdd4e67e2d5 ++R 2171c849337bd842c8b5ea7647d1f610 U larrybr - Z c190fc10a0d856e1e83e9ea407a32719 -Z 22bca9d238f29be7cbeeb6418fbc3f83 ++Z 86bfffa52bc0d70bd85c5a9f759d466b # Remove this line to create a well-formed Fossil manifest. diff --cc manifest.uuid index 3b9949a547,b9b364e4c1..5dfa7fb4c4 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 2f2f1aaed691ba31ba70577012f785aae1f4a53ac7582bf30d26fbbec1eb3f3c -7a073931752d16ba71f1a606091461e427ca5ccf4d135d3c5141bfdd4e67e2d5 ++5e7020441514d1febdd6d97d6c5be4429f65310e9bcde2eb50c071aa9459a333 diff --cc src/shell.c.in index 96da26b6a3,0a903672cc..74154decab --- a/src/shell.c.in +++ b/src/shell.c.in @@@ -677,88 -677,6 +677,89 @@@ static char *local_getline(char *zLine return zLine; } +/* +** Arrange for shell input from either a FILE or a string. +** See getline_from(...) for applicable invariants. It is +** the only modifier of data within this struct, except for +** initialization which is either {0, zS} or {pF, 0}, (with +** the iReadOffset always 0-initialized), left to whatever +** routine is switching input sources. +*/ +typedef struct InSource { + FILE *inFile; /* Will be 0 when input is to be taken from string. */ + char *zStrIn; /* Will be 0 when no input is available from string. */ + int iReadOffset; /* Offset into ZStrIn where next "read" to be done. */ - const char *zSourceSay; /* For complaints, keep a name for this source. */ ++ const char *zSourceSay; /* For complaints, keep a name for this source */ ++ struct InSource *pFrom; /* and redirect tracking to aid unraveling. */ +} InSource; + +/* +** Read single lines of input text from source selected by pInSource. +** This function presents an interface similar to local_getline(...), +** to which it defers for FILE input. (This function may be considered +** to be an adapter except for the source parameter. With a rewrite, +** it and local_getline can merge, saving most of 26 LOC. ) +*/ +static char *getline_from( char *zLine, InSource *pSource ){ + if( pSource->inFile!=0 ){ + return local_getline(zLine, pSource->inFile); + }else if( pSource->zStrIn!=0 + && pSource->zStrIn[pSource->iReadOffset]!=0 ){ + /* This code mostly copied from local_getline(); It begs for refactor. */ + int nLine = zLine==0 ? 0 : 100; + int n = 0; + + while( 1 ){ + if( n+100>nLine ){ + nLine = nLine*2 + 100; + zLine = realloc(zLine, nLine); + shell_check_oom(zLine); + } + { + /* This block is where fgets() became "read string". */ + char *zBegin = pSource->zStrIn + pSource->iReadOffset; + int iTake = 0; + char c = zBegin[iTake]; + if( c==0 ){ + if( n==0 ){ + free(zLine); + return 0; + } + zLine[n] = 0; + break; + }else{ + while( iTakeiReadOffset += iTake; + } + } + /* Above block replaced this commented code: */ + /* if( fgets(&zLine[n], nLine - n, in)==0 ){ */ + /* if( n==0 ){ */ + /* free(zLine); */ + /* return 0; */ + /* } */ + /* zLine[n] = 0; */ + /* break; */ + /* } */ + while( zLine[n] ) n++; + if( n>0 && zLine[n-1]=='\n' ){ + n--; + if( n>0 && zLine[n-1]=='\r' ) n--; + zLine[n] = 0; + break; + } + } + return zLine; + }else{ + return 0; + } +} + /* ** Retrieve a single line of input text. ** @@@ -7726,66 -7604,6 +7734,67 @@@ static int recoverDatabaseCmd(ShellStat } #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */ +static int execute_variables(char *azArg[], int nArg, ShellState *p){ + int ia, rc, nErrors = 0; + sqlite3_stmt *pStmt = 0; + open_db(p, 0); + if( p->db==0 ){ + utf8_printf(stderr, ".x can only be done with a database open.\n"); + return 1; + } + if( sqlite3_table_column_metadata(p->db, "TEMP", "sqlite_parameters", + "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ + utf8_printf(stderr, "No temp.sqlite_parameters table exists.\n"); + return 1; + } + rc = sqlite3_prepare_v2 + (p->db, "SELECT value FROM temp.sqlite_parameters WHERE key=$1", + -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + utf8_printf(stderr, "temp.sqlite_parameters is wrongly created.\n"); + return 1; + } + for( ia=1; ia < nArg; ++ia ){ + if( isalpha(azArg[ia][0]) ){ + rc = sqlite3_reset(pStmt); + rc = sqlite3_bind_text(pStmt, 1, azArg[ia], -1, 0); + rc = sqlite3_step(pStmt); + if( rc==SQLITE_ROW ){ + const unsigned char *zValue = sqlite3_column_text(pStmt, 0); + int nb = sqlite3_column_bytes(pStmt, 0); + /* If it's empty, just silently pretend to execute it. */ + if( nb==0 ) continue; + while( IsSpace(zValue[nb-1]) ) --nb; + if( nb>0 ){ + char *zSubmit = sqlite3_mprintf( "%.*s\n", nb, zValue ); - InSource inSourceDivert = {0, zSubmit, 0, azArg[ia]}; ++ InSource inSourceDivert = ++ {0, zSubmit, 0, azArg[ia], p->pInSource }; + InSource *pInSrcSave = p->pInSource; + int savedLineno = p->lineno; + shell_check_oom(zSubmit); + p->pInSource = &inSourceDivert; + rc = process_input(p); + sqlite3_free(zSubmit); + p->pInSource = pInSrcSave; + p->lineno = savedLineno; + }else{ + continue; /* All white, ignore. */ + } + }else{ + utf8_printf(stderr, + "Skipping parameter '%s' (not set.)\n", azArg[ia]); + ++nErrors; + } + }else{ + utf8_printf(stderr, + "Skipping badly named %s. Run \".help x\"\n", azArg[ia]); + ++nErrors; + } + } + sqlite3_finalize(pStmt); + return nErrors>0; +} + /* ** If an input line begins with "." then invoke this routine to ** process that line. @@@ -9574,19 -9385,11 +9583,20 @@@ static int do_meta_command(char *zLine utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]); rc = 1; }else{ + fCloser = fclose; + } + if( inUse!=0 ){ + InSource *pInSrcSave = p->pInSource; + int savedLineno = p->lineno; - InSource inSourceDivert = {inUse, 0, 0, azArg[1]}; ++ InSource inSourceDivert = ++ {inUse, 0, 0, azArg[1], p->pInSource}; + p->pInSource = &inSourceDivert; rc = process_input(p); - fclose(p->in); + assert(fCloser!=0); + fCloser(inUse); + p->pInSource = pInSrcSave; + p->lineno = savedLineno; } - p->in = inSaved; - p->lineno = savedLineno; }else if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){ @@@ -11155,15 -10947,20 +11165,27 @@@ static int process_input(ShellState *p) int startline = 0; /* Line number for start of current input */ QuickScanState qss = QSS_Start; /* Accumulated line status (so far) */ + if( p->inputNesting==MAX_INPUT_NESTING ){ - /* This will be more informative in a later version. */ - utf8_printf(stderr,"Input nesting limit (%d) reached at line %d." - " Check recursion.\n", MAX_INPUT_NESTING, p->lineno); ++ InSource *pInSrc = p->pInSource; ++ utf8_printf ++ (stderr, ++ "Input nesting limit (%d) reached, from line %d of \"%s\",\n", ++ MAX_INPUT_NESTING, p->lineno-1, pInSrc->zSourceSay); ++ for( rc=0; rc<3 && (pInSrc=pInSrc->pFrom)!=0; ++rc ) ++ utf8_printf(stderr, " from \"%s\"", pInSrc->zSourceSay); ++ utf8_printf(stderr, " ...\nCheck recursion.\n"); + return 1; + } + ++p->inputNesting; p->lineno = 0; - while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){ + while( errCnt==0 + || !bail_on_error + || (p->pInSource==0 && stdin_is_interactive) ){ fflush(p->out); - zLine = one_input_line(p->in, zLine, nSql>0); + zLine = one_input_line(p->pInSource, zLine, nSql>0); if( zLine==0 ){ /* End of input */ - if( p->in==0 && stdin_is_interactive ) printf("\n"); + if( p->pInSource==0 && stdin_is_interactive ) printf("\n"); break; } if( seenInterrupt ){ @@@ -11337,13 -11136,8 +11360,13 @@@ static void process_sqliterc shell_check_oom(zBuf); sqliterc = zBuf; } - p->in = fopen(sqliterc,"rb"); - if( p->in ){ + inUse = fopen(sqliterc,"rb"); + if( inUse!=0 ){ + InSource *pInSrcSave = p->pInSource; + int savedLineno = p->lineno; - InSource inSourceDivert = {inUse, 0, 0, sqliterc}; ++ InSource inSourceDivert = {inUse, 0, 0, sqliterc, p->pInSource}; + int rc; + p->pInSource = &inSourceDivert; if( stdin_is_interactive ){ utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc); } diff --cc test/shell4.test index 9e3c58fbec,1be2c1d9ac..107125ed6f --- a/test/shell4.test +++ b/test/shell4.test @@@ -138,4 -139,11 +139,13 @@@ do_test shell4-3.2 exec $::CLI :memory: --interactive ".read t1.txt" } {pound: £} + do_test shell4-4.1 { + set fd [open t1.txt wb] + puts $fd ".read t1.txt" + close $fd + catchcmd ":memory:" ".read t1.txt" -} {1 {Input nesting limit (25) reached at line 1. Watch recursion.}} ++} {1 {Input nesting limit (25) reached, from line 0 of "t1.txt", ++ from "t1.txt" from "t1.txt" from "t1.txt" ... ++Check recursion.}} + finish_test