/** * 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 sql_cmd_dump(exec_context& ec, std::string cmdline, std::vector& args) { static auto& lnav_db = injector::get(); static auto& lnav_flags = injector::get(); 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(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 sql_cmd_read(exec_context& ec, std::string cmdline, std::vector& args) { static auto& lnav_db = injector::get(); static auto& lnav_flags = injector::get(); 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 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 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 sql_cmd_schema(exec_context& ec, std::string cmdline, std::vector& args) { std::string retval; if (args.empty()) { return Ok(retval); } ensure_view(LNV_SCHEMA); return Ok(retval); } static Result sql_cmd_generic(exec_context& ec, std::string cmdline, std::vector& 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::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