1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel

This commit is contained in:
Heiko Kernbach 2014-11-07 14:19:39 +01:00
commit c13679e33d
32 changed files with 896 additions and 225 deletions

View File

@ -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

View File

@ -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(){

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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;
};

View File

@ -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!
////////////////////////////////////////////////////////////////////////////////

View File

@ -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;

View File

@ -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: "));
}
// -----------------------------------------------------------------------------

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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;
}

View File

@ -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:

View File

@ -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);
}
// -----------------------------------------------------------------------------

View File

@ -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",

View File

@ -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
//////////////////////////////////////////////////////////////////////////////

View File

@ -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;

View File

@ -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

View File

@ -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());
}

View File

@ -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] + ")");
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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");

View File

@ -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);

View File

@ -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;
}

View File

@ -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
// -----------------------------------------------------------------------------

View 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;

View 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
// .............................................................................

View File

@ -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:
///

View File

@ -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}

View File

@ -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);
}
// -----------------------------------------------------------------------------

View File

@ -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)",

View File

@ -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);
}

View File

@ -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;

View File

@ -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
////////////////////////////////////////////////////////////////////////////////