From: drh <> Date: Sat, 13 Dec 2025 21:11:49 +0000 (+0000) Subject: In the CLI, if a command-line argument names a file and the filename ends X-Git-Tag: artiphishell~99 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5266818aed7a2a9a76e8053e36cf465c4fe7ab5c;p=thirdparty%2Fsqlite.git In the CLI, if a command-line argument names a file and the filename ends with .sql or .txt, then process that file as an SQL script. Use this feature for better testing. FossilOrigin-Name: 19cc0522e2255f21f6fb6af1442c2ba122fdd4deacd9ea2c181ce9e597b88770 --- diff --git a/manifest b/manifest index 0436699d8e..5716b342a6 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Back\sout\sthe\s--compat\soption\sfrom\sthe\sCLI. -D 2025-12-13T00:13:40.858 +C In\sthe\sCLI,\sif\sa\scommand-line\sargument\snames\sa\sfile\sand\sthe\sfilename\sends\nwith\s.sql\sor\s.txt,\sthen\sprocess\sthat\sfile\sas\san\sSQL\sscript.\s\sUse\sthis\nfeature\sfor\sbetter\stesting. +D 2025-12-13T21:11:49.724 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -737,7 +737,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 8d53771eb51a4ab5f970150c3a70969d8db79cd04a8774c2d296bbcf471a0dd0 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 344518c1bba9c4636bf651b7642304abd2e7075ba35feb4bae42a51e5efe991f -F src/shell.c.in 000a5c12e353b59b0a9276a7ff577804ba98f663528f818b91f8d25ea639fdee +F src/shell.c.in e885e048418c366c5aa3252bb83bb0a61297054fdf829bc2283fb4fdd0e9bebb F src/sqlite.h.in 706cacea5308b0244fb6cec92e08310fb427a125375c64137cc1f878ae4cf5c0 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5d5330f5f8461f5ce74960436ddcfa53ecd09c2b8b23901e22ae38aec3243998 @@ -1038,7 +1038,7 @@ F test/dbfuzz.c fc566102f72c8af84ae8077b4faf7f056c571e6fa7a32e98b66e42b7505f47b6 F test/dbfuzz001.test 6c9a4622029d69dc38926f115864b055cb2f39badd25ec22cbfb130c8ba8e9c3 F test/dbfuzz2-seed1.db e6225c6f3d7b63f9c5b6867146a5f329d997ab105bee64644dc2b3a2f2aebaee F test/dbfuzz2.c 4b3c12de4d98b1b2d908ab03d217d4619e47c8b23d5e67f8a6f2b1bdee7cae23 -F test/dblwidth-a.sql eb4141518610e52f931a55a984310075e98dc31eee5a28ae806b1e35377be85a +F test/dblwidth-a.sql 59dd59aa78ce8fd8ab631a3816516831f4e947b143039257e6fe132c3cea4171 F test/dbpage.test 63fab1eb026bada121107e53436fa749bbf83281dc9dea17af422f7a5c0f289f F test/dbpagefault.test ea39de2ca86041a9c6df1135645180a76d0a8da93ac159e2fafe38e39636530b F test/dbstatus.test 4a4221a883025ffd39696b3d1b3910b928fb097d77e671351acb35f3aed42759 @@ -1441,7 +1441,7 @@ F test/mmap4.test 2e2b4e32555b58da15176e6fe750f17c9dcf7f93 F test/mmapcorrupt.test 470fb44fe92e99c1d23701d156f8c17865f5b027063c9119dcfdb842791f4465 F test/mmapfault.test d4c9eff9cd8c2dc14bc43e71e042f175b0a26fe3 F test/mmapwarm.test 2272005969cd17a910077bd5082f70bc1fefad9a875afec7fc9af483898ecaf3 -F test/modeA.clitest 2db42f2814ba45d5e886fead881360e689bc27346cfc37e6bb46f63334bebc61 +F test/modeA.sql a804e7db0f672007a54f5b890737de7376ac4d08c6e309347e1c93be5d533c5c w test/modeA.clitest F test/multiplex.test d74c034e52805f6de8cc5432cef8c9eb774bb64ec29b83a22effc8ca4dac1f08 F test/multiplex2.test 580ca5817c7edbe4cc68fa150609c9473393003a F test/multiplex3.test fac575e0b1b852025575a6a8357701d80933e98b5d2fe6d35ddaa68f92f6a1f7 @@ -1620,7 +1620,7 @@ F test/shell7.test 43fd8e511c533bab5232e95c7b4be93b243451709e89582600d4b6e67693d F test/shell8.test 641cf21a99c59404c24e3062923734951c4099a6b6b6520de00cf7a1249ee871 F test/shell9.test 8742a5b390cdcef6369f5aa223e415aa4255a4129ef249b177887dc635a87209 F test/shellA.test 05cdaafa1f79913654487ce3aefa038d4106245d58f52e02faf506140a76d480 -F test/shellB.test de879b1ea7c25daf1a06b2c882b45a5d002e6580c81c57169ce47084cc6afb6b +F test/shellB.test 41730c85658e1caa7e421454deefb3837fbfcd68be13a2a2f3a1ed92342fb0a4 F test/shmlock.test 9f1f729a7fe2c46c88b156af819ac9b72c0714ac6f7246638a73c5752b5fd13c F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3 F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5 @@ -2184,8 +2184,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 1ca31e1a297c0d53d068afb658ce6602887cda0f3eaf42cd629c4d1b7204f0b0 -R cf0d7b71ff1f31fdee2bbe0931020fd5 +P 850d5dbfb0f1eacd1e5213759810ec7e5eba4fcc0b2718dabccd5b269b126f1c +R 7d316ab9e616e989cd7843c90446decc U drh -Z b8e82ce5183ee7bea000d8d3bf76900c +Z 1fff9a15000a4bc11ad069c242a3fa0b # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3126c25310..9919cbe856 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -850d5dbfb0f1eacd1e5213759810ec7e5eba4fcc0b2718dabccd5b269b126f1c +19cc0522e2255f21f6fb6af1442c2ba122fdd4deacd9ea2c181ce9e597b88770 diff --git a/src/shell.c.in b/src/shell.c.in index 86f41dc865..0a26fbde18 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -4122,6 +4122,52 @@ static int session_filter(void *pCtx, const char *zTab){ } #endif +/* +** Return the size of the named file in bytes. Or return a negative +** number if the file does not exist. +*/ +static sqlite3_int64 fileSize(const char *zFile){ +#if defined(_WIN32) || defined(WIN32) + struct _stat64 x; + if( _stat64(zFile, &x)!=0 ) return -1; + return (sqlite3_int64)x.st_size; +#else + struct stat x; + if( stat(zFile, &x)!=0 ) return -1; + return (sqlite3_int64)x.st_size; +#endif +} + +/* +** Return true if zFile is an SQLite database. +** +** Algorithm: +** * If the file does not exist -> return false +** * If the size of the file is not a multiple of 512 -> return false +** * If sqlite3_open() fails -> return false +** * if sqlite3_prepare() or sqlite3_step() fails -> return false +** * Otherwise -> return true +*/ +static int isDatabaseFile(const char *zFile, int openFlags){ + sqlite3 *db = 0; + sqlite3_stmt *pStmt = 0; + int rc; + sqlite3_int64 sz = fileSize(zFile); + if( sz<512 || (sz%512)!=0 ) return 0; + if( sqlite3_open_v2(zFile, &db, openFlags, 0)==SQLITE_OK + && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) + ==SQLITE_OK + && sqlite3_step(pStmt)==SQLITE_ROW + ){ + rc = 1; + }else{ + rc = 0; + } + sqlite3_finalize(pStmt); + sqlite3_close(db); + return rc; +} + /* ** Try to deduce the type of file for zName based on its content. Return ** one of the SHELL_OPEN_* constants. @@ -4134,20 +4180,12 @@ static int session_filter(void *pCtx, const char *zTab){ int deduceDatabaseType(const char *zName, int dfltZip, int openFlags){ FILE *f; size_t n; - sqlite3 *db = 0; - sqlite3_stmt *pStmt = 0; int rc = SHELL_OPEN_UNSPEC; char zBuf[100]; if( access(zName,0)!=0 ) goto database_type_by_name; - if( sqlite3_open_v2(zName, &db, openFlags, 0)==SQLITE_OK - && sqlite3_prepare_v2(db,"SELECT count(*) FROM sqlite_schema",-1,&pStmt,0) - ==SQLITE_OK - && sqlite3_step(pStmt)==SQLITE_ROW - ){ + if( isDatabaseFile(zName, openFlags) ){ rc = SHELL_OPEN_NORMAL; } - sqlite3_finalize(pStmt); - sqlite3_close(db); if( rc==SHELL_OPEN_NORMAL ) return SHELL_OPEN_NORMAL; f = sqlite3_fopen(zName, "rb"); if( f==0 ) goto database_type_by_name; @@ -4182,6 +4220,33 @@ database_type_by_name: return rc; } +/* +** If the text in z[] is the name of a readable file and that file appears +** to contain SQL text and/or dot-commands, then return true. If z[] is +** not a file, or if the file is unreadable, or if the file is a database +** or anything else that is not SQL text and dot-commands, then return false. +** +** If the bLeaveUninit flag is set, then be sure to leave SQLite in an +** uninitialized state. This means invoking sqlite3_shutdown() after any +** SQLite API is used. +** +** Some amount of guesswork is involved in this decision. +*/ +static int isScriptFile(const char *z, int bLeaveUninit){ + sqlite3_int64 sz = fileSize(z); + if( sz<=0 ) return 0; + if( (sz%512)==0 ){ + int rc = isDatabaseFile(z, SQLITE_OPEN_READONLY); + if( bLeaveUninit ){ + sqlite3_shutdown(); + } + if( rc ) return 0; /* Is a database */ + } + if( sqlite3_strlike("*.sql",z,0)==0 ) return 1; + if( sqlite3_strlike("*.txt",z,0)==0 ) return 1; + return 0; +} + #ifndef SQLITE_OMIT_DESERIALIZE /* ** Reconstruct an in-memory database using the output from the "dbtotxt" @@ -12635,7 +12700,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ } #endif - /* Do an initial pass through the command-line argument to locate + /* Do an initial pass through the command-line arguments to locate ** the name of the database file, the name of the initialization file, ** the size of the alternative malloc heap, options affecting commands ** or SQL run from the command line, and the first command to execute. @@ -12647,7 +12712,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ char *z; z = argv[i]; if( z[0]!='-' || i>nOptsEnd ){ - if( data.aAuxDb->zDbFilename==0 ){ + if( data.aAuxDb->zDbFilename==0 && !isScriptFile(z,1) ){ data.aAuxDb->zDbFilename = z; }else{ /* Excess arguments are interpreted as SQL (or dot-commands) and @@ -12882,7 +12947,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ */ if( !noInit ) process_sqliterc(&data,zInitFile); - /* Make a second pass through the command-line argument and set + /* Make a second pass through the command-line arguments and set ** options. This second pass is delayed until after the initialization ** file is processed so that the command-line arguments will override ** settings in the initialization file. @@ -13107,7 +13172,17 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ */ for(i=0; i'); -.print .mode box SELECT * FROM data; +.check < │ +╰────────────┴──────────────────────────────╯ +END + +.testcase 200 .mode table -.print .mode table SELECT * FROM data; +.check < | ++------------+------------------------------+ +END + +.testcase 300 .mode qbox -.print .mode qbox SELECT * FROM data; +.check <' │ +╰──────────────┴────────────────────────────────╯ +END + +.testcase 400 .mode column -.print .mode column SELECT * FROM data; +.check < +END diff --git a/test/modeA.clitest b/test/modeA.sql similarity index 99% rename from test/modeA.clitest rename to test/modeA.sql index 2dcbf0c45c..7a9c476029 100644 --- a/test/modeA.clitest +++ b/test/modeA.sql @@ -1,3 +1,5 @@ +#!sqlite3 +# # 2025-11-12 # # The author disclaims copyright to this source code. In place of diff --git a/test/shellB.test b/test/shellB.test index f71c02e2a4..d0717a6e54 100644 --- a/test/shellB.test +++ b/test/shellB.test @@ -39,6 +39,7 @@ proc do_clitest {name} { do_test shellB-$name $script {error count: 0} } -do_clitest modeA.clitest +do_clitest modeA.sql +do_clitest dblwidth-a.sql finish_test