From 1c3bb969e918e73e1f7714c79adf8dade5b5d54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Budiseli=C4=87?= Date: Fri, 21 Oct 2022 13:34:13 +0200 Subject: [PATCH] Decouple interactive planning manual test (#585) --- tests/manual/CMakeLists.txt | 8 +- .../db_accessor.hpp} | 280 +----------------- tests/manual/interactive/plan.hpp | 70 +++++ tests/manual/interactive/planning.cpp | 158 ++++++++++ .../planning.hpp} | 13 +- tests/manual/interactive/readline.hpp | 65 ++++ tests/manual/interactive/timer.hpp | 57 ++++ tests/manual/query_execution_dummy.cpp | 183 ++++++++++++ tests/manual/query_planner.cpp | 2 +- 9 files changed, 547 insertions(+), 289 deletions(-) rename tests/manual/{interactive_planning.cpp => interactive/db_accessor.hpp} (51%) create mode 100644 tests/manual/interactive/plan.hpp create mode 100644 tests/manual/interactive/planning.cpp rename tests/manual/{interactive_planning.hpp => interactive/planning.hpp} (80%) create mode 100644 tests/manual/interactive/readline.hpp create mode 100644 tests/manual/interactive/timer.hpp create mode 100644 tests/manual/query_execution_dummy.cpp diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index 0a46b8e60..ea680105c 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -35,12 +35,18 @@ target_link_libraries(${test_prefix}kvstore_console mg-kvstore gflags mg-utils) add_manual_test(query_hash.cpp) target_link_libraries(${test_prefix}query_hash mg-query) -add_manual_test(query_planner.cpp interactive_planning.cpp) +add_manual_test(query_planner.cpp interactive/planning.cpp) target_link_libraries(${test_prefix}query_planner mg-query) if (READLINE_FOUND) target_link_libraries(${test_prefix}query_planner readline) endif() +add_manual_test(query_execution_dummy.cpp) +target_link_libraries(${test_prefix}query_execution_dummy mg-query) +if (READLINE_FOUND) + target_link_libraries(${test_prefix}query_execution_dummy readline) +endif() + add_manual_test(expression_pretty_printer.cpp) target_link_libraries(${test_prefix}expression_pretty_printer mg-query) diff --git a/tests/manual/interactive_planning.cpp b/tests/manual/interactive/db_accessor.hpp similarity index 51% rename from tests/manual/interactive_planning.cpp rename to tests/manual/interactive/db_accessor.hpp index 09566e11b..1001d81b2 100644 --- a/tests/manual/interactive_planning.cpp +++ b/tests/manual/interactive/db_accessor.hpp @@ -9,82 +9,13 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -#include "interactive_planning.hpp" +#pragma once -#include -#include -#include -#include - -#include - -#include "query/context.hpp" #include "query/db_accessor.hpp" -#include "query/frontend/ast/cypher_main_visitor.hpp" -#include "query/frontend/opencypher/parser.hpp" -#include "query/frontend/semantic/symbol_generator.hpp" -#include "query/plan/operator.hpp" -#include "query/plan/planner.hpp" -#include "query/plan/pretty_print.hpp" -#include "query/typed_value.hpp" -#include "storage/v2/property_value.hpp" -#include "utils/string.hpp" +#include "readline.hpp" +#include "timer.hpp" -DEFINE_string(save_mock_db_file, "", "File where the mock database should be saved (on exit)"); - -DEFINE_string(load_mock_db_file, "", "File from which the mock database should be loaded"); - -#ifdef HAS_READLINE -// TODO: This should probably be moved to some utils file. - -#include "readline/history.h" -#include "readline/readline.h" - -/** - * Helper function that reads a line from the - * standard input using the 'readline' lib. - * Adds support for history and reverse-search. - * - * @param prompt The prompt to display. - * @return A single command the user entered, or nullopt on EOF. - */ -std::optional ReadLine(const std::string &prompt) { - char *line = readline(prompt.c_str()); - if (!line) return std::nullopt; - - if (*line) add_history(line); - std::string r_val(line); - free(line); - return r_val; -} - -#else - -std::optional ReadLine(const std::string &prompt) { - std::cout << prompt; - std::string line; - std::getline(std::cin, line); - if (std::cin.eof()) return std::nullopt; - return line; -} - -#endif // HAS_READLINE - -// Repeats the prompt untile the user inputs an integer. -int64_t ReadInt(const std::string &prompt) { - int64_t val = 0; - std::stringstream ss; - do { - auto line = ReadLine(prompt); - if (!line) continue; - ss.str(*line); - ss.clear(); - ss >> val; - } while (ss.fail() || !ss.eof()); - return val; -} - -bool AskYesNo(const std::string &prompt) { +inline bool AskYesNo(const std::string &prompt) { while (auto line = ReadLine(prompt + " (y/n) ")) { if (*line == "y" || *line == "Y") return true; if (*line == "n" || *line == "N") return false; @@ -92,48 +23,6 @@ bool AskYesNo(const std::string &prompt) { return false; } -class Timer { - public: - void Start() { - duration_ = duration_.zero(); - start_time_ = std::chrono::steady_clock::now(); - } - - void Pause() { - if (pause_ == 0) { - duration_ += std::chrono::steady_clock::now() - start_time_; - } - ++pause_; - } - - void Resume() { - if (pause_ == 1) { - start_time_ = std::chrono::steady_clock::now(); - } - pause_ = std::max(0, pause_ - 1); - } - - template - auto WithPause(const TFun &fun) { - Pause(); - auto ret = fun(); - Resume(); - return std::move(ret); - } - - std::chrono::duration Elapsed() { - if (pause_ == 0) { - return duration_ + (std::chrono::steady_clock::now() - start_time_); - } - return duration_; - } - - private: - std::chrono::duration duration_; - std::chrono::time_point start_time_; - int pause_ = 0; -}; - // Dummy DbAccessor which forwards user input for various vertex counts. class InteractiveDbAccessor { public: @@ -343,164 +232,3 @@ class InteractiveDbAccessor { return memgraph::storage::PropertyValue(val); } }; - -DEFCOMMAND(Top) { - int64_t n_plans = 0; - std::stringstream ss(args[0]); - ss >> n_plans; - if (ss.fail() || !ss.eof()) return; - n_plans = std::min(static_cast(plans.size()), n_plans); - for (int64_t i = 0; i < n_plans; ++i) { - std::cout << "---- Plan #" << i << " ---- " << std::endl; - std::cout << "cost: " << plans[i].cost << std::endl; - memgraph::query::plan::PrettyPrint(dba, plans[i].final_plan.get()); - std::cout << std::endl; - } -} - -DEFCOMMAND(Show) { - int64_t plan_ix = 0; - std::stringstream ss(args[0]); - ss >> plan_ix; - if (ss.fail() || !ss.eof() || plan_ix >= plans.size()) return; - const auto &plan = plans[plan_ix].final_plan; - auto cost = plans[plan_ix].cost; - std::cout << "Plan cost: " << cost << std::endl; - memgraph::query::plan::PrettyPrint(dba, plan.get()); -} - -DEFCOMMAND(ShowUnoptimized) { - int64_t plan_ix = 0; - std::stringstream ss(args[0]); - ss >> plan_ix; - if (ss.fail() || !ss.eof() || plan_ix >= plans.size()) return; - const auto &plan = plans[plan_ix].unoptimized_plan; - memgraph::query::plan::PrettyPrint(dba, plan.get()); -} - -DEFCOMMAND(Help); - -std::map commands = { - {"top", {TopCommand, 1, "Show top N plans"}}, - {"show", {ShowCommand, 1, "Show the Nth plan"}}, - {"show-unoptimized", {ShowUnoptimizedCommand, 1, "Show the Nth plan in its original, unoptimized form"}}, - {"help", {HelpCommand, 0, "Show available commands"}}, -}; - -void AddCommand(const std::string &name, const Command &command) { commands[name] = command; } - -DEFCOMMAND(Help) { - std::cout << "Available commands:" << std::endl; - for (const auto &command : commands) { - std::cout << command.first; - for (int i = 1; i <= command.second.arg_count; ++i) { - std::cout << " arg" << i; - } - std::cout << " -- " << command.second.documentation << std::endl; - } -} - -void ExaminePlans(memgraph::query::DbAccessor *dba, const memgraph::query::SymbolTable &symbol_table, - std::vector &plans, const memgraph::query::AstStorage &ast) { - while (true) { - auto line = ReadLine("plan? "); - if (!line || *line == "quit") break; - auto words = memgraph::utils::Split(memgraph::utils::ToLowerCase(*line)); - if (words.empty()) continue; - auto command_name = words[0]; - std::vector args(words.begin() + 1, words.end()); - auto command_it = commands.find(command_name); - if (command_it == commands.end()) { - std::cout << "Undefined command: '" << command_name << "'. Try 'help'." << std::endl; - continue; - } - const auto &command = command_it->second; - if (args.size() < command.arg_count) { - std::cout << command_name << " expects " << command.arg_count << " arguments" << std::endl; - continue; - } - command.function(*dba, symbol_table, plans, args, ast); - } -} - -memgraph::query::Query *MakeAst(const std::string &query, memgraph::query::AstStorage *storage) { - memgraph::query::frontend::ParsingContext parsing_context; - parsing_context.is_query_cached = false; - // query -> AST - auto parser = std::make_unique(query); - // AST -> high level tree - memgraph::query::frontend::CypherMainVisitor visitor(parsing_context, storage); - visitor.visit(parser->tree()); - return visitor.query(); -} - -// Returns a list of InteractivePlan instances, sorted in the ascending order by -// cost. -auto MakeLogicalPlans(memgraph::query::CypherQuery *query, memgraph::query::AstStorage &ast, - memgraph::query::SymbolTable &symbol_table, InteractiveDbAccessor *dba) { - auto query_parts = memgraph::query::plan::CollectQueryParts(symbol_table, ast, query); - std::vector interactive_plans; - auto ctx = memgraph::query::plan::MakePlanningContext(&ast, &symbol_table, query, dba); - if (query_parts.query_parts.size() <= 0) { - std::cerr << "Failed to extract query parts" << std::endl; - std::exit(EXIT_FAILURE); - } - memgraph::query::Parameters parameters; - memgraph::query::plan::PostProcessor post_process(parameters); - auto plans = memgraph::query::plan::MakeLogicalPlanForSingleQuery( - query_parts.query_parts.at(0).single_query_parts, &ctx); - for (auto plan : plans) { - memgraph::query::AstStorage ast_copy; - auto unoptimized_plan = plan->Clone(&ast_copy); - auto rewritten_plan = post_process.Rewrite(std::move(plan), &ctx); - double cost = post_process.EstimatePlanCost(rewritten_plan, dba); - interactive_plans.push_back( - InteractivePlan{std::move(unoptimized_plan), std::move(ast_copy), std::move(rewritten_plan), cost}); - } - std::stable_sort(interactive_plans.begin(), interactive_plans.end(), - [](const auto &a, const auto &b) { return a.cost < b.cost; }); - return interactive_plans; -} - -void RunInteractivePlanning(memgraph::query::DbAccessor *dba) { - std::string in_db_filename(memgraph::utils::Trim(FLAGS_load_mock_db_file)); - if (!in_db_filename.empty() && !std::filesystem::exists(in_db_filename)) { - std::cerr << "File '" << in_db_filename << "' does not exist!" << std::endl; - std::exit(EXIT_FAILURE); - } - Timer planning_timer; - InteractiveDbAccessor interactive_db(dba, in_db_filename.empty() ? ReadInt("Vertices in DB: ") : 0, planning_timer); - if (!in_db_filename.empty()) { - std::ifstream db_file(in_db_filename); - interactive_db.Load(db_file); - } - while (true) { - auto line = ReadLine("query? "); - if (!line || *line == "quit") break; - if (line->empty()) continue; - try { - memgraph::query::AstStorage ast; - auto *query = dynamic_cast(MakeAst(*line, &ast)); - if (!query) { - throw memgraph::utils::BasicException( - "Interactive planning is only avaialable for regular openCypher " - "queries."); - } - auto symbol_table = memgraph::query::MakeSymbolTable(query); - planning_timer.Start(); - auto plans = MakeLogicalPlans(query, ast, symbol_table, &interactive_db); - auto planning_time = planning_timer.Elapsed(); - std::cout << "Planning took " << std::chrono::duration(planning_time).count() << "ms" - << std::endl; - std::cout << "Generated " << plans.size() << " plans" << std::endl; - ExaminePlans(dba, symbol_table, plans, ast); - } catch (const memgraph::utils::BasicException &e) { - std::cout << "Error: " << e.what() << std::endl; - } - } - std::string db_filename(memgraph::utils::Trim(FLAGS_save_mock_db_file)); - if (!db_filename.empty()) { - std::ofstream db_file(db_filename); - interactive_db.Save(db_file); - } -} diff --git a/tests/manual/interactive/plan.hpp b/tests/manual/interactive/plan.hpp new file mode 100644 index 000000000..6b4124da6 --- /dev/null +++ b/tests/manual/interactive/plan.hpp @@ -0,0 +1,70 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include + +#include "query/frontend/ast/cypher_main_visitor.hpp" +#include "query/frontend/opencypher/parser.hpp" +#include "query/plan/planner.hpp" + +#include "db_accessor.hpp" + +struct InteractivePlan { + // Original plan after going only through the RuleBasedPlanner. + std::unique_ptr unoptimized_plan; + // Storage for the AST used in unoptimized_plan + memgraph::query::AstStorage ast_storage; + // Final plan after being rewritten and optimized. + std::unique_ptr final_plan; + // Cost of the final plan. + double cost; +}; + +inline memgraph::query::Query *MakeAst(const std::string &query, memgraph::query::AstStorage *storage) { + memgraph::query::frontend::ParsingContext parsing_context; + parsing_context.is_query_cached = false; + // query -> AST + auto parser = std::make_unique(query); + // AST -> high level tree + memgraph::query::frontend::CypherMainVisitor visitor(parsing_context, storage); + visitor.visit(parser->tree()); + return visitor.query(); +} + +// Returns a list of InteractivePlan instances, sorted in the ascending order by +// cost. +inline auto MakeLogicalPlans(memgraph::query::CypherQuery *query, memgraph::query::AstStorage &ast, + memgraph::query::SymbolTable &symbol_table, InteractiveDbAccessor *dba) { + auto query_parts = memgraph::query::plan::CollectQueryParts(symbol_table, ast, query); + std::vector interactive_plans; + auto ctx = memgraph::query::plan::MakePlanningContext(&ast, &symbol_table, query, dba); + if (query_parts.query_parts.size() <= 0) { + std::cerr << "Failed to extract query parts" << std::endl; + std::exit(EXIT_FAILURE); + } + memgraph::query::Parameters parameters; + memgraph::query::plan::PostProcessor post_process(parameters); + auto plans = memgraph::query::plan::MakeLogicalPlanForSingleQuery( + query_parts.query_parts.at(0).single_query_parts, &ctx); + for (auto plan : plans) { + memgraph::query::AstStorage ast_copy; + auto unoptimized_plan = plan->Clone(&ast_copy); + auto rewritten_plan = post_process.Rewrite(std::move(plan), &ctx); + double cost = post_process.EstimatePlanCost(rewritten_plan, dba); + interactive_plans.push_back( + InteractivePlan{std::move(unoptimized_plan), std::move(ast_copy), std::move(rewritten_plan), cost}); + } + std::stable_sort(interactive_plans.begin(), interactive_plans.end(), + [](const auto &a, const auto &b) { return a.cost < b.cost; }); + return interactive_plans; +} diff --git a/tests/manual/interactive/planning.cpp b/tests/manual/interactive/planning.cpp new file mode 100644 index 000000000..e5893485a --- /dev/null +++ b/tests/manual/interactive/planning.cpp @@ -0,0 +1,158 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#include "planning.hpp" + +#include +#include +#include +#include + +#include + +#include "db_accessor.hpp" +#include "plan.hpp" +#include "query/context.hpp" +#include "query/db_accessor.hpp" +#include "query/frontend/ast/cypher_main_visitor.hpp" +#include "query/frontend/opencypher/parser.hpp" +#include "query/frontend/semantic/symbol_generator.hpp" +#include "query/plan/operator.hpp" +#include "query/plan/planner.hpp" +#include "query/plan/pretty_print.hpp" +#include "query/typed_value.hpp" +#include "storage/v2/property_value.hpp" +#include "utils/string.hpp" + +DEFINE_string(save_mock_db_file, "", "File where the mock database should be saved (on exit)"); +DEFINE_string(load_mock_db_file, "", "File from which the mock database should be loaded"); + +DEFCOMMAND(Top) { + int64_t n_plans = 0; + std::stringstream ss(args[0]); + ss >> n_plans; + if (ss.fail() || !ss.eof()) return; + n_plans = std::min(static_cast(plans.size()), n_plans); + for (int64_t i = 0; i < n_plans; ++i) { + std::cout << "---- Plan #" << i << " ---- " << std::endl; + std::cout << "cost: " << plans[i].cost << std::endl; + memgraph::query::plan::PrettyPrint(dba, plans[i].final_plan.get()); + std::cout << std::endl; + } +} + +DEFCOMMAND(Show) { + int64_t plan_ix = 0; + std::stringstream ss(args[0]); + ss >> plan_ix; + if (ss.fail() || !ss.eof() || plan_ix >= plans.size()) return; + const auto &plan = plans[plan_ix].final_plan; + auto cost = plans[plan_ix].cost; + std::cout << "Plan cost: " << cost << std::endl; + memgraph::query::plan::PrettyPrint(dba, plan.get()); +} + +DEFCOMMAND(ShowUnoptimized) { + int64_t plan_ix = 0; + std::stringstream ss(args[0]); + ss >> plan_ix; + if (ss.fail() || !ss.eof() || plan_ix >= plans.size()) return; + const auto &plan = plans[plan_ix].unoptimized_plan; + memgraph::query::plan::PrettyPrint(dba, plan.get()); +} + +DEFCOMMAND(Help); + +std::map commands = { + {"top", {TopCommand, 1, "Show top N plans"}}, + {"show", {ShowCommand, 1, "Show the Nth plan"}}, + {"show-unoptimized", {ShowUnoptimizedCommand, 1, "Show the Nth plan in its original, unoptimized form"}}, + {"help", {HelpCommand, 0, "Show available commands"}}, +}; + +void AddCommand(const std::string &name, const Command &command) { commands[name] = command; } + +DEFCOMMAND(Help) { + std::cout << "Available commands:" << std::endl; + for (const auto &command : commands) { + std::cout << command.first; + for (int i = 1; i <= command.second.arg_count; ++i) { + std::cout << " arg" << i; + } + std::cout << " -- " << command.second.documentation << std::endl; + } +} + +void ExaminePlans(memgraph::query::DbAccessor *dba, const memgraph::query::SymbolTable &symbol_table, + std::vector &plans, const memgraph::query::AstStorage &ast) { + while (true) { + auto line = ReadLine("plan? "); + if (!line || *line == "quit") break; + auto words = memgraph::utils::Split(memgraph::utils::ToLowerCase(*line)); + if (words.empty()) continue; + auto command_name = words[0]; + std::vector args(words.begin() + 1, words.end()); + auto command_it = commands.find(command_name); + if (command_it == commands.end()) { + std::cout << "Undefined command: '" << command_name << "'. Try 'help'." << std::endl; + continue; + } + const auto &command = command_it->second; + if (args.size() < command.arg_count) { + std::cout << command_name << " expects " << command.arg_count << " arguments" << std::endl; + continue; + } + command.function(*dba, symbol_table, plans, args, ast); + } +} + +void RunInteractivePlanning(memgraph::query::DbAccessor *dba) { + std::string in_db_filename(memgraph::utils::Trim(FLAGS_load_mock_db_file)); + if (!in_db_filename.empty() && !std::filesystem::exists(in_db_filename)) { + std::cerr << "File '" << in_db_filename << "' does not exist!" << std::endl; + std::exit(EXIT_FAILURE); + } + Timer planning_timer; + InteractiveDbAccessor interactive_db(dba, in_db_filename.empty() ? ReadInt("Vertices in DB: ") : 0, planning_timer); + if (!in_db_filename.empty()) { + std::ifstream db_file(in_db_filename); + interactive_db.Load(db_file); + } + while (true) { + auto line = ReadLine("query? "); + if (!line || *line == "quit") break; + if (line->empty()) continue; + try { + memgraph::query::AstStorage ast; + auto *query = dynamic_cast(MakeAst(*line, &ast)); + if (!query) { + throw memgraph::utils::BasicException( + "Interactive planning is only avaialable for regular openCypher " + "queries."); + } + auto symbol_table = memgraph::query::MakeSymbolTable(query); + planning_timer.Start(); + auto plans = MakeLogicalPlans(query, ast, symbol_table, &interactive_db); + auto planning_time = planning_timer.Elapsed(); + std::cout << "Planning took " << std::chrono::duration(planning_time).count() << "ms" + << std::endl; + std::cout << "Generated " << plans.size() << " plans" << std::endl; + ExaminePlans(dba, symbol_table, plans, ast); + } catch (const memgraph::utils::BasicException &e) { + std::cout << "Error: " << e.what() << std::endl; + } + } + std::string db_filename(memgraph::utils::Trim(FLAGS_save_mock_db_file)); + if (!db_filename.empty()) { + std::ofstream db_file(db_filename); + interactive_db.Save(db_file); + } +} diff --git a/tests/manual/interactive_planning.hpp b/tests/manual/interactive/planning.hpp similarity index 80% rename from tests/manual/interactive_planning.hpp rename to tests/manual/interactive/planning.hpp index f062849cd..c7774da36 100644 --- a/tests/manual/interactive_planning.hpp +++ b/tests/manual/interactive/planning.hpp @@ -21,21 +21,12 @@ #include "query/frontend/semantic/symbol_table.hpp" #include "query/plan/operator.hpp" +#include "plan.hpp" + namespace database { class GraphDbAccessor; } -struct InteractivePlan { - // Original plan after going only through the RuleBasedPlanner. - std::unique_ptr unoptimized_plan; - // Storage for the AST used in unoptimized_plan - memgraph::query::AstStorage ast_storage; - // Final plan after being rewritten and optimized. - std::unique_ptr final_plan; - // Cost of the final plan. - double cost; -}; - typedef std::vector PlansWithCost; // Encapsulates a consoles command function. diff --git a/tests/manual/interactive/readline.hpp b/tests/manual/interactive/readline.hpp new file mode 100644 index 000000000..8eeeea942 --- /dev/null +++ b/tests/manual/interactive/readline.hpp @@ -0,0 +1,65 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include + +#ifdef HAS_READLINE +// TODO: This should probably be moved to some utils file. + +#include "readline/history.h" +#include "readline/readline.h" + +/** + * Helper function that reads a line from the + * standard input using the 'readline' lib. + * Adds support for history and reverse-search. + * + * @param prompt The prompt to display. + * @return A single command the user entered, or nullopt on EOF. + */ +inline std::optional ReadLine(const std::string &prompt) { + char *line = readline(prompt.c_str()); + if (!line) return std::nullopt; + + if (*line) add_history(line); + std::string r_val(line); + free(line); + return r_val; +} + +#else + +inline std::optional ReadLine(const std::string &prompt) { + std::cout << prompt; + std::string line; + std::getline(std::cin, line); + if (std::cin.eof()) return std::nullopt; + return line; +} + +#endif // HAS_READLINE + +// Repeats the prompt untile the user inputs an integer. +inline int64_t ReadInt(const std::string &prompt) { + int64_t val = 0; + std::stringstream ss; + do { + auto line = ReadLine(prompt); + if (!line) continue; + ss.str(*line); + ss.clear(); + ss >> val; + } while (ss.fail() || !ss.eof()); + return val; +} diff --git a/tests/manual/interactive/timer.hpp b/tests/manual/interactive/timer.hpp new file mode 100644 index 000000000..2858d4d3f --- /dev/null +++ b/tests/manual/interactive/timer.hpp @@ -0,0 +1,57 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#pragma once + +#include +#include + +class Timer { + public: + void Start() { + duration_ = duration_.zero(); + start_time_ = std::chrono::steady_clock::now(); + } + + void Pause() { + if (pause_ == 0) { + duration_ += std::chrono::steady_clock::now() - start_time_; + } + ++pause_; + } + + void Resume() { + if (pause_ == 1) { + start_time_ = std::chrono::steady_clock::now(); + } + pause_ = std::max(0, pause_ - 1); + } + + template + auto WithPause(const TFun &fun) { + Pause(); + auto ret = fun(); + Resume(); + return std::move(ret); + } + + std::chrono::duration Elapsed() { + if (pause_ == 0) { + return duration_ + (std::chrono::steady_clock::now() - start_time_); + } + return duration_; + } + + private: + std::chrono::duration duration_; + std::chrono::time_point start_time_; + int pause_ = 0; +}; diff --git a/tests/manual/query_execution_dummy.cpp b/tests/manual/query_execution_dummy.cpp new file mode 100644 index 000000000..b4367fc6e --- /dev/null +++ b/tests/manual/query_execution_dummy.cpp @@ -0,0 +1,183 @@ +// Copyright 2022 Memgraph Ltd. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source +// License, and you may not use this file except in compliance with the Business Source License. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +#include "interactive/planning.hpp" + +#include +#include + +#include + +#include "interactive/db_accessor.hpp" +#include "interactive/plan.hpp" +#include "query/frontend/semantic/symbol_generator.hpp" +#include "storage/v2/storage.hpp" +#include "utils/string.hpp" + +namespace memgraph::query::plan { + +class TestLogicalOperatorVisitor final : public HierarchicalLogicalOperatorVisitor { + public: + TestLogicalOperatorVisitor() {} + + using HierarchicalLogicalOperatorVisitor::PostVisit; + using HierarchicalLogicalOperatorVisitor::PreVisit; + using HierarchicalLogicalOperatorVisitor::Visit; + + void Start() {} + + bool IsDone() { return true; } + + bool Visit(Once &) override { + std::cout << "Visit Once" << std::endl; + return true; + } + + bool PreVisit(Filter &op) override { return true; } + bool PostVisit(Filter &op) override { return true; } + + bool PreVisit(ScanAll &op) override { + std::cout << "PreVisit ScanAll, output " << op.output_symbol_.name_ << std::endl; + return true; + } + bool PostVisit(ScanAll &scan) override { return true; } + + bool PreVisit(Expand &op) override { return true; } + bool PostVisit(Expand &expand) override { return true; } + + bool PreVisit(ExpandVariable &op) override { return true; } + bool PostVisit(ExpandVariable &expand) override { return true; } + + bool PreVisit(Merge &op) override { return false; } + bool PostVisit(Merge &) override { return true; } + + bool PreVisit(Optional &op) override { return false; } + bool PostVisit(Optional &) override { return true; } + + bool PreVisit(Cartesian &op) override { return true; } + bool PostVisit(Cartesian &) override { return true; } + + bool PreVisit(Union &op) override { return false; } + bool PostVisit(Union &) override { return true; } + + bool PreVisit(CreateNode &op) override { return true; } + bool PostVisit(CreateNode &) override { return true; } + + bool PreVisit(CreateExpand &op) override { return true; } + bool PostVisit(CreateExpand &) override { return true; } + + bool PreVisit(ScanAllByLabel &op) override { return true; } + bool PostVisit(ScanAllByLabel &) override { return true; } + + bool PreVisit(ScanAllByLabelPropertyRange &op) override { return true; } + bool PostVisit(ScanAllByLabelPropertyRange &) override { return true; } + + bool PreVisit(ScanAllByLabelPropertyValue &op) override { return true; } + bool PostVisit(ScanAllByLabelPropertyValue &) override { return true; } + + bool PreVisit(ScanAllByLabelProperty &op) override { return true; } + bool PostVisit(ScanAllByLabelProperty &) override { return true; } + + bool PreVisit(ScanAllById &op) override { return true; } + bool PostVisit(ScanAllById &) override { return true; } + + bool PreVisit(ConstructNamedPath &op) override { return true; } + bool PostVisit(ConstructNamedPath &) override { return true; } + + bool PreVisit(Produce &op) override { + std::cout << "PreVisit Produce, named expressions: "; + for (const auto &name_expr : op.named_expressions_) { + std::cout << name_expr->name_ << " "; + } + std::cout << std::endl; + return true; + } + bool PostVisit(Produce &) override { return true; } + + bool PreVisit(Delete &op) override { return true; } + bool PostVisit(Delete &) override { return true; } + + bool PreVisit(SetProperty &op) override { return true; } + bool PostVisit(SetProperty &) override { return true; } + + bool PreVisit(SetProperties &op) override { return true; } + bool PostVisit(SetProperties &) override { return true; } + + bool PreVisit(SetLabels &op) override { return true; } + bool PostVisit(SetLabels &) override { return true; } + + bool PreVisit(RemoveProperty &op) override { return true; } + bool PostVisit(RemoveProperty &) override { return true; } + + bool PreVisit(RemoveLabels &op) override { return true; } + bool PostVisit(RemoveLabels &) override { return true; } + + bool PreVisit(EdgeUniquenessFilter &op) override { return true; } + bool PostVisit(EdgeUniquenessFilter &) override { return true; } + + bool PreVisit(Accumulate &op) override { return true; } + bool PostVisit(Accumulate &) override { return true; } + + bool PreVisit(Aggregate &op) override { return true; } + bool PostVisit(Aggregate &) override { return true; } + + bool PreVisit(Skip &op) override { return true; } + bool PostVisit(Skip &) override { return true; } + + bool PreVisit(Limit &op) override { return true; } + bool PostVisit(Limit &) override { return true; } + + bool PreVisit(OrderBy &op) override { return true; } + bool PostVisit(OrderBy &) override { return true; } + + bool PreVisit(Unwind &op) override { return true; } + bool PostVisit(Unwind &) override { return true; } + + bool PreVisit(Distinct &op) override { return true; } + bool PostVisit(Distinct &) override { return true; } + + bool PreVisit(CallProcedure &op) override { return true; } + bool PostVisit(CallProcedure &) override { return true; } +}; +} // namespace memgraph::query::plan + +int main(int argc, char *argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); + spdlog::set_level(spdlog::level::info); + + memgraph::storage::Storage db; + auto storage_dba = db.Access(); + memgraph::query::DbAccessor dba(&storage_dba); + + Timer planning_timer; + InteractiveDbAccessor interactive_db(&dba, 10, planning_timer); + std::string input_query = "MATCH (n) RETURN n;"; + memgraph::query::AstStorage ast; + auto *query = dynamic_cast(MakeAst(input_query, &ast)); + if (!query) { + throw memgraph::utils::BasicException("Create CypherQuery failed"); + } + auto symbol_table = memgraph::query::MakeSymbolTable(query); + planning_timer.Start(); + auto plans = MakeLogicalPlans(query, ast, symbol_table, &interactive_db); + if (plans.size() == 0) { + throw memgraph::utils::BasicException("No plans"); + } + + memgraph::query::plan::TestLogicalOperatorVisitor executor; + plans[0].unoptimized_plan->Accept(executor); + executor.Start(); + while (!executor.IsDone()) { + std::cout << "Executor NOT done yet" << std::endl; + } + std::cout << "Executor done" << std::endl; + return 0; +} diff --git a/tests/manual/query_planner.cpp b/tests/manual/query_planner.cpp index 67875734b..fc46b0915 100644 --- a/tests/manual/query_planner.cpp +++ b/tests/manual/query_planner.cpp @@ -9,7 +9,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. -#include "interactive_planning.hpp" +#include "interactive/planning.hpp" #include