summaryrefslogtreecommitdiffstats
path: root/src/sql_commands.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/sql_commands.cc273
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