//===----------------------------------------------------------------------===// // DuckDB // // duckdb/common/tree_renderer.hpp // // //===----------------------------------------------------------------------===// #pragma once #include "duckdb/common/constants.hpp" #include "duckdb/common/vector.hpp" #include "duckdb/main/query_profiler.hpp" namespace duckdb { class LogicalOperator; class PhysicalOperator; class Pipeline; struct PipelineRenderNode; struct RenderTreeNode { string name; string extra_text; }; struct RenderTree { RenderTree(idx_t width, idx_t height); unique_ptr[]> nodes; idx_t width; idx_t height; public: RenderTreeNode *GetNode(idx_t x, idx_t y); void SetNode(idx_t x, idx_t y, unique_ptr node); bool HasNode(idx_t x, idx_t y); idx_t GetPosition(idx_t x, idx_t y); }; struct TreeRendererConfig { void enable_detailed() { MAX_EXTRA_LINES = 1000; detailed = true; } void enable_standard() { MAX_EXTRA_LINES = 30; detailed = false; } idx_t MAXIMUM_RENDER_WIDTH = 240; idx_t NODE_RENDER_WIDTH = 29; idx_t MINIMUM_RENDER_WIDTH = 15; idx_t MAX_EXTRA_LINES = 30; bool detailed = false; #ifndef DUCKDB_ASCII_TREE_RENDERER const char *LTCORNER = "\342\224\214"; // "┌"; const char *RTCORNER = "\342\224\220"; // "┐"; const char *LDCORNER = "\342\224\224"; // "└"; const char *RDCORNER = "\342\224\230"; // "┘"; const char *MIDDLE = "\342\224\274"; // "┼"; const char *TMIDDLE = "\342\224\254"; // "┬"; const char *LMIDDLE = "\342\224\234"; // "├"; const char *RMIDDLE = "\342\224\244"; // "┤"; const char *DMIDDLE = "\342\224\264"; // "┴"; const char *VERTICAL = "\342\224\202"; // "│"; const char *HORIZONTAL = "\342\224\200"; // "─"; #else // ASCII version const char *LTCORNER = "<"; const char *RTCORNER = ">"; const char *LDCORNER = "<"; const char *RDCORNER = ">"; const char *MIDDLE = "+"; const char *TMIDDLE = "+"; const char *LMIDDLE = "+"; const char *RMIDDLE = "+"; const char *DMIDDLE = "+"; const char *VERTICAL = "|"; const char *HORIZONTAL = "-"; #endif }; class TreeRenderer { public: explicit TreeRenderer(TreeRendererConfig config_p = TreeRendererConfig()) : config(std::move(config_p)) { } string ToString(const LogicalOperator &op); string ToString(const PhysicalOperator &op); string ToString(const QueryProfiler::TreeNode &op); string ToString(const Pipeline &op); void Render(const LogicalOperator &op, std::ostream &ss); void Render(const PhysicalOperator &op, std::ostream &ss); void Render(const QueryProfiler::TreeNode &op, std::ostream &ss); void Render(const Pipeline &op, std::ostream &ss); void ToStream(RenderTree &root, std::ostream &ss); void EnableDetailed() { config.enable_detailed(); } void EnableStandard() { config.enable_standard(); } private: unique_ptr CreateTree(const LogicalOperator &op); unique_ptr CreateTree(const PhysicalOperator &op); unique_ptr CreateTree(const QueryProfiler::TreeNode &op); unique_ptr CreateTree(const Pipeline &op); string ExtraInfoSeparator(); unique_ptr CreateRenderNode(string name, string extra_info); unique_ptr CreateNode(const LogicalOperator &op); unique_ptr CreateNode(const PhysicalOperator &op); unique_ptr CreateNode(const QueryProfiler::TreeNode &op); unique_ptr CreateNode(const PipelineRenderNode &op); private: //! The configuration used for rendering TreeRendererConfig config; private: void RenderTopLayer(RenderTree &root, std::ostream &ss, idx_t y); void RenderBoxContent(RenderTree &root, std::ostream &ss, idx_t y); void RenderBottomLayer(RenderTree &root, std::ostream &ss, idx_t y); bool CanSplitOnThisChar(char l); bool IsPadding(char l); string RemovePadding(string l); void SplitUpExtraInfo(const string &extra_info, vector &result); void SplitStringBuffer(const string &source, vector &result); template idx_t CreateRenderTreeRecursive(RenderTree &result, const T &op, idx_t x, idx_t y); template unique_ptr CreateRenderTree(const T &op); string ExtractExpressionsRecursive(ExpressionInfo &states); }; } // namespace duckdb