From: drh <> Date: Thu, 8 Aug 2024 10:10:38 +0000 (+0000) Subject: Add the --memory option to the ".parameter init" CLI command. When present, X-Git-Tag: version-3.47.0~252 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6d02d2647b4f1bbc455b252ef8794aa8b5708586;p=thirdparty%2Fsqlite.git Add the --memory option to the ".parameter init" CLI command. When present, the --memory option causes the sqlite_parameters table to be created in a completely independent :memory: database. This can be done to avoid parameter binding queries from being affected by debug settings such as ".wheretrace", ".treetrace", "PRAGMA vdbe_addoptrace=on", and similar. FossilOrigin-Name: 4e69dce2093b75b7db4fbdca4953b664b907be15d991ed352ea1d87c64fbf9d2 --- diff --git a/manifest b/manifest index 49d8819fe6..587d9af3bb 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sa\sbroken\sassert\sin\sfts5_expr.c. -D 2024-08-07T21:20:08.849 +C Add\sthe\s--memory\soption\sto\sthe\s".parameter\sinit"\sCLI\scommand.\s\sWhen\spresent,\nthe\s--memory\soption\scauses\sthe\ssqlite_parameters\stable\sto\sbe\screated\sin\sa\ncompletely\sindependent\s:memory:\sdatabase.\s\sThis\scan\sbe\sdone\sto\savoid\nparameter\sbinding\squeries\sfrom\sbeing\saffected\sby\sdebug\ssettings\ssuch\sas\n".wheretrace",\s".treetrace",\s"PRAGMA\svdbe_addoptrace=on",\sand\ssimilar. +D 2024-08-08T10:10:38.385 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -760,7 +760,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 7e8d23ce7cdbfedf351a47e759f2722e8182ca10fd7580be43f4ce1f1a228145 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 6a95a2bffa6c09584dea99db5a7ae10c813305c09c92920ffc54f6eae2ba399e -F src/shell.c.in 664d443867c2a6d3f17da7dd864af5660012750847a985d0b22ee0c8cd6fc18a +F src/shell.c.in 8b1a82ddbcf93580f991eecec0a22e8edc7d62fa856388d9bed2392da1cbfa42 F src/sqlite.h.in 1ad9110150773c38ebababbad11b5cb361bcd3997676dec1c91ac5e0416a7b86 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54 @@ -2203,8 +2203,8 @@ F vsixtest/vsixtest.tcl 6195aba1f12a5e10efc2b8c0009532167be5e301abe5b31385638080 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 433f2b942ee6f79d50ebe9b08fa3ea8162db6a10ce9d80e2bc193124baa1b083 -R 4344cfe486b9af9c627d27d1110ce2f0 -U dan -Z 57ed22c9ed030aee910e69827579449e +P 08cc5488404d068e59378b82988460793710df43ec21b4a83a794b497abd035f +R d4d35f38c7731eed804015561204e2a7 +U drh +Z 252eafc7dac030f0d53111914f8dbebb # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index a97e2bbfc9..950203b129 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -08cc5488404d068e59378b82988460793710df43ec21b4a83a794b497abd035f +4e69dce2093b75b7db4fbdca4953b664b907be15d991ed352ea1d87c64fbf9d2 diff --git a/src/shell.c.in b/src/shell.c.in index 4434a97aba..2d6c76d3b0 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -1356,6 +1356,8 @@ struct ShellState { #endif } aAuxDb[5], /* Array of all database connections */ *pAuxDb; /* Currently active database connection */ + sqlite3 *dbParam; /* Database to use for query parameters. Often the + ** same as "db", but might be different. */ int *aiIndent; /* Array of indents used in MODE_Explain */ int nIndent; /* Size of array aiIndent[] */ int iIndent; /* Index of current op in aiIndent[] */ @@ -3480,22 +3482,44 @@ static void restore_debug_trace_modes(void){ sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &savedWhereTrace); } -/* Create the TEMP table used to store parameter bindings */ -static void bind_table_init(ShellState *p){ +/* Create the TEMP table used to store parameter bindings. +** +** If bInMemory is true, create a separate in-memory database for +** the parameter binding table. +** +** The database that stores the sqlite_parameter table will be +** p->dbParam. This might be a copy of p->db. Or it might be a +** completely separate database (if bInMemory is true, because of +** the ".param init --memory" command). +*/ +static void bind_table_init(ShellState *p, int bInMemory){ int wrSchema = 0; int defensiveMode = 0; - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &defensiveMode); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, -1, &wrSchema); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0); - sqlite3_exec(p->db, + sqlite3 *dbx; + if( p->dbParam ) return; + if( sqlite3_table_column_metadata(p->db, "TEMP", "sqlite_parameters", + "key", 0, 0, 0, 0, 0)==SQLITE_OK ){ + return; + } + if( bInMemory && p->dbParam==0 ){ + sqlite3_open(":memory:", &p->dbParam); + }else{ + p->dbParam = p->db; + } + dbx = p->dbParam; + + sqlite3_db_config(dbx, SQLITE_DBCONFIG_DEFENSIVE, -1, &defensiveMode); + sqlite3_db_config(dbx, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); + sqlite3_db_config(dbx, SQLITE_DBCONFIG_WRITABLE_SCHEMA, -1, &wrSchema); + sqlite3_db_config(dbx, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0); + sqlite3_exec(dbx, "CREATE TABLE IF NOT EXISTS temp.sqlite_parameters(\n" " key TEXT PRIMARY KEY,\n" " value\n" ") WITHOUT ROWID;", 0, 0, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0); - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, defensiveMode, 0); + sqlite3_db_config(dbx, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0); + sqlite3_db_config(dbx, SQLITE_DBCONFIG_DEFENSIVE, defensiveMode, 0); } /* @@ -3518,12 +3542,14 @@ static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){ nVar = sqlite3_bind_parameter_count(pStmt); if( nVar==0 ) return; /* Nothing to do */ - if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters", - "key", 0, 0, 0, 0, 0)!=SQLITE_OK ){ + if( pArg->dbParam==0 + || sqlite3_table_column_metadata(pArg->dbParam, "TEMP", "sqlite_parameters", + "key", 0, 0, 0, 0, 0)!=SQLITE_OK + ){ rc = SQLITE_NOTFOUND; pQ = 0; }else{ - rc = sqlite3_prepare_v2(pArg->db, + rc = sqlite3_prepare_v2(pArg->dbParam, "SELECT value FROM temp.sqlite_parameters" " WHERE key=?1", -1, &pQ, 0); } @@ -4829,7 +4855,7 @@ static const char *(azHelp[]) = { #endif ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", - " init Initialize the TEMP table that holds bindings", + " init ?--memory? Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", " PARAMETER should start with one of: $ : @ ?", @@ -5485,9 +5511,17 @@ static void open_db(ShellState *p, int openFlags){ /* ** Attempt to close the database connection. Report errors. +** +** If dbParam is not NULL and is different from db, then close it +** too. No error checking is done on the close of dbParam, as it +** should be a :memory: database which cannot really fail on close. */ -void close_db(sqlite3 *db){ - int rc = sqlite3_close(db); +void close_db(sqlite3 *db, sqlite3 *dbParam){ + int rc; + if( dbParam && dbParam!=db ){ + sqlite3_close(dbParam); + } + rc = sqlite3_close(db); if( rc ){ eputf("Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db)); } @@ -6146,7 +6180,7 @@ static void tryToClone(ShellState *p, const char *zNewDb){ sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); } - close_db(newDb); + close_db(newDb, 0); } #ifndef SQLITE_SHELL_FIDDLE @@ -7581,7 +7615,7 @@ static int arDotCommand( } end_ar_command: if( cmd.db!=pState->db ){ - close_db(cmd.db); + close_db(cmd.db, 0); } sqlite3_free(cmd.zSrcTable); @@ -8106,7 +8140,7 @@ static int do_meta_command(char *zLine, ShellState *p){ SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs); if( rc!=SQLITE_OK ){ eputf("Error: cannot open \"%s\"\n", zDestFile); - close_db(pDest); + close_db(pDest, 0); return 1; } if( bAsync ){ @@ -8117,7 +8151,7 @@ static int do_meta_command(char *zLine, ShellState *p){ pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb); if( pBackup==0 ){ eputf("Error: %s\n", sqlite3_errmsg(pDest)); - close_db(pDest); + close_db(pDest, 0); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ){} @@ -8128,7 +8162,7 @@ static int do_meta_command(char *zLine, ShellState *p){ eputf("Error: %s\n", sqlite3_errmsg(pDest)); rc = 1; } - close_db(pDest); + close_db(pDest, 0); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ @@ -8268,7 +8302,7 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = 1; }else if( p->aAuxDb[i].db ){ session_close_all(p, i); - close_db(p->aAuxDb[i].db); + close_db(p->aAuxDb[i].db, 0); p->aAuxDb[i].db = 0; } }else{ @@ -9558,8 +9592,8 @@ static int do_meta_command(char *zLine, ShellState *p){ /* Close the existing database */ session_close_all(p, -1); - close_db(p->db); - p->db = 0; + close_db(p->db, p->dbParam); + p->db = p->dbParam = 0; p->pAuxDb->zDbFilename = 0; sqlite3_free(p->pAuxDb->zFreeOnClose); p->pAuxDb->zFreeOnClose = 0; @@ -9725,8 +9759,15 @@ static int do_meta_command(char *zLine, ShellState *p){ ** Clear all bind parameters by dropping the TEMP table that holds them. */ if( nArg==2 && cli_strcmp(azArg[1],"clear")==0 ){ - sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp.sqlite_parameters;", - 0, 0, 0); + if( p->dbParam==0 ){ + /* no-op */ + }else if( p->dbParam==p->db ){ + sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp.sqlite_parameters;", + 0, 0, 0); + }else{ + sqlite3_close(p->dbParam); + } + p->dbParam = 0; }else /* .parameter list @@ -9736,33 +9777,45 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_stmt *pStmt = 0; int rx; int len = 0; - rx = sqlite3_prepare_v2(p->db, - "SELECT max(length(key)) " - "FROM temp.sqlite_parameters;", -1, &pStmt, 0); - if( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - len = sqlite3_column_int(pStmt, 0); - if( len>40 ) len = 40; - } - sqlite3_finalize(pStmt); - pStmt = 0; - if( len ){ - rx = sqlite3_prepare_v2(p->db, - "SELECT key, quote(value) " - "FROM temp.sqlite_parameters;", -1, &pStmt, 0); - while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ - oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), - sqlite3_column_text(pStmt,1)); + if( p->dbParam ){ + rx = sqlite3_prepare_v2(p->dbParam, + "SELECT max(length(key)) " + "FROM temp.sqlite_parameters;", -1, &pStmt, 0); + if( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ + len = sqlite3_column_int(pStmt, 0); + if( len>40 ) len = 40; } sqlite3_finalize(pStmt); + pStmt = 0; + if( len ){ + rx = sqlite3_prepare_v2(p->db, + "SELECT key, quote(value) " + "FROM temp.sqlite_parameters;", -1, &pStmt, 0); + while( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ + oputf("%-*s %s\n", len, sqlite3_column_text(pStmt,0), + sqlite3_column_text(pStmt,1)); + } + sqlite3_finalize(pStmt); + } } }else - /* .parameter init + /* .parameter init ?--memory? ** Make sure the TEMP table used to hold bind parameters exists. ** Create it if necessary. + ** + ** If the --memory option is specified, the sqlite_parameters table + ** is held in a separate database so that parameter binding queries + ** do not show up in debugging output from .treetrace, .wheretrace, + ** PRAGMA vdbe_addoptrace=on, and similar. */ if( nArg==2 && cli_strcmp(azArg[1],"init")==0 ){ - bind_table_init(p); + bind_table_init(p, 0); + }else + if( nArg==3 && cli_strcmp(azArg[1],"init")==0 + && cli_strcmp(azArg[2],"--memory")==0 + ){ + bind_table_init(p, 1); }else /* .parameter set NAME VALUE @@ -9777,28 +9830,30 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_stmt *pStmt; const char *zKey = azArg[2]; const char *zValue = azArg[3]; - bind_table_init(p); - zSql = sqlite3_mprintf( - "REPLACE INTO temp.sqlite_parameters(key,value)" - "VALUES(%Q,%s);", zKey, zValue); - shell_check_oom(zSql); - pStmt = 0; - rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rx!=SQLITE_OK ){ - sqlite3_finalize(pStmt); - pStmt = 0; + bind_table_init(p, 0); + if( p->dbParam ){ zSql = sqlite3_mprintf( - "REPLACE INTO temp.sqlite_parameters(key,value)" - "VALUES(%Q,%Q);", zKey, zValue); + "REPLACE INTO temp.sqlite_parameters(key,value)" + "VALUES(%Q,%s);", zKey, zValue); shell_check_oom(zSql); - rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + pStmt = 0; + rx = sqlite3_prepare_v2(p->dbParam, zSql, -1, &pStmt, 0); sqlite3_free(zSql); if( rx!=SQLITE_OK ){ - oputf("Error: %s\n", sqlite3_errmsg(p->db)); sqlite3_finalize(pStmt); pStmt = 0; - rc = 1; + zSql = sqlite3_mprintf( + "REPLACE INTO temp.sqlite_parameters(key,value)" + "VALUES(%Q,%Q);", zKey, zValue); + shell_check_oom(zSql); + rx = sqlite3_prepare_v2(p->dbParam, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rx!=SQLITE_OK ){ + oputf("Error: %s\n", sqlite3_errmsg(p->db)); + sqlite3_finalize(pStmt); + pStmt = 0; + rc = 1; + } } } sqlite3_step(pStmt); @@ -9813,7 +9868,7 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zSql = sqlite3_mprintf( "DELETE FROM temp.sqlite_parameters WHERE key=%Q", azArg[2]); shell_check_oom(zSql); - sqlite3_exec(p->db, zSql, 0, 0, 0); + if( p->dbParam ) sqlite3_exec(p->dbParam, zSql, 0, 0, 0); sqlite3_free(zSql); }else /* If no command name matches, show a syntax error */ @@ -9951,14 +10006,14 @@ static int do_meta_command(char *zLine, ShellState *p){ rc = sqlite3_open(zSrcFile, &pSrc); if( rc!=SQLITE_OK ){ eputf("Error: cannot open \"%s\"\n", zSrcFile); - close_db(pSrc); + close_db(pSrc, 0); return 1; } open_db(p, 0); pBackup = sqlite3_backup_init(p->db, zDb, pSrc, "main"); if( pBackup==0 ){ eputf("Error: %s\n", sqlite3_errmsg(p->db)); - close_db(pSrc); + close_db(pSrc, 0); return 1; } while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK @@ -9978,7 +10033,7 @@ static int do_meta_command(char *zLine, ShellState *p){ eputf("Error: %s\n", sqlite3_errmsg(p->db)); rc = 1; } - close_db(pSrc); + close_db(pSrc, 0); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ @@ -12845,13 +12900,13 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){ set_table_name(&data, 0); if( data.db ){ session_close_all(&data, -1); - close_db(data.db); + close_db(data.db, data.dbParam); } for(i=0; i