mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
c13679e33d
|
@ -264,7 +264,7 @@ arangosh> stmt.explain({ allPlans: true }).plans.length;
|
|||
2
|
||||
```
|
||||
|
||||
To see the minified version of the plan:
|
||||
To see a slightly more compact version of the plan, the following transformation can be applied:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ allPlans: true }).plans.map(function(plan) { return formatPlan(plan); });
|
||||
|
@ -292,6 +292,19 @@ arangosh> stmt.explain({ allPlans: true }).plans.map(function(plan) { return for
|
|||
]
|
||||
```
|
||||
|
||||
`explain` will also accept the following additional options:
|
||||
- *maxPlans*: limits the maximum number of plans that are created by the AQL query optimizer
|
||||
- *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules
|
||||
can be put into this attribute, telling the optimizer to include or exclude
|
||||
specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it
|
||||
with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules.
|
||||
|
||||
The following example disables all optimizer rules but `remove-redundant-calculations`:
|
||||
|
||||
```
|
||||
arangosh> stmt.explain({ optimizer: { rules: [ "-all", "+remove-redundant-calculations" ] } });
|
||||
```
|
||||
|
||||
!SECTION Parsing queries
|
||||
|
||||
Clients can use ArangoDB to check if a given AQL query is syntactically valid. ArangoDB provides
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
{{ articles(summary.chapters) }}
|
||||
</ul>
|
||||
</div>
|
||||
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function(){
|
||||
jQuery(".summary>li").each(function(){
|
||||
|
|
|
@ -57,13 +57,25 @@ AstNode const Ast::NullNode{ NODE_TYPE_VALUE, VALUE_TYPE_NULL };
|
|||
/// @brief initialise a singleton false node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const Ast::FalseNode{ false };
|
||||
AstNode const Ast::FalseNode{ false, VALUE_TYPE_BOOL };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialise a singleton true node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const Ast::TrueNode{ true };
|
||||
AstNode const Ast::TrueNode{ true, VALUE_TYPE_BOOL };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialise a singleton zero node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const Ast::ZeroNode{ static_cast<int64_t>(0), VALUE_TYPE_INT };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief initialise a singleton empty string node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode const Ast::EmptyStringNode{ "", VALUE_TYPE_STRING };
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief inverse comparison operators
|
||||
|
@ -606,6 +618,7 @@ AstNode* Ast::createNodeIterator (char const* variableName,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodeValueNull () {
|
||||
// performance optimization:
|
||||
// return a pointer to the singleton null node
|
||||
// note: this node is never registered nor freed
|
||||
return const_cast<AstNode*>(&NullNode);
|
||||
|
@ -616,11 +629,13 @@ AstNode* Ast::createNodeValueNull () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodeValueBool (bool value) {
|
||||
// performance optimization:
|
||||
// return a pointer to the singleton bool nodes
|
||||
// note: these nodes are never registered nor freed
|
||||
if (value) {
|
||||
return const_cast<AstNode*>(&TrueNode);
|
||||
}
|
||||
|
||||
return const_cast<AstNode*>(&FalseNode);
|
||||
}
|
||||
|
||||
|
@ -629,6 +644,13 @@ AstNode* Ast::createNodeValueBool (bool value) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::createNodeValueInt (int64_t value) {
|
||||
if (value == 0) {
|
||||
// performance optimization:
|
||||
// return a pointer to the singleton zero node
|
||||
// note: these nodes are never registered nor freed
|
||||
return const_cast<AstNode*>(&ZeroNode);
|
||||
}
|
||||
|
||||
AstNode* node = createNode(NODE_TYPE_VALUE);
|
||||
node->setValueType(VALUE_TYPE_INT);
|
||||
node->setIntValue(value);
|
||||
|
@ -657,6 +679,13 @@ AstNode* Ast::createNodeValueString (char const* value) {
|
|||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
if (*value == '\0') {
|
||||
// performance optimization:
|
||||
// return a pointer to the singleton empty string node
|
||||
// note: these nodes are never registered nor freed
|
||||
return const_cast<AstNode*>(&EmptyStringNode);
|
||||
}
|
||||
|
||||
AstNode* node = createNode(NODE_TYPE_VALUE);
|
||||
node->setValueType(VALUE_TYPE_STRING);
|
||||
node->setStringValue(value);
|
||||
|
@ -953,6 +982,11 @@ void Ast::optimize () {
|
|||
return optimizeLet(node);
|
||||
}
|
||||
|
||||
// FOR
|
||||
if (node->type == NODE_TYPE_FOR) {
|
||||
return optimizeFor(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
|
@ -1614,6 +1648,34 @@ AstNode* Ast::optimizeLet (AstNode* node) {
|
|||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the FOR statement
|
||||
/// no real optimizations are done here, but we do an early check if the
|
||||
/// FOR loop operand is actually a list
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* Ast::optimizeFor (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_FOR);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
AstNode* expression = node->getMember(1);
|
||||
|
||||
if (expression == nullptr) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (expression->isConstant() &&
|
||||
expression->type != NODE_TYPE_LIST) {
|
||||
// right-hand operand to FOR statement is no list
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_LIST_EXPECTED,
|
||||
TRI_errno_string(TRI_ERROR_QUERY_LIST_EXPECTED) + std::string(" in FOR loop"));
|
||||
}
|
||||
|
||||
// no real optimizations will be done here
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -575,6 +575,14 @@ namespace triagens {
|
|||
|
||||
AstNode* optimizeLet (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the FOR statement
|
||||
/// no real optimizations are done here, but we do an early check if the
|
||||
/// FOR loop operand is actually a list
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeFor (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -609,6 +617,19 @@ namespace triagens {
|
|||
|
||||
AstNode* createNode (AstNodeType);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief inverse comparison operators
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unordered_map<int, AstNodeType> const ReverseOperators;
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -682,10 +703,16 @@ namespace triagens {
|
|||
static AstNode const TrueNode;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief inverse comparison operators
|
||||
/// @brief a singleton zero node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static std::unordered_map<int, AstNodeType> const ReverseOperators;
|
||||
static AstNode const ZeroNode;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief a singleton empty string node instance
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AstNode const EmptyStringNode;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -264,14 +264,44 @@ AstNode::AstNode (AstNodeType type,
|
|||
/// @brief create a boolean node, with defining a value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode::AstNode (bool v)
|
||||
: AstNode(NODE_TYPE_VALUE, VALUE_TYPE_BOOL) {
|
||||
AstNode::AstNode (bool v,
|
||||
AstNodeValueType valueType)
|
||||
: AstNode(NODE_TYPE_VALUE, valueType) {
|
||||
|
||||
TRI_ASSERT(valueType == VALUE_TYPE_BOOL);
|
||||
value.value._bool = v;
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedJson == nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an int node, with defining a value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode::AstNode (int64_t v,
|
||||
AstNodeValueType valueType)
|
||||
: AstNode(NODE_TYPE_VALUE, valueType) {
|
||||
|
||||
TRI_ASSERT(valueType == VALUE_TYPE_INT);
|
||||
value.value._int = v;
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedJson == nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a string node, with defining a value
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode::AstNode (char const* v,
|
||||
AstNodeValueType valueType)
|
||||
: AstNode(NODE_TYPE_VALUE, valueType) {
|
||||
|
||||
TRI_ASSERT(valueType == VALUE_TYPE_STRING);
|
||||
value.value._string = v;
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedJson == nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create the node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -796,9 +826,11 @@ AstNode* AstNode::castToString (Ast* ast) {
|
|||
return this;
|
||||
}
|
||||
|
||||
TRI_ASSERT(isConstant());
|
||||
|
||||
// stringify node
|
||||
triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE);
|
||||
append(&buffer, false);
|
||||
stringify(&buffer, false);
|
||||
|
||||
char const* value = ast->query()->registerString(buffer.c_str(), buffer.length(), false);
|
||||
TRI_ASSERT(value != nullptr);
|
||||
|
@ -1199,16 +1231,20 @@ AstNode* AstNode::clone (Ast* ast) const {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief append a string representation of the node to a string buffer
|
||||
/// the string representation does not need to be JavaScript-compatible
|
||||
/// except for node types NODE_TYPE_VALUE, NODE_TYPE_LIST and NODE_TYPE_ARRAY
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void AstNode::append (triagens::basics::StringBuffer* buffer,
|
||||
void AstNode::stringify (triagens::basics::StringBuffer* buffer,
|
||||
bool verbose) const {
|
||||
if (type == NODE_TYPE_VALUE) {
|
||||
// must be JavaScript-compatible!
|
||||
appendValue(buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_LIST) {
|
||||
// must be JavaScript-compatible!
|
||||
size_t const n = numMembers();
|
||||
if (verbose || n > 0) {
|
||||
if (verbose || n > 1) {
|
||||
|
@ -1221,7 +1257,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
|
||||
AstNode* member = getMember(i);
|
||||
if (member != nullptr) {
|
||||
member->append(buffer, verbose);
|
||||
member->stringify(buffer, verbose);
|
||||
}
|
||||
}
|
||||
if (verbose || n > 1) {
|
||||
|
@ -1232,6 +1268,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
}
|
||||
|
||||
if (type == NODE_TYPE_ARRAY) {
|
||||
// must be JavaScript-compatible!
|
||||
if (verbose) {
|
||||
buffer->appendChar('{');
|
||||
size_t const n = numMembers();
|
||||
|
@ -1249,7 +1286,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
buffer->appendJsonEncoded(member->getStringValue());
|
||||
buffer->appendText("\":", 2);
|
||||
|
||||
member->getMember(0)->append(buffer, verbose);
|
||||
member->getMember(0)->stringify(buffer, verbose);
|
||||
}
|
||||
}
|
||||
buffer->appendChar('}');
|
||||
|
@ -1260,7 +1297,8 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_REFERENCE) {
|
||||
if (type == NODE_TYPE_REFERENCE ||
|
||||
type == NODE_TYPE_VARIABLE) {
|
||||
// not used by V8
|
||||
auto variable = static_cast<Variable*>(getData());
|
||||
TRI_ASSERT(variable != nullptr);
|
||||
|
@ -1275,9 +1313,9 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
// not used by V8
|
||||
auto member = getMember(0);
|
||||
auto index = getMember(1);
|
||||
member->append(buffer, verbose);
|
||||
member->stringify(buffer, verbose);
|
||||
buffer->appendChar('[');
|
||||
index->append(buffer, verbose);
|
||||
index->stringify(buffer, verbose);
|
||||
buffer->appendChar(']');
|
||||
return;
|
||||
}
|
||||
|
@ -1285,17 +1323,53 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
if (type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
// not used by V8
|
||||
auto member = getMember(0);
|
||||
member->append(buffer, verbose);
|
||||
member->stringify(buffer, verbose);
|
||||
buffer->appendChar('.');
|
||||
buffer->appendText(getStringValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_BOUND_ATTRIBUTE_ACCESS) {
|
||||
// not used by V8
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendChar('.');
|
||||
getMember(1)->stringify(buffer, verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_PARAMETER) {
|
||||
// not used by V8
|
||||
buffer->appendChar('@');
|
||||
buffer->appendText(getStringValue());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_FCALL) {
|
||||
// not used by V8
|
||||
auto func = static_cast<Function*>(getData());
|
||||
buffer->appendText(func->externalName);
|
||||
buffer->appendChar('(');
|
||||
getMember(0)->append(buffer, verbose);
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_EXPAND) {
|
||||
// not used by V8
|
||||
buffer->appendText("_EXPAND(");
|
||||
getMember(1)->stringify(buffer, verbose);
|
||||
buffer->appendChar(',');
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_ITERATOR) {
|
||||
// not used by V8
|
||||
buffer->appendText("_ITERATOR(");
|
||||
getMember(1)->stringify(buffer, verbose);
|
||||
buffer->appendChar(',');
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendChar(')');
|
||||
return;
|
||||
}
|
||||
|
@ -1303,13 +1377,14 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
if (type == NODE_TYPE_OPERATOR_UNARY_NOT ||
|
||||
type == NODE_TYPE_OPERATOR_UNARY_PLUS ||
|
||||
type == NODE_TYPE_OPERATOR_UNARY_MINUS) {
|
||||
// not used by V8
|
||||
TRI_ASSERT(numMembers() == 1);
|
||||
auto it = Operators.find(static_cast<int>(type));
|
||||
TRI_ASSERT(it != Operators.end());
|
||||
buffer->appendChar(' ');
|
||||
buffer->appendText((*it).second);
|
||||
|
||||
getMember(0)->append(buffer, verbose);
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1328,20 +1403,31 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
type == NODE_TYPE_OPERATOR_BINARY_GE ||
|
||||
type == NODE_TYPE_OPERATOR_BINARY_IN ||
|
||||
type == NODE_TYPE_OPERATOR_BINARY_NIN) {
|
||||
// not used by V8
|
||||
TRI_ASSERT(numMembers() == 2);
|
||||
auto it = Operators.find(type);
|
||||
TRI_ASSERT(it != Operators.end());
|
||||
|
||||
getMember(0)->append(buffer, verbose);
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendChar(' ');
|
||||
buffer->appendText((*it).second);
|
||||
buffer->appendChar(' ');
|
||||
getMember(1)->append(buffer, verbose);
|
||||
getMember(1)->stringify(buffer, verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_RANGE) {
|
||||
// not used by V8
|
||||
TRI_ASSERT(numMembers() == 2);
|
||||
getMember(0)->stringify(buffer, verbose);
|
||||
buffer->appendText("..", 2);
|
||||
getMember(1)->stringify(buffer, verbose);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string message("stringification not supported for node type ");
|
||||
message.append(getTypeString());
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, message);
|
||||
}
|
||||
|
||||
|
@ -1351,6 +1437,7 @@ void AstNode::append (triagens::basics::StringBuffer* buffer,
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief stringify the value of a node into a string buffer
|
||||
/// this creates an equivalent to what JSON.stringify() would do
|
||||
/// this method is used when generated JavaScript code for the node!
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -193,10 +193,22 @@ namespace triagens {
|
|||
explicit AstNode (AstNodeType, AstNodeValueType);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a boolean, with defining a value type
|
||||
/// @brief create a boolean node, with defining a value type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
explicit AstNode (bool);
|
||||
explicit AstNode (bool, AstNodeValueType);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a boolean node, with defining a value type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
explicit AstNode (int64_t, AstNodeValueType);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a string node, with defining a value type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
explicit AstNode (char const*, AstNodeValueType);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create the node from JSON
|
||||
|
@ -634,7 +646,7 @@ namespace triagens {
|
|||
/// @brief append a JavaScript representation of the node into a string buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void append (triagens::basics::StringBuffer*,
|
||||
void stringify (triagens::basics::StringBuffer*,
|
||||
bool) const;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -643,6 +655,8 @@ namespace triagens {
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief stringify the value of a node into a string buffer
|
||||
/// this method is used when generated JavaScript code for the node!
|
||||
/// this creates an equivalent to what JSON.stringify() would do
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void appendValue (triagens::basics::StringBuffer*) const;
|
||||
|
|
|
@ -1650,9 +1650,8 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
|
|||
_collection = nullptr;
|
||||
switch (inVarReg._type) {
|
||||
case AqlValue::JSON: {
|
||||
if(! inVarReg._json->isList()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
||||
"EnumerateListBlock: JSON is not a list");
|
||||
if (! inVarReg._json->isList()) {
|
||||
throwListExpectedException();
|
||||
}
|
||||
sizeInVar = inVarReg._json->size();
|
||||
break;
|
||||
|
@ -1664,9 +1663,9 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
|
|||
}
|
||||
|
||||
case AqlValue::DOCVEC: {
|
||||
if( _index == 0) { // this is a (maybe) new DOCVEC
|
||||
if (_index == 0) { // this is a (maybe) new DOCVEC
|
||||
_DOCVECsize = 0;
|
||||
//we require the total number of items
|
||||
// we require the total number of items
|
||||
|
||||
for (size_t i = 0; i < inVarReg._vector->size(); i++) {
|
||||
_DOCVECsize += inVarReg._vector->at(i)->size();
|
||||
|
@ -1680,13 +1679,11 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
|
|||
}
|
||||
|
||||
case AqlValue::SHAPED: {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
||||
"EnumerateListBlock: cannot iterate over shaped value");
|
||||
throwListExpectedException();
|
||||
}
|
||||
|
||||
case AqlValue::EMPTY: {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
||||
"EnumerateListBlock: cannot iterate over empty value");
|
||||
throwListExpectedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1773,17 +1770,18 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
|
|||
// get the size of the thing we are looping over
|
||||
switch (inVarReg._type) {
|
||||
case AqlValue::JSON: {
|
||||
if(! inVarReg._json->isList()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
||||
"EnumerateListBlock: JSON is not a list");
|
||||
if (! inVarReg._json->isList()) {
|
||||
throwListExpectedException();
|
||||
}
|
||||
sizeInVar = inVarReg._json->size();
|
||||
break;
|
||||
}
|
||||
|
||||
case AqlValue::RANGE: {
|
||||
sizeInVar = inVarReg._range->size();
|
||||
break;
|
||||
}
|
||||
|
||||
case AqlValue::DOCVEC: {
|
||||
if( _index == 0) { // this is a (maybe) new DOCVEC
|
||||
_DOCVECsize = 0;
|
||||
|
@ -1795,9 +1793,10 @@ size_t EnumerateListBlock::skipSome (size_t atLeast, size_t atMost) {
|
|||
sizeInVar = _DOCVECsize;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
||||
"EnumerateListBlock: unexpected type in register");
|
||||
|
||||
case AqlValue::SHAPED:
|
||||
case AqlValue::EMPTY: {
|
||||
throwListExpectedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1852,7 +1851,17 @@ AqlValue EnumerateListBlock::getAqlValue (AqlValue inVarReg) {
|
|||
}
|
||||
}
|
||||
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "unexpected value in variable to iterate over");
|
||||
throwListExpectedException();
|
||||
TRI_ASSERT(false);
|
||||
|
||||
// cannot be reached. function call above will always throw an exception
|
||||
return AqlValue();
|
||||
}
|
||||
|
||||
void EnumerateListBlock::throwListExpectedException () {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_QUERY_LIST_EXPECTED,
|
||||
TRI_errno_string(TRI_ERROR_QUERY_LIST_EXPECTED) +
|
||||
std::string("in FOR loop: "));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -681,10 +681,30 @@ namespace triagens {
|
|||
/// @brief create an AqlValue from the inVariable using the current _index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AqlValue from the inVariable using the current _index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AqlValue getAqlValue (AqlValue inVarReg);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief throws a "list expected" exception
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void throwListExpectedException ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief current position in the _inVariable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -761,7 +761,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
|
@ -837,7 +837,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return ENUMERATE_COLLECTION;
|
||||
}
|
||||
|
||||
|
@ -990,7 +990,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return ENUMERATE_LIST;
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1107,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return INDEX_RANGE;
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1127,22 @@ namespace triagens {
|
|||
return _collection;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return out variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable const* outVariable () const {
|
||||
return _outVariable;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the ranges
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<std::vector<RangeInfo>> const& ranges () const {
|
||||
return _ranges;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export to JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1166,7 +1182,7 @@ namespace triagens {
|
|||
double estimateCost () const override final;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check whether the pattern matches this nodes index
|
||||
/// @brief check whether the pattern matches this node's index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IndexMatch MatchesIndex (IndexMatchVec const& pattern) const;
|
||||
|
@ -1266,7 +1282,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return LIMIT;
|
||||
}
|
||||
|
||||
|
@ -1299,7 +1315,7 @@ namespace triagens {
|
|||
|
||||
double estimateCost () const override final {
|
||||
return 1.005 * static_cast<double>(_limit) + _dependencies.at(0)->getCost();
|
||||
//FIXME improve this estimate . . .
|
||||
// FIXME: improve this estimate . . .
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1381,7 +1397,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return CALCULATION;
|
||||
}
|
||||
|
||||
|
@ -1517,7 +1533,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SUBQUERY;
|
||||
}
|
||||
|
||||
|
@ -1650,7 +1666,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return FILTER;
|
||||
}
|
||||
|
||||
|
@ -1675,9 +1691,9 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
double estimateCost () const override final {
|
||||
return _dependencies.at(0)->getCost() * 0.105;
|
||||
//FIXME! 0.105 is the cost of doing the filter node under the
|
||||
//assumption that it returns 10% of the results of its dependency
|
||||
return _dependencies.at(0)->getCost() * 1.105;
|
||||
// FIXME! 1.105 is the cost of doing the filter node under the
|
||||
// assumption that it returns 10% of the results of its dependency
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1798,7 +1814,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SORT;
|
||||
}
|
||||
|
||||
|
@ -1937,7 +1953,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return AGGREGATE;
|
||||
}
|
||||
|
||||
|
@ -2057,7 +2073,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return RETURN;
|
||||
}
|
||||
|
||||
|
@ -2244,7 +2260,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REMOVE;
|
||||
}
|
||||
|
||||
|
@ -2357,7 +2373,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return INSERT;
|
||||
}
|
||||
|
||||
|
@ -2472,7 +2488,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return UPDATE;
|
||||
}
|
||||
|
||||
|
@ -2597,7 +2613,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REPLACE;
|
||||
}
|
||||
|
||||
|
@ -2709,7 +2725,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return NORESULTS;
|
||||
}
|
||||
|
||||
|
@ -2786,7 +2802,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REMOTE;
|
||||
}
|
||||
|
||||
|
@ -2969,7 +2985,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SCATTER;
|
||||
}
|
||||
|
||||
|
@ -3072,7 +3088,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return DISTRIBUTE;
|
||||
}
|
||||
|
||||
|
@ -3180,7 +3196,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return GATHER;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
|||
{ "NEAR", Function("NEAR", "AQL_NEAR", "h,n,n|nz,s", false, true, false) },
|
||||
{ "WITHIN", Function("WITHIN", "AQL_WITHIN", "h,n,n,n|s", false, true, false) },
|
||||
{ "WITHIN_RECTANGLE", Function("WITHIN_RECTANGLE", "AQL_WITHIN_RECTANGLE", "h,d,d,d,d", false, true, false) },
|
||||
{ "IS_IN_POLYGON", Function("IS_IN_POLYGON", "AQL_IS_IN_POLYGON", "d,d,l", true, false, true) },
|
||||
{ "IS_IN_POLYGON", Function("IS_IN_POLYGON", "AQL_IS_IN_POLYGON", "l,ln|nb", true, false, true) },
|
||||
|
||||
// fulltext functions
|
||||
{ "FULLTEXT", Function("FULLTEXT", "AQL_FULLTEXT", "h,s,s", false, true, false) },
|
||||
|
@ -724,7 +724,7 @@ void Executor::generateCodeExpand (AstNode const* node) {
|
|||
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
|
||||
_buffer->appendText("vars[\"");
|
||||
_buffer->appendText(variable->name);
|
||||
_buffer->appendText("\"] = v; ");
|
||||
_buffer->appendText("\"]=v; ");
|
||||
|
||||
_buffer->appendText("r.push(");
|
||||
generateCodeNode(node->getMember(1));
|
||||
|
@ -812,7 +812,7 @@ void Executor::generateCodeNode (AstNode const* node) {
|
|||
|
||||
switch (node->type) {
|
||||
case NODE_TYPE_VALUE:
|
||||
node->append(_buffer, true);
|
||||
node->appendValue(_buffer);
|
||||
break;
|
||||
|
||||
case NODE_TYPE_LIST:
|
||||
|
|
|
@ -142,12 +142,14 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx,
|
|||
return _func->execute(_ast->query(), trx, docColls, argv, startPos, vars, regs);
|
||||
}
|
||||
catch (triagens::arango::Exception& ex) {
|
||||
if (_ast->query()->verboseErrors()) {
|
||||
ex.addToMessage(" while evaluating expression ");
|
||||
auto json = _node->toJson(TRI_UNKNOWN_MEM_ZONE, false);
|
||||
if (json != nullptr) {
|
||||
ex.addToMessage(triagens::basics::JsonHelper::toString(json));
|
||||
TRI_Free(TRI_UNKNOWN_MEM_ZONE, json);
|
||||
}
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -744,7 +746,7 @@ std::pair<std::string, std::string> Expression::getMultipleAttributes() {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Expression::stringify (triagens::basics::StringBuffer* buffer) const {
|
||||
_node->append(buffer, true);
|
||||
_node->stringify(buffer, true);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -453,8 +453,8 @@ void Optimizer::setupRules () {
|
|||
|
||||
// try to replace simple OR conditions with IN
|
||||
registerRule("replace-OR-with-IN",
|
||||
replaceORwithIN,
|
||||
replaceORwithIN_pass6,
|
||||
replaceOrWithIn,
|
||||
replaceOrWithIn_pass6,
|
||||
true);
|
||||
|
||||
// try to find a filter after an enumerate collection and find an index . . .
|
||||
|
@ -469,6 +469,15 @@ void Optimizer::setupRules () {
|
|||
useIndexForSort_pass6,
|
||||
true);
|
||||
|
||||
#if 0
|
||||
// try to remove filters which are covered by index ranges
|
||||
// rule seems to work, but tests are still missing
|
||||
registerRule("remove-filter-covered-by-index",
|
||||
removeFiltersCoveredByIndex,
|
||||
removeFiltersCoveredByIndex_pass6,
|
||||
true);
|
||||
#endif
|
||||
|
||||
if (ExecutionEngine::isCoordinator()) {
|
||||
// distribute operations in cluster
|
||||
registerRule("scatter-in-cluster",
|
||||
|
|
|
@ -128,7 +128,7 @@ namespace triagens {
|
|||
pass6 = 800,
|
||||
|
||||
// replace simple OR conditions with IN
|
||||
replaceORwithIN_pass6 = 810,
|
||||
replaceOrWithIn_pass6 = 810,
|
||||
|
||||
// try to find a filter after an enumerate collection and find an index . . .
|
||||
useIndexRange_pass6 = 820,
|
||||
|
@ -136,6 +136,9 @@ namespace triagens {
|
|||
// try to find sort blocks which are superseeded by indexes
|
||||
useIndexForSort_pass6 = 830,
|
||||
|
||||
// try to remove filters covered by index ranges
|
||||
removeFiltersCoveredByIndex_pass6 = 840,
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// "Pass 10": final transformations for the cluster
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -1477,6 +1477,248 @@ int triagens::aql::useIndexForSort (Optimizer* opt,
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO: finish rule and test it
|
||||
|
||||
struct FilterCondition {
|
||||
std::string variableName;
|
||||
std::string attributeName;
|
||||
AstNode const* lowNode = nullptr;
|
||||
AstNode const* highNode = nullptr;
|
||||
bool lowInclusive = false;
|
||||
bool highInclusive = false;
|
||||
|
||||
FilterCondition () {
|
||||
}
|
||||
|
||||
bool isFullyCoveredBy (RangeInfo const& other) {
|
||||
if (! other.isConstant()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (other._var != variableName ||
|
||||
other._attr != attributeName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool const lowDefined = (lowNode != nullptr);
|
||||
bool const highDefined = (highNode != nullptr);
|
||||
|
||||
if (lowDefined != other._lowConst.isDefined()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (highDefined != other._highConst.isDefined()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lowDefined) {
|
||||
if (other._lowConst.inclusive() != lowInclusive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Json json(TRI_UNKNOWN_MEM_ZONE, lowNode->toJsonValue(TRI_UNKNOWN_MEM_ZONE));
|
||||
|
||||
if (TRI_CompareValuesJson(other._lowConst.bound().json(), json.json()) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (highDefined) {
|
||||
if (other._highConst.inclusive() != highInclusive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Json json(TRI_UNKNOWN_MEM_ZONE, highNode->toJsonValue(TRI_UNKNOWN_MEM_ZONE));
|
||||
|
||||
if (TRI_CompareValuesJson(other._highConst.bound().json(), json.json()) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool analyze (AstNode const* node) {
|
||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_EQ ||
|
||||
node->type == NODE_TYPE_OPERATOR_BINARY_LT ||
|
||||
node->type == NODE_TYPE_OPERATOR_BINARY_LE ||
|
||||
node->type == NODE_TYPE_OPERATOR_BINARY_GT ||
|
||||
node->type == NODE_TYPE_OPERATOR_BINARY_GE) {
|
||||
auto lhs = node->getMember(0);
|
||||
auto rhs = node->getMember(1);
|
||||
AstNodeType op = node->type;
|
||||
bool found = false;
|
||||
|
||||
if (lhs->isConstant() &&
|
||||
rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
found = true;
|
||||
}
|
||||
else if (rhs->isConstant() &&
|
||||
lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
found = true;
|
||||
// reverse the nodes
|
||||
lhs = node->getMember(1);
|
||||
rhs = node->getMember(0);
|
||||
|
||||
auto it = Ast::ReverseOperators.find(static_cast<int>(node->type));
|
||||
TRI_ASSERT(it != Ast::ReverseOperators.end());
|
||||
|
||||
op = (*it).second;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
TRI_ASSERT(lhs->type == NODE_TYPE_VALUE);
|
||||
TRI_ASSERT(rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
|
||||
std::function<void(AstNode const*)> buildName = [&] (AstNode const* node) -> void {
|
||||
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
buildName(node->getMember(0));
|
||||
|
||||
if (! attributeName.empty()) {
|
||||
attributeName.push_back('.');
|
||||
}
|
||||
|
||||
attributeName.append(node->getStringValue());
|
||||
}
|
||||
else if (node->type == NODE_TYPE_REFERENCE) {
|
||||
auto variable = static_cast<Variable const*>(node->getData());
|
||||
variableName = variable->name;
|
||||
}
|
||||
};
|
||||
|
||||
if (attributeName.empty()) {
|
||||
buildName(rhs);
|
||||
if (op == NODE_TYPE_OPERATOR_BINARY_EQ ||
|
||||
op == NODE_TYPE_OPERATOR_BINARY_NE) {
|
||||
lowInclusive = true;
|
||||
lowNode = lhs;
|
||||
highInclusive = true;
|
||||
highNode = lhs;
|
||||
}
|
||||
else if (op == NODE_TYPE_OPERATOR_BINARY_LT) {
|
||||
lowInclusive = false;
|
||||
lowNode = lhs;
|
||||
}
|
||||
else if (op == NODE_TYPE_OPERATOR_BINARY_LE) {
|
||||
lowInclusive = true;
|
||||
lowNode = lhs;
|
||||
}
|
||||
else if (op == NODE_TYPE_OPERATOR_BINARY_GT) {
|
||||
highInclusive = false;
|
||||
highNode = lhs;
|
||||
}
|
||||
else if (op == NODE_TYPE_OPERATOR_BINARY_GE) {
|
||||
highInclusive = true;
|
||||
highNode = lhs;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// else if (attributeName == std::string(buffer.c_str(), buffer.length())) {
|
||||
// same attribute
|
||||
// TODO
|
||||
// }
|
||||
|
||||
// fall-through
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (node->type == NODE_TYPE_OPERATOR_BINARY_AND) {
|
||||
auto lhs = node->getMember(0);
|
||||
auto rhs = node->getMember(1);
|
||||
|
||||
return (analyze(lhs) && analyze(rhs));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief try to remove filters which are covered by indexes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int triagens::aql::removeFiltersCoveredByIndex (Optimizer* opt,
|
||||
ExecutionPlan* plan,
|
||||
Optimizer::Rule const* rule) {
|
||||
std::unordered_set<ExecutionNode*> toUnlink;
|
||||
std::vector<ExecutionNode*>&& nodes= plan->findNodesOfType(EN::FILTER, true);
|
||||
|
||||
for (auto n : nodes) {
|
||||
auto fn = static_cast<FilterNode*>(n);
|
||||
// find the node with the filter expression
|
||||
auto inVar = fn->getVariablesUsedHere();
|
||||
TRI_ASSERT(inVar.size() == 1);
|
||||
// auto outVar = cn->getVariablesSetHere();
|
||||
|
||||
auto setter = plan->getVarSetBy(inVar[0]->id);
|
||||
TRI_ASSERT(setter != nullptr);
|
||||
|
||||
if (setter->getType() != EN::CALCULATION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check the filter condition
|
||||
FilterCondition condition;
|
||||
if (! condition.analyze(static_cast<CalculationNode const*>(setter)->expression()->node())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
auto current = n;
|
||||
while (current != nullptr) {
|
||||
if (current->getType() == EN::INDEX_RANGE) {
|
||||
// found an index range, now check if the expression is covered by the index
|
||||
auto variable = static_cast<IndexRangeNode const*>(current)->outVariable();
|
||||
TRI_ASSERT(variable != nullptr);
|
||||
|
||||
auto const& ranges = static_cast<IndexRangeNode const*>(current)->ranges();
|
||||
|
||||
// TODO: this is not prepared for OR conditions
|
||||
for (auto it : ranges) {
|
||||
for (auto it2 : it) {
|
||||
if (condition.isFullyCoveredBy(it2)) {
|
||||
toUnlink.insert(setter);
|
||||
toUnlink.insert(n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto deps = current->getDependencies();
|
||||
if (deps.size() != 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
current = deps[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (! toUnlink.empty()) {
|
||||
plan->unlinkNodes(toUnlink);
|
||||
plan->findVarUsage();
|
||||
}
|
||||
|
||||
opt->addPlan(plan, rule->level, ! toUnlink.empty());
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief helper to compute lots of permutation tuples
|
||||
/// a permutation tuple is represented as a single vector together with
|
||||
|
@ -1518,7 +1760,7 @@ static bool nextPermutationTuple (std::vector<size_t>& data,
|
|||
int triagens::aql::interchangeAdjacentEnumerations (Optimizer* opt,
|
||||
ExecutionPlan* plan,
|
||||
Optimizer::Rule const* rule) {
|
||||
std::vector<ExecutionNode*> nodes
|
||||
std::vector<ExecutionNode*>&& nodes
|
||||
= plan->findNodesOfType(EN::ENUMERATE_COLLECTION,
|
||||
true);
|
||||
std::unordered_set<ExecutionNode*> nodesSet;
|
||||
|
@ -2274,7 +2516,7 @@ int triagens::aql::undistributeRemoveAfterEnumColl (Optimizer* opt,
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief
|
||||
/// @brief auxilliary struct for the OR-to-IN conversion
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct OrToInConverter {
|
||||
|
@ -2286,7 +2528,7 @@ struct OrToInConverter {
|
|||
|
||||
std::string getString (AstNode const* node) {
|
||||
triagens::basics::StringBuffer buffer(TRI_UNKNOWN_MEM_ZONE);
|
||||
node->append(&buffer, false);
|
||||
node->stringify(&buffer, false);
|
||||
return std::string(buffer.c_str(), buffer.length());
|
||||
}
|
||||
|
||||
|
@ -2425,7 +2667,16 @@ struct OrToInConverter {
|
|||
}
|
||||
};
|
||||
|
||||
int triagens::aql::replaceORwithIN (Optimizer* opt,
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief this rule replaces expressions of the type:
|
||||
/// x.val == 1 || x.val == 2 || x.val == 3
|
||||
// with
|
||||
// x.val IN [1,2,3]
|
||||
// when the OR conditions are present in the same FILTER node, and refer to the
|
||||
// same (single) attribute.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int triagens::aql::replaceOrWithIn (Optimizer* opt,
|
||||
ExecutionPlan* plan,
|
||||
Optimizer::Rule const* rule) {
|
||||
ENTER_BLOCK;
|
||||
|
|
|
@ -99,6 +99,12 @@ namespace triagens {
|
|||
|
||||
int useIndexForSort (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief try to remove filters which are covered by indexes
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int removeFiltersCoveredByIndex (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief interchange adjacent EnumerateCollectionNodes in all possible ways
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -174,7 +180,7 @@ namespace triagens {
|
|||
// same (single) attribute.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int replaceORwithIN (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||
int replaceOrWithIn (Optimizer*, ExecutionPlan*, Optimizer::Rule const*);
|
||||
|
||||
} // namespace aql
|
||||
} // namespace triagens
|
||||
|
|
|
@ -190,7 +190,10 @@ void Parser::registerParseError (int errorCode,
|
|||
<< std::string("' at position ")
|
||||
<< line
|
||||
<< std::string(":")
|
||||
<< (column + 1)
|
||||
<< (column + 1);
|
||||
|
||||
if (_query->verboseErrors()) {
|
||||
errorMessage
|
||||
<< std::endl
|
||||
<< _query->queryString()
|
||||
<< std::endl;
|
||||
|
@ -206,6 +209,7 @@ void Parser::registerParseError (int errorCode,
|
|||
errorMessage << '^'
|
||||
<< '^'
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
registerError(errorCode, errorMessage.str().c_str());
|
||||
}
|
||||
|
|
|
@ -555,19 +555,19 @@ QueryResult Query::prepare (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,19 +625,19 @@ QueryResult Query::execute (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,23 +697,22 @@ QueryResultV8 Query::executeV8 (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResultV8(ex.code(), getStateString() + ex.message());
|
||||
return QueryResultV8(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResultV8(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResultV8(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief parse an AQL query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -811,16 +810,16 @@ QueryResult Query::explain () {
|
|||
return result;
|
||||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1051,7 +1050,7 @@ QueryResult Query::transactionError (int errorCode) const {
|
|||
err += std::string(" (") + detail + std::string(")");
|
||||
}
|
||||
|
||||
if (_queryString != nullptr) {
|
||||
if (_queryString != nullptr && verboseErrors()) {
|
||||
err += std::string("\nwhile executing:\n") + _queryString + std::string("\n");
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1144,7 @@ void Query::enterState (ExecutionState state) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string Query::getStateString () const {
|
||||
return "while " + StateNames[_state] + ": ";
|
||||
return std::string(" (while " + StateNames[_state] + ")");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -382,6 +382,14 @@ namespace triagens {
|
|||
return _plan;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the query returns verbose error messages
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool verboseErrors () const {
|
||||
return getBooleanOption("verboseErrors", false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set the plan for the query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -618,6 +626,7 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool const _contextOwnedByExterior;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -338,7 +338,10 @@ namespace triagens {
|
|||
RangeInfoBound low,
|
||||
RangeInfoBound high,
|
||||
bool equality)
|
||||
: _var(var), _attr(attr), _valid(true), _defined(true),
|
||||
: _var(var),
|
||||
_attr(attr),
|
||||
_valid(true),
|
||||
_defined(true),
|
||||
_equality(equality) {
|
||||
|
||||
if (low.isConstant()) {
|
||||
|
@ -347,6 +350,7 @@ namespace triagens {
|
|||
else {
|
||||
_lows.emplace_back(low);
|
||||
}
|
||||
|
||||
if (high.isConstant()) {
|
||||
_highConst.assign(high);
|
||||
}
|
||||
|
@ -373,13 +377,19 @@ namespace triagens {
|
|||
}
|
||||
}
|
||||
|
||||
RangeInfo ( std::string var,
|
||||
std::string attr)
|
||||
: _var(var), _attr(attr), _valid(true), _defined(true),
|
||||
RangeInfo (std::string const& var,
|
||||
std::string const& attr)
|
||||
: _var(var),
|
||||
_attr(attr),
|
||||
_valid(true),
|
||||
_defined(true),
|
||||
_equality(false) {
|
||||
}
|
||||
|
||||
RangeInfo () : _valid(false), _defined(false), _equality(false) {
|
||||
RangeInfo ()
|
||||
: _valid(false),
|
||||
_defined(false),
|
||||
_equality(false) {
|
||||
}
|
||||
|
||||
RangeInfo (basics::Json const& json);
|
||||
|
|
|
@ -424,6 +424,7 @@ void RestAqlHandler::useQuery (std::string const& operation,
|
|||
|
||||
try {
|
||||
handleUseQuery(operation, query, queryJson);
|
||||
if (_qId != 0) {
|
||||
try {
|
||||
_queryRegistry->close(_vocbase, _qId);
|
||||
}
|
||||
|
@ -432,6 +433,7 @@ void RestAqlHandler::useQuery (std::string const& operation,
|
|||
// an error might occur if "shutdown" is called
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
_queryRegistry->close(_vocbase, _qId);
|
||||
LOG_ERROR("failed during use of Query: %s", ex.message().c_str());
|
||||
|
@ -853,6 +855,7 @@ void RestAqlHandler::handleUseQuery (std::string const& operation,
|
|||
|
||||
// delete the query from the registry
|
||||
_queryRegistry->destroy(_vocbase, _qId, errorCode);
|
||||
_qId = 0;
|
||||
}
|
||||
catch (...) {
|
||||
LOG_ERROR("shutdown lead to an exception");
|
||||
|
|
|
@ -361,13 +361,14 @@ ClusterCommResult* ClusterComm::syncRequest (
|
|||
headersCopy);
|
||||
|
||||
if (res->result == nullptr || ! res->result->isComplete()) {
|
||||
cm->brokenConnection(connection);
|
||||
if (client->getErrorMessage() == "Request timeout reached") {
|
||||
res->status = CL_COMM_TIMEOUT;
|
||||
}
|
||||
else {
|
||||
res->status = CL_COMM_ERROR;
|
||||
}
|
||||
cm->brokenConnection(connection);
|
||||
client->invalidateConnection();
|
||||
}
|
||||
else {
|
||||
cm->returnConnection(connection);
|
||||
|
@ -747,6 +748,7 @@ void ClusterComm::asyncAnswer (string& coordinatorHeader,
|
|||
"/_api/shard-comm", body, len, headers);
|
||||
if (result == nullptr || ! result->isComplete()) {
|
||||
cm->brokenConnection(connection);
|
||||
client->invalidateConnection();
|
||||
}
|
||||
else {
|
||||
cm->returnConnection(connection);
|
||||
|
@ -1044,13 +1046,14 @@ void ClusterCommThread::run () {
|
|||
}
|
||||
|
||||
if (op->result == nullptr || ! op->result->isComplete()) {
|
||||
cm->brokenConnection(connection);
|
||||
if (client->getErrorMessage() == "Request timeout reached") {
|
||||
op->status = CL_COMM_TIMEOUT;
|
||||
}
|
||||
else {
|
||||
op->status = CL_COMM_ERROR;
|
||||
}
|
||||
cm->brokenConnection(connection);
|
||||
client->invalidateConnection();
|
||||
}
|
||||
else {
|
||||
cm->returnConnection(connection);
|
||||
|
|
|
@ -543,6 +543,7 @@ int createDocumentOnCoordinator (
|
|||
shared_ptr<CollectionInfo> collinfo = ci->getCollection(dbname, collname);
|
||||
|
||||
if (collinfo->empty()) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
||||
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
using namespace std;
|
||||
using namespace triagens::arango;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief controls if backtraces are printed with exceptions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool WithBackTrace = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor, without format string
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -46,9 +52,11 @@ Exception::Exception (int code,
|
|||
_code(code) {
|
||||
#ifdef TRI_ENABLE_MAINTAINER_MODE
|
||||
#if HAVE_BACKTRACE
|
||||
if (WithBackTrace) {
|
||||
_errorMessage += std::string("\n\n");
|
||||
TRI_GetBacktrace(_errorMessage);
|
||||
_errorMessage += std::string("\n\n");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@ -61,28 +69,19 @@ Exception::Exception (int code,
|
|||
Exception::Exception (int code,
|
||||
string const& errorMessage,
|
||||
char const* file,
|
||||
int line,
|
||||
bool errnoStringResolved)
|
||||
int line)
|
||||
: _errorMessage(errorMessage),
|
||||
_file(file),
|
||||
_line(line),
|
||||
_code(code) {
|
||||
|
||||
if (code != TRI_ERROR_INTERNAL) {
|
||||
if (! errnoStringResolved) {
|
||||
_errorMessage = std::string("(");
|
||||
_errorMessage += TRI_errno_string(code);
|
||||
_errorMessage += std::string(") ");
|
||||
_errorMessage += errorMessage;
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
#ifdef TRI_ENABLE_MAINTAINER_MODE
|
||||
#if HAVE_BACKTRACE
|
||||
if (WithBackTrace) {
|
||||
_errorMessage += std::string("\n\n");
|
||||
TRI_GetBacktrace(_errorMessage);
|
||||
_errorMessage += std::string("\n\n");
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
@ -137,6 +136,14 @@ std::string Exception::FillExceptionString (int code,
|
|||
return std::string(buffer);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief controls whether a backtrace is created for each exception
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Exception::SetVerbose (bool verbose) {
|
||||
WithBackTrace = verbose;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define THROW_ARANGO_EXCEPTION_PARAMS(code, ...) \
|
||||
throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__, true)
|
||||
throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief throws an arango exception with an error code and an already-built
|
||||
|
@ -60,7 +60,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define THROW_ARANGO_EXCEPTION_MESSAGE(code, message) \
|
||||
throw triagens::arango::Exception(code, message, __FILE__, __LINE__, false)
|
||||
throw triagens::arango::Exception(code, message, __FILE__, __LINE__)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public types
|
||||
|
@ -84,8 +84,7 @@ namespace triagens {
|
|||
Exception (int code,
|
||||
std::string const& errorMessage,
|
||||
char const* file,
|
||||
int line,
|
||||
bool errnoStringResolved);
|
||||
int line);
|
||||
|
||||
~Exception () throw ();
|
||||
|
||||
|
@ -107,6 +106,12 @@ namespace triagens {
|
|||
|
||||
static std::string FillExceptionString (int, ...);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief controls whether a backtrace is created for each exception
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void SetVerbose (bool);
|
||||
|
||||
protected:
|
||||
std::string _errorMessage;
|
||||
char const* _file;
|
||||
|
|
|
@ -533,6 +533,22 @@ static v8::Handle<v8::Value> JS_normalize_string (v8::Arguments const& argv) {
|
|||
return scope.Close(TRI_normalize_V8_Obj(argv[0]));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enables or disables native backtrace
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_EnableNativeBacktraces (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
if (argv.Length() != 1) {
|
||||
TRI_V8_EXCEPTION_USAGE(scope, "ENABLE_NATIVE_BACKTRACES(<value>)");
|
||||
}
|
||||
|
||||
triagens::arango::Exception::SetVerbose(TRI_ObjectToBoolean(argv[0]));
|
||||
|
||||
return scope.Close(v8::Undefined());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief compare two UTF 16 strings
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2543,6 +2559,8 @@ void TRI_InitV8VocBridge (triagens::arango::ApplicationV8* applicationV8,
|
|||
TRI_AddGlobalFunctionVocbase(context, "WAL_FLUSH", JS_FlushWal, true);
|
||||
TRI_AddGlobalFunctionVocbase(context, "WAL_PROPERTIES", JS_PropertiesWal, true);
|
||||
|
||||
TRI_AddGlobalFunctionVocbase(context, "ENABLE_NATIVE_BACKTRACES", JS_EnableNativeBacktraces, true);
|
||||
|
||||
// .............................................................................
|
||||
// create global variables
|
||||
// .............................................................................
|
||||
|
|
|
@ -92,6 +92,14 @@ var internal = require("internal");
|
|||
/// be present in the result if the query has a LIMIT clause and the LIMIT clause is
|
||||
/// actually used in the query.
|
||||
///
|
||||
/// - *maxPlans*: limits the maximum number of plans that are created by the AQL
|
||||
/// query optimizer.
|
||||
///
|
||||
/// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules
|
||||
/// can be put into this attribute, telling the optimizer to include or exclude
|
||||
/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it
|
||||
/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules.
|
||||
///
|
||||
/// If the result set can be created by the server, the server will respond with
|
||||
/// *HTTP 201*. The body of the response will contain a JSON object with the
|
||||
/// result set.
|
||||
|
@ -207,7 +215,7 @@ var internal = require("internal");
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Using a query option:
|
||||
/// Using query option "fullCount":
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorOption}
|
||||
/// var url = "/_api/cursor";
|
||||
|
@ -226,6 +234,28 @@ var internal = require("internal");
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Enabling and disabling optimizer rules:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestCursorOptimizerRules}
|
||||
/// var url = "/_api/cursor";
|
||||
/// var body = {
|
||||
/// query: "FOR i IN 1..10 LET a = 1 LET b = 2 FILTER a + b == 3 RETURN i",
|
||||
/// count: true,
|
||||
/// options: {
|
||||
/// maxPlans: 1,
|
||||
/// optimizer: {
|
||||
/// rules: [ "-all", "+remove-unnecessary-filters" ]
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// var response = logCurlRequest('POST', url, JSON.stringify(body));
|
||||
///
|
||||
/// assert(response.code === 201);
|
||||
///
|
||||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Executes a data-modification query and retrieves the number of
|
||||
/// modified documents:
|
||||
///
|
||||
|
|
|
@ -51,12 +51,15 @@ var ERRORS = require("internal").errors;
|
|||
/// The currently supported options are:
|
||||
/// - *allPlans*: if set to *true*, all possible execution plans will be returned.
|
||||
/// The default is *false*, meaning only the optimal plan will be returned.
|
||||
///
|
||||
/// - *maxPlans*: an optional maximum number of plans that the optimizer is
|
||||
/// allowed to generate. Setting this attribute to a low value allows to put a
|
||||
/// cap on the amount of work the optimizer does.
|
||||
///
|
||||
/// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules
|
||||
/// can be put into this attribute, telling the optimizer to include or exclude
|
||||
/// specific rules.
|
||||
/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it
|
||||
/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules.
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
|
@ -84,11 +87,15 @@ var ERRORS = require("internal").errors;
|
|||
/// Each plan in the result is a JSON object with the following attributes:
|
||||
/// - *nodes*: the list of execution nodes of the plan. The list of available node types
|
||||
/// can be found [here](.../Aql/Optimizer.html)
|
||||
///
|
||||
/// - *estimatedCost*: the total estimated cost for the plan. If there are multiple
|
||||
/// plans, the optimizer will choose the plan with the lowest total cost.
|
||||
///
|
||||
/// - *collections*: a list of collections used in the query
|
||||
///
|
||||
/// - *rules*: a list of rules the optimizer applied. The list of rules can be
|
||||
/// found [here](../Aql/Optimizer.html)
|
||||
///
|
||||
/// - *variables*: list of variables used in the query (note: this may contain
|
||||
/// internal variables created by the optimizer)
|
||||
///
|
||||
|
@ -152,6 +159,33 @@ var ERRORS = require("internal").errors;
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Using some options:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestExplainOptions}
|
||||
/// var url = "/_api/explain";
|
||||
/// var cn = "products";
|
||||
/// db._drop(cn);
|
||||
/// db._create(cn);
|
||||
/// db.products.ensureSkiplist("id");
|
||||
/// for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); }
|
||||
/// body = {
|
||||
/// query : "FOR p IN products LET a = p.id FILTER a == 4 LET name = p.name SORT p.id LIMIT 1 RETURN name",
|
||||
/// options : {
|
||||
/// maxPlans : 2,
|
||||
/// allPlans : true,
|
||||
/// optimizer : {
|
||||
/// rules: [ "-all", "+use-index-for-sort", "+use-index-range" ]
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// var response = logCurlRequest('POST', url, JSON.stringify(body));
|
||||
///
|
||||
/// assert(response.code === 200);
|
||||
///
|
||||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Returning all plans:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestExplainAllPlans}
|
||||
|
|
|
@ -2962,28 +2962,25 @@ function AQL_IS_IN_POLYGON (points, latitude, longitude) {
|
|||
return false;
|
||||
}
|
||||
|
||||
var searchLat, searchLon, pointLat, pointLon, geoJson = false;
|
||||
var search, pointLat, pointLon, geoJson = false;
|
||||
if (TYPEWEIGHT(latitude) === TYPEWEIGHT_LIST) {
|
||||
geoJson = AQL_TO_BOOL(longitude);
|
||||
if (geoJson) {
|
||||
// first list value is longitude, then latitude
|
||||
searchLat = latitude[1];
|
||||
searchLon = latitude[0];
|
||||
search = latitude;
|
||||
pointLat = 1;
|
||||
pointLon = 0;
|
||||
}
|
||||
else {
|
||||
// first list value is latitude, then longitude
|
||||
searchLat = latitude[0];
|
||||
searchLon = latitude[1];
|
||||
search = latitude;
|
||||
pointLat = 0;
|
||||
pointLon = 1;
|
||||
}
|
||||
}
|
||||
else if (TYPEWEIGHT(latitude) === TYPEWEIGHT_NUMBER &&
|
||||
TYPEWEIGHT(longitude) === TYPEWEIGHT_NUMBER) {
|
||||
searchLat = latitude;
|
||||
searchLon = longitude;
|
||||
search = [ latitude, longitude ];
|
||||
pointLat = 0;
|
||||
pointLon = 1;
|
||||
}
|
||||
|
@ -2992,30 +2989,37 @@ function AQL_IS_IN_POLYGON (points, latitude, longitude) {
|
|||
return false;
|
||||
}
|
||||
|
||||
var i, j = points.length - 1;
|
||||
var oddNodes = false;
|
||||
|
||||
for (i = 0; i < points.length; ++i) {
|
||||
if (TYPEWEIGHT(points[i]) !== TYPEWEIGHT_LIST) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (((points[i][pointLat] < searchLat && points[j][pointLat] >= searchLat) ||
|
||||
(points[j][pointLat] < searchLat && points[i][pointLat] >= searchLat)) &&
|
||||
(points[i][pointLon] <= searchLon || points[j][pointLon] <= searchLon)) {
|
||||
oddNodes ^= ((points[i][pointLon] + (searchLat - points[i][pointLat]) /
|
||||
(points[j][pointLat] - points[i][pointLat]) *
|
||||
(points[j][pointLon] - points[i][pointLon])) < searchLon);
|
||||
}
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
if (oddNodes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (points.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var i, n = points.length;
|
||||
var wn = 0;
|
||||
points.push(points[0]);
|
||||
|
||||
var isLeft = function (p0, p1, p2) {
|
||||
return ((p1[pointLon] - p0[pointLon]) * (p2[pointLat] - p0[pointLat]) -
|
||||
(p2[pointLon] - p0[pointLon]) * (p1[pointLat] - p0[pointLat]));
|
||||
};
|
||||
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (points[i][pointLat] <= search[pointLat]) {
|
||||
if (points[i + 1][pointLat] >= search[pointLat]) {
|
||||
if (isLeft(points[i], points[i + 1], search) >= 0) {
|
||||
++wn;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (points[i + 1][pointLat] <= search[pointLat]) {
|
||||
if (isLeft(points[i], points[i + 1], search) <= 0) {
|
||||
--wn;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (wn !== 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -101,14 +101,14 @@ function optimizerRuleTestSuite () {
|
|||
"FOR a IN [1, 2, 3, 4, 5, 6] RETURN SLICE(a, 4, 1)",
|
||||
"FOR a IN [17.33] RETURN FLOOR(a)",
|
||||
"FOR a IN ['-12'] RETURN TO_LIST(a)",
|
||||
"FOR a IN { \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } } RETURN TO_LIST(a)",
|
||||
"FOR a IN -12 RETURN ABS(a)",
|
||||
"FOR a IN -12 RETURN ABS(a + 17)",
|
||||
"FOR a IN 17.33 RETURN ROUND(a)",
|
||||
"FOR a IN 17.33 RETURN SQRT(a)",
|
||||
"FOR a IN -17.33 RETURN SQRT(a)",
|
||||
"FOR a IN CHAR_LENGTH('äöボカド名üÄÖÜß') return a + 1",
|
||||
"FOR a IN 7 return a..12",
|
||||
"FOR a IN [{ \"a\" : null, \"b\" : -63, \"c\" : [ 1, 2 ], \"d\": { \"a\" : \"b\" } }] RETURN TO_LIST(a)",
|
||||
"FOR a IN [-12] RETURN ABS(a)",
|
||||
"FOR a IN [-12] RETURN ABS(a + 17)",
|
||||
"FOR a IN [17.33] RETURN ROUND(a)",
|
||||
"FOR a IN [17.33] RETURN SQRT(a)",
|
||||
"FOR a IN [-17.33] RETURN SQRT(a)",
|
||||
"FOR a IN [CHAR_LENGTH('äöボカド名üÄÖÜß')] return a + 1",
|
||||
"FOR a IN [7] return a..12",
|
||||
"FOR a IN [1, 7, 3, 12] RETURN AVERAGE(a)",
|
||||
"FOR a IN [1, 7, 3, 12, null] RETURN NOT_NULL(a)",
|
||||
"FOR a IN [1, 7, 3, 12, null] RETURN FIRST_LIST(a)",
|
||||
|
|
|
@ -260,26 +260,26 @@ Endpoint* Endpoint::factory (const Endpoint::EndpointType type,
|
|||
if (found != string::npos && found > 2 && found + 2 < copy.size()) {
|
||||
// hostname and port (e.g. [address]:port)
|
||||
uint16_t port = (uint16_t) StringUtils::uint32(copy.substr(found + 2));
|
||||
|
||||
std::string portStr = copy.substr(1, found - 1);
|
||||
return new EndpointIpV6(type,
|
||||
encryption,
|
||||
specification,
|
||||
listenBacklog,
|
||||
reuseAddress,
|
||||
copy.substr(1, found - 1),
|
||||
portStr,
|
||||
port);
|
||||
}
|
||||
|
||||
found = copy.find("]", 1);
|
||||
if (found != string::npos && found > 2 && found + 1 == copy.size()) {
|
||||
// hostname only (e.g. [address])
|
||||
|
||||
std::string portStr = copy.substr(1, found - 1);
|
||||
return new EndpointIpV6(type,
|
||||
encryption,
|
||||
specification,
|
||||
listenBacklog,
|
||||
reuseAddress,
|
||||
copy.substr(1, found - 1),
|
||||
portStr,
|
||||
EndpointIp::_defaultPort);
|
||||
}
|
||||
|
||||
|
@ -293,13 +293,13 @@ Endpoint* Endpoint::factory (const Endpoint::EndpointType type,
|
|||
if (found != string::npos && found + 1 < copy.size()) {
|
||||
// hostname and port
|
||||
uint16_t port = (uint16_t) StringUtils::uint32(copy.substr(found + 1));
|
||||
|
||||
std::string portStr = copy.substr(0, found);
|
||||
return new EndpointIpV4(type,
|
||||
encryption,
|
||||
specification,
|
||||
listenBacklog,
|
||||
reuseAddress,
|
||||
copy.substr(0, found),
|
||||
portStr,
|
||||
port);
|
||||
}
|
||||
|
||||
|
|
|
@ -66,16 +66,21 @@ namespace triagens {
|
|||
_maxPacketSize(128 * 1024 * 1024),
|
||||
_keepAlive(true) {
|
||||
|
||||
TRI_ASSERT(connection != nullptr);
|
||||
|
||||
if (_connection->isConnected()) {
|
||||
_state = FINISHED;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleHttpClient::~SimpleHttpClient () {
|
||||
// connection may have been invalidated by other objects
|
||||
if (_connection != nullptr) {
|
||||
if (! _keepConnectionOnDestruction || ! _connection->isConnected()) {
|
||||
_connection->disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// public methods
|
||||
|
@ -86,6 +91,9 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SimpleHttpClient::close () {
|
||||
// ensure connection has not yet been invalidated
|
||||
TRI_ASSERT(_connection != nullptr);
|
||||
|
||||
_connection->disconnect();
|
||||
_state = IN_CONNECT;
|
||||
|
||||
|
@ -104,6 +112,9 @@ namespace triagens {
|
|||
size_t bodyLength,
|
||||
std::map<std::string, std::string> const& headerFields) {
|
||||
|
||||
// ensure connection has not yet been invalidated
|
||||
TRI_ASSERT(_connection != nullptr);
|
||||
|
||||
TRI_ASSERT(_result == nullptr);
|
||||
|
||||
_result = new SimpleHttpResult;
|
||||
|
@ -213,6 +224,9 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SimpleHttpClient::handleConnect () {
|
||||
// ensure connection has not yet been invalidated
|
||||
TRI_ASSERT(_connection != nullptr);
|
||||
|
||||
if (! _connection->connect()) {
|
||||
setErrorMessage("Could not connect to '" + _connection->getEndpoint()->getSpecification() + "'", errno);
|
||||
_state = DEAD;
|
||||
|
|
|
@ -84,6 +84,17 @@ namespace triagens {
|
|||
|
||||
~SimpleHttpClient ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief invalidates the connection used by the client
|
||||
/// this may be called from other objects that are responsible for managing
|
||||
/// connections. after this method has been called, the client must not be
|
||||
/// used for any further HTTP operations, but should be destroyed instantly.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void invalidateConnection () {
|
||||
_connection = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief close connection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue