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());
 }

Reply via email to