1
0
Fork 0
This commit is contained in:
Jan Steemann 2016-12-07 12:18:34 +01:00
parent b8c72dece8
commit d2742f2ef3
5 changed files with 133 additions and 66 deletions

View File

@ -1634,6 +1634,7 @@ void Ast::validateAndOptimize() {
struct TraversalContext {
std::unordered_set<std::string> writeCollectionsSeen;
std::unordered_map<std::string, int64_t> collectionsFirstSeen;
std::unordered_map<Variable const*, AstNode const*> variableDefinitions;
int64_t stopOptimizationRequests = 0;
int64_t nestingLevel = 0;
bool isInFilter = false;
@ -1769,7 +1770,7 @@ void Ast::validateAndOptimize() {
// attribute access
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
return this->optimizeAttributeAccess(node);
return this->optimizeAttributeAccess(node, static_cast<TraversalContext*>(data)->variableDefinitions);
}
// passthru node
@ -1813,6 +1814,22 @@ void Ast::validateAndOptimize() {
// LET
if (node->type == NODE_TYPE_LET) {
// remember variable assignments
TRI_ASSERT(node->numMembers() == 2);
auto context = static_cast<TraversalContext*>(data);
Variable const* variable = static_cast<Variable const*>(node->getMember(0)->getData());
AstNode const* definition = node->getMember(1);
// recursively process assignments so we can track LET a = b LET c = b
while (definition->type == NODE_TYPE_REFERENCE) {
auto it = context->variableDefinitions.find(static_cast<Variable const*>(definition->getData()));
if (it == context->variableDefinitions.end()) {
break;
}
definition = (*it).second;
}
context->variableDefinitions.emplace(variable, definition);
return this->optimizeLet(node);
}
@ -2669,12 +2686,21 @@ AstNode* Ast::optimizeTernaryOperator(AstNode* node) {
}
/// @brief optimizes an attribute access
AstNode* Ast::optimizeAttributeAccess(AstNode* node) {
AstNode* Ast::optimizeAttributeAccess(AstNode* node, std::unordered_map<Variable const*, AstNode const*> const& variableDefinitions) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
TRI_ASSERT(node->numMembers() == 1);
AstNode* what = node->getMember(0);
AstNode const* what = node->getMember(0);
if (what->type == NODE_TYPE_REFERENCE) {
// check if the access value is a variable and if it is an alias
auto it = variableDefinitions.find(static_cast<Variable const*>(what->getData()));
if (it != variableDefinitions.end()) {
what = (*it).second;
}
}
if (!what->isConstant()) {
return node;
@ -2689,6 +2715,7 @@ AstNode* Ast::optimizeAttributeAccess(AstNode* node) {
for (size_t i = 0; i < n; ++i) {
AstNode const* member = what->getMember(0);
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
member->getStringLength() == length &&
memcmp(name, member->getStringValue(), length) == 0) {
@ -2943,6 +2970,8 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
node->addMember(nodeFromVPack(it, copyStringValues));
}
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
return node;
}
@ -2964,12 +2993,69 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
attributeName, static_cast<size_t>(nameLength), nodeFromVPack(it.value, copyStringValues)));
}
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
return node;
}
return createNodeValueNull();
}
/// @brief resolve an attribute access
AstNode const* Ast::resolveConstAttributeAccess(AstNode const* node) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
std::vector<std::string> attributeNames;
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
attributeNames.emplace_back(node->getString());
node = node->getMember(0);
}
size_t which = attributeNames.size();
TRI_ASSERT(which > 0);
while (which > 0) {
TRI_ASSERT(node->type == NODE_TYPE_VALUE || node->type == NODE_TYPE_ARRAY ||
node->type == NODE_TYPE_OBJECT);
bool found = false;
if (node->type == NODE_TYPE_OBJECT) {
TRI_ASSERT(which > 0);
std::string const& attributeName = attributeNames[which - 1];
--which;
size_t const n = node->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = node->getMember(i);
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
member->getString() == attributeName) {
// found the attribute
node = member->getMember(0);
if (which == 0) {
// we found what we looked for
return node;
}
// we found the correct attribute but there is now an attribute
// access on the result
found = true;
break;
}
}
}
if (!found) {
break;
}
}
// attribute not found or non-array
return createNodeValueNull();
}
/// @brief traverse the AST, using pre- and post-order visitors
AstNode* Ast::traverseAndModify(
AstNode* node, std::function<bool(AstNode const*, void*)> preVisitor,

View File

@ -413,6 +413,9 @@ class Ast {
/// @brief create an AST node from vpack
AstNode* nodeFromVPack(arangodb::velocypack::Slice const&, bool);
/// @brief resolve an attribute access
static AstNode const* resolveConstAttributeAccess(AstNode const*);
/// @brief traverse the AST using a depth-first visitor
static AstNode* traverseAndModify(AstNode*,
std::function<AstNode*(AstNode*, void*)>,
@ -457,7 +460,7 @@ class Ast {
AstNode* optimizeTernaryOperator(AstNode*);
/// @brief optimizes an attribute access
AstNode* optimizeAttributeAccess(AstNode*);
AstNode* optimizeAttributeAccess(AstNode*, std::unordered_map<Variable const*, AstNode const*> const&);
/// @brief optimizes a call to a built-in function
AstNode* optimizeFunctionCall(AstNode*);

View File

@ -187,61 +187,6 @@ std::unordered_map<int, std::string const> const AstNode::ValueTypeNames{
namespace {
/// @brief resolve an attribute access
static AstNode const* ResolveAttribute(AstNode const* node) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
std::vector<std::string> attributeNames;
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
attributeNames.emplace_back(node->getString());
node = node->getMember(0);
}
size_t which = attributeNames.size();
TRI_ASSERT(which > 0);
while (which > 0) {
TRI_ASSERT(node->type == NODE_TYPE_VALUE || node->type == NODE_TYPE_ARRAY ||
node->type == NODE_TYPE_OBJECT);
bool found = false;
if (node->type == NODE_TYPE_OBJECT) {
TRI_ASSERT(which > 0);
std::string const& attributeName = attributeNames[which - 1];
--which;
size_t const n = node->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = node->getMember(i);
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
member->getString() == attributeName) {
// found the attribute
node = member->getMember(0);
if (which == 0) {
// we found what we looked for
return node;
}
// we found the correct attribute but there is now an attribute
// access on the result
found = true;
break;
}
}
}
if (!found) {
break;
}
}
// attribute not found or non-array
return Ast::createNodeValueNull();
}
/// @brief get the node type for inter-node comparisons
static VPackValueType GetNodeCompareType(AstNode const* node) {
TRI_ASSERT(node != nullptr);
@ -276,10 +221,10 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
TRI_ASSERT(rhs != nullptr);
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
lhs = ResolveAttribute(lhs);
lhs = Ast::resolveConstAttributeAccess(lhs);
}
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
rhs = ResolveAttribute(rhs);
rhs = Ast::resolveConstAttributeAccess(rhs);
}
auto lType = GetNodeCompareType(lhs);
@ -419,6 +364,7 @@ AstNode::AstNode(bool v, AstNodeValueType valueType)
value.value._bool = v;
TRI_ASSERT(flags == 0);
TRI_ASSERT(computedValue == nullptr);
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
}
/// @brief create an int node, with defining a value
@ -428,6 +374,7 @@ AstNode::AstNode(int64_t v, AstNodeValueType valueType)
value.value._int = v;
TRI_ASSERT(flags == 0);
TRI_ASSERT(computedValue == nullptr);
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
}
/// @brief create a string node, with defining a value
@ -437,6 +384,7 @@ AstNode::AstNode(char const* v, size_t length, AstNodeValueType valueType)
setStringValue(v, length);
TRI_ASSERT(flags == 0);
TRI_ASSERT(computedValue == nullptr);
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
}
/// @brief create the node from VPack

View File

@ -722,14 +722,20 @@ void Condition::optimize(ExecutionPlan* plan) {
auto operand = andNode->getMemberUnchecked(j);
if (operand->isComparisonOperator()) {
auto lhs = operand->getMember(0);
auto rhs = operand->getMember(1);
AstNode const* lhs = operand->getMember(0);
AstNode const* rhs = operand->getMember(1);
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
if (lhs->isConstant()) {
lhs = Ast::resolveConstAttributeAccess(lhs);
}
storeAttributeAccess(varAccess, variableUsage, lhs, j, ATTRIBUTE_LEFT);
}
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
rhs->type == NODE_TYPE_EXPANSION) {
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && rhs->isConstant()) {
rhs = Ast::resolveConstAttributeAccess(rhs);
}
storeAttributeAccess(varAccess, variableUsage, rhs, j, ATTRIBUTE_RIGHT);
}
}

View File

@ -53,6 +53,30 @@ IndexBlock::IndexBlock(ExecutionEngine* engine, IndexNode const* en)
_hasV8Expression(false) {
_mmdr.reset(new ManagedDocumentResult(transaction()));
if (_condition != nullptr) {
// fix const attribute accesses, e.g. { "a": 1 }.a
for (size_t i = 0; i < _condition->numMembers(); ++i) {
auto andCond = _condition->getMemberUnchecked(i);
for (size_t j = 0; j < andCond->numMembers(); ++j) {
auto leaf = andCond->getMemberUnchecked(j);
// We only support binary conditions
TRI_ASSERT(leaf->numMembers() == 2);
AstNode* lhs = leaf->getMember(0);
AstNode* rhs = leaf->getMember(1);
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && lhs->isConstant()) {
lhs = const_cast<AstNode*>(Ast::resolveConstAttributeAccess(lhs));
leaf->changeMember(0, lhs);
}
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && rhs->isConstant()) {
rhs = const_cast<AstNode*>(Ast::resolveConstAttributeAccess(rhs));
leaf->changeMember(1, rhs);
}
}
}
}
}
IndexBlock::~IndexBlock() {
@ -176,8 +200,8 @@ int IndexBlock::initialize() {
// We only support binary conditions
TRI_ASSERT(leaf->numMembers() == 2);
auto lhs = leaf->getMember(0);
auto rhs = leaf->getMember(1);
AstNode* lhs = leaf->getMember(0);
AstNode* rhs = leaf->getMember(1);
if (lhs->isAttributeAccessForVariable(outVariable, false)) {
// Index is responsible for the left side, check if right side has to be