You've already forked mariadb-columnstore-engine
mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-08-01 06:46:55 +03:00
472 lines
12 KiB
C++
472 lines
12 KiB
C++
/* Copyright (C) 2014 InfiniDB, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; version 2 of
|
|
the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
// $Id: jlf_graphics.cpp 9550 2013-05-17 23:58:07Z xlou $
|
|
|
|
// Cross engine at the top due to MySQL includes
|
|
#define PREFER_MY_CONFIG_H
|
|
#include "crossenginestep.h"
|
|
#include <iostream>
|
|
using namespace std;
|
|
|
|
#include <cstddef>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
|
|
#include "joblist.h"
|
|
#include "jobstep.h"
|
|
#include "primitivestep.h"
|
|
#include "subquerystep.h"
|
|
#include "windowfunctionstep.h"
|
|
#include "tupleaggregatestep.h"
|
|
#include "tupleannexstep.h"
|
|
#include "tuplehashjoin.h"
|
|
#include "tupleunion.h"
|
|
using namespace joblist;
|
|
|
|
#include "jlf_graphics.h"
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wpotentially-evaluated-expression"
|
|
// for warnings on typeid :expression with side effects will be evaluated despite being used as an operand to
|
|
// 'typeid'
|
|
#endif
|
|
|
|
namespace jlf_graphics
|
|
{
|
|
std::string generateDotFileName(const std::string& prefix)
|
|
{
|
|
ostringstream oss;
|
|
struct timeval tvbuf;
|
|
gettimeofday(&tvbuf, 0);
|
|
struct tm tmbuf;
|
|
localtime_r(reinterpret_cast<time_t*>(&tvbuf.tv_sec), &tmbuf);
|
|
oss << prefix << setfill('0') << setw(4) << (tmbuf.tm_year + 1900) << setw(2) << (tmbuf.tm_mon + 1)
|
|
<< setw(2) << (tmbuf.tm_mday) << setw(2) << (tmbuf.tm_hour) << setw(2) << (tmbuf.tm_min) << setw(2)
|
|
<< (tmbuf.tm_sec) << setw(6) << (tvbuf.tv_usec) << ".dot";
|
|
return oss.str();
|
|
}
|
|
|
|
JobStepVector GraphGeneratorInterface::extractSubquerySteps(const SJSTEP& sqs)
|
|
{
|
|
JobStepVector res;
|
|
auto* subQueryStep = dynamic_cast<SubQueryStep*>(sqs.get());
|
|
|
|
if (subQueryStep)
|
|
{
|
|
JobStepVector subQuerySteps;
|
|
auto& stepsBeforeRecursion = subQueryStep->subJoblist()->querySteps();
|
|
for (auto& step : stepsBeforeRecursion)
|
|
{
|
|
auto steps = extractJobSteps(step);
|
|
subQuerySteps.insert(subQuerySteps.end(), steps.begin(), steps.end());
|
|
}
|
|
res.insert(res.end(), subQuerySteps.begin(), subQuerySteps.end());
|
|
}
|
|
res.push_back(sqs);
|
|
return res;
|
|
}
|
|
|
|
// This f() is recursive to handle nested subqueries
|
|
JobStepVector GraphGeneratorInterface::extractJobSteps(const SJSTEP& umbrella)
|
|
{
|
|
JobStepVector res;
|
|
if (typeid(*umbrella) == typeid(SubAdapterStep))
|
|
{
|
|
auto* subAdapterStep = dynamic_cast<SubAdapterStep*>(umbrella.get());
|
|
assert(subAdapterStep);
|
|
auto subQuerySteps = extractSubquerySteps(subAdapterStep->subStep());
|
|
res.insert(res.end(), subQuerySteps.begin(), subQuerySteps.end());
|
|
res.push_back(umbrella);
|
|
}
|
|
|
|
else if (typeid(*umbrella) == typeid(SubQueryStep))
|
|
{
|
|
auto subQuerySteps = extractSubquerySteps(umbrella);
|
|
res.insert(res.end(), subQuerySteps.begin(), subQuerySteps.end());
|
|
}
|
|
else
|
|
{
|
|
res.push_back(umbrella);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
std::string getLadderRepr(const JobStepVector& steps, const std::vector<size_t>& tabsToPretify)
|
|
{
|
|
std::ostringstream oss;
|
|
assert(tabsToPretify.size() == steps.size());
|
|
// Tabs are in the reverse order of the steps
|
|
auto tabsIt = tabsToPretify.begin();
|
|
// Reverse the order of the steps to draw the graph from top to bottom
|
|
for (auto s = steps.rbegin(); s != steps.rend(); ++s, ++tabsIt)
|
|
{
|
|
oss << std::string(*tabsIt, '\t');
|
|
oss << (*s)->extendedInfo() << std::endl;
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
std::string GraphGeneratorInterface::getGraphNode(const SJSTEP& stepPtr)
|
|
{
|
|
auto& step = *stepPtr;
|
|
uint16_t stepidIn = step.stepId();
|
|
std::ostringstream oss;
|
|
oss << stepidIn << " [label=\"st_" << stepidIn << " ";
|
|
|
|
if (typeid(step) == typeid(pColStep))
|
|
{
|
|
oss << "(" << step.tableOid() << "/" << step.oid() << ")" << "\"";
|
|
oss << " shape=ellipse";
|
|
}
|
|
else if (typeid(step) == typeid(pColScanStep))
|
|
{
|
|
oss << "(" << step.tableOid() << "/" << step.oid() << ")" << "\"";
|
|
oss << " shape=box";
|
|
}
|
|
else if (typeid(step) == typeid(TupleBPS))
|
|
{
|
|
bool isTuple = false;
|
|
BatchPrimitive* bps = dynamic_cast<BatchPrimitive*>(stepPtr.get());
|
|
|
|
if (dynamic_cast<TupleBPS*>(bps) != 0)
|
|
isTuple = true;
|
|
|
|
oss << "(" << bps->tableOid() << "/" << bps->oid() << "/" << bps->alias();
|
|
OIDVector projectOids = bps->getProjectOids();
|
|
|
|
if (projectOids.size() > 0)
|
|
{
|
|
oss << "\\l";
|
|
oss << "PC: ";
|
|
}
|
|
|
|
for (unsigned int i = 0; i < projectOids.size(); i++)
|
|
{
|
|
oss << projectOids[i] << " ";
|
|
|
|
if ((i + 1) % 3 == 0)
|
|
oss << "\\l";
|
|
}
|
|
|
|
oss << ")\"";
|
|
oss << " shape=box style=bold";
|
|
|
|
if (isTuple)
|
|
oss << " peripheries=2";
|
|
}
|
|
else if (typeid(step) == typeid(CrossEngineStep))
|
|
{
|
|
CrossEngineStep* cej = dynamic_cast<CrossEngineStep*>(stepPtr.get());
|
|
oss << "(" << cej->tableName() << "/" << cej->tableAlias() << ")\"";
|
|
oss << " shape=cylinder style=bold";
|
|
}
|
|
else if (typeid(step) == typeid(TupleHashJoinStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=diamond peripheries=2";
|
|
}
|
|
else if (typeid(step) == typeid(TupleUnion))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=triangle";
|
|
}
|
|
else if (typeid(step) == typeid(pDictionaryStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=trapezium";
|
|
}
|
|
else if (typeid(step) == typeid(FilterStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=invhouse";
|
|
}
|
|
|
|
else if (typeid(step) == typeid(TupleAggregateStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=invtriangle";
|
|
}
|
|
else if (typeid(step) == typeid(TupleAnnexStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=star";
|
|
}
|
|
else if (typeid(step) == typeid(WindowFunctionStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=invtriangle";
|
|
oss << " peripheries=2";
|
|
}
|
|
else if (typeid(step) == typeid(SubAdapterStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=polygon";
|
|
oss << " peripheries=2";
|
|
}
|
|
else if (typeid(step) == typeid(SubQueryStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=polygon";
|
|
}
|
|
else
|
|
{
|
|
oss << "\"";
|
|
}
|
|
|
|
oss << "]" << endl;
|
|
return oss.str();
|
|
}
|
|
|
|
std::pair<size_t, std::string> GraphGeneratorInterface::getTabsAndEdges(
|
|
const JobStepVector& querySteps, const JobStepVector& projectSteps, const SJSTEP& stepPtr,
|
|
const std::vector<size_t>& tabsToPretify)
|
|
{
|
|
auto& step = *stepPtr;
|
|
uint16_t stepidIn = step.stepId();
|
|
std::ostringstream oss;
|
|
size_t tab = 0;
|
|
|
|
for (unsigned int i = 0; i < step.outputAssociation().outSize(); i++)
|
|
{
|
|
ptrdiff_t dloutptr = 0;
|
|
auto* dlout = step.outputAssociation().outAt(i)->rowGroupDL();
|
|
uint32_t numConsumers = step.outputAssociation().outAt(i)->getNumConsumers();
|
|
|
|
if (dlout)
|
|
{
|
|
dloutptr = (ptrdiff_t)dlout;
|
|
}
|
|
|
|
for (auto it = querySteps.rbegin(); it != querySteps.rend(); ++it)
|
|
{
|
|
auto& otherStep = *it;
|
|
// Reverse order idx
|
|
auto otherIdx = std::distance(querySteps.rbegin(), it);
|
|
uint16_t stepidOut = otherStep.get()->stepId();
|
|
JobStepAssociation queryInputSA = otherStep.get()->inputAssociation();
|
|
|
|
for (unsigned int j = 0; j < queryInputSA.outSize(); j++)
|
|
{
|
|
ptrdiff_t dlinptr = 0;
|
|
auto* dlin = queryInputSA.outAt(j)->rowGroupDL();
|
|
|
|
if (dlin)
|
|
{
|
|
dlinptr = (ptrdiff_t)dlin;
|
|
}
|
|
|
|
if ((ptrdiff_t)dloutptr == (ptrdiff_t)dlinptr)
|
|
{
|
|
oss << stepidIn << " -> " << stepidOut;
|
|
|
|
if (dlin)
|
|
{
|
|
oss << " [label=\"[" << numConsumers << "]\"]" << endl;
|
|
tab = tabsToPretify[otherIdx] + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& otherProjectStep : projectSteps)
|
|
{
|
|
uint16_t stepidOut = otherProjectStep->stepId();
|
|
JobStepAssociation projectInputSA = otherProjectStep->inputAssociation();
|
|
|
|
for (unsigned int j = 0; j < projectInputSA.outSize(); j++)
|
|
{
|
|
ptrdiff_t dlinptr = 0;
|
|
auto* dlin = projectInputSA.outAt(j)->rowGroupDL();
|
|
|
|
if (dlin)
|
|
dlinptr = (ptrdiff_t)dlin;
|
|
|
|
if (dloutptr == dlinptr)
|
|
{
|
|
oss << stepidIn << " -> " << stepidOut;
|
|
|
|
if (dlin)
|
|
{
|
|
oss << " [label=\"[" << numConsumers << "]\"]" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return {tab, oss.str()};
|
|
}
|
|
|
|
std::string GraphGeneratorInterface::getGraphProjectionNode(SJSTEP& step)
|
|
{
|
|
std::ostringstream oss;
|
|
uint16_t stepidIn = step->stepId();
|
|
oss << stepidIn << " [label=\"st_" << stepidIn << " ";
|
|
|
|
if (typeid(*(step)) == typeid(pColStep))
|
|
{
|
|
oss << "(" << step->tableOid() << "/" << step->oid() << ")" << "\"";
|
|
oss << " shape=ellipse";
|
|
}
|
|
else if (typeid(*(step)) == typeid(pColScanStep))
|
|
{
|
|
oss << "(" << step->tableOid() << "/" << step->oid() << ")" << "\"";
|
|
oss << " shape=box";
|
|
}
|
|
else if (typeid(*(step)) == typeid(pDictionaryStep))
|
|
{
|
|
oss << "\"";
|
|
oss << " shape=trapezium";
|
|
}
|
|
else if (typeid(*(step)) == typeid(PassThruStep))
|
|
{
|
|
oss << "(" << step->tableOid() << "/" << step->oid() << ")" << "\"";
|
|
oss << " shape=octagon";
|
|
}
|
|
else if (typeid(*(step)) == typeid(TupleBPS))
|
|
{
|
|
bool isTuple = false;
|
|
BatchPrimitive* bps = dynamic_cast<BatchPrimitive*>(step.get());
|
|
|
|
if (dynamic_cast<TupleBPS*>(bps) != 0)
|
|
isTuple = true;
|
|
|
|
oss << "(" << bps->tableOid() << ":\\l";
|
|
OIDVector projectOids = bps->getProjectOids();
|
|
|
|
for (unsigned int i = 0; i < projectOids.size(); i++)
|
|
{
|
|
oss << projectOids[i] << " ";
|
|
|
|
if ((i + 1) % 3 == 0)
|
|
oss << "\\l";
|
|
}
|
|
|
|
oss << ")\"";
|
|
oss << " shape=box style=bold";
|
|
|
|
if (isTuple)
|
|
oss << " peripheries=2";
|
|
}
|
|
else if (typeid(*(step)) == typeid(CrossEngineStep))
|
|
{
|
|
CrossEngineStep* cej = dynamic_cast<CrossEngineStep*>(step.get());
|
|
oss << "(" << cej->tableName() << "/" << cej->tableAlias() << ")\"";
|
|
oss << " shape=cylinder style=bold";
|
|
}
|
|
else
|
|
oss << "\"";
|
|
|
|
oss << "]" << endl;
|
|
return oss.str();
|
|
}
|
|
|
|
std::string GraphGeneratorInterface::getProjectionEdges(JobStepVector& steps, SJSTEP& step,
|
|
const std::size_t ctn)
|
|
{
|
|
uint16_t stepidIn = step->stepId();
|
|
std::ostringstream oss;
|
|
|
|
for (unsigned int i = 0; i < step->outputAssociation().outSize(); i++)
|
|
{
|
|
ptrdiff_t dloutptr = 0;
|
|
auto* dlout = step->outputAssociation().outAt(i)->rowGroupDL();
|
|
uint32_t numConsumers = step->outputAssociation().outAt(i)->getNumConsumers();
|
|
|
|
if (dlout)
|
|
{
|
|
dloutptr = (ptrdiff_t)dlout;
|
|
}
|
|
|
|
for (auto k = ctn + 1; k < steps.size(); k++)
|
|
{
|
|
uint16_t stepidOut = steps[k].get()->stepId();
|
|
JobStepAssociation projectInputSA = steps[k].get()->inputAssociation();
|
|
|
|
for (unsigned int j = 0; j < projectInputSA.outSize(); j++)
|
|
{
|
|
ptrdiff_t dlinptr = 0;
|
|
auto* dlin = projectInputSA.outAt(j)->rowGroupDL();
|
|
|
|
if (dlin)
|
|
dlinptr = (ptrdiff_t)dlin;
|
|
|
|
if ((ptrdiff_t)dloutptr == (ptrdiff_t)dlinptr)
|
|
{
|
|
oss << stepidIn << " -> " << stepidOut;
|
|
|
|
if (dlin)
|
|
{
|
|
oss << " [label=\"[" << numConsumers << "]\"]" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
std::string GraphGeneratorInterface::writeDotCmds()
|
|
{
|
|
// Graphic view draw
|
|
std::ostringstream oss;
|
|
oss << "digraph G {" << endl;
|
|
|
|
// merge in the subquery steps
|
|
JobStepVector querySteps;
|
|
for (auto& step : query)
|
|
{
|
|
auto steps = extractJobSteps(step);
|
|
querySteps.insert(querySteps.end(), steps.begin(), steps.end());
|
|
}
|
|
|
|
JobStepVector projectSteps = project;
|
|
std::vector<size_t> tabsToPretify;
|
|
// Reverse the order of the steps to draw the graph from top to bottom
|
|
for (auto it = querySteps.rbegin(); it != querySteps.rend(); ++it)
|
|
{
|
|
auto& step = *it;
|
|
oss << getGraphNode(step);
|
|
auto [tab, graphEdges] = getTabsAndEdges(querySteps, projectSteps, step, tabsToPretify);
|
|
tabsToPretify.push_back(tab);
|
|
oss << graphEdges;
|
|
}
|
|
|
|
for (auto psi = projectSteps.begin(); psi != projectSteps.end(); ++psi)
|
|
{
|
|
auto& step = *psi;
|
|
oss << getGraphProjectionNode(step);
|
|
auto idx = std::distance(projectSteps.begin(), psi);
|
|
oss << getProjectionEdges(projectSteps, step, idx);
|
|
}
|
|
|
|
oss << "}" << endl;
|
|
|
|
auto ladderRepr = getLadderRepr(querySteps, tabsToPretify);
|
|
cout << endl;
|
|
cout << ladderRepr;
|
|
|
|
return oss.str();
|
|
}
|
|
|
|
} // end namespace jlf_graphics
|
|
|
|
#ifdef __clang__
|
|
#pragma clang diagnostic pop
|
|
#endif
|