2017-06-15 00:53:02 +08:00
|
|
|
#include "query/interpreter.hpp"
|
|
|
|
|
2017-10-05 00:38:17 +08:00
|
|
|
#include <glog/logging.h>
|
|
|
|
|
|
|
|
#include "query/exceptions.hpp"
|
2017-09-19 22:58:22 +08:00
|
|
|
#include "query/plan/cost_estimator.hpp"
|
|
|
|
#include "query/plan/planner.hpp"
|
|
|
|
#include "query/plan/vertex_count_cache.hpp"
|
|
|
|
#include "utils/flag_validation.hpp"
|
|
|
|
|
2017-10-05 00:38:17 +08:00
|
|
|
DEFINE_bool(query_cost_planner, true, "Use the cost-estimating query planner.");
|
2017-09-19 22:58:22 +08:00
|
|
|
DEFINE_bool(query_plan_cache, true, "Cache generated query plans");
|
Flags cleanup and QueryEngine removal
Summary:
I started with cleaning flags up (removing unused ones, documenting undocumented ones). There were some flags to remove in `QueryEngine`. Seeing how we never use hardcoded queries (AFAIK last Mislav's testing also indicated they aren't faster then interpretation), when removing those unused flags the `QueryEngine` becomes obsolete. That means that a bunch of other stuff becomes obsolete, along with the hardcoded queries. So I removed it all (this has been discussed and approved on the daily).
Some flags that were previously undocumented in `docs/user_technical/installation` are now documented. The following flags are NOT documented and in my opinion should not be displayed when starting `./memgraph --help` (@mferencevic):
```
query_vertex_count_to_expand_existsing (from rule_based_planner.cpp)
query_max_plans (rule_based_planner.cpp)
```
If you think that another organization is needed w.r.t. flag visibility, comment.
@teon.banek: I had to remove some stuff from CMakeLists to make it buildable. Please review what I removed and clean up if necessary if/when this lands. If the needed changes are minor, you can also comment.
Reviewers: buda, mislav.bradac, teon.banek, mferencevic
Reviewed By: buda, mislav.bradac
Subscribers: pullbot, mferencevic, teon.banek
Differential Revision: https://phabricator.memgraph.io/D825
2017-09-22 22:17:09 +08:00
|
|
|
DEFINE_VALIDATED_int32(query_plan_cache_ttl, 60,
|
|
|
|
"Time to live for cached query plans, in seconds.",
|
|
|
|
FLAG_IN_RANGE(0, std::numeric_limits<int32_t>::max()));
|
2017-09-19 22:58:22 +08:00
|
|
|
|
|
|
|
namespace query {
|
|
|
|
|
|
|
|
AstTreeStorage Interpreter::QueryToAst(const StrippedQuery &stripped,
|
|
|
|
Context &ctx) {
|
|
|
|
if (!ctx.is_query_cached_) {
|
|
|
|
// stripped query -> AST
|
|
|
|
auto parser = [&] {
|
|
|
|
// Be careful about unlocking since parser can throw.
|
|
|
|
std::unique_lock<SpinLock> guard(antlr_lock_);
|
|
|
|
return std::make_unique<frontend::opencypher::Parser>(
|
|
|
|
stripped.original_query());
|
|
|
|
}();
|
|
|
|
auto low_level_tree = parser->tree();
|
|
|
|
// AST -> high level tree
|
|
|
|
frontend::CypherMainVisitor visitor(ctx);
|
|
|
|
visitor.visit(low_level_tree);
|
|
|
|
return std::move(visitor.storage());
|
|
|
|
}
|
|
|
|
auto ast_cache_accessor = ast_cache_.access();
|
|
|
|
auto ast_it = ast_cache_accessor.find(stripped.hash());
|
|
|
|
if (ast_it == ast_cache_accessor.end()) {
|
|
|
|
// stripped query -> AST
|
|
|
|
auto parser = [&] {
|
|
|
|
// Be careful about unlocking since parser can throw.
|
|
|
|
std::unique_lock<SpinLock> guard(antlr_lock_);
|
2017-10-05 00:38:17 +08:00
|
|
|
try {
|
|
|
|
return std::make_unique<frontend::opencypher::Parser>(stripped.query());
|
|
|
|
} catch (const SyntaxException &e) {
|
|
|
|
// There is syntax exception in stripped query. Rerun parser with
|
|
|
|
// original query to get appropriate error messsage.
|
|
|
|
auto parser = std::make_unique<frontend::opencypher::Parser>(
|
|
|
|
stripped.original_query());
|
|
|
|
// If exception was not thrown here, it means StrippedQuery messed up
|
|
|
|
// something.
|
|
|
|
LOG(FATAL) << "Stripped query can't be parsed, original can";
|
|
|
|
return parser;
|
|
|
|
}
|
2017-09-19 22:58:22 +08:00
|
|
|
}();
|
|
|
|
auto low_level_tree = parser->tree();
|
|
|
|
// AST -> high level tree
|
|
|
|
frontend::CypherMainVisitor visitor(ctx);
|
|
|
|
visitor.visit(low_level_tree);
|
|
|
|
// Cache it.
|
|
|
|
ast_it =
|
|
|
|
ast_cache_accessor.insert(stripped.hash(), std::move(visitor.storage()))
|
|
|
|
.first;
|
|
|
|
}
|
|
|
|
AstTreeStorage new_ast;
|
|
|
|
ast_it->second.query()->Clone(new_ast);
|
|
|
|
return new_ast;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<std::unique_ptr<plan::LogicalOperator>, double>
|
|
|
|
Interpreter::MakeLogicalPlan(AstTreeStorage &ast_storage,
|
|
|
|
const GraphDbAccessor &db_accessor,
|
|
|
|
Context &context) {
|
|
|
|
std::unique_ptr<plan::LogicalOperator> logical_plan;
|
|
|
|
double min_cost = std::numeric_limits<double>::max();
|
|
|
|
auto vertex_counts = plan::MakeVertexCountCache(db_accessor);
|
|
|
|
if (FLAGS_query_cost_planner) {
|
|
|
|
auto plans = plan::MakeLogicalPlan<plan::VariableStartPlanner>(
|
|
|
|
ast_storage, context.symbol_table_, vertex_counts);
|
|
|
|
for (auto &plan : plans) {
|
|
|
|
auto cost = EstimatePlanCost(vertex_counts, context.parameters_, *plan);
|
|
|
|
if (!logical_plan || cost < min_cost) {
|
|
|
|
// We won't be iterating over plans anymore, so it's ok to invalidate
|
|
|
|
// unique_ptrs inside.
|
|
|
|
logical_plan = std::move(plan);
|
|
|
|
min_cost = cost;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
logical_plan = plan::MakeLogicalPlan<plan::RuleBasedPlanner>(
|
|
|
|
ast_storage, context.symbol_table_, vertex_counts);
|
|
|
|
min_cost =
|
|
|
|
EstimatePlanCost(vertex_counts, context.parameters_, *logical_plan);
|
|
|
|
}
|
|
|
|
return {std::move(logical_plan), min_cost};
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace query
|