]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add the --memory option to the ".parameter init" CLI command. When present,
authordrh <>
Thu, 8 Aug 2024 10:10:38 +0000 (10:10 +0000)
committerdrh <>
Thu, 8 Aug 2024 10:10:38 +0000 (10:10 +0000)
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

manifest
manifest.uuid
src/shell.c.in

index 49d8819fe60148675d822cfff3a4aaafc82eef6a..587d9af3bbb6ac7948d42d8f58253c236105d365 100644 (file)
--- 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.
index a97e2bbfc93fd5a5c170c990e629d452e569065a..950203b129ad23a17e12934e5a4da9f5adc2aa78 100644 (file)
@@ -1 +1 @@
-08cc5488404d068e59378b82988460793710df43ec21b4a83a794b497abd035f
+4e69dce2093b75b7db4fbdca4953b664b907be15d991ed352ea1d87c64fbf9d2
index 4434a97ababb59094e2cad66ebcaf148dd608f2c..2d6c76d3b0f93e5d175adcff5862c52ba6882861 100644 (file)
@@ -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<ArraySize(data.aAuxDb); i++){
     sqlite3_free(data.aAuxDb[i].zFreeOnClose);
     if( data.aAuxDb[i].db ){
       session_close_all(&data, i);
-      close_db(data.aAuxDb[i].db);
+      close_db(data.aAuxDb[i].db, 0);
     }
   }
   find_home_dir(1);