From 9cb02640419614ae3771ebbffce076474380029b Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 28 Feb 2019 20:10:52 +0000 Subject: [PATCH] Add the ".parameter" command to the CLI. FossilOrigin-Name: 1f9fa58541dc974989eee9c9a5d453956f7dbcf42965ece2db2cb5dee3f3f5e2 --- manifest | 12 ++--- manifest.uuid | 2 +- src/shell.c.in | 121 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 4f2d856111..e2da2af99e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\snew\s"bind_fallback"\smethod\sto\sthe\s"sqlite3"\sobject\sin\sthe\sTCL\ninterface. -D 2019-02-28T17:29:19.212 +C Add\sthe\s".parameter"\scommand\sto\sthe\sCLI. +D 2019-02-28T20:10:52.766 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 1ad7263f38329c0ecea543c80f30af839ee714ea77fc391bf1a3fbb919a5b6b5 @@ -516,7 +516,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 09419ad5c432190b69be7c0c326e03abb548a97c2c50675b81b459e1b382d1d2 F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93 F src/select.c c998f694759e37799929e28df8a2649747f8774d4fc233529ab6bda689388e15 -F src/shell.c.in a238f3f80f7085d31056c69930ec13a87872da0d0d08fd561f5aff78cc618f5d +F src/shell.c.in 249c0bf34f7ce272cb17162c297c45ab674a52a5d85193a86191f131196de47f F src/sqlite.h.in 8859e0b45b48d4186fbc466885e508f8272420a349099acdebcdb8d410d54824 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683 @@ -1805,7 +1805,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 00ae0c6c4815366bd2f36bc054b13bc7b568dd0a3caceddf0eba4db33f010ee4 -R 448ca78435be9ef8ac1ee1c7bdd459a0 +P c7f70b6d96338dba201e005104e7f7148c1a8cd767ab05e35b44617c4c797bc5 +R 2fecd2e603ada3a0c6460ef11e6e9c28 U drh -Z 8dd6bd727b5289a2522f6d5168203921 +Z f7dd879e1ba76ea61ec7d6d44725ec74 diff --git a/manifest.uuid b/manifest.uuid index 2283367472..91cf4ac998 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c7f70b6d96338dba201e005104e7f7148c1a8cd767ab05e35b44617c4c797bc5 \ No newline at end of file +1f9fa58541dc974989eee9c9a5d453956f7dbcf42965ece2db2cb5dee3f3f5e2 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 3474c10ca8..df81c2e99c 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -2750,6 +2750,16 @@ static void restore_debug_trace_modes(void){ /* Name of the TEMP table that holds bind parameter values */ #define BIND_PARAM_TABLE "$Parameters" +/* Create the TEMP table used to store parameter bindings */ +static void bind_table_init(ShellState *p){ + sqlite3_exec(p->db, + "CREATE TABLE IF NOT EXISTS temp.[" BIND_PARAM_TABLE "](\n" + " key TEXT PRIMARY KEY,\n" + " value ANY\n" + ") WITHOUT ROWID;", + 0, 0, 0); +} + /* ** Bind parameters on a prepared statement. ** @@ -3547,6 +3557,13 @@ static const char *(azHelp[]) = { " --zip FILE is a ZIP archive", ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", + ".parameter CMD ... Manage SQL parameter bindings", + " clear Erase all bindings", + " init 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 '$', ':', '@', or '?'", + " unset PARAMETER Remove PARAMETER from the binding table", ".print STRING... Print literal STRING", #ifndef SQLITE_OMIT_PROGRESS_CALLBACK ".progress N Invoke progress handler after every N opcodes", @@ -7078,6 +7095,110 @@ static int do_meta_command(char *zLine, ShellState *p){ } }else + if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){ + open_db(p,0); + if( nArg<=1 ) goto parameter_syntax_error; + + /* .parameter clear + ** Clear all bind parameters by dropping the TEMP table that holds them. + */ + if( nArg==2 && strcmp(azArg[1],"clear")==0 ){ + sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp.[" BIND_PARAM_TABLE "];", + 0, 0, 0); + }else + + /* .parameter list + ** List all bind parameters. + */ + if( nArg==2 && strcmp(azArg[1],"list")==0 ){ + sqlite3_stmt *pStmt = 0; + int rx; + int len = 0; + rx = sqlite3_prepare_v2(p->db, + "SELECT max(length(key)) " + "FROM temp.[" BIND_PARAM_TABLE "];", -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.[" BIND_PARAM_TABLE "];", -1, &pStmt, 0); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0), + sqlite3_column_text(pStmt,1)); + } + sqlite3_finalize(pStmt); + } + }else + + /* .parameter init + ** Make sure the TEMP table used to hold bind parameters exists. + ** Create it if necessary. + */ + if( nArg==2 && strcmp(azArg[1],"init")==0 ){ + bind_table_init(p); + }else + + /* .parameter set NAME VALUE + ** Set or reset a bind parameter. NAME should be the full parameter + ** name exactly as it appears in the query. (ex: $abc, @def). The + ** VALUE can be in either SQL literal notation, or if not it will be + ** understood to be a text string. + */ + if( nArg==4 && strcmp(azArg[1],"set")==0 ){ + int rx; + char *zSql; + sqlite3_stmt *pStmt; + const char *zKey = azArg[2]; + const char *zValue = azArg[3]; + bind_table_init(p); + zSql = sqlite3_mprintf( + "REPLACE INTO temp.[" BIND_PARAM_TABLE "](key,value)" + "VALUES(%Q,%s);", zKey, zValue); + if( zSql==0 ) shell_out_of_memory(); + pStmt = 0; + rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rx!=SQLITE_OK ){ + sqlite3_finalize(pStmt); + pStmt = 0; + zSql = sqlite3_mprintf( + "REPLACE INTO temp.[" BIND_PARAM_TABLE "](key,value)" + "VALUES(%Q,%Q);", zKey, zValue); + if( zSql==0 ) shell_out_of_memory(); + rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rx!=SQLITE_OK ){ + utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db)); + sqlite3_finalize(pStmt); + pStmt = 0; + rc = 1; + } + } + sqlite3_step(pStmt); + sqlite3_finalize(pStmt); + }else + + /* .parameter unset NAME + ** Remove the NAME binding from the parameter binding table, if it + ** exists. + */ + if( nArg==3 && strcmp(azArg[1],"unset")==0 ){ + char *zSql = sqlite3_mprintf( + "DELETE FROM temp.[" BIND_PARAM_TABLE "] WHERE key=%Q", azArg[2]); + if( zSql==0 ) shell_out_of_memory(); + sqlite3_exec(p->db, zSql, 0, 0, 0); + sqlite3_free(zSql); + }else + /* If no command name matches, show a syntax error */ + parameter_syntax_error: + showHelp(p->out, "parameter"); + }else + if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){ int i; for(i=1; i