1
0
mirror of https://github.com/mariadb-corporation/mariadb-columnstore-engine.git synced 2025-07-30 19:23:07 +03:00

Replace recursion with iteration in ParseTree (and some related walkers)

This commit is contained in:
mariadb-AndreyPiskunov
2023-04-18 17:34:54 +03:00
parent 765dd46b61
commit 6ff121a91c
5 changed files with 404 additions and 111 deletions

View File

@ -195,33 +195,102 @@ void ObjectReader::writeParseTree(const ParseTree* tree, messageqcpp::ByteStream
return; return;
} }
b << (id_t)PARSETREE; DFSStack stack;
writeParseTree(tree->left(), b); stack.emplace_back(const_cast<ParseTree*>(tree));
writeParseTree(tree->right(), b); while (!stack.empty())
{
if (tree->data() == NULL) auto [node, dir] = stack.back();
b << (id_t)NULL_CLASS; if (node == NULL)
else {
tree->data()->serialize(b); b << (id_t)NULL_CLASS;
stack.pop_back();
continue;
}
if (dir == ParseTree::GoTo::Left)
{
b << (id_t)PARSETREE;
stack.back().direction = ParseTree::GoTo::Right;
stack.emplace_back(node->left());
}
else if (dir == ParseTree::GoTo::Right)
{
stack.back().direction = ParseTree::GoTo::Up;
stack.emplace_back(node->right());
}
else
{
if (node->data() == NULL)
b << (id_t)NULL_CLASS;
else
node->data()->serialize(b);
stack.pop_back();
}
}
} }
ParseTree* ObjectReader::createParseTree(messageqcpp::ByteStream& b) ParseTree* ObjectReader::createParseTree(messageqcpp::ByteStream& b)
{ {
CLASSID id = ZERO; CLASSID id = ZERO;
ParseTree* ret; ParseTree* ret;
DFSStack stack;
b >> (id_t&)id; b >> (id_t&)id;
if (id == NULL_CLASS) if (id == NULL_CLASS)
return NULL; return NULL;
if (id != PARSETREE) if (id != PARSETREE)
throw UnserializeException("Not a ParseTree"); throw UnserializeException("Not a ParseTree");
ret = new ParseTree(); ret = new ParseTree();
ret->left(createParseTree(b)); stack.emplace_back(ret);
ret->right(createParseTree(b)); while (!stack.empty())
ret->data(createTreeNode(b)); {
auto [node, dir] = stack.back();
if (dir == ParseTree::GoTo::Left)
{
id = ZERO;
ParseTree* cur = NULL;
b >> (id_t&)id;
if (id == NULL_CLASS)
{
stack.back().node->left(cur);
stack.back().direction = ParseTree::GoTo::Right;
continue;
}
if (id != PARSETREE)
throw UnserializeException("Not a ParseTree");
cur = new ParseTree();
stack.back().direction = ParseTree::GoTo::Right;
stack.back().node->left(cur);
stack.emplace_back(node->left());
}
else if (dir == ParseTree::GoTo::Right)
{
id = ZERO;
ParseTree* cur = NULL;
b >> (id_t&)id;
if (id == NULL_CLASS)
{
stack.back().node->right(cur);
stack.back().direction = ParseTree::GoTo::Up;
continue;
}
if (id != PARSETREE)
throw UnserializeException("Not a ParseTree");
cur = new ParseTree();
stack.back().direction = ParseTree::GoTo::Up;
stack.back().node->right(cur);
stack.emplace_back(node->right());
}
else
{
stack.back().node->data(createTreeNode(b));
stack.pop_back();
}
}
return ret; return ret;
} }

View File

@ -227,6 +227,22 @@ class ParseTree
inline void setDerivedTable(); inline void setDerivedTable();
enum class GoTo
{
Left,
Right,
Up
};
struct StackFrame
{
ParseTree* node;
GoTo direction;
StackFrame(ParseTree* node_, GoTo direction_ = GoTo::Left) : node(node_), direction(direction_)
{
}
};
private: private:
TreeNode* fData; TreeNode* fData;
ParseTree* fLeft; ParseTree* fLeft;
@ -378,65 +394,174 @@ inline ParseTree::ParseTree(const ParseTree& rhs)
copyTree(rhs); copyTree(rhs);
} }
using DFSStack = std::vector<ParseTree::StackFrame>;
inline ParseTree::~ParseTree() inline ParseTree::~ParseTree()
{ {
if (fLeft != NULL) if (fLeft == nullptr && fRight == nullptr)
delete fLeft; {
if (fRight != NULL)
delete fRight;
if (fData != NULL)
delete fData; delete fData;
fData = nullptr;
fLeft = NULL; }
fRight = NULL; else
fData = NULL; {
DFSStack stack;
stack.emplace_back(this);
while (!stack.empty())
{
auto [node, dir] = stack.back();
if (dir == GoTo::Left)
{
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
{
stack.emplace_back(node->fLeft);
}
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
{
stack.emplace_back(node->fRight);
}
}
else
{
if (stack.size() == 1)
{
node->fLeft = nullptr;
node->fRight = nullptr;
delete fData;
fData = nullptr;
stack.pop_back();
}
else
{
node->fLeft = nullptr;
node->fRight = nullptr;
delete node;
stack.pop_back();
}
}
}
}
} }
inline void ParseTree::walk(void (*fn)(ParseTree* n)) const inline void ParseTree::walk(void (*fn)(ParseTree* n)) const
{ {
if (fLeft != 0) DFSStack stack;
fLeft->walk(fn); stack.emplace_back(const_cast<ParseTree*>(this));
if (fRight != 0) while (!stack.empty())
fRight->walk(fn); {
auto [node, dir] = stack.back();
ParseTree* temp = const_cast<ParseTree*>(this); if (dir == GoTo::Left)
fn(temp); {
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
stack.emplace_back(node->fLeft);
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
stack.emplace_back(node->fRight);
}
else
{
ParseTree* temp = const_cast<ParseTree*>(node);
fn(temp);
stack.pop_back();
}
}
} }
inline void ParseTree::walk(void (*fn)(const ParseTree* n)) const inline void ParseTree::walk(void (*fn)(const ParseTree* n)) const
{ {
if (fLeft != 0) DFSStack stack;
fLeft->walk(fn); stack.emplace_back(const_cast<ParseTree*>(this));
if (fRight != 0) while (!stack.empty())
fRight->walk(fn); {
auto [node, dir] = stack.back();
fn(this); if (dir == GoTo::Left)
{
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
stack.emplace_back(node->fLeft);
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
stack.emplace_back(node->fRight);
}
else
{
ParseTree* temp = const_cast<ParseTree*>(node);
fn(temp);
stack.pop_back();
}
}
} }
inline void ParseTree::walk(void (*fn)(const ParseTree* n, std::ostream& output), std::ostream& output) const inline void ParseTree::walk(void (*fn)(const ParseTree* n, std::ostream& output), std::ostream& output) const
{ {
if (fLeft != 0) DFSStack stack;
fLeft->walk(fn, output); stack.emplace_back(const_cast<ParseTree*>(this));
if (fRight != 0) while (!stack.empty())
fRight->walk(fn, output); {
auto [node, dir] = stack.back();
fn(this, output); if (dir == GoTo::Left)
{
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
stack.emplace_back(node->fLeft);
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
stack.emplace_back(node->fRight);
}
else
{
ParseTree* temp = const_cast<ParseTree*>(node);
fn(temp, output);
stack.pop_back();
}
}
} }
inline void ParseTree::walk(void (*fn)(const ParseTree* n, void* obj), void* obj) const inline void ParseTree::walk(void (*fn)(const ParseTree* n, void* obj), void* obj) const
{ {
if (fLeft != 0) DFSStack stack;
fLeft->walk(fn, obj); stack.emplace_back(const_cast<ParseTree*>(this));
if (fRight != 0) while (!stack.empty())
fRight->walk(fn, obj); {
auto [node, dir] = stack.back();
fn(this, obj); if (dir == GoTo::Left)
{
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
stack.emplace_back(node->fLeft);
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
stack.emplace_back(node->fRight);
}
else
{
ParseTree* temp = const_cast<ParseTree*>(node);
fn(temp, obj);
stack.pop_back();
}
}
} }
inline std::string ParseTree::toString() const inline std::string ParseTree::toString() const
@ -448,13 +573,31 @@ inline std::string ParseTree::toString() const
inline void ParseTree::walk(void (*fn)(ParseTree* n, void* obj), void* obj) const inline void ParseTree::walk(void (*fn)(ParseTree* n, void* obj), void* obj) const
{ {
if (fLeft != 0) DFSStack stack;
fLeft->walk(fn, obj); stack.emplace_back(const_cast<ParseTree*>(this));
if (fRight != 0) while (!stack.empty())
fRight->walk(fn, obj); {
auto [node, dir] = stack.back();
fn(const_cast<ParseTree*>(this), obj); if (dir == GoTo::Left)
{
stack.back().direction = GoTo::Right;
if (node->fLeft != nullptr)
stack.emplace_back(node->fLeft);
}
else if (dir == GoTo::Right)
{
stack.back().direction = GoTo::Up;
if (node->fRight != nullptr)
stack.emplace_back(node->fRight);
}
else
{
ParseTree* temp = const_cast<ParseTree*>(node);
fn(temp, obj);
stack.pop_back();
}
}
} }
inline ParseTree& ParseTree::operator=(const ParseTree& rhs) inline ParseTree& ParseTree::operator=(const ParseTree& rhs)

View File

@ -8,8 +8,6 @@
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#include "rewrites.h" #include "rewrites.h"
#include <typeinfo> #include <typeinfo>
#include "objectreader.h" #include "objectreader.h"
@ -42,7 +40,6 @@ void printContainer(std::ostream& os, const T& container, const std::string& del
os << std::endl; os << std::endl;
} }
using CommonContainer = using CommonContainer =
std::pair<std::set<execplan::ParseTree*, NodeSemanticComparator>, std::set<execplan::ParseTree*>>; std::pair<std::set<execplan::ParseTree*, NodeSemanticComparator>, std::set<execplan::ParseTree*>>;
@ -110,44 +107,104 @@ bool simpleFiltersCmp(const SimpleFilter* left, const SimpleFilter* right)
} }
// Walk the tree and find out common conjuctions // Walk the tree and find out common conjuctions
void collectCommonConjuctions(execplan::ParseTree* root, CommonContainer& accumulator, int level = 0, struct StackFrameWithSet
bool orMeeted = false, bool andParent = false) {
execplan::ParseTree* node;
ParseTree::GoTo direction;
bool orMet;
bool andParent;
CommonContainer localset;
StackFrameWithSet(execplan::ParseTree* node_, ParseTree::GoTo direction_, bool orMet_ = false, bool andParent_ = false)
: node(node_), direction(direction_), orMet(orMet_), andParent(andParent_), localset({{}, {}})
{
}
};
void advanceSetUp(std::vector<StackFrameWithSet>& stack, CommonContainer& accumulator)
{
if (stack.size() == 1)
accumulator = stack.back().localset;
else
{
auto sz = stack.size();
if (operatorType(stack.at(sz - 2).node) == OP_OR)
{
if (stack.at(sz - 2).direction == ParseTree::GoTo::Right)
stack[sz - 2].localset = stack.back().localset;
else
{
CommonContainer intsersection;
std::set_intersection(stack[sz - 2].localset.first.begin(), stack[sz - 2].localset.first.end(),
stack.back().localset.first.begin(), stack.back().localset.first.end(),
std::inserter(intsersection.first, intsersection.first.begin()),
NodeSemanticComparator{});
stack[sz - 2].localset.first = intsersection.first;
}
}
else
{
if (stack.at(sz - 2).direction == ParseTree::GoTo::Right)
stack[sz - 2].localset = stack.back().localset;
else
{
std::set_union(stack[sz - 2].localset.first.begin(), stack[sz - 2].localset.first.end(),
stack.back().localset.first.begin(), stack.back().localset.first.end(),
std::inserter(stack[sz - 2].localset.first, stack[sz - 2].localset.first.begin()),
NodeSemanticComparator{});
}
}
}
}
void collectCommonConjuctions(execplan::ParseTree* root, CommonContainer& accumulator)
{ {
if (root == nullptr) if (root == nullptr)
{ {
return; return;
} }
printTreeLevel(root, level); std::vector<StackFrameWithSet> stack;
// the condition below means leaf node stack.emplace_back(root, ParseTree::GoTo::Left);
if (root->left() == nullptr && root->right() == nullptr && orMeeted && andParent) while (!stack.empty())
{ {
// we want to collect it if it is a child of and node and or node was met before auto [node, dir, orMet, andParent, localset] = stack.back();
if (castToFilter(root))
if (dir == ParseTree::GoTo::Left)
{ {
accumulator.first.insert(root); stack.back().direction = ParseTree::GoTo::Right;
if (node->left() != nullptr)
{
if (operatorType(node) == OP_OR)
stack.emplace_back(node->left(), ParseTree::GoTo::Left, true);
else
stack.emplace_back(node->left(), ParseTree::GoTo::Left, orMet, operatorType(node) == OP_AND);
}
continue;
}
else if (dir == ParseTree::GoTo::Right)
{
stack.back().direction = ParseTree::GoTo::Up;
if (node->right() != nullptr)
{
if (operatorType(node) == OP_OR)
stack.emplace_back(node->right(), ParseTree::GoTo::Left, true);
else
stack.emplace_back(node->right(), ParseTree::GoTo::Left, orMet, operatorType(node) == OP_AND);
}
continue;
}
else
{
if (node->left() == nullptr && node->right() == nullptr && orMet && andParent)
{
if (castToFilter(node))
stack.back().localset.first.insert(node);
}
advanceSetUp(stack, accumulator);
stack.pop_back();
continue;
} }
return;
} }
// we do set intersection for all the lower levels for the or node
if (operatorType(root) == OP_OR)
{
CommonContainer leftAcc;
CommonContainer rightAcc;
collectCommonConjuctions(root->left(), leftAcc, ++level, true, false);
collectCommonConjuctions(root->right(), rightAcc, ++level, true, false);
CommonContainer intersection;
std::set_intersection(leftAcc.first.begin(), leftAcc.first.end(), rightAcc.first.begin(),
rightAcc.first.end(), std::inserter(intersection.first, intersection.first.begin()),
NodeSemanticComparator{});
accumulator = intersection;
return;
}
collectCommonConjuctions(root->left(), accumulator, ++level, orMeeted, operatorType(root) == OP_AND);
collectCommonConjuctions(root->right(), accumulator, ++level, orMeeted, operatorType(root) == OP_AND);
return;
} }
// this utility function creates new and node // this utility function creates new and node
@ -191,20 +248,13 @@ execplan::ParseTree* appendToRoot(execplan::ParseTree* tree, const Common& commo
return result; return result;
} }
enum class GoTo
{
Left,
Right,
Up
};
struct StackFrame struct StackFrame
{ {
execplan::ParseTree** node; execplan::ParseTree** node;
GoTo direction; ParseTree::GoTo direction;
ChildType containsLeft; ChildType containsLeft;
ChildType containsRight; ChildType containsRight;
StackFrame(execplan::ParseTree** node_, GoTo direction_) StackFrame(execplan::ParseTree** node_, ParseTree::GoTo direction_)
: node(node_), direction(direction_), containsLeft(ChildType::Leave), containsRight(ChildType::Leave) : node(node_), direction(direction_), containsLeft(ChildType::Leave), containsRight(ChildType::Leave)
{ {
} }
@ -231,24 +281,24 @@ void deleteOneNode(execplan::ParseTree** node)
} }
// this utility function adds one stack frame to a stack for dfs traversal // this utility function adds one stack frame to a stack for dfs traversal
void addStackFrame(DFSStack& stack, GoTo direction, execplan::ParseTree* node) void addStackFrame(DFSStack& stack, ParseTree::GoTo direction, execplan::ParseTree* node)
{ {
if (direction == GoTo::Left) if (direction == ParseTree::GoTo::Left)
{ {
stack.back().direction = GoTo::Right; stack.back().direction = ParseTree::GoTo::Right;
if (node->left() != nullptr) if (node->left() != nullptr)
{ {
auto left = node->leftRef(); auto left = node->leftRef();
stack.emplace_back(left, GoTo::Left); stack.emplace_back(left, ParseTree::GoTo::Left);
} }
} }
else if (direction == GoTo::Right) else if (direction == ParseTree::GoTo::Right)
{ {
stack.back().direction = GoTo::Up; stack.back().direction = ParseTree::GoTo::Up;
if (node->right() != nullptr) if (node->right() != nullptr)
{ {
auto right = node->rightRef(); auto right = node->rightRef();
stack.emplace_back(right, GoTo::Left); stack.emplace_back(right, ParseTree::GoTo::Left);
} }
} }
} }
@ -258,7 +308,7 @@ void addStackFrame(DFSStack& stack, GoTo direction, execplan::ParseTree* node)
// specified in the stack frame // specified in the stack frame
void replaceContainsTypeFlag(StackFrame& stackframe, ChildType containsflag) void replaceContainsTypeFlag(StackFrame& stackframe, ChildType containsflag)
{ {
if (stackframe.direction == GoTo::Right) if (stackframe.direction == ParseTree::GoTo::Right)
stackframe.containsLeft = containsflag; stackframe.containsLeft = containsflag;
else else
stackframe.containsRight = containsflag; stackframe.containsRight = containsflag;
@ -270,18 +320,18 @@ void fixUpTree(execplan::ParseTree** node, ChildType ltype, ChildType rtype,
{ {
if (ltype == ChildType::Leave) if (ltype == ChildType::Leave)
{ {
if (rtype != ChildType::Leave) // if we don't leave the right node, we replace if (rtype != ChildType::Leave) // if we don't leave the right node, we replace
{ // the parent node with the left child { // the parent node with the left child
execplan::ParseTree* oldNode = *node; execplan::ParseTree* oldNode = *node;
if (rtype == ChildType::Delete) // we delete the node that is a duplicate if (rtype == ChildType::Delete) // we delete the node that is a duplicate
deleteOneNode((*node)->rightRef()); // of something in the common deleteOneNode((*node)->rightRef()); // of something in the common
*node = (*node)->left(); *node = (*node)->left();
deleteOneNode(&oldNode); deleteOneNode(&oldNode);
} }
} }
else else
{ {
if (ltype == ChildType::Delete) // same as above if (ltype == ChildType::Delete) // same as above
deleteOneNode((*node)->leftRef()); deleteOneNode((*node)->leftRef());
if (rtype == ChildType::Leave) // replace the parent with the right child if (rtype == ChildType::Leave) // replace the parent with the right child
{ {
@ -310,11 +360,11 @@ void removeFromTreeIterative(execplan::ParseTree** root, const CommonContainer&
return; return;
DFSStack stack; DFSStack stack;
stack.emplace_back(root, GoTo::Left); stack.emplace_back(root, ParseTree::GoTo::Left);
while (!stack.empty()) while (!stack.empty())
{ {
auto [node, flag, ltype, rtype] = stack.back(); auto [node, flag, ltype, rtype] = stack.back();
if (flag != GoTo::Up) if (flag != ParseTree::GoTo::Up)
{ {
addStackFrame(stack, flag, *node); addStackFrame(stack, flag, *node);
continue; continue;
@ -418,7 +468,6 @@ execplan::OpType oppositeOperator(execplan::OpType op)
return op; return op;
} }
template execplan::ParseTree* extractCommonLeafConjunctionsToRoot<false>(execplan::ParseTree* tree); template execplan::ParseTree* extractCommonLeafConjunctionsToRoot<false>(execplan::ParseTree* tree);
template execplan::ParseTree* extractCommonLeafConjunctionsToRoot<true>(execplan::ParseTree* tree); template execplan::ParseTree* extractCommonLeafConjunctionsToRoot<true>(execplan::ParseTree* tree);

View File

@ -0,0 +1,2 @@
m0
0

File diff suppressed because one or more lines are too long