diff options
Diffstat (limited to '')
-rw-r--r-- | src/sql_commands.cc | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/src/sql_commands.cc b/src/sql_commands.cc new file mode 100644 index 0000000..e145331 --- /dev/null +++ b/src/sql_commands.cc @@ -0,0 +1,273 @@ +/** + * Copyright (c) 2021, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "base/auto_mem.hh" +#include "base/fs_util.hh" +#include "base/injector.bind.hh" +#include "base/lnav_log.hh" +#include "bound_tags.hh" +#include "command_executor.hh" +#include "config.h" +#include "readline_context.hh" +#include "shlex.hh" +#include "sqlite-extension-func.hh" +#include "sqlitepp.hh" +#include "view_helpers.hh" + +static Result<std::string, lnav::console::user_message> +sql_cmd_dump(exec_context& ec, + std::string cmdline, + std::vector<std::string>& args) +{ + static auto& lnav_db = injector::get<auto_sqlite3&>(); + static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>(); + + std::string retval; + + if (args.empty()) { + args.emplace_back("filename"); + args.emplace_back("tables"); + return Ok(retval); + } + + if (args.size() < 2) { + return ec.make_error("expecting a file name to write to"); + } + + if (lnav_flags & LNF_SECURE_MODE) { + return ec.make_error("{} -- unavailable in secure mode", args[0]); + } + + auto_mem<FILE> file(fclose); + + if ((file = fopen(args[1].c_str(), "w+")) == nullptr) { + return ec.make_error( + "unable to open '{}' for writing: {}", args[1], strerror(errno)); + } + + for (size_t lpc = 2; lpc < args.size(); lpc++) { + sqlite3_db_dump(lnav_db.in(), + "main", + args[lpc].c_str(), + (int (*)(const char*, void*)) fputs, + file.in()); + } + + retval = "generated"; + return Ok(retval); +} + +static Result<std::string, lnav::console::user_message> +sql_cmd_read(exec_context& ec, + std::string cmdline, + std::vector<std::string>& args) +{ + static auto& lnav_db = injector::get<auto_sqlite3&>(); + static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>(); + + std::string retval; + + if (args.empty()) { + args.emplace_back("filename"); + return Ok(retval); + } + + if (lnav_flags & LNF_SECURE_MODE) { + return ec.make_error("{} -- unavailable in secure mode", args[0]); + } + + std::vector<std::string> split_args; + shlex lexer(cmdline); + + if (!lexer.split(split_args, ec.create_resolver())) { + return ec.make_error("unable to parse arguments"); + } + + for (size_t lpc = 1; lpc < split_args.size(); lpc++) { + auto read_res = lnav::filesystem::read_file(split_args[lpc]); + + if (read_res.isErr()) { + return ec.make_error("unable to read script file: {} -- {}", + split_args[lpc], + read_res.unwrapErr()); + } + + auto script = read_res.unwrap(); + auto_mem<sqlite3_stmt> stmt(sqlite3_finalize); + const char* start = script.c_str(); + + do { + const char* tail; + auto rc = sqlite3_prepare_v2( + lnav_db.in(), start, -1, stmt.out(), &tail); + + if (rc != SQLITE_OK) { + const char* errmsg = sqlite3_errmsg(lnav_db.in()); + + return ec.make_error("{}", errmsg); + } + + if (stmt.in() != nullptr) { + std::string alt_msg; + auto exec_res = execute_sql( + ec, std::string(start, tail - start), alt_msg); + if (exec_res.isErr()) { + return exec_res; + } + } + + start = tail; + } while (start[0]); + } + + return Ok(retval); +} + +static Result<std::string, lnav::console::user_message> +sql_cmd_schema(exec_context& ec, + std::string cmdline, + std::vector<std::string>& args) +{ + std::string retval; + + if (args.empty()) { + return Ok(retval); + } + + ensure_view(LNV_SCHEMA); + + return Ok(retval); +} + +static Result<std::string, lnav::console::user_message> +sql_cmd_generic(exec_context& ec, + std::string cmdline, + std::vector<std::string>& args) +{ + std::string retval; + + if (args.empty()) { + args.emplace_back("*"); + return Ok(retval); + } + + return Ok(retval); +} + +static readline_context::command_t sql_commands[] = { + { + ".dump", + sql_cmd_dump, + help_text(".dump", "Dump the contents of the database") + .sql_command() + .with_parameter({"path", "The path to the file to write"}) + .with_tags({ + "io", + }), + }, + { + ".msgformats", + sql_cmd_schema, + help_text(".msgformats", "df").sql_command(), + }, + { + ".read", + sql_cmd_read, + help_text(".read", "Execute the SQLite statements in the given file") + .sql_command() + .with_parameter({"path", "The path to the file to write"}) + .with_tags({ + "io", + }), + }, + { + ".schema", + sql_cmd_schema, + help_text(".schema", + "Switch to the SCHEMA view that contains a dump of the " + "current database schema") + .sql_command(), + }, + { + "ATTACH", + sql_cmd_generic, + }, + { + "CREATE", + sql_cmd_generic, + }, + { + "DELETE", + sql_cmd_generic, + }, + { + "DETACH", + sql_cmd_generic, + }, + { + "DROP", + sql_cmd_generic, + }, + { + "INSERT", + sql_cmd_generic, + }, + { + "SELECT", + sql_cmd_generic, + }, + { + "UPDATE", + sql_cmd_generic, + }, + { + "WITH", + sql_cmd_generic, + }, +}; + +static readline_context::command_map_t sql_cmd_map; + +static auto bound_sql_cmd_map + = injector::bind<readline_context::command_map_t, + sql_cmd_map_tag>::to_instance(+[]() { + for (auto& cmd : sql_commands) { + sql_cmd_map[cmd.c_name] = &cmd; + } + + return &sql_cmd_map; + }); + +namespace injector { +template<> +void +force_linking(sql_cmd_map_tag anno) +{ +} +} // namespace injector |