1
0
Fork 0

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

This commit is contained in:
Michael Hackstein 2014-11-08 22:38:00 +00:00
commit fc2a4d82d0
80 changed files with 2238 additions and 546 deletions

View File

@ -1,6 +1,23 @@
v2.3.0 (XXXX-XX-XX)
-------------------
* front-end: new icons for uploading and downloading JSON documents into a collection
* front-end: fixed documents pagination css display error
* front-end: fixed flickering of the progress view
* front-end: fixed missing event for documents filter function
* front-end: jsoneditor: added CMD+Return (Mac) CTRL+Return (Linux/Win) shortkey for
saving a document
* front-end: added information tooltip for uploading json documents.
* front-end: added database management view to the collapsed navigation menu
* front-end: added collection truncation feature
* fixed issue #1086: arangoimp: Odd errors if arguments are not given properly
* performance improvements for AQL queries that use JavaScript-based expressions
@ -78,6 +95,16 @@ v2.3.0-beta1 (2014-11-01)
specified, the number of V8 contexts created will be equal to the number of
server threads. Thus no change in configuration is required to keep the old
behavior.
However, the default configuration files shipped with ArangoDB have been changed.
The number of server threads has been increased in the configuration files, and
the number of V8 contexts is now explicitly set in the configuration files (to
the same value as the number of server threads was set to in 2.2).
If you are using the default config files or merge them with your local config files,
please review if the higher default number of server threads is okay in your
environment. Additionally you should verify that the number of V8 contexts
created (as specified in option `--javascript.v8-contexts`) is okay.
* removed index type "bitarray"

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

@ -4,7 +4,7 @@
To upgrade an existing ArangoDB database to a newer version of ArangoDB (e.g. 1.2 to 1.3, or 2.0 to 2.1), the following method is recommended:
* Check the *CHANGELOG* for API or other changes in the new version of ArangoDB and make sure your applications can deal with them
* Check the *CHANGELOG* and the [list of incompatible changes](../Upgrading/UpgradingChanges23.html) for API or other changes in the new version of ArangoDB and make sure your applications can deal with them
* Stop the "old" arangod service or binary
* Copy the entire "old" data directory to a safe place (that is, a backup)
* Install the new version of ArangoDB and start the server with the *--upgrade* option once. This might write to the logfile of ArangoDB, so you may want to check the logs for any issues before going on.
@ -16,4 +16,4 @@ If anything goes wrong during or shortly after the upgrade:
* Revert to the "old" arangod binary and restore the "old" data directory
* Start the "old" version again
It is not supported to use datafiles created or modified by a newer version of ArangoDB with an older ArangoDB version. For example, it is unsupported and is likely to cause problems when using 1.4 datafiles with an ArangoDB 1.3 instance.
It is not supported to use datafiles created or modified by a newer version of ArangoDB with an older ArangoDB version. For example, it is unsupported and is likely to cause problems when using 2.3 datafiles with an ArangoDB 2.2 instance.

View File

@ -2,33 +2,30 @@
Welcome to the ArangoDB documentation!
The documentation introduces ArangoDB for you as an user, developer and administrator and describes all of his functions in detail.
The documentation introduces ArangoDB for you as an user, developer and administrator and describes all of its functions in detail.
ArangoDB is a multi-purpose open-source database with a flexible data
model for documents, graphs and key-values. You can easily build high
performance applications using a convenient
[SQL-like query language](../Aql/README.md) or [JavaScript](../Foxx/README.md) extensions.
ArangoDB is a multi-purpose, open-source database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions. Use ACID transactions if you require them. Scale horizontally and vertically with a few mouse clicks.
The database server [_arangod_](../FirstSteps/Arangod.md) stores all documents and serves them
using a REST interface. There are [drivers](https://www.arangodb.com/drivers) for all major languages like
Ruby, Python, PHP, JavaScript, and Perl. In the following sections we
will use the JavaScript shell to communicate with the database and
demonstrate some of ArangoDB's features using JavaScript.
Key features include:
Some of the features and programs of ArangoDB are:
- A powerful query language
- Open Source
- A database daemon
- An ArangoDB shell
- Flexible data modeling
- And many more!
* **Schema-free schemata** let you combine the space efficiency of MySQL with the performance power of NoSQL
* Use ArangoDB as an **application server** and fuse your application and database together for maximal throughput
* JavaScript for all: **no language zoo**, you can use one language from your browser to your back-end
* ArangoDB is **multi-threaded** - exploit the power of all your cores
* **Flexible data modelling**: model your data as combination of key-value pairs, documents or graphs - perfect for social relations
* Free **index choice**: use the correct index for your problem, be it a skip list or a fulltext search
* Configurable **durability**: let the application decide if it needs more durability or more performance
* No-nonsense storage: ArangoDB uses all of the power of **modern storage hardware**, like SSD and large caches
* **Powerful query language** (AQL) to retrieve and modify data
* **Transactions**: run queries on multiple documents or collections with optional transactional consistency and isolation
* **Replication** and **Sharding**: set up the database in a master-slave configuration or spread bigger datasets across multiple servers
* It is **open source** (Apache Licence 2.0)
In this documentation you can inform yourself about all the functions, features and programs ArangoDB provides for you.
If you want to test the shell go [here](https://www.arangodb.com/shtutorial).
If you want to test the shell go [here](https://www.arangodb.com/shtutorial/).
If you want to play with our query language, go to our [AQL Tutorial](https://www.arangodb.com/shtutorial).
If you want to play with our query language, go to our [AQL Tutorial](https://www.arangodb.com/aqltutorial/).
!SUBSECTION Community

View File

@ -1,7 +1,8 @@
!CHAPTER Upgrading to ArangoDB 2.3
Please read the following sections if you upgrade from a previous version to
ArangoDB 2.3.
ArangoDB 2.3. Please be sure that you have checked the list of [changes in 2.3](../Upgrading/UpgradingChanges23.html)
before upgrading.
Please note first that a database directory used with ArangoDB 2.3
cannot be used with earlier versions (e.g. ArangoDB 2.2) any
@ -113,151 +114,3 @@ above in the graphical front end. You have to replace `"root"` with
a user name and `""` with a password that is valid for authentication
with the cluster.
!SECTION AQL: Changed behavior
!SUBSECTION AQL queries throw less exceptions
ArangoDB 2.3 contains a completely rewritten AQL query optimizer and execution
engine. This means that AQL queries will be executed with a different engine than
in ArangoDB 2.2 and earlier. Parts of AQL queries might be executed in different
order than before because the AQL optimizer has more freedom to move things
around in a query.
In previous versions of ArangoDB, AQL queries aborted with an exception in many
situations and threw a runtime exception. Exceptions were thrown when trying to
find a value using the `IN` operator in a non-list element, when trying to use
non-boolean values with the logical operands `&&` or `||` or `!`, when using non-numeric
values in arithmetic operations, when passing wrong parameters into functions etc.
In ArangoDB 2.3 this has been changed in many cases to make AQL more user-friendly
and to allow the optimization to perform much more query optimizations.
Here is a summary of changes:
- when a non-list value is used on the right-hand side of the `IN` operator, the
result will be `false` in ArangoDB 2.3, and no exception will be thrown.
- the boolean operators `&&` and `||` do not throw in ArangoDB 2.3 if any of the
operands is not a boolean value. Instead, they will perform an implicit cast of
the values to booleans. Their result will be as follows:
- `lhs && rhs` will return `lhs` if it is `false` or would be `false` when converted
into a boolean. If `lhs` is `true` or would be `true` when converted to a boolean,
`rhs` will be returned.
- `lhs || rhs` will return 'lhs` if it is `true` or would be `true` when converted
into a boolean. If `lhs` is `false` or would be `false` when converted to a boolean,
`rhs` will be returned.
- `! value` will return the negated value of `value` converted into a boolean
- the arithmetic operators (`+`, `-`, `*`, `/`, `%`) can be applied to any value and
will not throw exceptions when applied to non-numeric values. Instead, any value used
in these operators will be casted to a numeric value implicitly. If no numeric result
can be produced by an arithmetic operator, it will return `null` in ArangoDB 2.3. This
is also true for division by zero.
- passing arguments of invalid types into AQL functions does not throw a runtime
exception in most cases, but may produce runtime warnings. Built-in AQL functions that
receive invalid arguments will then return `null`.
!SUBSECTION Changed return values in ArangoQueryCursor.getExtra()
The return value of `ArangoQueryCursor.getExtra()` has been changed in ArangoDB 2.3.
It now contains a `stats` attribute with statistics about the query previously executed.
It also contains a `warnings` attribute with warnings that happened during query
execution.
The new return value format looks like this:
```
arangosh> stmt = db._createStatement("FOR i IN mycollection RETURN i"); stmt.execute().getExtra()
{
"stats" : {
"writesExecuted" : 0,
"writesIgnored" : 0,
"scannedFull" : 2600,
"scannedIndex" : 0
},
"warnings" : [ ]
}
arangosh> stmt = db._createStatement("FOR i IN xx REMOVE i IN xx"); stmt.execute().getExtra()
{
"stats" : {
"writesExecuted" : 2600,
"writesIgnored" : 0,
"scannedFull" : 2600,
"scannedIndex" : 0
},
"warnings" : [ ]
}
```
In ArangoDB 2.2, the return value of `ArangoQueryCursor.getExtra()` was empty for read-only
queries and contained two queries for data-modification queries:
```
arangosh> stmt = db._createStatement("FOR i IN mycollection RETURN i"); stmt.execute().getExtra()
{
}
arangosh> stmt = db._createStatement("FOR i IN mycollection REMOVE i IN mycollection"); stmt.execute().getExtra()
{
"operations" : {
"executed" : 2600,
"ignored" : 0
}
}
```
!SUBSECTION Changed return values in ArangoStatement.explain()
The return value of `ArangoStatement.explain()` has been changed in ArangoDB 2.3.
In ArangoDB 2.3, the full execution plan for an AQL query is returned alongside all
applied optimizer rules, optimization warnings etc. It is also possible to have the
optimizer return all execution plans. This required a new data structure.
Client programs that use `ArangoStatement.explain()` or the HTTP REST API method
`POST /_api/explain` may need to be adjusted to use the new return format.
The return value of `ArangoStatement.parse()` has been extended in ArangoDB 2.3.
In addition to the existing attributes, ArangoDB 2.3 will also return an `ast` attribute
containing the abstract syntax tree of the statement. This extra attribute can
safely be ignored by client programs.
!SUBSECTION New AQL keywords
The following keywords have been added to AQL in ArangoDB 2.3:
- *NOT*
- *AND*
- *OR*
Unquoted usage of these keywords for attribute names in AQL queries will likely
fail in ArangoDB 2.3. If any such attribute name needs to be used in a query, it
should be enclosed in backticks to indicate the usage of a literal attribute
name.
!SECTION Removed features
!SUBSECTION Bitarray indexes
Bitarray indexes were only half-way documented and integrated in previous versions
of ArangoDB so their benefit was limited. The support for bitarray indexes has
thus been removed in ArangoDB 2.3. It is not possible to create indexes of type
"bitarray" with ArangoDB 2.3.
When a collection is openend that contains a bitarray index definition created
with a previous version of ArangoDB, ArangoDB will ignore it and log the following
warning:
index type 'bitarray' is not supported in this version of ArangoDB and is ignored
Future versions of ArangoDB may automatically remove such index definitions so the
warnings will eventually disappear.
!SUBSECTION Other removed features
The HTTP API method at `POST /_admin/modules/flush` has been removed.

View File

@ -4,6 +4,37 @@ It is recommended to check the following list of incompatible changes **before**
upgrading to ArangoDB 2.3, and adjust any client programs if necessary.
!SECTION Default configuration file changes
With ArangoDB 2.3, the number of server threads can be configured independently of
the number of V8 contexts. The configuration option `--javascript.v8-contexts` was
added to arangod to provide better control over the number of V8 contexts created
in arangod.
Previously, the number of V8 contexts arangod created at startup was equal
to the number of server threads (as specified by option `--server.threads`).
In some situations it may be more sensible to create different amounts of threads
and V8 contexts. This is because each V8 contexts created will consume memory
and requires CPU resources for periodic garbage collection. Contrary, server
threads do not have such high memory or CPU footprint.
If the option `--javascript.v8-contexts` is not specified, the number of V8
contexts created at startup will remain equal to the number of server threads.
Thus no change in configuration is required to keep the same behavior as in
previous ArangoDB versions.
However, the default configuration files shipped with ArangoDB have been changed.
The number of server threads has been increased in the configuration files, and
the number of V8 contexts is now explicitly set in the configuration files (to
the same value as the number of server threads was set to in 2.2).
If you are using the default config files or merge them with your local config files,
please review if the higher default number of server threads is okay in your
environment. Additionally you should verify that the number of V8 contexts
created (as specified in option `--javascript.v8-contexts`) is okay.
!SECTION AQL
!SUBSECTION AQL queries throw less exceptions
@ -143,13 +174,20 @@ will look like this:
}
```
If the query option `fullCount` is requested, the `fullCount` result value will also
be returned inside the `stats` attribute of the `extra` attribute, and not directly
as an attribute inside the `extra` attribute as in 2.2. Note that a `fullCount` will
only be present in `extra`.`stats` if it was requested as an option for the query.
The result in ArangoDB 2.3 will also contain a `warnings` attribute with the list of
warnings that happened during query execution.
!SUBSECTION Changed return values in ArangoStatement.explain()
The return value of `ArangoStatement.explain()` has been changed in ArangoDB 2.3.
The return value of `ArangoStatement.explain()` has changed significantly in
ArangoDB 2.3. The new return value structure is not compatible with the structure
returned by 2.2.
In ArangoDB 2.3, the full execution plan for an AQL query is returned alongside all
applied optimizer rules, optimization warnings etc. It is also possible to have the

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

@ -438,6 +438,7 @@ SHELL_COMMON = \
@top_srcdir@/js/common/tests/shell-rename-noncluster.js \
@top_srcdir@/js/common/tests/shell-simple-query.js \
@top_srcdir@/js/common/tests/shell-statement.js \
@top_srcdir@/js/common/tests/shell-statement-noncluster.js \
@top_srcdir@/js/common/tests/shell-explain-noncluster.js \
@top_srcdir@/js/common/tests/shell-transactions.js \
@top_srcdir@/js/common/tests/shell-unload.js \

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,16 @@ void Ast::optimize () {
return optimizeLet(node);
}
// FILTER
if (node->type == NODE_TYPE_FILTER) {
return optimizeFilter(node);
}
// FOR
if (node->type == NODE_TYPE_FOR) {
return optimizeFor(node);
}
return node;
};
@ -1614,6 +1653,62 @@ AstNode* Ast::optimizeLet (AstNode* node) {
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief optimizes the FILTER statement
////////////////////////////////////////////////////////////////////////////////
AstNode* Ast::optimizeFilter (AstNode* node) {
TRI_ASSERT(node != nullptr);
TRI_ASSERT(node->type == NODE_TYPE_FILTER);
TRI_ASSERT(node->numMembers() == 1);
AstNode* expression = node->getMember(0);
if (expression == nullptr || ! expression->isDeterministic()) {
return node;
}
if (expression->isTrue()) {
// optimize away the filter if it is always true
return createNodeFilter(createNodeValueBool(true));
}
if (expression->isFalse()) {
// optimize away the filter if it is always false
return createNodeFilter(createNodeValueBool(false));
}
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,20 @@ namespace triagens {
AstNode* optimizeLet (AstNode*);
////////////////////////////////////////////////////////////////////////////////
/// @brief optimizes the FILTER statement
////////////////////////////////////////////////////////////////////////////////
AstNode* optimizeFilter (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 +623,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 +709,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);
@ -891,6 +923,21 @@ bool AstNode::isTrue () const {
type == NODE_TYPE_ARRAY) {
return true;
}
else if (type == NODE_TYPE_OPERATOR_BINARY_OR) {
if (getMember(0)->isTrue()) {
return true;
}
return getMember(1)->isTrue();
}
else if (type == NODE_TYPE_OPERATOR_BINARY_AND) {
if (! getMember(0)->isTrue()) {
return false;
}
return getMember(1)->isTrue();
}
else if (type == NODE_TYPE_OPERATOR_UNARY_NOT) {
return ! getMember(0)->isTrue();
}
return false;
}
@ -900,7 +947,41 @@ bool AstNode::isTrue () const {
////////////////////////////////////////////////////////////////////////////////
bool AstNode::isFalse () const {
return ! isTrue();
if (type == NODE_TYPE_VALUE) {
switch (value.type) {
case VALUE_TYPE_NULL:
return true;
case VALUE_TYPE_BOOL:
return ! value.value._bool;
case VALUE_TYPE_INT:
return (value.value._int == 0);
case VALUE_TYPE_DOUBLE:
return value.value._double == 0.0;
case VALUE_TYPE_STRING:
return (*value.value._string == '\0');
}
}
else if (type == NODE_TYPE_LIST ||
type == NODE_TYPE_ARRAY) {
return false;
}
else if (type == NODE_TYPE_OPERATOR_BINARY_OR) {
if (! getMember(0)->isFalse()) {
return false;
}
return getMember(1)->isFalse();
}
else if (type == NODE_TYPE_OPERATOR_BINARY_AND) {
if (getMember(0)->isFalse()) {
return true;
}
return getMember(1)->isFalse();
}
else if (type == NODE_TYPE_OPERATOR_UNARY_NOT) {
return ! getMember(0)->isFalse();
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
@ -1199,16 +1280,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,
bool verbose) const {
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 +1306,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 +1317,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 +1335,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 +1346,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 +1362,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,31 +1372,68 @@ 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;
}
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 +1452,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 +1486,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,8 +646,8 @@ namespace triagens {
/// @brief append a JavaScript representation of the node into a string buffer
////////////////////////////////////////////////////////////////////////////////
void append (triagens::basics::StringBuffer*,
bool) const;
void stringify (triagens::basics::StringBuffer*,
bool) const;
// -----------------------------------------------------------------------------
// --SECTION-- private methods
@ -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

@ -1649,45 +1649,42 @@ AqlItemBlock* EnumerateListBlock::getSome (size_t, size_t atMost) {
// get the size of the thing we are looping over
_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");
}
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;
//we require the total number of items
for (size_t i = 0; i < inVarReg._vector->size(); i++) {
_DOCVECsize += inVarReg._vector->at(i)->size();
case AqlValue::JSON: {
if (! inVarReg._json->isList()) {
throwListExpectedException();
}
sizeInVar = inVarReg._json->size();
break;
}
sizeInVar = _DOCVECsize;
if (sizeInVar > 0) {
_collection = inVarReg._vector->at(0)->getDocumentCollection(0);
case AqlValue::RANGE: {
sizeInVar = inVarReg._range->size();
break;
}
break;
}
case AqlValue::SHAPED: {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"EnumerateListBlock: cannot iterate over shaped value");
}
case AqlValue::DOCVEC: {
if (_index == 0) { // this is a (maybe) new DOCVEC
_DOCVECsize = 0;
// we require the total number of items
case AqlValue::EMPTY: {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"EnumerateListBlock: cannot iterate over empty value");
}
for (size_t i = 0; i < inVarReg._vector->size(); i++) {
_DOCVECsize += inVarReg._vector->at(i)->size();
}
}
sizeInVar = _DOCVECsize;
if (sizeInVar > 0) {
_collection = inVarReg._vector->at(0)->getDocumentCollection(0);
}
break;
}
case AqlValue::SHAPED: {
throwListExpectedException();
}
case AqlValue::EMPTY: {
throwListExpectedException();
}
}
if (sizeInVar == 0) {
@ -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,11 +142,13 @@ AqlValue Expression::execute (triagens::arango::AqlTransaction* trx,
return _func->execute(_ast->query(), trx, docColls, argv, startPos, vars, regs);
}
catch (triagens::arango::Exception& ex) {
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);
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 . . .
@ -468,6 +468,15 @@ void Optimizer::setupRules () {
useIndexForSort,
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

View File

@ -128,13 +128,16 @@ 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,
// 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

@ -217,8 +217,10 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt,
auto s = static_cast<CalculationNode*>(setter);
auto root = s->expression()->node();
if (! root->isConstant()) {
// filter expression can only be evaluated at runtime
TRI_ASSERT(root != nullptr);
if (root->canThrow() || ! root->isDeterministic()) {
// we better not tamper with this filter
continue;
}
@ -232,7 +234,7 @@ int triagens::aql::removeUnnecessaryFiltersRule (Optimizer* opt,
toUnlink.insert(n);
modified = true;
}
else {
else if (root->isFalse()) {
// filter is always false
// now insert a NoResults node below it
auto noResults = new NoResultsNode(plan, plan->nextId());
@ -1477,6 +1479,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,8 +1762,8 @@ static bool nextPermutationTuple (std::vector<size_t>& data,
int triagens::aql::interchangeAdjacentEnumerations (Optimizer* opt,
ExecutionPlan* plan,
Optimizer::Rule const* rule) {
std::vector<ExecutionNode*> nodes
= plan->findNodesOfType(EN::ENUMERATE_COLLECTION,
std::vector<ExecutionNode*>&& nodes
= plan->findNodesOfType(EN::ENUMERATE_COLLECTION,
true);
std::unordered_set<ExecutionNode*> nodesSet;
for (auto n : nodes) {
@ -2274,7 +2518,7 @@ int triagens::aql::undistributeRemoveAfterEnumColl (Optimizer* opt,
}
////////////////////////////////////////////////////////////////////////////////
/// @brief
/// @brief auxilliary struct for the OR-to-IN conversion
////////////////////////////////////////////////////////////////////////////////
struct OrToInConverter {
@ -2286,7 +2530,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 +2669,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

@ -41,7 +41,7 @@ using namespace triagens::aql;
/// @brief create the parser
////////////////////////////////////////////////////////////////////////////////
Parser::Parser (Query* query)
Parser::Parser (Query* query)
: _query(query),
_ast(query->ast()),
_scanner(nullptr),
@ -190,22 +190,26 @@ void Parser::registerParseError (int errorCode,
<< std::string("' at position ")
<< line
<< std::string(":")
<< (column + 1)
<< std::endl
<< _query->queryString()
<< std::endl;
<< (column + 1);
if (_query->verboseErrors()) {
errorMessage
<< std::endl
<< _query->queryString()
<< std::endl;
// create a neat pointer to the location of the error.
size_t i;
for (i = 0; i + 1 < (size_t) column; i++) {
errorMessage << ' ';
// create a neat pointer to the location of the error.
size_t i;
for (i = 0; i + 1 < (size_t) column; i++) {
errorMessage << ' ';
}
if (i > 0) {
errorMessage << '^';
}
errorMessage << '^'
<< '^'
<< std::endl;
}
if (i > 0) {
errorMessage << '^';
}
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,12 +424,14 @@ void RestAqlHandler::useQuery (std::string const& operation,
try {
handleUseQuery(operation, query, queryJson);
try {
_queryRegistry->close(_vocbase, _qId);
}
catch (...) {
// ignore errors on unregistering
// an error might occur if "shutdown" is called
if (_qId != 0) {
try {
_queryRegistry->close(_vocbase, _qId);
}
catch (...) {
// ignore errors on unregistering
// an error might occur if "shutdown" is called
}
}
}
catch (triagens::arango::Exception const& ex) {
@ -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

@ -33,7 +33,8 @@
#include "Basics/WriteLocker.h"
#include "Basics/ConditionLocker.h"
#include "Basics/StringUtils.h"
#include "lib/SimpleHttpClient/ConnectionManager.h"
#include "SimpleHttpClient/ConnectionManager.h"
#include "Dispatcher/DispatcherThread.h"
#include "VocBase/server.h"
@ -211,6 +212,17 @@ ClusterCommResult* ClusterComm::asyncRequest (
basics::StringUtils::itoa(coordTransactionID);
(*headerFields)["Authorization"] = ServerState::instance()->getAuthentication();
#ifdef DEBUG_CLUSTER_COMM
#ifdef TRI_ENABLE_MAINTAINER_MODE
#if HAVE_BACKTRACE
std::string bt;
TRI_GetBacktrace(bt);
std::replace( bt.begin(), bt.end(), '\n', ';'); // replace all '\n' to ';'
(*headerFields)["X-Arango-BT-A-SYNC"] = bt;
#endif
#endif
#endif
op->status = CL_COMM_SUBMITTED;
op->reqtype = reqtype;
op->path = path;
@ -336,18 +348,28 @@ ClusterCommResult* ClusterComm::syncRequest (
map<string, string> headersCopy(headerFields);
headersCopy.emplace(make_pair(string("Authorization"), ServerState::instance()->getAuthentication()));
#ifdef DEBUG_CLUSTER_COMM
#ifdef TRI_ENABLE_MAINTAINER_MODE
#if HAVE_BACKTRACE
std::string bt;
TRI_GetBacktrace(bt);
std::replace( bt.begin(), bt.end(), '\n', ';'); // replace all '\n' to ';'
headersCopy["X-Arango-BT-SYNC"] = bt;
#endif
#endif
#endif
res->result = client->request(reqtype, path, body.c_str(), body.size(),
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);
@ -485,6 +507,11 @@ ClusterCommResult* ClusterComm::wait (
endtime = TRI_microtime() + timeout;
}
// tell Dispatcher that we are waiting:
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->blockThread();
}
if (0 != operationID) {
// In this case we only have to look into at most one operation.
basics::ConditionLocker locker(&somethingReceived);
@ -499,6 +526,10 @@ ClusterCommResult* ClusterComm::wait (
res = new ClusterCommResult();
res->operationID = operationID;
res->status = CL_COMM_DROPPED;
// tell Dispatcher that we are back in business
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->unblockThread();
}
return res;
}
}
@ -511,6 +542,10 @@ ClusterCommResult* ClusterComm::wait (
receivedByOpID.erase(i);
received.erase(q);
res = static_cast<ClusterCommResult*>(op);
// tell Dispatcher that we are back in business
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->unblockThread();
}
return res;
}
// It is in the receive queue but still waiting, now wait actually
@ -541,6 +576,10 @@ ClusterCommResult* ClusterComm::wait (
receivedByOpID.erase(i);
received.erase(q);
res = static_cast<ClusterCommResult*>(op);
// tell Dispatcher that we are back in business
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->unblockThread();
}
return res;
}
}
@ -564,6 +603,10 @@ ClusterCommResult* ClusterComm::wait (
res->operationID = operationID;
res->shardID = shardID;
res->status = CL_COMM_DROPPED;
// tell Dispatcher that we are back in business
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->unblockThread();
}
return res;
}
// Here it could either be in the receive or the send queue, let's wait
@ -580,6 +623,10 @@ ClusterCommResult* ClusterComm::wait (
res->operationID = operationID;
res->shardID = shardID;
res->status = CL_COMM_TIMEOUT;
// tell Dispatcher that we are back in business
if (triagens::rest::DispatcherThread::currentDispatcherThread != nullptr) {
triagens::rest::DispatcherThread::currentDispatcherThread->unblockThread();
}
return res;
}
@ -727,6 +774,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);
@ -1024,13 +1072,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
_errorMessage += std::string("\n\n");
TRI_GetBacktrace(_errorMessage);
_errorMessage += std::string("\n\n");
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
_errorMessage += std::string("\n\n");
TRI_GetBacktrace(_errorMessage);
_errorMessage += std::string("\n\n");
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
////////////////////////////////////////////////////////////////////////////////
@ -2542,6 +2558,8 @@ void TRI_InitV8VocBridge (triagens::arango::ApplicationV8* applicationV8,
TRI_AddGlobalFunctionVocbase(context, "TRANSACTION", JS_Transaction, true);
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

@ -15,6 +15,7 @@ threads = 1
startup-directory = @PKGDATADIR@/js
app-path = @LOCALSTATEDIR@/lib/arangodb-apps
script = @PKGDATADIR@/js/server/arango-dfdb.js
v8-contexts = 1
[log]
level = info

View File

@ -34,8 +34,8 @@ endpoint = tcp://0.0.0.0:8529
# disable authentication for the admin frontend
disable-authentication = yes
# number of worker threads for V8
threads = 5
# number of server threads
threads = 10
# the user and group are normally set in the start script
# uid = arangodb
@ -49,6 +49,9 @@ startup-directory = @PKGDATADIR@/js
app-path = @LOCALSTATEDIR@/lib/arangodb-apps
# app-path = @HOMEDRIVE@/@HOMEPATH@/arangodb/apps
# number of worker threads for V8
v8-contexts = 5
[log]
level = info
severity = human

View File

@ -14,6 +14,7 @@ threads = 1
startup-directory = ./js
app-path = ./js/apps
script = ./js/server/arango-dfdb.js
v8-contexts = 1
[ruby]
modules-path = ./mr

View File

@ -5,7 +5,7 @@
[server]
disable-authentication = true
endpoint = tcp://localhost:8529
threads = 5
threads = 10
keyfile = UnitTests/server.pem
# reuse-address = false
@ -16,6 +16,7 @@ threads = 3
startup-directory = ./js
app-path = ./js/apps
frontend-development = false
v8-contexts = 5
[ruby]
action-directory = ./mr/actions/system

View File

@ -5,7 +5,7 @@
[server]
disable-authentication = true
endpoint = tcp://localhost:8529
threads = 5
threads = 10
keyfile = UnitTests/server.pem
# reuse-address = false
@ -16,6 +16,7 @@ threads = 3
startup-directory = ./js
app-path = ./js/apps
frontend-development = false
v8-contexts = 5
[ruby]
action-directory = ./mr/actions/system

View File

@ -5,7 +5,7 @@
[server]
disable-authentication = true
endpoint = tcp://localhost:8529
threads = 5
threads = 10
# reuse-address = false
[scheduler]
@ -15,6 +15,7 @@ threads = 3
startup-directory = ./js
app-path = ./js/apps
frontend-development = false
v8-contexts = 5
[ruby]
action-directory = ./mr/actions/system

View File

@ -15,6 +15,7 @@ threads = 3
startup-directory = ./js
app-path = ./js/apps
frontend-development = false
v8-contexts = 4
[ruby]
action-directory = ./mr/actions/system

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

@ -407,7 +407,8 @@ controller.get("/query/download/:user", function(req, res) {
res.set("Content-Type", "application/json");
res.set("Content-Disposition", "attachment; filename=queries.json");
res.json(result.extra.queries);
res.json(result.userData.queries);
}).summary("Download all user queries")
.notes("This function downloads all user queries from the given user");
@ -424,7 +425,6 @@ controller.get("/query/result/download/:query", function(req, res) {
var internal = require("internal");
query = internal.base64Decode(query);
internal.print(query);
try {
parsedQuery = JSON.parse(query);
}

View File

@ -113,6 +113,9 @@
content: [{
label: "Insert",
letter: "Ctrl + Insert"
},{
label: "Save",
letter: "Ctrl + Return, CMD + Return"
},{
label: "Append",
letter: "Ctrl + Shift + Insert"

View File

@ -147,7 +147,8 @@
},
getDocuments: function (callback) {
window.progressView.show("Fetching documents...");
// window.progressView.show("Fetching documents...");
window.progressView.showWithDelay(300, "Fetching documents...");
var self = this,
query,
bindVars,
@ -201,6 +202,7 @@
data: JSON.stringify(queryObj),
contentType: "application/json",
success: function(data) {
window.progressView.toShow = false;
self.clearDocuments();
if (data.extra && data.extra.fullCount !== undefined) {
self.setTotal(data.extra.fullCount);
@ -254,7 +256,7 @@
return queryObj;
},
updloadDocuments : function (file) {
uploadDocuments : function (file) {
var result;
$.ajax({
type: "POST",

File diff suppressed because one or more lines are too long

View File

@ -129,10 +129,25 @@
return returnval;
},
truncateCollection: function () {
$.ajax({
async: false,
cache: false,
type: 'PUT',
url: "/_api/collection/" + this.get("id") + "/truncate",
success: function () {
arangoHelper.arangoNotification('Collection truncated');
},
error: function () {
arangoHelper.arangoError('Collection error');
}
});
},
loadCollection: function () {
var self = this;
$.ajax({
async:false,
async: false,
cache: false,
type: 'PUT',
url: "/_api/collection/" + this.get("id") + "/load",

View File

@ -22,12 +22,12 @@
</li>
<li class="enabled">
<a id="importCollection" class="headerButton">
<span class="icon_arangodb_import" title="Upload documents from JSON file"></span>
<span title="Upload documents from JSON file"><i class="fa fa-upload"></i></span>
</a>
</li>
<li class="enabled">
<a id="exportCollection" class="headerButton">
<span class="icon_arangodb_export" title="Download documents as JSON file"></span>
<span title="Download documents as JSON file"><i class="fa fa-download"></i></span>
</a>
</li>
<li class="enabled">
@ -70,6 +70,7 @@
<button id="confirmDocImport" class="button-success btn-old-padding" style="float:right">
<img id="uploadIndicator" class="upload-indicator" src="img/ajax-loader.gif"/>Import JSON
</button>
<div title='Example input data:</br></br> I. Line-wise: </br> { "_key": "key1", ... } { "_key": "key2", ... } or </br></br> II. JSON documents embedded into a list: </br> [ </br> { "_key": "key1", ... }, </br> { "_key": "key2", ... }, </br> ... </br> ]' class="upload-info" style="float:left; margin-top:7px; margin-right: 10px;"><i style="font-size: 13pt;" class="fa fa-info-circle"></i></div>
</div>
</div>

View File

@ -77,6 +77,7 @@
<% if(isSystem) { %>
<option value="#logs">Logs</option>
<option value="#databases">Databases</option>
<% } %>

View File

@ -74,6 +74,12 @@
window.modalView.hide();
},
truncateCollection: function () {
this.model.truncateCollection();
this.render();
window.modalView.hide();
},
deleteCollection: function () {
this.model.destroy(
{
@ -249,6 +255,18 @@
"change-collection-status", "Status", this.model.get('status'), ""
)
);
buttons.push(
window.modalView.createDeleteButton(
"Delete",
this.deleteCollection.bind(this)
)
);
buttons.push(
window.modalView.createDeleteButton(
"Truncate",
this.truncateCollection.bind(this)
)
);
if(collectionIsLoaded) {
buttons.push(
window.modalView.createNotificationButton(
@ -265,12 +283,6 @@
);
}
buttons.push(
window.modalView.createDeleteButton(
"Delete",
this.deleteCollection.bind(this)
)
);
buttons.push(
window.modalView.createSuccessButton(
"Save",

View File

@ -81,7 +81,7 @@
"keyup #createEdge" : "listenKey",
"click .key" : "nop",
"keyup" : "returnPressedHandler",
"keydown .filterValue" : "filterValueKeydown",
"keydown .queryline input" : "filterValueKeydown",
"click #importModal" : "showImportModal",
"click #resetView" : "resetView",
"click #confirmDocImport" : "startUpload",
@ -184,7 +184,7 @@
var result;
if (this.allowUpload === true) {
this.showSpinner();
result = this.collection.updloadDocuments(this.file);
result = this.collection.uploadDocuments(this.file);
if (result !== true) {
this.hideSpinner();
if (result.substr(0, 5 ) === "Error") {
@ -300,7 +300,7 @@
$('#importCollection').removeClass('activated');
$('#filterHeader').removeClass('activated');
$('#markDocuments').removeClass('activated'); this.changeEditMode(false);
$('#exportCollection').addClass('activated');
$('#exportCollection').toggleClass('activated');
this.markFilterToggle();
$('#exportHeader').slideToggle(200);
$('#importHeader').hide();
@ -942,6 +942,7 @@
this.uploadSetup();
$("[data-toggle=tooltip]").tooltip();
$('.upload-info').tooltip();
arangoHelper.fixTooltips(".icon_arangodb, .arangoicon", "top");
this.drawTable();

View File

@ -14,25 +14,40 @@
el2: "#progressPlaceholderIcon",
toShow: false,
action: function(){},
events: {
"click .progress-action button": "performAction"
"click .progress-action button": "performAction",
},
performAction: function() {
this.action();
//this.action();
window.progressView.hide();
},
initialize: function() {
},
showWithDelay: function(delay, msg, action, button) {
var self = this;
self.toShow = true;
setTimeout(function(delay) {
if (self.toShow === true) {
self.show(msg, action, button);
}
}, delay);
},
show: function(msg, action, button) {
$(this.el).html(this.template.render({}));
$(".progress-text").text(msg);
$(".progress-action").html(button);
this.action = action;
$(".progress-action").html('<button class="button-danger">Cancel</button>');
this.action = this.hide();
//$(".progress-action").html(button);
//this.action = action;
$(this.el).show();
//$(this.el2).html('<i class="fa fa-spinner fa-spin"></i>');

View File

@ -298,7 +298,7 @@
var inputEditor = ace.edit("aqlEditor");
var query = inputEditor.getValue();
if (query !== '' || query !== undefined || query !== null) {
window.open(encodeURI("query/result/download/" + btoa(JSON.stringify({ query: query }))));
window.open("query/result/download/" + encodeURIComponent(btoa(JSON.stringify({ query: query }))));
}
else {
arangoHelper.arangoError("Query error", "could not query result.");
@ -326,7 +326,7 @@
});
window.open(encodeURI("query/download/" + name));
window.open("query/download/" + encodeURIComponent(name));
},
deselect: function (editor) {
@ -580,12 +580,16 @@
// clear result
outputEditor.setValue('');
window.progressView.show(
/*window.progressView.show(
"Query is operating...",
self.abortQuery("id"),
'<button class="button-danger">Abort Query</button>'
);*/
window.progressView.show(
"Query is operating..."
);
$.ajax({
type: "POST",
url: "/_api/cursor",
@ -597,9 +601,11 @@
self.switchTab("result-switch");
window.progressView.hide();
self.deselect(outputEditor);
$('#downloadQueryResult').show();
},
error: function (data) {
self.switchTab("result-switch");
$('#downloadQueryResult').hide();
try {
var temp = JSON.parse(data.responseText);
outputEditor.setValue('[' + temp.errorNum + '] ' + temp.errorMessage);

View File

@ -170,6 +170,20 @@ a.headerButton {
}
}
#exportCollection {
i {
margin-left: -3px !important;
}
}
#importCollection {
i {
margin-left: -3px !important;
position: relative;
top: -1px;
}
}
//Graph Viewer
div.toolbox {

View File

@ -122,6 +122,12 @@
width: 115px;
}
.queryline {
.fa-info-circle:hover {
cursor: pointer;
}
}
.ace_error {
background: none !important;
}

View File

@ -64,7 +64,7 @@
background: $c-neutral;
border: 0;
color: $c-black;
height: 20px;
height: 21px;
position: relative;
width: 14px;
}

View File

@ -1760,6 +1760,14 @@ a.headerButton {
background-color: #fff;
color: #788f3d; }
#exportCollection i {
margin-left: -3px !important; }
#importCollection i {
margin-left: -3px !important;
position: relative;
top: -1px; }
div.toolbox {
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
@ -5320,7 +5328,7 @@ pre.gv-object-view {
background: #8f8d8c;
border: 0;
color: #000;
height: 20px;
height: 21px;
position: relative;
width: 14px; }
.pagination-line li.disabled:last-child a,
@ -6123,6 +6131,9 @@ table .sorting {
margin-top: -58px;
width: 115px; }
.queryline .fa-info-circle:hover {
cursor: pointer; }
.ace_error {
background: none !important; }

View File

@ -0,0 +1,106 @@
/*global require, assertEqual, assertTrue, assertFalse */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the statement class
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var db = arangodb.db;
// -----------------------------------------------------------------------------
// --SECTION-- statement-related tests
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: statements
////////////////////////////////////////////////////////////////////////////////
function StatementSuiteNonCluster () {
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
db._useDatabase("_system");
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
try {
db._dropDatabase("UnitTestsDatabase0");
}
catch (err) {
// ignore this error
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test bind method, bind variables
////////////////////////////////////////////////////////////////////////////////
testExplainBindCollection : function () {
var st = db._createStatement({ query : "FOR i IN @@collection RETURN i" });
st.bind("@collection", "_users");
var result = st.explain();
assertEqual([ ], result.warnings);
assertTrue(result.hasOwnProperty("plan"));
assertFalse(result.hasOwnProperty("plans"));
var plan = result.plan;
assertTrue(plan.hasOwnProperty("estimatedCost"));
assertTrue(plan.hasOwnProperty("rules"));
assertEqual([ "scatter-in-cluster", "remove-unnecessary-remote-scatter" ], plan.rules);
assertTrue(plan.hasOwnProperty("nodes"));
assertTrue(plan.hasOwnProperty("collections"));
assertEqual([ { "name" : "_users", "type" : "read" } ], plan.collections);
assertTrue(plan.hasOwnProperty("variables"));
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(StatementSuiteNonCluster);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -0,0 +1,106 @@
/*global require, assertEqual, assertTrue, assertFalse */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the statement class
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var db = arangodb.db;
// -----------------------------------------------------------------------------
// --SECTION-- statement-related tests
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: statements
////////////////////////////////////////////////////////////////////////////////
function StatementSuiteNonCluster () {
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
db._useDatabase("_system");
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
try {
db._dropDatabase("UnitTestsDatabase0");
}
catch (err) {
// ignore this error
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test bind method, bind variables
////////////////////////////////////////////////////////////////////////////////
testExplainBindCollection : function () {
var st = db._createStatement({ query : "FOR i IN @@collection RETURN i" });
st.bind("@collection", "_users");
var result = st.explain();
assertEqual([ ], result.warnings);
assertTrue(result.hasOwnProperty("plan"));
assertFalse(result.hasOwnProperty("plans"));
var plan = result.plan;
assertTrue(plan.hasOwnProperty("estimatedCost"));
assertTrue(plan.hasOwnProperty("rules"));
assertEqual([ ], plan.rules);
assertTrue(plan.hasOwnProperty("nodes"));
assertTrue(plan.hasOwnProperty("collections"));
assertEqual([ { "name" : "_users", "type" : "read" } ], plan.collections);
assertTrue(plan.hasOwnProperty("variables"));
}
};
}
// -----------------------------------------------------------------------------
// --SECTION-- main
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(StatementSuiteNonCluster);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -1,4 +1,4 @@
/*global require, assertEqual, assertTrue */
/*global require, assertEqual, assertTrue, assertFalse, assertUndefined, fail */
////////////////////////////////////////////////////////////////////////////////
/// @brief test the statement class
@ -30,7 +30,6 @@
var jsunity = require("jsunity");
var arangodb = require("org/arangodb");
var ArangoStatement = require("org/arangodb/arango-statement").ArangoStatement;
var db = arangodb.db;
var ERRORS = arangodb.errors;
@ -72,7 +71,7 @@ function StatementSuite () {
testConstructNoQuery : function () {
try {
new ArangoStatement(db);
db._createStatement();
fail();
}
catch (e) {
@ -85,7 +84,7 @@ function StatementSuite () {
testConstructQueryOnly : function () {
var query = "for u in users return u";
var st = new ArangoStatement(db, { query: query });
var st = db._createStatement({ query: query });
assertEqual(query, st.getQuery());
assertEqual([ ], st.getBindVariables());
@ -100,7 +99,7 @@ function StatementSuite () {
testConstructWithBind : function () {
var query = "for v in @values return v";
var bind = { values: [ 1, 2, 3 ] };
var st = new ArangoStatement(db, { query: query, bindVars: bind });
var st = db._createStatement({ query: query, bindVars: bind });
assertEqual(query, st.getQuery());
assertEqual(bind, st.getBindVariables());
@ -115,7 +114,7 @@ function StatementSuite () {
testConstructWithBindExecute : function () {
var query = "for v in @values return v";
var bind = { values: [ 1, 2, 3 ] };
var st = new ArangoStatement(db, { query: query, bindVars: bind, count: true });
var st = db._createStatement({ query: query, bindVars: bind, count: true });
var result = st.execute().toArray();
assertEqual(3, result.length);
@ -126,7 +125,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testParseError : function () {
var st = new ArangoStatement(db, { query : "for u in" });
var st = db._createStatement({ query : "for u in" });
try {
st.parse();
@ -142,7 +141,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testParseOk1 : function () {
var st = new ArangoStatement(db, { query : "for u in users return u" });
var st = db._createStatement({ query : "for u in users return u" });
var result = st.parse();
assertEqual([ "users" ], result.collections);
@ -155,7 +154,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testParseOk2 : function () {
var st = new ArangoStatement(db, { query : "for u in users for f in friends return u" });
var st = db._createStatement({ query : "for u in users for f in friends return u" });
var result = st.parse();
assertEqual([ "friends", "users" ], result.collections.sort());
@ -168,7 +167,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testParseBind1 : function () {
var st = new ArangoStatement(db, { query : "for u in @@users filter u.name == @name return u" });
var st = db._createStatement({ query : "for u in @@users filter u.name == @name return u" });
var result = st.parse();
assertEqual([ ], result.collections);
@ -181,7 +180,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testParseBind2 : function () {
var st = new ArangoStatement(db, { query : "for u in @@users for f in friends filter u.name == @name && f.friendId == u._id return u" });
var st = db._createStatement({ query : "for u in @@users for f in friends filter u.name == @name && f.friendId == u._id return u" });
var result = st.parse();
assertEqual([ "friends" ], result.collections);
@ -194,7 +193,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainError : function () {
var st = new ArangoStatement(db, { query : "for u in" });
var st = db._createStatement({ query : "for u in" });
try {
st.explain();
@ -210,7 +209,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainOk : function () {
var st = new ArangoStatement(db, { query : "FOR i IN 1..10 RETURN i" });
var st = db._createStatement({ query : "FOR i IN 1..10 RETURN i" });
var result = st.explain();
assertEqual([ ], result.warnings);
@ -232,7 +231,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainAllPlans : function () {
var st = new ArangoStatement(db, { query : "FOR i IN 1..10 RETURN i" });
var st = db._createStatement({ query : "FOR i IN 1..10 RETURN i" });
var result = st.explain({ allPlans: true });
assertEqual([ ], result.warnings);
@ -255,7 +254,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainBindMissing : function () {
var st = new ArangoStatement(db, { query : "FOR i IN @@list FILTER i == @value RETURN i" });
var st = db._createStatement({ query : "FOR i IN @@list FILTER i == @value RETURN i" });
try {
st.explain();
}
@ -269,7 +268,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainBindInvalidType : function () {
var st = new ArangoStatement(db, { query : "FOR i IN @@list RETURN i" });
var st = db._createStatement({ query : "FOR i IN @@list RETURN i" });
st.bind("@list", [ 1, 2, 3 ]);
try {
@ -285,7 +284,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainBindInvalid : function () {
var st = new ArangoStatement(db, { query : "FOR i IN @list FILTER i == @value RETURN i" });
var st = db._createStatement({ query : "FOR i IN @list FILTER i == @value RETURN i" });
st.bind("list", [ 1, 2, 3 ]);
st.bind("value", 3);
st.bind("foo", "bar");
@ -303,7 +302,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExplainBind : function () {
var st = new ArangoStatement(db, { query : "FOR i IN @list FILTER i == @value RETURN i" });
var st = db._createStatement({ query : "FOR i IN @list FILTER i == @value RETURN i" });
st.bind("list", [ 1, 2, 3 ]);
st.bind("value", 3);
var result = st.explain();
@ -322,35 +321,13 @@ function StatementSuite () {
assertTrue(plan.hasOwnProperty("variables"));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test bind method, bind variables
////////////////////////////////////////////////////////////////////////////////
testExplainBindCollection : function () {
var st = new ArangoStatement(db, { query : "FOR i IN @@collection RETURN i" });
st.bind("@collection", "_users");
var result = st.explain();
assertEqual([ ], result.warnings);
assertTrue(result.hasOwnProperty("plan"));
assertFalse(result.hasOwnProperty("plans"));
var plan = result.plan;
assertTrue(plan.hasOwnProperty("estimatedCost"));
assertTrue(plan.hasOwnProperty("rules"));
assertEqual([ ], plan.rules);
assertTrue(plan.hasOwnProperty("nodes"));
assertTrue(plan.hasOwnProperty("collections"));
assertEqual([ { "name" : "_users", "type" : "read" } ], plan.collections);
assertTrue(plan.hasOwnProperty("variables"));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test bind method, bind variables
////////////////////////////////////////////////////////////////////////////////
testExplainBindWarnings : function () {
var st = new ArangoStatement(db, { query : "FOR i IN 1..10 RETURN 1 / 0" });
var st = db._createStatement({ query : "FOR i IN 1..10 RETURN 1 / 0" });
var result = st.explain();
assertEqual(1, result.warnings.length);
@ -372,9 +349,10 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteError : function () {
var st = new ArangoStatement(db, { query : "for u in" });
var st = db._createStatement({ query : "for u in" });
try {
result = st.execute();
var result = st.execute();
result;
fail();
}
catch (e) {
@ -387,7 +365,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteOk1 : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1, 2, 3 ] return u" });
var st = db._createStatement({ query : "for u in [ 1, 2, 3 ] return u" });
var result = st.execute();
var docs = [ ];
@ -403,7 +381,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteOk2 : function () {
var st = new ArangoStatement(db, { query : "return 1" });
var st = db._createStatement({ query : "return 1" });
st.setCount(true);
st.setBatchSize(1);
st.setQuery("for u in [ 1, 2, 3 ] return u");
@ -422,7 +400,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteExtra : function () {
var st = new ArangoStatement(db, { query : "for i in 1..50 limit 1,2 return i", count: true, options: { fullCount: true } });
var st = db._createStatement({ query : "for i in 1..50 limit 1,2 return i", count: true, options: { fullCount: true } });
var result = st.execute();
assertEqual(2, result.count());
@ -444,7 +422,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteExtraFullCount : function () {
var st = new ArangoStatement(db, { query : "for i in 1..12345 limit 4564, 2123 return i", count: true, options: { fullCount: true } });
var st = db._createStatement({ query : "for i in 1..12345 limit 4564, 2123 return i", count: true, options: { fullCount: true } });
var result = st.execute();
assertEqual(2123, result.count());
@ -470,7 +448,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteExtraFullCountLimit0 : function () {
var st = new ArangoStatement(db, { query : "for i in 1..12345 limit 4564, 0 return i", count: true, options: { fullCount: true } });
var st = db._createStatement({ query : "for i in 1..12345 limit 4564, 0 return i", count: true, options: { fullCount: true } });
var result = st.execute();
assertEqual(0, result.count());
@ -490,7 +468,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testExecuteExtraNoFullCount : function () {
var st = new ArangoStatement(db, { query : "for i in 1..10 return i", count: true, options: { fullCount: false } });
var st = db._createStatement({ query : "for i in 1..10 return i", count: true, options: { fullCount: false } });
var result = st.execute();
assertEqual(10, result.count());
@ -507,7 +485,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBind : function () {
var st = new ArangoStatement(db, { query : "for u in @list return @value" });
var st = db._createStatement({ query : "for u in @list return @value" });
st.bind("list", [ 1, 2, 3 ]);
st.bind("value", 25);
var result = st.execute();
@ -525,7 +503,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBindVariables1 : function () {
var st = new ArangoStatement(db, { query : "for u in @list return @value + @something" });
var st = db._createStatement({ query : "for u in @list return @value + @something" });
var result = st.getBindVariables();
assertEqual({ }, result);
@ -536,7 +514,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBindVariables2 : function () {
var st = new ArangoStatement(db, { query : "for u in @list return @value + @something" });
var st = db._createStatement({ query : "for u in @list return @value + @something" });
st.bind("list", [ 1, 2 ]);
st.bind("value", "something");
st.bind("something", "something else");
@ -551,7 +529,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBindInvalid : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return @value" });
var st = db._createStatement({ query : "for u in [ 1 ] return @value" });
st.bind("list", [ 1, 2, 3 ]);
try {
st.execute();
@ -567,7 +545,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBindRedeclare : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return @value" });
var st = db._createStatement({ query : "for u in [ 1 ] return @value" });
st.bind("value", 1);
try {
st.bind("value", 1);
@ -582,7 +560,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testQuery : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return 1" });
var st = db._createStatement({ query : "for u in [ 1 ] return 1" });
assertEqual("for u in [ 1 ] return 1", st.getQuery());
@ -595,7 +573,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testCount : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return 1" });
var st = db._createStatement({ query : "for u in [ 1 ] return 1" });
assertEqual(false, st.getCount());
@ -611,7 +589,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testBatchSize: function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return 1" });
var st = db._createStatement({ query : "for u in [ 1 ] return 1" });
assertEqual(null, st.getBatchSize());
@ -630,7 +608,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testOptions : function () {
var st = new ArangoStatement(db, { query : "for u in [ 1 ] return 1", options : { foo: 1, bar: 2 } });
var st = db._createStatement({ query : "for u in [ 1 ] return 1", options : { foo: 1, bar: 2 } });
assertEqual({ foo: 1, bar: 2 }, st.getOptions());
@ -646,7 +624,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testIncremental : function () {
var st = new ArangoStatement(db, { query : "for i in 1..10 return i", batchSize : 1 });
var st = db._createStatement({ query : "for i in 1..10 return i", batchSize : 1 });
var c = st.execute();
@ -663,7 +641,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testDispose1 : function () {
var st = new ArangoStatement(db, { query : "for i in 1..10 return i", batchSize : 1 });
var st = db._createStatement({ query : "for i in 1..10 return i", batchSize : 1 });
var c = st.execute();
@ -684,7 +662,7 @@ function StatementSuite () {
////////////////////////////////////////////////////////////////////////////////
testDispose2 : function () {
var st = new ArangoStatement(db, { query : "for i in 1..10 return i", batchSize : 1 });
var st = db._createStatement({ query : "for i in 1..10 return i", batchSize : 1 });
var c = st.execute();
@ -709,7 +687,7 @@ function StatementSuite () {
testDatabaseChange : function () {
assertEqual("_system", db._name());
var st = new ArangoStatement(db, { query : "for i in 1..10 return i", batchSize : 1 });
var st = db._createStatement({ query : "for i in 1..10 return i", batchSize : 1 });
var c = st.execute();
var result = [ ];

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 (points.length === 0) {
return false;
}
var i, n = points.length;
var wn = 0;
points.push(points[0]);
if (oddNodes) {
return true;
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 false;
return (wn !== 0);
}
// -----------------------------------------------------------------------------

View File

@ -223,7 +223,8 @@ function makeTestingArgs () {
"--javascript.app-path", fs.join(topDir, "js", "apps"),
"--javascript.startup-directory", fs.join(topDir, "js"),
"--ruby.modules-path", fs.join(topDir,"mr", "common", "modules"),
"--server.threads", "4",
"--server.threads", "20",
"--javascript.v8-contexts", "5",
"--server.disable-authentication", "true",
"--server.allow-use-database", "true" ];
}

View File

@ -0,0 +1,97 @@
/*jshint strict: false, maxlen: 500 */
/*global require, AQL_EXECUTE, assertTrue */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2014 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Florian Bartels
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function isInPolygonSuite () {
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test WITHIN_RECTANGLE as result
////////////////////////////////////////////////////////////////////////////////
testIsInPolygonSuiteAsResult : function () {
assertTrue(AQL_EXECUTE("RETURN IS_IN_POLYGON ([[ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 10, 0 ] ], 10, 9)").json[0]);
}
/*
testIsNotInPolygonSuiteAsResult : function () {
assertFalse(AQL_EXECUTE("RETURN IS_IN_POLYGON ([[ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 10, 0 ] ], 10, 11)").json[0]);
},
testIsInPolygonSuiteWithListParameter : function () {
assertTrue(AQL_EXECUTE("RETURN IS_IN_POLYGON ([[ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 11, 0 ] ], [ 11, 0 ])").json[0]);
},
testIsInPolygonSuiteWithListParameterWithReverseParameterOrder : function () {
assertFalse(AQL_EXECUTE("RETURN IS_IN_POLYGON ([[ 0, 0 ], [ 0, 10 ], [ 10, 10 ], [ 11, 0 ] ], [ 0, 11 ], false)").json[0]);
},
testIsInPolygonSuiteWithSinglePointPolygon : function () {
assertTrue(AQL_EXECUTE("RETURN IS_IN_POLYGON ([[ 0, 0 ]], [ 0, 0 ])").json[0]);
}*/
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(isInPolygonSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

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

@ -0,0 +1,147 @@
/*jshint strict: false, maxlen: 500 */
/*global require, assertEqual, AQL_EXECUTE, assertTrue, fail */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for optimizer rules
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2014 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Florian Bartels
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var jsunity = require("jsunity");
var internal = require("internal");
var errors = internal.errors;
var db = require("org/arangodb").db, indexId;
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function withinRectangleSuite () {
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
var i, j ;
db._create("geo");
indexId = db.geo.ensureGeoIndex("lat", "lon");
for (i = -40; i < 40; ++i) {
for (j = -40; j < 40; ++j) {
db.geo.save({ lat: i, lon: j });
}
}
db._create("geo2");
indexId = db.geo2.ensureGeoIndex("pos");
for (i = -40; i < 40; ++i) {
for (j = -40; j < 40; ++j) {
db.geo2.save({ pos : [i, j] });
}
}
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
db._drop("geo");
db._drop("geo2");
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test WITHIN_RECTANGLE as result
////////////////////////////////////////////////////////////////////////////////
testWithinRectangleAsResult : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -1, -1, 1, 1)").json[0];
assertEqual(actual.length , 9);
},
testWithinRectangleAsResultForSingleDocument : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -0.8, -1.2, -1.2, -0.8)").json[0];
assertEqual(actual.length , 1);
},
testWithinRectangleAsResultForMissingDocument : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo, -41, -41, -41, -41)").json[0];
assertEqual(actual.length , 0);
},
testWithinRectangleAsResultForUnknownCollection : function () {
try {
AQL_EXECUTE("RETURN WITHIN_RECTANGLE(unknown, -41, -41, -41, -41)");
fail();
} catch (e) {
assertTrue(e.errorNum === errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code);
}
},
testWithinRectangleAsResultForCollectionWithoutGeoIndex : function () {
try {
AQL_EXECUTE("RETURN WITHIN_RECTANGLE(_graphs, -41, -41, -41, -41)");
fail();
} catch (e) {
assertTrue(e.errorNum === errors.ERROR_QUERY_GEO_INDEX_MISSING.code);
}
},
testWithinRectangleAsResultWithPositionBasedGeoIndex : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo2, -1, -1, 1, 1)").json[0];
assertEqual(actual.length , 9);
},
testWithinRectangleAsResultForSingleDocumentWithPositionBasedGeoIndex : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo2, -0.8, -1.2, -1.2, -0.8)").json[0];
assertEqual(actual.length , 1);
},
testWithinRectangleAsResultForMissingDocumentWithPositionBasedGeoIndex : function () {
var actual =AQL_EXECUTE("RETURN WITHIN_RECTANGLE(geo2, -41, -41, -41, -41)").json[0];
assertEqual(actual.length , 0);
}
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(withinRectangleSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -30,13 +30,15 @@
#include "ApplicationAdminServer.h"
#include "Admin/RestAdminLogHandler.h"
#include "Admin/RestJobHandler.h"
#include "Admin/RestHandlerCreator.h"
#include "Admin/RestJobHandler.h"
#include "Basics/ProgramOptionsDescription.h"
#include "Basics/logging.h"
#include "HttpServer/HttpHandlerFactory.h"
#include "HttpServer/PathHandler.h"
#include "Rest/HttpResponse.h"
#include "Admin/RestVersionHandler.h"
#include "Admin/RestDebugHelperHandler.h"
using namespace std;
using namespace triagens;
@ -108,6 +110,7 @@ void ApplicationAdminServer::addBasicHandlers (HttpHandlerFactory* factory,
Dispatcher* dispatcher,
AsyncJobManager* jobManager) {
factory->addHandler(prefix + "/version", RestHandlerCreator<RestVersionHandler>::createNoData, 0);
factory->addHandler(prefix + "/debug-helper", RestHandlerCreator<RestDebugHelperHandler>::createNoData, 0);
if (_jobPayload == 0) {
_jobPayload = new pair<Dispatcher*, AsyncJobManager*>(dispatcher, jobManager);

View File

@ -33,7 +33,6 @@
#include "Basics/Common.h"
#include "ApplicationServer/ApplicationFeature.h"
#include "Admin/RestVersionHandler.h"
// -----------------------------------------------------------------------------
// --SECTION-- forward declarations
@ -43,6 +42,7 @@ namespace triagens {
namespace rest {
class ApplicationServer;
class AsyncJobManager;
class Dispatcher;
class HttpHandlerFactory;
class HttpResponse;
class HttpRequest;

View File

@ -0,0 +1,143 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief debug helper handler
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "RestDebugHelperHandler.h"
#include "Basics/conversions.h"
#include "Basics/json.h"
#include "Basics/tri-strings.h"
#include "Dispatcher/DispatcherThread.h"
#include "Rest/HttpRequest.h"
#include "Rest/Version.h"
using namespace triagens::basics;
using namespace triagens::rest;
using namespace triagens::admin;
using namespace std;
////////////////////////////////////////////////////////////////////////////////
/// @brief name of the queue
////////////////////////////////////////////////////////////////////////////////
const string RestDebugHelperHandler::QUEUE_NAME = "STANDARD";
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
RestDebugHelperHandler::RestDebugHelperHandler (HttpRequest* request)
: RestBaseHandler(request) {
}
// -----------------------------------------------------------------------------
// --SECTION-- Handler methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
bool RestDebugHelperHandler::isDirect () {
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
string const& RestDebugHelperHandler::queue () const {
return QUEUE_NAME;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the server version number
///
/// Parameters:
/// - sleep: sleep for X seconds
////////////////////////////////////////////////////////////////////////////////
HttpHandler::status_t RestDebugHelperHandler::execute () {
TRI_json_t result;
RequestStatisticsAgentSetIgnore(this);
TRI_InitArray2Json(TRI_CORE_MEM_ZONE, &result, 3);
TRI_json_t server;
TRI_InitStringJson(&server, TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, "arango"));
TRI_Insert2ArrayJson(TRI_CORE_MEM_ZONE, &result, "server", &server);
TRI_json_t version;
TRI_InitStringJson(&version, TRI_DuplicateStringZ(TRI_CORE_MEM_ZONE, TRI_VERSION));
TRI_Insert2ArrayJson(TRI_CORE_MEM_ZONE, &result, "version", &version);
bool found;
char const* sleepStr = _request->value("sleep", found);
uint64_t s = StringUtils::doubleDecimal(sleepStr) * 1000.0 * 1000.0;
char const* blockStr = _request->value("block", found);
bool block = (found && StringUtils::boolean(blockStr));
if (block && _dispatcherThread != nullptr) {
_dispatcherThread->blockThread();
}
if (0 < s) {
usleep(s);
}
if (block && _dispatcherThread != nullptr) {
_dispatcherThread->unblockThread();
}
TRI_json_t sleepNumber;
TRI_InitNumberJson(&sleepNumber, s / 1000000.0);
TRI_Insert2ArrayJson(TRI_CORE_MEM_ZONE, &result, "sleep", &sleepNumber);
TRI_json_t blockFlag;
TRI_InitBooleanJson(&blockFlag, block);
TRI_Insert2ArrayJson(TRI_CORE_MEM_ZONE, &result, "block", &blockFlag);
generateResult(&result);
TRI_DestroyJson(TRI_CORE_MEM_ZONE, &result);
return status_t(HANDLER_DONE);
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:

View File

@ -0,0 +1,112 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief debug helper handler
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#ifndef ARANGODB_ADMIN_REST_DEBUG_HELPER_HANDLER_H
#define ARANGODB_ADMIN_REST_DEBUG_HELPER_HANDLER_H 1
#include "Basics/Common.h"
#include "Admin/RestBaseHandler.h"
#include "Rest/HttpResponse.h"
namespace triagens {
namespace admin {
// -----------------------------------------------------------------------------
// --SECTION-- class RestVersionHandler
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief version request handler
////////////////////////////////////////////////////////////////////////////////
class RestDebugHelperHandler : public RestBaseHandler {
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
RestDebugHelperHandler (rest::HttpRequest*);
// -----------------------------------------------------------------------------
// --SECTION-- Handler methods
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
bool isDirect ();
////////////////////////////////////////////////////////////////////////////////
/// {@inheritDoc}
////////////////////////////////////////////////////////////////////////////////
std::string const& queue () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the server version number
////////////////////////////////////////////////////////////////////////////////
status_t execute ();
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
private:
////////////////////////////////////////////////////////////////////////////////
/// @brief name of the queue
////////////////////////////////////////////////////////////////////////////////
static const std::string QUEUE_NAME;
};
}
}
#endif
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End:

View File

@ -44,7 +44,7 @@ using namespace std;
/// @brief name of the queue
////////////////////////////////////////////////////////////////////////////////
const string RestVersionHandler::QUEUE_NAME = "STANDARD";
const string RestVersionHandler::QUEUE_NAME = "STANDARD";
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors

View File

@ -156,6 +156,7 @@ add_library(
Admin/RestAdminBaseHandler.cpp
Admin/RestAdminLogHandler.cpp
Admin/RestBaseHandler.cpp
Admin/RestDebugHelperHandler.cpp
Admin/RestJobHandler.cpp
Admin/RestShutdownHandler.cpp
Admin/RestVersionHandler.cpp

View File

@ -72,6 +72,7 @@ DispatcherQueue::DispatcherQueue (Scheduler* scheduler,
_nrWaiting(0),
_nrStopped(0),
_nrSpecial(0),
_nrBlocked(0),
_nrThreads(nrThreads),
_scheduler(scheduler),
_dispatcher(dispatcher),
@ -106,6 +107,11 @@ bool DispatcherQueue::addJob (Job* job) {
return false;
}
// if all threads are block, we start new threads
if (0 == _nrWaiting && _nrRunning + _nrStarted <= _nrBlocked) {
startQueueThread();
}
// add the job to the list of ready jobs
_readyJobs.push_back(job);
@ -192,6 +198,35 @@ void DispatcherQueue::specializeThread (DispatcherThread* thread) {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread is doing a blocking operation
////////////////////////////////////////////////////////////////////////////////
void DispatcherQueue::blockThread (DispatcherThread* thread) {
CONDITION_LOCKER(guard, _accessQueue);
if (thread->_jobType == Job::READ_JOB || thread->_jobType == Job::WRITE_JOB) {
_nrBlocked++;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread has resumed work
////////////////////////////////////////////////////////////////////////////////
void DispatcherQueue::unblockThread (DispatcherThread* thread) {
CONDITION_LOCKER(guard, _accessQueue);
if (thread->_jobType == Job::READ_JOB || thread->_jobType == Job::WRITE_JOB) {
if (_nrBlocked == 0) {
LOG_ERROR("unblocking too many threads");
}
else {
_nrBlocked--;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief starts a queue
////////////////////////////////////////////////////////////////////////////////

View File

@ -79,9 +79,9 @@ namespace triagens {
size_t nrThreads,
size_t maxSize);
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
~DispatcherQueue ();
@ -109,6 +109,18 @@ namespace triagens {
void specializeThread (DispatcherThread*);
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread is doing a blocking operation
////////////////////////////////////////////////////////////////////////////////
void blockThread (DispatcherThread* thread);
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread has resumed work
////////////////////////////////////////////////////////////////////////////////
void unblockThread (DispatcherThread* thread);
////////////////////////////////////////////////////////////////////////////////
/// @brief starts a queue
////////////////////////////////////////////////////////////////////////////////
@ -151,123 +163,174 @@ namespace triagens {
private:
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief name
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
std::string const _name;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief thread data
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void* _threadData;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief monopolistic jobs
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
basics::ConditionVariable _accessQueue;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief list of ready jobs
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
std::list<Job*> _readyJobs;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief current running job
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
std::set<Job*> _runningJobs;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief maximum queue size (number of jobs)
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
size_t _maxSize;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief queue is shutting down
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
volatile sig_atomic_t _stopping;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief monopolistic job
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
DispatcherThread* _monopolizer;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief list of started threads
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
std::set<DispatcherThread*> _startedThreads;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief list of stopped threads
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
std::list<DispatcherThread*> _stoppedThreads;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of started jobs
/////////////////////////////////////////////////////////////////////////////
///
/// Whenever a new thread is started, this number is increased by 1. As soon as
/// the thread enters its main event loop, this number is decreased by 1 and
/// the number running threads _nrRunning is increased by 1.
///
/// Therefore _nrStarted and _nrRunning is the number of threads which are or
/// soon be available for the dispatcher queue.
////////////////////////////////////////////////////////////////////////////////
size_t _nrStarted;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of threads that are up
/////////////////////////////////////////////////////////////////////////////
///
/// As soon as a thread enters its main event loop, this number is increased by
/// 1. As soon as the thread leaves its main event loop, this number is
/// decreased by 1.
///
/// This indicates the current number of threads currently within their main
/// event loop. The difference to _nrRunning is, that _nrRunning is decreased by
/// 1, if a thread enters a special job.
////////////////////////////////////////////////////////////////////////////////
size_t _nrUp;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of running jobs
/////////////////////////////////////////////////////////////////////////////
///
/// As soon as a thread enters its main event loop, this number is increased by
/// one. As soon as the thread leaves its main event loop or works on a special
/// job, this number is decreaes by 1.
////////////////////////////////////////////////////////////////////////////////
size_t _nrRunning;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of waiting jobs
/////////////////////////////////////////////////////////////////////////////
///
/// Whenever a threads waits for more work, this number is increased by 1. As
/// soon as the thread leaves the wait because it received a broadcast, this
/// number is decreased by 1.
////////////////////////////////////////////////////////////////////////////////
size_t _nrWaiting;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of stopped jobs
/////////////////////////////////////////////////////////////////////////////
///
/// As soon as a thread leaves its main event loop, this number is increased by
/// 1 and the thread is put onto a cleanup list. Eventually all threads on that
/// list are deleted and the number is reset to zero.
////////////////////////////////////////////////////////////////////////////////
size_t _nrStopped;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of special jobs
/////////////////////////////////////////////////////////////////////////////
///
/// This is the number of special jobs. We start with 0 special jobs. Whenever a
/// job of type SPECIAL_JOB is started or a running job is specialized, this
/// number is increased by 1. The number _nrRunning of running jobs is decreased
/// by 1.
///
/// If a special job is started or a running job is specialized, a new thread is
/// started. This will increase the number _nrStarted.
////////////////////////////////////////////////////////////////////////////////
size_t _nrSpecial;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief number of blocked threads
///
/// The number of threads, that are blocked for some reason.
////////////////////////////////////////////////////////////////////////////////
size_t _nrBlocked;
////////////////////////////////////////////////////////////////////////////////
/// @brief total number of threads
/////////////////////////////////////////////////////////////////////////////
///
/// This number is fixed. It is the number of pre-configured threads from the
/// configuration file.
///
/// The following hold, after startup and before shutdown:
///
/// _nrRunning + _nrStarted = _nrThreads
/// _nrRunning + _nrSpecial + _nrWaiting = _nrUp
////////////////////////////////////////////////////////////////////////////////
size_t _nrThreads;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief scheduler
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Scheduler* _scheduler;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief dispatcher
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Dispatcher* _dispatcher;
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief thread creator function
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Dispatcher::newDispatcherThread_fptr createDispatcherThread;
};

View File

@ -71,6 +71,9 @@ DispatcherThread::DispatcherThread (DispatcherQueue* queue)
////////////////////////////////////////////////////////////////////////////////
void DispatcherThread::run () {
currentDispatcherThread = this;
_queue->_accessQueue.lock();
_queue->_nrStarted--;
@ -293,6 +296,26 @@ void DispatcherThread::run () {
LOG_TRACE("dispatcher thread has finished");
}
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread is doing a blocking operation
////////////////////////////////////////////////////////////////////////////////
void DispatcherThread::blockThread () {
_queue->blockThread(this);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread has resumed work
////////////////////////////////////////////////////////////////////////////////
void DispatcherThread::unblockThread () {
_queue->unblockThread(this);
}
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------
@ -311,6 +334,13 @@ void DispatcherThread::reportStatus () {
void DispatcherThread::tick (bool) {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief a global, but thread-local place to hold the current dispatcher
/// thread. If we are not in a dispatcher thread this is set to nullptr.
////////////////////////////////////////////////////////////////////////////////
thread_local DispatcherThread* DispatcherThread::currentDispatcherThread = nullptr;
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------

View File

@ -84,6 +84,31 @@ namespace triagens {
void run ();
// -----------------------------------------------------------------------------
// --SECTION-- public methods
// -----------------------------------------------------------------------------
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread is doing a blocking operation
////////////////////////////////////////////////////////////////////////////////
void blockThread ();
////////////////////////////////////////////////////////////////////////////////
/// @brief indicates that thread has resumed work
////////////////////////////////////////////////////////////////////////////////
void unblockThread ();
////////////////////////////////////////////////////////////////////////////////
/// @brief a global, but thread-local place to hold the current dispatcher
/// thread. If we are not in a dispatcher thread this is set to nullptr.
////////////////////////////////////////////////////////////////////////////////
static thread_local DispatcherThread* currentDispatcherThread;
// -----------------------------------------------------------------------------
// --SECTION-- protected methods
// -----------------------------------------------------------------------------

View File

@ -142,6 +142,7 @@ lib_libarango_fe_a_SOURCES = \
lib/Admin/RestAdminBaseHandler.cpp \
lib/Admin/RestAdminLogHandler.cpp \
lib/Admin/RestBaseHandler.cpp \
lib/Admin/RestDebugHelperHandler.cpp \
lib/Admin/RestJobHandler.cpp \
lib/Admin/RestShutdownHandler.cpp \
lib/Admin/RestVersionHandler.cpp \

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

@ -40,7 +40,8 @@ using namespace std;
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
Handler::Handler () {
Handler::Handler ()
: _dispatcherThread(0) {
}
////////////////////////////////////////////////////////////////////////////////
@ -75,7 +76,8 @@ string const& Handler::queue () const {
/// @brief sets the thread which currently dealing with the job
////////////////////////////////////////////////////////////////////////////////
void Handler::setDispatcherThread (DispatcherThread*) {
void Handler::setDispatcherThread (DispatcherThread* dispatcherThread) {
_dispatcherThread = dispatcherThread;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -182,6 +182,18 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
virtual Job* createJob (AsyncJobServer*, bool) = 0;
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------
protected:
////////////////////////////////////////////////////////////////////////////////
/// @brief current dispatcher thread
////////////////////////////////////////////////////////////////////////////////
DispatcherThread* _dispatcherThread;
};
}
}

View File

@ -66,14 +66,19 @@ namespace triagens {
_maxPacketSize(128 * 1024 * 1024),
_keepAlive(true) {
TRI_ASSERT(connection != nullptr);
if (_connection->isConnected()) {
_state = FINISHED;
}
}
SimpleHttpClient::~SimpleHttpClient () {
if (! _keepConnectionOnDestruction || ! _connection->isConnected()) {
_connection->disconnect();
// connection may have been invalidated by other objects
if (_connection != nullptr) {
if (! _keepConnectionOnDestruction || ! _connection->isConnected()) {
_connection->disconnect();
}
}
}
@ -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;
@ -103,6 +111,9 @@ namespace triagens {
char const* body,
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);
@ -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
////////////////////////////////////////////////////////////////////////////////