8000 Feature/explain spliced subqueries (#10298) · arangodb/arangodb@bf2423d · GitHub
[go: up one dir, main page]

Skip to content

Commit bf2423d

Browse files
authored
Feature/explain spliced subqueries (#10298)
* Implemented explain output for Subquery start and End nodes. Unfortunately i had to inject a temporary output variable for the Subquery start.
1 parent 4413ba5 commit bf2423d

File tree

5 files changed

+41
-9
lines changed

5 files changed

+41
-9
lines changed

arangod/Aql/OptimizerRules.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7277,7 +7277,7 @@ void arangodb::aql::spliceSubqueriesRule(Optimizer* opt, std::unique_ptr<Executi
72777277
auto sq = ExecutionNode::castTo<SubqueryNode*>(n);
72787278

72797279
// insert a SubqueryStartNode before the SubqueryNode
7280-
SubqueryStartNode* start = new SubqueryStartNode(plan.get(), plan->nextId());
7280+
SubqueryStartNode* start = new SubqueryStartNode(plan.get(), plan->nextId(), sq->outVariable());
72817281
plan->registerNode(start);
72827282
plan->insertBefore(sq, start);
72837283

arangod/Aql/SubqueryStartExecutionNode.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ namespace aql {
4141

4242
SubqueryStartNode::SubqueryStartNode(ExecutionPlan* plan,
4343
arangodb::velocypack::Slice const& base)
44-
: ExecutionNode(plan, base) {}
44+
: ExecutionNode(plan, base), _subqueryOutVariable(nullptr) {
45+
// On purpose exclude the _subqueryOutVariable
46+
// A query cannot be explained after nodes have been serialized and deserialized
47+
}
4548

4649
CostEstimate SubqueryStartNode::estimateCost() const {
4750
TRI_ASSERT(!_dependencies.empty());
@@ -53,6 +56,11 @@ CostEstimate SubqueryStartNode::estimateCost() const {
5356
void SubqueryStartNode::toVelocyPackHelper(VPackBuilder& nodes, unsigned flags,
5457
std::unordered_set<ExecutionNode const*>& seen) const {
5558
ExecutionNode::toVelocyPackHelperGeneric(nodes, flags, seen);
59+
// We need this for the Explainer
60+
if (_subqueryOutVariable != nullptr) {
61+
nodes.add(VPackValue("subqueryOutVariable"));
62+
_subqueryOutVariable->toVelocyPack(nodes);
63+
}
5664
nodes.close();
5765
}
5866

@@ -70,17 +78,20 @@ std::unique_ptr<ExecutionBlock> SubqueryStartNode::createBlock(
7078
getRegisterPlan()->nrRegs[previousNode->getDepth()],
7179
getRegisterPlan()->nrRegs[getDepth()], getRegsToClear(),
7280
calcRegsToKeep());
81+
// On purpose exclude the _subqueryOutVariable
7382
return std::make_unique<ExecutionBlockImpl<SubqueryStartExecutor>>(&engine, this,
7483
std::move(infos));
7584
}
7685

7786
ExecutionNode* SubqueryStartNode::clone(ExecutionPlan* plan, bool withDependencies,
7887
bool withProperties) const {
79-
auto c = std::make_unique<SubqueryStartNode>(plan, _id);
88+
// On purpose exclude the _subqueryOutVariable
89+
auto c = std::make_unique<SubqueryStartNode>(plan, _id, nullptr);
8090
return cloneHelper(std::move(c), withDependencies, withProperties);
8191
}
8292

8393
bool SubqueryStartNode::isEqualTo(ExecutionNode const& other) const {
94+
// On purpose exclude the _subqueryOutVariable
8495
try {
8596
SubqueryStartNode const& p = dynamic_cast<SubqueryStartNode const&>(other);
8697
return ExecutionNode::isEqualTo(p);

arangod/Aql/SubqueryStartExecutionNode.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@
3030
namespace arangodb {
3131
namespace aql {
3232

33+
struct Variable;
34+
3335
class SubqueryStartNode : public ExecutionNode {
3436
friend class ExecutionNode;
3537
friend class ExecutionBlock;
3638

3739
public:
3840
SubqueryStartNode(ExecutionPlan*, arangodb::velocypack::Slice const& base);
39-
SubqueryStartNode(ExecutionPlan* plan, size_t id) : ExecutionNode(plan, id) {}
41+
SubqueryStartNode(ExecutionPlan* plan, size_t id, Variable const* subqueryOutVariable)
42+
: ExecutionNode(plan, id), _subqueryOutVariable(subqueryOutVariable) {}
4043

4144
CostEstimate estimateCost() const override final;
4245

@@ -53,6 +56,11 @@ class SubqueryStartNode : public ExecutionNode {
5356
bool withProperties) const override final;
5457

5558
bool isEqualTo(ExecutionNode const& other) const override final;
59+
60+
private:
61+
/// @brief This is only required for Explain output.
62+
/// it has no practical usage other then to print this information during explain.
63+
Variable const* _subqueryOutVariable;
5664
};
5765

5866
} // namespace aql

js/common/modules/@arangodb/aql/explainer.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,11 @@ function processQuery(query, explain, planIndex) {
821821
return variable(node.name);
822822
};
823823

824+
var lateVariableCallback = function () {
825+
require("internal").print("Called");
826+
return (node) => variableName(node);
827+
};
828+
824829
var buildExpression = function (node) {
825830
var binaryOperator = function (node, name) {
826831
var lhs = buildExpression(node.subNodes[0]);
@@ -1464,6 +1469,10 @@ function processQuery(query, explain, planIndex) {
14641469
return keyword('RETURN') + ' ' + variableName(node.inVariable);
14651470
case 'SubqueryNode':
14661471
return keyword('LET') + ' ' + variableN F438 ame(node.outVariable) + ' = ... ' + annotation('/* ' + (node.isConst ? 'const ' : '') + 'subquery */');
1472+
case 'SubqueryStartNode':
1473+
return `${keyword('LET')} ${variableName(node.subqueryOutVariable)} = ( ${annotation(`/* subquery begin */`)}` ;
1474+
case 'SubqueryEndNode':
1475+
return `) ${annotation(`/* subquery end */`)}`;
14671476
case 'InsertNode': {
14681477
modificationFlags = node.modificationFlags;
14691478
let restrictString = '';
@@ -1653,7 +1662,7 @@ function processQuery(query, explain, planIndex) {
16531662
return 'unhandled node type (' + node.type + ')';
16541663
};
16551664

1656-
var level = 0, subqueries = [];
1665+
var level = 0, subqueries = [], subqueryCallbacks = [];
16571666
var indent = function (level, isRoot) {
16581667
return pad(1 + level + level) + (isRoot ? '* ' : '- ');
16591668
};
@@ -1662,9 +1671,12 @@ function processQuery(query, explain, planIndex) {
16621671
usedVariables = {};
16631672
currentNode = node.id;
16641673
isConst = true;
1665-
if (node.type === 'SubqueryNode') {
1674+
if (node.type === 'SubqueryNode' || node.type === 'SubqueryStartNode') {
16661675
subqueries.push(level);
16671676
}
1677+
if (node.type === 'SubqueryEndNode' && subqueries.length > 0) {
1678+
level = subqueries.pop();
1679+
}
16681680
};
16691681

16701682
var postHandle = function (node) {
@@ -1676,6 +1688,7 @@ function processQuery(query, explain, planIndex) {
16761688
'IndexRangeNode',
16771689
'IndexNode',
16781690
'TraversalNode',
1691+
'SubqueryStartNode',
16791692
'SubqueryNode'].indexOf(node.type) !== -1) {
16801693
level++;
16811694
} else if (isLeafNode && subqueries.length > 0) {

tests/Aql/ExecutionNodeTest.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ TEST_F(ExecutionNodeTest, start_node_velocypack_roundtrip) {
5959
std::unique_ptr<SubqueryStartNode> node, nodeFromVPack;
6060
std::unordered_set<ExecutionNode const*> seen{};
6161

62-
node = std::make_unique<SubqueryStartNode>(&plan, 0);
62+
node = std::make_unique<SubqueryStartNode>(&plan, 0, nullptr);
6363

6464
builder.openArray();
6565
node->toVelocyPackHelper(builder, ExecutionNode::SERIALIZE_DETAILS, seen);
@@ -75,8 +75,8 @@ TEST_F(ExecutionNodeTest, start_node_velocypack_roundtrip) {
7575
TEST_F(ExecutionNodeTest, start_node_not_equal_different_id) {
7676
std::unique_ptr<SubqueryStartNode> node1, node2;
7777

78-
node1 = std::make_unique<SubqueryStartNode>(&plan, 0);
79-
node2 = std::make_unique<SubqueryStartNode>(&plan, 1);
78+
node1 = std::make_unique<SubqueryStartNode>(&plan, 0, nullptr);
79+
node2 = std::make_unique<SubqueryStartNode>(&plan, 1, nullptr);
8080

8181
ASSERT_FALSE(node1->isEqualTo(*node2));
8282
}

0 commit comments

Comments
 (0)
0