1
0
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:
Leonid Fedorov
2025-08-19 16:18:06 +04:00
committed by GitHub
parent 56d04cb711
commit 98a301c1c5
7 changed files with 283 additions and 37 deletions

View File

@@ -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();
}
@@ -588,15 +624,15 @@ void AggregateColumn::evaluate(Row& row, bool& isNull)
case CalpontSystemCatalog::VARBINARY:
case CalpontSystemCatalog::BLOB:
{
auto const str = row.getConstString(fInputIndex);
fResult.strVal.dropString();
if (!str.isNull())
fResult.strVal.assign((const uint8_t*)str.str(), str.length());
{
auto const str = row.getConstString(fInputIndex);
fResult.strVal.dropString();
if (!str.isNull())
fResult.strVal.assign((const uint8_t*)str.str(), str.length());
isNull = isNull || fResult.strVal.isNull();
}
break;
isNull = isNull || fResult.strVal.isNull();
}
break;
default: // treat as int64
if (row.equals<8>(BIGINTNULL, fInputIndex))

View File

@@ -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;
/**

View File

@@ -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,12 +201,140 @@ 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
CalpontSelectExecutionPlan*& plan) const
{
if (plan)
{
@@ -243,7 +375,7 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
for (unsigned int i = 0; i < retCols.size(); i++)
{
output << endlWithIndent(ident+2) << *retCols[i]; // WIP replace with constant
output << endlWithIndent(ident + 2) << *retCols[i]; // WIP replace with constant
if (retCols[i]->colSource() & SELECT_SUB)
{
@@ -257,7 +389,7 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
// From Clause
CalpontSelectExecutionPlan::TableList tables = tableList();
output << endlWithIndent(ident) <<">>From Tables";
output << endlWithIndent(ident) << ">>From Tables";
seq = 0;
for (unsigned int i = 0; i < tables.size(); i++)
@@ -265,7 +397,7 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
// derived table
if (tables[i].schema.length() == 0 && tables[i].table.length() == 0)
{
output << endlWithIndent(ident+2) << "derived table - " << tables[i].alias;
output << endlWithIndent(ident + 2) << "derived table - " << tables[i].alias;
CalpontSelectExecutionPlan* plan =
dynamic_cast<CalpontSelectExecutionPlan*>(fDerivedTableList[seq++].get());
@@ -273,7 +405,7 @@ string CalpontSelectExecutionPlan::toString(const size_t ident) const
}
else
{
output << endlWithIndent(ident+2) << tables[i];
output << endlWithIndent(ident + 2) << tables[i];
}
}
@@ -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));

View File

@@ -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");

View File

@@ -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
*

View File

@@ -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");

View File

@@ -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