This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new e4d035214 feat(search): add debug mode for FT.EXPLAINSQL (#2886)
e4d035214 is described below
commit e4d035214a7198d539eb0641b43e7e9358fecf3b
Author: Twice <[email protected]>
AuthorDate: Sat Apr 19 23:24:18 2025 +0800
feat(search): add debug mode for FT.EXPLAINSQL (#2886)
Signed-off-by: PragmaTwice <[email protected]>
---
src/commands/cmd_search.cc | 21 ++++++++++++++++++++-
src/search/index_manager.cc | 14 ++++++++++++++
src/search/index_manager.h | 2 ++
src/search/ir_pass.h | 3 +++
src/search/passes/index_selection.h | 2 ++
src/search/passes/interval_analysis.h | 3 +++
src/search/passes/lower_to_plan.h | 2 ++
src/search/passes/manager.h | 9 +++++----
src/search/passes/push_down_not_expr.h | 2 ++
src/search/passes/recorder.h | 15 ++++++++++++---
src/search/passes/simplify_and_or_expr.h | 2 ++
src/search/passes/simplify_boolean.h | 2 ++
src/search/passes/sort_limit_fuse.h | 2 ++
src/search/passes/sort_limit_to_knn.h | 2 ++
tests/cppunit/ir_pass_test.cc | 4 +++-
15 files changed, 76 insertions(+), 9 deletions(-)
diff --git a/src/commands/cmd_search.cc b/src/commands/cmd_search.cc
index 1fcb08dc5..6fffbe91b 100644
--- a/src/commands/cmd_search.cc
+++ b/src/commands/cmd_search.cc
@@ -264,6 +264,8 @@ class CommandFTExplainSQL : public Commander {
format_ = SIMPLE;
} else if (parser.EatEqICase("dot")) {
format_ = DOT_GRAPH;
+ } else if (parser.EatEqICase("debug")) {
+ format_ = DEBUG;
} else {
return {Status::NotOK, "output format should be SIMPLE or DOT"};
}
@@ -277,6 +279,23 @@ class CommandFTExplainSQL : public Commander {
}
Status Execute([[maybe_unused]] engine::Context &ctx, Server *srv,
Connection *conn, std::string *output) override {
+ if (format_ == DEBUG) {
+ auto results = GET_OR_RET(srv->index_mgr.DebugPlan(std::move(ir_),
conn->GetNamespace()));
+
+ output->append(MultiLen(results.size()));
+ for (const auto &res : results) {
+ output->append(MultiLen(2));
+ if (res.after_pass.empty()) {
+ output->append(SimpleString("Initial"));
+ } else {
+ output->append(SimpleString("After " + std::string(res.after_pass) +
" Pass"));
+ }
+ output->append(BulkString(res.ir->Dump()));
+ }
+
+ return Status::OK();
+ }
+
auto plan = GET_OR_RET(srv->index_mgr.GeneratePlan(std::move(ir_),
conn->GetNamespace()));
if (format_ == SIMPLE) {
@@ -292,7 +311,7 @@ class CommandFTExplainSQL : public Commander {
return Status::OK();
};
- enum OutputFormat { SIMPLE, DOT_GRAPH } format_ = SIMPLE;
+ enum OutputFormat { SIMPLE, DOT_GRAPH, DEBUG } format_ = SIMPLE;
std::unique_ptr<kqir::Node> ir_;
};
diff --git a/src/search/index_manager.cc b/src/search/index_manager.cc
index 7275a62dc..c51f30d3e 100644
--- a/src/search/index_manager.cc
+++ b/src/search/index_manager.cc
@@ -27,6 +27,7 @@
#include "search/ir.h"
#include "search/ir_sema_checker.h"
#include "search/passes/manager.h"
+#include "search/passes/recorder.h"
#include "search/plan_executor.h"
#include "search/search_encoding.h"
#include "search/value.h"
@@ -200,6 +201,19 @@ StatusOr<std::unique_ptr<kqir::PlanOperator>>
IndexManager::GeneratePlan(std::un
return plan_op;
}
+StatusOr<std::vector<kqir::Recorder::Result>>
IndexManager::DebugPlan(std::unique_ptr<kqir::Node> ir,
+ const
std::string &ns) const {
+ kqir::SemaChecker sema_checker(index_map);
+ sema_checker.ns = ns;
+
+ GET_OR_RET(sema_checker.Check(ir.get()));
+
+ std::vector<kqir::Recorder::Result> results;
+ kqir::PassManager::Execute(kqir::PassManager::Debug(results), std::move(ir));
+
+ return results;
+}
+
StatusOr<std::vector<kqir::ExecutorContext::RowType>>
IndexManager::Search(std::unique_ptr<kqir::Node> ir,
const std::string &ns) const {
auto plan_op = GET_OR_RET(GeneratePlan(std::move(ir), ns));
diff --git a/src/search/index_manager.h b/src/search/index_manager.h
index cfa7712ad..a19395107 100644
--- a/src/search/index_manager.h
+++ b/src/search/index_manager.h
@@ -23,6 +23,7 @@
#include "search/index_info.h"
#include "search/indexer.h"
#include "search/ir.h"
+#include "search/passes/recorder.h"
#include "search/plan_executor.h"
#include "status.h"
#include "storage/storage.h"
@@ -42,6 +43,7 @@ struct IndexManager {
StatusOr<std::unique_ptr<kqir::PlanOperator>>
GeneratePlan(std::unique_ptr<kqir::Node> ir,
const std::string
&ns) const;
+ StatusOr<std::vector<kqir::Recorder::Result>>
DebugPlan(std::unique_ptr<kqir::Node> ir, const std::string &ns) const;
StatusOr<std::vector<kqir::ExecutorContext::RowType>>
Search(std::unique_ptr<kqir::Node> ir,
const
std::string &ns) const;
diff --git a/src/search/ir_pass.h b/src/search/ir_pass.h
index e783ca8f4..5fc068e06 100644
--- a/src/search/ir_pass.h
+++ b/src/search/ir_pass.h
@@ -20,6 +20,8 @@
#pragma once
+#include <string_view>
+
#include "ir.h"
#include "search/ir_plan.h"
@@ -28,6 +30,7 @@ namespace kqir {
struct Pass {
virtual std::unique_ptr<Node> Transform(std::unique_ptr<Node> node) = 0;
+ virtual std::string_view Name() = 0;
virtual void Reset() {}
virtual ~Pass() = default;
diff --git a/src/search/passes/index_selection.h
b/src/search/passes/index_selection.h
index 09e1bcb34..2a2263f14 100644
--- a/src/search/passes/index_selection.h
+++ b/src/search/passes/index_selection.h
@@ -51,6 +51,8 @@ struct IndexSelection : Visitor {
intervals.clear();
}
+ std::string_view Name() override { return "Index Selection"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<Projection> node) override {
IntervalAnalysis analysis(false);
node = Node::MustAs<Projection>(analysis.Transform(std::move(node)));
diff --git a/src/search/passes/interval_analysis.h
b/src/search/passes/interval_analysis.h
index 5ed561251..4d1294105 100644
--- a/src/search/passes/interval_analysis.h
+++ b/src/search/passes/interval_analysis.h
@@ -24,6 +24,7 @@
#include <cmath>
#include <memory>
#include <set>
+#include <string_view>
#include <type_traits>
#include "search/interval.h"
@@ -51,6 +52,8 @@ struct IntervalAnalysis : Visitor {
void Reset() override { result.clear(); }
+ std::string_view Name() override { return "Interval Analysis and
Simplification"; }
+
template <typename T>
std::unique_ptr<Node> VisitImpl(std::unique_ptr<T> node) {
node = Node::MustAs<T>(Visitor::Visit(std::move(node)));
diff --git a/src/search/passes/lower_to_plan.h
b/src/search/passes/lower_to_plan.h
index 94828c22b..b2c0e438e 100644
--- a/src/search/passes/lower_to_plan.h
+++ b/src/search/passes/lower_to_plan.h
@@ -29,6 +29,8 @@
namespace kqir {
struct LowerToPlan : Visitor {
+ std::string_view Name() override { return "Syntax to Plan Lowering"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<SearchExpr> node) override {
auto scan =
std::make_unique<FullIndexScan>(node->index->CloneAs<IndexRef>());
diff --git a/src/search/passes/manager.h b/src/search/passes/manager.h
index ce2d6a3db..2780cbe5f 100644
--- a/src/search/passes/manager.h
+++ b/src/search/passes/manager.h
@@ -62,13 +62,14 @@ struct PassManager {
return result;
}
- static PassSequence FullRecord(PassSequence &&seq,
std::vector<std::unique_ptr<Node>> &results) {
+ static PassSequence FullRecord(PassSequence &&seq,
std::vector<Recorder::Result> &results) {
PassSequence res_seq;
- res_seq.push_back(std::make_unique<Recorder>(results));
+ res_seq.push_back(std::make_unique<Recorder>("", results));
for (auto &p : seq) {
+ auto name = p->Name();
res_seq.push_back(std::move(p));
- res_seq.push_back(std::make_unique<Recorder>(results));
+ res_seq.push_back(std::make_unique<Recorder>(name, results));
}
return res_seq;
@@ -94,7 +95,7 @@ struct PassManager {
static PassSequence PlanPasses() { return Create(LowerToPlan{},
IndexSelection{}, SortLimitFuse{}); }
static PassSequence Default() { return Merge(ExprPasses(), NumericPasses(),
PlanPasses()); }
- static PassSequence Debug(std::vector<std::unique_ptr<Node>> &recorded) {
return FullRecord(Default(), recorded); }
+ static PassSequence Debug(std::vector<Recorder::Result> &recorded) { return
FullRecord(Default(), recorded); }
};
} // namespace kqir
diff --git a/src/search/passes/push_down_not_expr.h
b/src/search/passes/push_down_not_expr.h
index 3c286c091..47696ff2a 100644
--- a/src/search/passes/push_down_not_expr.h
+++ b/src/search/passes/push_down_not_expr.h
@@ -28,6 +28,8 @@
namespace kqir {
struct PushDownNotExpr : Visitor {
+ std::string_view Name() override { return "NOT Expr Pushing-down"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<NotExpr> node) override {
std::unique_ptr<Node> res;
diff --git a/src/search/passes/recorder.h b/src/search/passes/recorder.h
index e68fa6739..0bdc00e36 100644
--- a/src/search/passes/recorder.h
+++ b/src/search/passes/recorder.h
@@ -28,12 +28,21 @@
namespace kqir {
struct Recorder : Pass {
- std::vector<std::unique_ptr<Node>> &results;
+ struct Result {
+ std::unique_ptr<Node> ir;
+ std::string_view after_pass;
+ };
- explicit Recorder(std::vector<std::unique_ptr<Node>> &results) :
results(results) {}
+ std::vector<Result> &results;
+ std::string_view after_pass;
+
+ std::string_view Name() override { return "Recorder"; }
+
+ explicit Recorder(std::string_view after_pass, std::vector<Result> &results)
+ : results(results), after_pass(after_pass) {}
std::unique_ptr<Node> Transform(std::unique_ptr<Node> node) override {
- results.push_back(node->Clone());
+ results.push_back(Result{node->Clone(), after_pass});
return node;
}
};
diff --git a/src/search/passes/simplify_and_or_expr.h
b/src/search/passes/simplify_and_or_expr.h
index 22ac7b57f..69c535105 100644
--- a/src/search/passes/simplify_and_or_expr.h
+++ b/src/search/passes/simplify_and_or_expr.h
@@ -28,6 +28,8 @@
namespace kqir {
struct SimplifyAndOrExpr : Visitor {
+ std::string_view Name() override { return "AND/OR Expr Simplification"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<OrExpr> node) override {
node = Node::MustAs<OrExpr>(Visitor::Visit(std::move(node)));
diff --git a/src/search/passes/simplify_boolean.h
b/src/search/passes/simplify_boolean.h
index 79281e8ee..3cca0c397 100644
--- a/src/search/passes/simplify_boolean.h
+++ b/src/search/passes/simplify_boolean.h
@@ -28,6 +28,8 @@
namespace kqir {
struct SimplifyBoolean : Visitor {
+ std::string_view Name() override { return "Boolean Literal Simplification"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<OrExpr> node) override {
node = Node::MustAs<OrExpr>(Visitor::Visit(std::move(node)));
diff --git a/src/search/passes/sort_limit_fuse.h
b/src/search/passes/sort_limit_fuse.h
index 0e8572971..d7d247f30 100644
--- a/src/search/passes/sort_limit_fuse.h
+++ b/src/search/passes/sort_limit_fuse.h
@@ -29,6 +29,8 @@
namespace kqir {
struct SortLimitFuse : Visitor {
+ std::string_view Name() override { return "SORTBY-LIMIT Fusion"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<Limit> node) override {
node = Node::MustAs<Limit>(Visitor::Visit(std::move(node)));
diff --git a/src/search/passes/sort_limit_to_knn.h
b/src/search/passes/sort_limit_to_knn.h
index e0f7b958c..70956787f 100644
--- a/src/search/passes/sort_limit_to_knn.h
+++ b/src/search/passes/sort_limit_to_knn.h
@@ -29,6 +29,8 @@
namespace kqir {
struct SortByWithLimitToKnnExpr : Visitor {
+ std::string_view Name() override { return "SORTBY-LIMIT to KNN
Transformation"; }
+
std::unique_ptr<Node> Visit(std::unique_ptr<SearchExpr> node) override {
node = Node::MustAs<SearchExpr>(Visitor::Visit(std::move(node)));
diff --git a/tests/cppunit/ir_pass_test.cc b/tests/cppunit/ir_pass_test.cc
index 9d576678d..00957a8c0 100644
--- a/tests/cppunit/ir_pass_test.cc
+++ b/tests/cppunit/ir_pass_test.cc
@@ -41,7 +41,9 @@ TEST(IRPassTest, Simple) {
auto original = ir->Dump();
- Visitor visitor;
+ struct MyVisitor : Visitor {
+ std::string_view Name() override { return ""; };
+ } visitor;
auto ir2 = visitor.Transform(std::move(ir));
ASSERT_EQ(original, ir2->Dump());
}