1
0
Fork 0

do not simplify non-deterministic conditions (#7926)

This commit is contained in:
Jan 2019-01-11 14:48:34 +01:00 committed by GitHub
parent 79257310c0
commit e42befdc52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 147 additions and 94 deletions

View File

@ -421,7 +421,8 @@ void AqlFunctionFeature::addMiscFunctions() {
add({"VERSION", "", Function::makeFlags(FF::Deterministic), &Functions::Version}); // deterministic, not cacheable. only on
// coordinator
add({"FAIL", "|.", Function::makeFlags(FF::CanRunOnDBServer), &Functions::Fail}); // not deterministic and not cacheable
add({"NOOPT", ".", Function::makeFlags(FF::CanRunOnDBServer), &Functions::Passthru}); // prevents all optimizations!
add({"NOOPT", ".", Function::makeFlags(FF::CanRunOnDBServer, FF::NoEval), &Functions::Passthru}); // prevents all optimizations!
add({"NOEVAL", ".", Function::makeFlags(FF::Deterministic, FF::CanRunOnDBServer, FF::NoEval), &Functions::Passthru}); // prevents all optimizations!
add({"SLEEP", ".", Function::makeFlags(FF::CanRunOnDBServer), &Functions::Sleep}); // not deterministic and not cacheable
add({"COLLECTIONS", "", Function::makeFlags(), &Functions::Collections}); // not deterministic and not cacheable
add({"CURRENT_USER", "", Function::makeFlags(FF::Deterministic),
@ -439,6 +440,8 @@ void AqlFunctionFeature::addMiscFunctions() {
// NEAR, WITHIN, WITHIN_RECTANGLE and FULLTEXT are replaced by the AQL
// optimizer with collection-based subqueries they are all not marked as
// non-deterministic and non-cacheable here as they refer to documents
// note further that all of these function call will be replaced by equivalent
// subqueries by the optimizer
add({"NEAR", ".h,.,.|.,.", Function::makeFlags(), &Functions::NotImplemented});
add({"WITHIN", ".h,.,.,.|.", Function::makeFlags(), &Functions::NotImplemented});
add({"WITHIN_RECTANGLE", "h.,.,.,.,.", Function::makeFlags(), &Functions::NotImplemented});

View File

@ -31,6 +31,7 @@
#include "Aql/Graphs.h"
#include "Aql/Query.h"
#include "Basics/Exceptions.h"
#include "Basics/SmallVector.h"
#include "Basics/StringRef.h"
#include "Basics/StringUtils.h"
#include "Basics/tri-strings.h"
@ -98,7 +99,7 @@ LogicalDataSource::Category const* injectDataSourceInQuery(
// name has changed by the lookup, so we need to reserve the collection
// name on the heap and update our StringRef
char* p = query.registerString(dataSourceName.data(), dataSourceName.size());
nameRef = StringRef(p, dataSourceName.size());
nameRef = arangodb::StringRef(p, dataSourceName.size());
}
// add views to the collection list
@ -664,7 +665,7 @@ AstNode* Ast::createNodeDataSource(arangodb::CollectionNameResolver const& resol
char const* name, size_t nameLength,
AccessMode::Type accessType,
bool validateName, bool failIfDoesNotExist) {
StringRef nameRef(name, nameLength);
arangodb::StringRef nameRef(name, nameLength);
// will throw if validation fails
validateDataSourceName(nameRef, validateName);
@ -691,7 +692,7 @@ AstNode* Ast::createNodeDataSource(arangodb::CollectionNameResolver const& resol
AstNode* Ast::createNodeCollection(arangodb::CollectionNameResolver const& resolver,
char const* name, size_t nameLength,
AccessMode::Type accessType) {
StringRef nameRef(name, nameLength);
arangodb::StringRef nameRef(name, nameLength);
// will throw if validation fails
validateDataSourceName(nameRef, true);
@ -1158,7 +1159,7 @@ AstNode* Ast::createNodeWithCollections(AstNode const* collections,
if (c->isStringValue()) {
std::string const name = c->getString();
// this call may update nameRef
StringRef nameRef(name);
arangodb::StringRef nameRef(name);
LogicalDataSource::Category const* category =
injectDataSourceInQuery(*_query, resolver, AccessMode::Type::READ, false, nameRef);
if (category == LogicalCollection::category()) {
@ -1174,7 +1175,7 @@ AstNode* Ast::createNodeWithCollections(AstNode const* collections,
auto names = coll->realNames();
for (auto const& n : names) {
StringRef shardsNameRef(n);
arangodb::StringRef shardsNameRef(n);
LogicalDataSource::Category const* shardsCategory =
injectDataSourceInQuery(*_query, resolver, AccessMode::Type::READ,
false, shardsNameRef);
@ -1206,7 +1207,7 @@ AstNode* Ast::createNodeCollectionList(AstNode const* edgeCollections,
auto ci = ClusterInfo::instance();
auto ss = ServerState::instance();
auto doTheAdd = [&](std::string const& name) {
StringRef nameRef(name);
arangodb::StringRef nameRef(name);
LogicalDataSource::Category const* category =
injectDataSourceInQuery(*_query, resolver, AccessMode::Type::READ, false, nameRef);
if (category == LogicalCollection::category()) {
@ -1216,7 +1217,7 @@ AstNode* Ast::createNodeCollectionList(AstNode const* edgeCollections,
auto const& names = c->realNames();
for (auto const& n : names) {
StringRef shardsNameRef(n);
arangodb::StringRef shardsNameRef(n);
LogicalDataSource::Category const* shardsCategory =
injectDataSourceInQuery(*_query, resolver, AccessMode::Type::READ,
false, shardsNameRef);
@ -1570,7 +1571,7 @@ void Ast::injectBindParameters(BindParameters& parameters,
auto const& c = it.first;
if (c->type == NODE_TYPE_PARAMETER_DATASOURCE &&
paramRef == StringRef(c->getStringValue(), c->getStringLength())) {
paramRef == arangodb::StringRef(c->getStringValue(), c->getStringLength())) {
isWriteCollection = true;
break;
}
@ -1588,7 +1589,7 @@ void Ast::injectBindParameters(BindParameters& parameters,
auto& c = _writeCollections[i].first;
if (c->type == NODE_TYPE_PARAMETER_DATASOURCE &&
paramRef == StringRef(c->getStringValue(), c->getStringLength())) {
paramRef == arangodb::StringRef(c->getStringValue(), c->getStringLength())) {
c = node;
// no break here. replace all occurrences
}
@ -1692,7 +1693,7 @@ AstNode* Ast::replaceAttributeAccess(AstNode* node, Variable const* variable,
return node;
}
std::vector<StringRef> attributePath;
std::vector<arangodb::StringRef> attributePath;
auto visitor = [&](AstNode* node) -> AstNode* {
if (node == nullptr) {
@ -1828,7 +1829,7 @@ void Ast::validateAndOptimize() {
auto func = static_cast<Function*>(node->getData());
TRI_ASSERT(func != nullptr);
if (func->name == "NOOPT") {
if (func->hasFlag(Function::Flags::NoEval)) {
// NOOPT will turn all function optimizations off
++(ctx->stopOptimizationRequests);
}
@ -1901,7 +1902,7 @@ void Ast::validateAndOptimize() {
auto func = static_cast<Function*>(node->getData());
TRI_ASSERT(func != nullptr);
if (func->name == "NOOPT") {
if (func->hasFlag(Function::Flags::NoEval)) {
// NOOPT will turn all function optimizations off
--ctx->stopOptimizationRequests;
}
@ -3307,7 +3308,7 @@ AstNode* Ast::optimizeIndexedAccess(AstNode* node) {
// found a string value (e.g. a['foo']). now turn this into
// an attribute access (e.g. a.foo) in order to make the node qualify
// for being turned into an index range later
StringRef indexValue(index->getStringValue(), index->getStringLength());
arangodb::StringRef indexValue(index->getStringValue(), index->getStringLength());
if (!indexValue.empty() && (indexValue[0] < '0' || indexValue[0] > '9')) {
// we have to be careful with numeric values here...
@ -3535,10 +3536,11 @@ AstNode const* Ast::resolveConstAttributeAccess(AstNode const* node) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
std::vector<std::string> attributeNames;
SmallVector<arangodb::StringRef>::allocator_type::arena_type a;
SmallVector<arangodb::StringRef> attributeNames{a};
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
attributeNames.emplace_back(node->getString());
attributeNames.push_back(node->getStringRef());
node = node->getMember(0);
}
@ -3553,15 +3555,15 @@ AstNode const* Ast::resolveConstAttributeAccess(AstNode const* node) {
if (node->type == NODE_TYPE_OBJECT) {
TRI_ASSERT(which > 0);
std::string const& attributeName = attributeNames[which - 1];
arangodb::StringRef 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);
auto member = node->getMemberUnchecked(i);
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
StringRef(member->getStringValue(), member->getStringLength()) == attributeName) {
arangodb::StringRef(member->getStringValue(), member->getStringLength()) == attributeName) {
// found the attribute
node = member->getMember(0);
if (which == 0) {
@ -3720,7 +3722,7 @@ AstNode* Ast::createNode(AstNodeType type) {
/// @brief validate the name of the given datasource
/// in case validation fails, will throw an exception
void Ast::validateDataSourceName(StringRef const& name, bool validateStrict) {
void Ast::validateDataSourceName(arangodb::StringRef const& name, bool validateStrict) {
// common validation
if (name.empty() ||
(validateStrict &&
@ -3734,7 +3736,7 @@ void Ast::validateDataSourceName(StringRef const& name, bool validateStrict) {
/// @brief create an AST collection node
/// private function, does no validation
AstNode* Ast::createNodeCollectionNoValidation(StringRef const& name,
AstNode* Ast::createNodeCollectionNoValidation(arangodb::StringRef const& name,
AccessMode::Type accessType) {
if (ServerState::instance()->isCoordinator()) {
auto ci = ClusterInfo::instance();

View File

@ -768,6 +768,17 @@ std::string AstNode::getString() const {
return std::string(getStringValue(), getStringLength());
}
/// @brief return the string value of a node, as a StringRef
arangodb::StringRef AstNode::getStringRef() const noexcept {
TRI_ASSERT(type == NODE_TYPE_VALUE || type == NODE_TYPE_OBJECT_ELEMENT ||
type == NODE_TYPE_ATTRIBUTE_ACCESS || type == NODE_TYPE_PARAMETER ||
type == NODE_TYPE_PARAMETER_DATASOURCE || type == NODE_TYPE_COLLECTION ||
type == NODE_TYPE_VIEW || type == NODE_TYPE_BOUND_ATTRIBUTE_ACCESS ||
type == NODE_TYPE_FCALL_USER);
TRI_ASSERT(value.type == VALUE_TYPE_STRING);
return arangodb::StringRef(getStringValue(), getStringLength());
}
/// @brief test if all members of a node are equality comparisons
bool AstNode::isOnlyEqualityMatch() const {
if (type != NODE_TYPE_OPERATOR_BINARY_AND && type != NODE_TYPE_OPERATOR_NARY_AND) {

View File

@ -27,6 +27,7 @@
#include "Basics/AttributeNameParser.h"
#include "Basics/Common.h"
#include "Basics/Exceptions.h"
#include "Basics/StringRef.h"
#include <velocypack/Slice.h>
@ -254,6 +255,9 @@ struct AstNode {
/// @brief return the string value of a node, as an std::string
std::string getString() const;
/// @brief return the string value of a node, as a StringRef
arangodb::StringRef getStringRef() const noexcept;
/// @brief test if all members of a node are equality comparisons
bool isOnlyEqualityMatch() const;

View File

@ -49,7 +49,11 @@ struct Function {
Cacheable = 2,
/// @brief whether or not the function may be executed on DB servers
CanRunOnDBServer = 4
CanRunOnDBServer = 4,
/// @brief exclude the function from being evaluated during AST optimizations
/// evaluation of function will only happen at query runtime
NoEval = 8
};
/// @brief helper for building flags

View File

@ -838,9 +838,8 @@ void arangodb::aql::sortInValuesRule(Optimizer* opt, std::unique_ptr<ExecutionPl
AstNode const* testNode = originalNode;
if (originalNode->type == NODE_TYPE_FCALL &&
static_cast<Function const*>(originalNode->getData())->name ==
"NOOPT") {
// bypass NOOPT(...)
static_cast<Function const*>(originalNode->getData())->hasFlag(Function::Flags::NoEval)) {
// bypass NOOPT(...) for testing
TRI_ASSERT(originalNode->numMembers() == 1);
auto args = originalNode->getMember(0);
@ -2189,9 +2188,12 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
return;
}
bool modified = false;
auto p = plan.get();
auto visitor = [p](AstNode* node) {
auto visitor = [p, &modified](AstNode* node) {
AstNode* original = node;
again:
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
auto const* accessed = node->getMemberUnchecked(0);
@ -2225,7 +2227,13 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
StringRef(member->getStringValue(), member->getStringLength()) == attributeName) {
// found the attribute!
node = member->getMember(0);
AstNode* next = member->getMember(0);
if (!next->isDeterministic()) {
// do not descend into non-deterministic nodes
return node;
}
// descend further
node = next;
// now try optimizing the simplified condition
// time for a goto...!
goto again;
@ -2237,6 +2245,7 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
// attribute not found
if (!isDynamic) {
modified = true;
return Ast::createNodeValueNull();
}
}
@ -2292,7 +2301,13 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
StringRef(member->getStringValue(), member->getStringLength()) == attributeName) {
// found the attribute!
node = member->getMember(0);
AstNode* next = member->getMember(0);
if (!next->isDeterministic()) {
// do not descend into non-deterministic nodes
return node;
}
// descend further
node = next;
// now try optimizing the simplified condition
// time for a goto...!
goto again;
@ -2304,6 +2319,7 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
// attribute not found
if (!isDynamic) {
modified = true;
return Ast::createNodeValueNull();
}
} else if (accessed->type == NODE_TYPE_ARRAY) {
@ -2317,6 +2333,7 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
valid);
if (!valid) {
// invalid index
modified = true;
return Ast::createNodeValueNull();
}
} else {
@ -2330,21 +2347,31 @@ void arangodb::aql::simplifyConditionsRule(Optimizer* opt,
position = n + position;
}
if (position >= 0 && position < n) {
node = accessed->getMember(static_cast<size_t>(position));
AstNode* next = accessed->getMember(static_cast<size_t>(position));
if (!next->isDeterministic()) {
// do not descend into non-deterministic nodes
return node;
}
// descend further
node = next;
// now try optimizing the simplified condition
// time for a goto...!
goto again;
}
// index out of bounds
modified = true;
return Ast::createNodeValueNull();
}
}
if (node != original) {
// we come out with a different, so we changed something...
modified = true;
}
return node;
};
bool modified = false;
for (auto const& n : nodes) {
auto nn = ExecutionNode::castTo<CalculationNode*>(n);

View File

@ -36,6 +36,8 @@
#include "GeneralServer/IoTask.h"
#include <list>
namespace arangodb {
class ConnectionStatistics;

View File

@ -21,14 +21,12 @@
/// @author Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_BASICS_AUTO_VECTOR_H
#define ARANGODB_BASICS_AUTO_VECTOR_H 1
#ifndef ARANGODB_BASICS_SMALL_VECTOR_H
#define ARANGODB_BASICS_SMALL_VECTOR_H 1
#include "Basics/Common.h"
#include "Basics/short_alloc.h"
#include <list>
namespace arangodb {
template <class T, std::size_t BufSize = 64>

View File

@ -45,31 +45,31 @@ function optimizerRuleTestSuite () {
testRuleDisabled : function () {
let queries = [
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[0]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[3]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[4]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[10]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-1]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-2]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-3]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-4]",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['0']",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['3']",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['4']",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['fffff']",
"LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['-2']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['a']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['d']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['d']['x']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['z']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['0']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['1']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['2']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['999']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.a",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.d",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.d.x",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.z",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[0]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[3]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[4]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[10]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-1]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-2]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-3]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-4]",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['0']",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['3']",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['4']",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['fffff']",
"LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['-2']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['a']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['d']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['d']['x']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['z']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['0']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['1']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['2']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['999']",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.a",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.d",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.d.x",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.z",
];
queries.forEach(function(query) {
@ -95,6 +95,8 @@ function optimizerRuleTestSuite () {
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: 4 } } RETURN data[NOOPT('999')]",
"LET data = { a: 1, b: 2, c: 3, [NOOPT('foo')] : 4 } RETURN data.z",
"LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOOPT('foo')] : 4 } } RETURN data.d.z",
"LET data = { a: RAND() } RETURN data.a",
"LET data = { a: RAND(), b: RAND() } RETURN [data.a, data.b]",
];
queries.forEach(function(query) {
@ -105,41 +107,41 @@ function optimizerRuleTestSuite () {
testResults : function () {
let queries = [
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[0]", 1 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[3]", 4 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[4]", null ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[10]", null ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-1]", 4 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-2]", 3 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-3]", 2 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-4]", 1 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data[-5]", null ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['0']", 1 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['3']", 4 ],
[ "LET data = [ 1, 2, 3, NOOPT(4) ] RETURN data['4']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['a']", 1 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['d']", { x: 'x', y: 'y', foo: 4 } ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['d']['x']", 'x' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['z']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['0']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['1']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['2']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data['999']", null ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data['1']", 1 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data[1]", 1 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data['99']", 3 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data[99]", 3 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data['d']['2']", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data.d.`2`", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data.`d`.`2`", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOOPT(4) } } RETURN data.d['2']", 'y' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.a", 1 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.d", { x: 'x', y: 'y', foo: 4 } ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.d.x", 'x' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOOPT(4) } } RETURN data.z", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOOPT('foo')] : 4 } } RETURN data.z", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOOPT('foo')] : 4 } } RETURN data.b", 2 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOOPT('foo')] : 4 } } RETURN data.d.y", 'y' ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[0]", 1 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[3]", 4 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[4]", null ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[10]", null ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-1]", 4 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-2]", 3 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-3]", 2 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-4]", 1 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data[-5]", null ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['0']", 1 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['3']", 4 ],
[ "LET data = [ 1, 2, 3, NOEVAL(4) ] RETURN data['4']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['a']", 1 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['d']", { x: 'x', y: 'y', foo: 4 } ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['d']['x']", 'x' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['z']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['0']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['1']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['2']", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data['999']", null ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data['1']", 1 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data[1]", 1 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data['99']", 3 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data[99]", 3 ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data['d']['2']", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data.d.`2`", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data.`d`.`2`", 'y' ],
[ "LET data = { '1': 1, '2': 2, '99': 3, d: { '1': 'x', '2': 'y', '3': NOEVAL(4) } } RETURN data.d['2']", 'y' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.a", 1 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.d", { x: 'x', y: 'y', foo: 4 } ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.d.x", 'x' ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', foo: NOEVAL(4) } } RETURN data.z", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOEVAL('foo')] : 4 } } RETURN data.z", null ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOEVAL('foo')] : 4 } } RETURN data.b", 2 ],
[ "LET data = { a: 1, b: 2, c: 3, d: { x: 'x', y: 'y', [NOEVAL('foo')] : 4 } } RETURN data.d.y", 'y' ],
];
queries.forEach(function(query) {