You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-10-31 18:30:33 +03:00
chore(execplan): MCOL: 6144 prerry print filters and havings as a tree (#3712)
* MCOL-6144: print Filters and Having indented for better CSEP visualization
This commit is contained in:
@@ -152,13 +152,49 @@ const string AggregateColumn::toString() const
|
||||
return output.str();
|
||||
}
|
||||
|
||||
const string AggregateColumn::toString(bool compact) const
|
||||
{
|
||||
if (!compact)
|
||||
{
|
||||
return toString();
|
||||
}
|
||||
|
||||
ostringstream output;
|
||||
|
||||
// Compact format for tree display - let tree printer handle indentation
|
||||
output << "AggregateColumn:";
|
||||
output << endl << "Function: " << (int)fAggOp << ", Distinct: " << fDistinct;
|
||||
|
||||
if (fAlias.length() > 0)
|
||||
output << endl << "Alias: " << fAlias;
|
||||
|
||||
if (fAggParms.size() > 0)
|
||||
{
|
||||
for (uint32_t i = 0; i < fAggParms.size(); ++i)
|
||||
{
|
||||
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(fAggParms[i].get());
|
||||
if (sc)
|
||||
{
|
||||
output << endl << sc->toString(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
output << endl << "Param: " << fAggParms[i]->data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
string AggregateColumn::toCppCode(IncludeSet& includes) const
|
||||
{
|
||||
includes.insert("aggregatecolumn.h");
|
||||
stringstream ss;
|
||||
auto fContent = fData.substr(fFunctionName.size() + 1, fData.size() - fFunctionName.size() - 2);
|
||||
|
||||
ss << "AggregateColumn(" << std::quoted(fFunctionName) << ", " << std::quoted(fContent) << ", " << sessionID() << ")";
|
||||
ss << "AggregateColumn(" << std::quoted(fFunctionName) << ", " << std::quoted(fContent) << ", "
|
||||
<< sessionID() << ")";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@@ -220,6 +220,7 @@ class AggregateColumn : public ReturnedColumn
|
||||
* Overloaded stream operator
|
||||
*/
|
||||
const std::string toString() const override;
|
||||
const std::string toString(bool compact) const;
|
||||
std::string toCppCode(IncludeSet& includes) const override;
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
***********************************************************************/
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
using namespace std;
|
||||
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
@@ -35,6 +36,9 @@ using namespace messageqcpp;
|
||||
#include "returnedcolumn.h"
|
||||
#include "simplecolumn.h"
|
||||
#include "querystats.h"
|
||||
#include "simplefilter.h"
|
||||
#include "operator.h"
|
||||
#include <boost/core/demangle.hpp>
|
||||
|
||||
#include "querytele.h"
|
||||
#include "utils/pron/pron.h"
|
||||
@@ -197,10 +201,138 @@ std::string endlWithIndent(const size_t ident)
|
||||
{
|
||||
ostringstream output;
|
||||
output << endl;
|
||||
output << std::string(ident, ' ');
|
||||
|
||||
for (size_t i = 0; i < ident; i++)
|
||||
output << " ";
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
// Iterative tree printer that preserves vertical branches for multi-line nodes
|
||||
static void printIndentedFilterTreeImpl(const ParseTree* root, ostringstream& output, size_t indent,
|
||||
const std::vector<bool>& rootAncestors, bool rootIsLast)
|
||||
{
|
||||
// Stack holds frames: (node, ancestorHasNext, isLastAtLevel)
|
||||
struct Frame
|
||||
{
|
||||
const ParseTree* node;
|
||||
std::vector<bool> ancestors;
|
||||
bool isLast;
|
||||
};
|
||||
|
||||
std::vector<Frame> stack;
|
||||
stack.push_back(Frame{root, rootAncestors, rootIsLast});
|
||||
|
||||
while (!stack.empty())
|
||||
{
|
||||
Frame frame = std::move(stack.back());
|
||||
stack.pop_back();
|
||||
|
||||
const ParseTree* node = frame.node;
|
||||
const std::vector<bool>& ancestorHasNext = frame.ancestors;
|
||||
const bool isLastAtLevel = frame.isLast;
|
||||
|
||||
if (!node)
|
||||
{
|
||||
// Build prefix for a null placeholder
|
||||
std::string base;
|
||||
for (bool hasNext : ancestorHasNext)
|
||||
base += hasNext ? "│ " : " ";
|
||||
std::string nodePrefix = base + (isLastAtLevel ? "└── " : "├── ");
|
||||
output << endlWithIndent(indent) << nodePrefix << "(null)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Gather children in left-to-right order
|
||||
std::vector<const ParseTree*> children;
|
||||
if (node->left())
|
||||
children.push_back(node->left());
|
||||
if (node->right())
|
||||
children.push_back(node->right());
|
||||
|
||||
// Helper to build prefixes
|
||||
auto makePrefixes = [&](bool isLast)
|
||||
{
|
||||
std::string base;
|
||||
for (bool hasNext : ancestorHasNext)
|
||||
base += hasNext ? "│ " : " ";
|
||||
std::string first = base + (isLast ? "└── " : "├── ");
|
||||
std::string cont = base + (isLast ? " " : "│ ");
|
||||
return std::pair<std::string, std::string>(first, cont);
|
||||
};
|
||||
|
||||
// Build node content string
|
||||
TreeNode* data = node->data();
|
||||
std::string nodeContent;
|
||||
if (data)
|
||||
{
|
||||
if (auto sf = dynamic_cast<SimpleFilter*>(data))
|
||||
{
|
||||
nodeContent = sf->toString(true);
|
||||
}
|
||||
else if (auto op = dynamic_cast<Operator*>(data))
|
||||
{
|
||||
nodeContent = op->toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeContent = boost::core::demangle(typeid(*data).name()) + ": " + data->toString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeContent = "(null data)";
|
||||
}
|
||||
|
||||
// Print current node content
|
||||
if (ancestorHasNext.empty())
|
||||
{
|
||||
// Root: print without branch glyphs
|
||||
std::istringstream contentStream(nodeContent);
|
||||
std::string line;
|
||||
while (std::getline(contentStream, line))
|
||||
output << endlWithIndent(indent) << line;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto prefixes = makePrefixes(isLastAtLevel);
|
||||
std::istringstream contentStream(nodeContent);
|
||||
std::string line;
|
||||
bool firstLine = true;
|
||||
while (std::getline(contentStream, line))
|
||||
{
|
||||
if (firstLine)
|
||||
{
|
||||
output << endlWithIndent(indent) << prefixes.first << line;
|
||||
firstLine = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
output << endlWithIndent(indent) << prefixes.second << line;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push children onto stack in reverse order to process left child first
|
||||
for (size_t i = children.size(); i-- > 0;)
|
||||
{
|
||||
bool childIsLast = (i == children.size() - 1);
|
||||
std::vector<bool> nextAncestors = ancestorHasNext;
|
||||
// For children, push whether THIS node has a next sibling; this keeps the vertical bar under this node
|
||||
// if we are not the last at our level.
|
||||
nextAncestors.push_back(!isLastAtLevel);
|
||||
stack.push_back(Frame{children[i], std::move(nextAncestors), childIsLast});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printIndentedFilterTree(const ParseTree* tree, ostringstream& output, size_t indent)
|
||||
{
|
||||
// Start with empty ancestor vector and indicate root is last at its (virtual) level
|
||||
std::vector<bool> ancestors; // empty => root
|
||||
printIndentedFilterTreeImpl(tree, output, indent, ancestors, true);
|
||||
}
|
||||
|
||||
void CalpontSelectExecutionPlan::printSubCSEP(const size_t& ident, ostringstream& output,
|
||||
CalpontSelectExecutionPlan*& plan) const
|
||||
{
|
||||
@@ -282,8 +414,8 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
|
||||
|
||||
if (filters() != nullptr)
|
||||
{
|
||||
output << endlWithIndent(ident + 2);
|
||||
filters()->walk(ParseTree::print, output);
|
||||
output << endlWithIndent(ident + 2) << "Filter Tree:";
|
||||
printIndentedFilterTree(filters(), output, ident + 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -308,8 +440,9 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
|
||||
// Having
|
||||
if (having() != nullptr)
|
||||
{
|
||||
output << endlWithIndent(ident) << ">>Having" << endlWithIndent(ident + 2);
|
||||
having()->walk(ParseTree::print, output);
|
||||
output << endlWithIndent(ident) << ">>Having";
|
||||
output << endlWithIndent(ident + 2) << "Having Tree:";
|
||||
printIndentedFilterTree(having(), output, ident + 4);
|
||||
}
|
||||
|
||||
// Order by columns
|
||||
@@ -863,7 +996,8 @@ void CalpontSelectExecutionPlan::pron(std::string&& pron)
|
||||
fPron = pron;
|
||||
}
|
||||
|
||||
// This routine doesn't copy derived table list, union vector, select subqueries, subquery list, and subselects.
|
||||
// This routine doesn't copy derived table list, union vector, select subqueries, subquery list, and
|
||||
// subselects.
|
||||
execplan::SCSEP CalpontSelectExecutionPlan::cloneWORecursiveSelects()
|
||||
{
|
||||
execplan::SCSEP newPlan(new CalpontSelectExecutionPlan(fLocation));
|
||||
|
||||
@@ -292,6 +292,26 @@ const string SimpleColumn::toString() const
|
||||
return output.str();
|
||||
}
|
||||
|
||||
const string SimpleColumn::toString(bool compact) const
|
||||
{
|
||||
if (!compact)
|
||||
{
|
||||
// Use the original detailed format
|
||||
return toString();
|
||||
}
|
||||
|
||||
ostringstream output;
|
||||
|
||||
// Compact format for tree display - let tree printer handle indentation
|
||||
output << "Column: " << data();
|
||||
datatypes::Charset cs(fResultType.charsetNumber);
|
||||
output << endl
|
||||
<< "Info: " << schemaName() << "." << tableName() << "." << columnName()
|
||||
<< " (Type: " << colDataTypeToString(fResultType.colDataType) << ", OID: " << oid() << ")";
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
string SimpleColumn::toCppCode(IncludeSet& includes) const
|
||||
{
|
||||
includes.insert("simplecolumn.h");
|
||||
|
||||
@@ -205,6 +205,7 @@ class SimpleColumn : public ReturnedColumn
|
||||
void unserialize(messageqcpp::ByteStream&) override;
|
||||
|
||||
const std::string toString() const override;
|
||||
const std::string toString(bool compact) const;
|
||||
std::string toCppCode(IncludeSet& includes) const override;
|
||||
/** @brief Do a deep, strict (as opposed to semantic) equivalence test
|
||||
*
|
||||
|
||||
@@ -29,16 +29,22 @@ using namespace std;
|
||||
|
||||
#include "returnedcolumn.h"
|
||||
#include "constantcolumn.h"
|
||||
#include "simplecolumn.h"
|
||||
#include "operator.h"
|
||||
#include "constantfilter.h"
|
||||
#include "simplefilter.h"
|
||||
#include "bytestream.h"
|
||||
#include "objectreader.h"
|
||||
#include "functioncolumn.h"
|
||||
#include "arithmeticcolumn.h"
|
||||
#include "simplefilter.h"
|
||||
#include "simplecolumn.h"
|
||||
#include "aggregatecolumn.h"
|
||||
#include "arithmeticcolumn.h"
|
||||
#include "functioncolumn.h"
|
||||
#include "constantcolumn.h"
|
||||
#include "selectfilter.h"
|
||||
#include "operator.h"
|
||||
#include "windowfunctioncolumn.h"
|
||||
#include "rowgroup.h"
|
||||
#include "joblisttypes.h"
|
||||
using namespace messageqcpp;
|
||||
using namespace std;
|
||||
using namespace execplan;
|
||||
|
||||
namespace execplan
|
||||
{
|
||||
@@ -250,6 +256,53 @@ const string SimpleFilter::toString() const
|
||||
return output.str();
|
||||
}
|
||||
|
||||
const string SimpleFilter::toString(bool compact) const
|
||||
{
|
||||
if (!compact)
|
||||
{
|
||||
// Use the original detailed format
|
||||
return toString();
|
||||
}
|
||||
|
||||
ostringstream output;
|
||||
|
||||
// Compact format for tree display
|
||||
output << "SimpleFilter(indexflag=" << fIndexFlag;
|
||||
output << " joinFlag= " << fJoinFlag;
|
||||
output << " card= " << fCardinality << ")";
|
||||
|
||||
// Handle different types of left operands with compact formatting
|
||||
SimpleColumn* sc = dynamic_cast<SimpleColumn*>(fLhs);
|
||||
AggregateColumn* ac = dynamic_cast<AggregateColumn*>(fLhs);
|
||||
|
||||
if (ac)
|
||||
{
|
||||
string acCompact = ac->toString(true);
|
||||
if (!acCompact.empty())
|
||||
{
|
||||
output << endl << acCompact;
|
||||
}
|
||||
}
|
||||
else if (sc)
|
||||
{
|
||||
string scCompact = sc->toString(true);
|
||||
if (!scCompact.empty())
|
||||
{
|
||||
output << endl << scCompact;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For other operand types, show them compactly
|
||||
output << endl << "LHS: " << fLhs->data();
|
||||
}
|
||||
|
||||
output << endl << "Op: " << fOp->data();
|
||||
output << endl << "RHS: " << fRhs->data();
|
||||
|
||||
return output.str();
|
||||
}
|
||||
|
||||
string SimpleFilter::toCppCode(IncludeSet& includes) const
|
||||
{
|
||||
includes.insert("simplefilter.h");
|
||||
|
||||
@@ -132,6 +132,7 @@ class SimpleFilter : public Filter
|
||||
void rhs(ReturnedColumn* rhs);
|
||||
|
||||
const std::string toString() const override;
|
||||
const std::string toString(bool compact) const;
|
||||
|
||||
/**
|
||||
* The serialization interface
|
||||
|
||||
Reference in New Issue
Block a user