mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
fc2a4d82d0
27
CHANGELOG
27
CHANGELOG
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(){
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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!
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: "));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -681,10 +681,30 @@ namespace triagens {
|
|||
/// @brief create an AqlValue from the inVariable using the current _index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AqlValue from the inVariable using the current _index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AqlValue getAqlValue (AqlValue inVarReg);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief throws a "list expected" exception
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void throwListExpectedException ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief current position in the _inVariable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -761,7 +761,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
|
@ -837,7 +837,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return ENUMERATE_COLLECTION;
|
||||
}
|
||||
|
||||
|
@ -990,7 +990,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return ENUMERATE_LIST;
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1107,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return INDEX_RANGE;
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1127,22 @@ namespace triagens {
|
|||
return _collection;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return out variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable const* outVariable () const {
|
||||
return _outVariable;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the ranges
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<std::vector<RangeInfo>> const& ranges () const {
|
||||
return _ranges;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export to JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1166,7 +1182,7 @@ namespace triagens {
|
|||
double estimateCost () const override final;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check whether the pattern matches this nodes index
|
||||
/// @brief check whether the pattern matches this node's index
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IndexMatch MatchesIndex (IndexMatchVec const& pattern) const;
|
||||
|
@ -1266,7 +1282,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return LIMIT;
|
||||
}
|
||||
|
||||
|
@ -1299,7 +1315,7 @@ namespace triagens {
|
|||
|
||||
double estimateCost () const override final {
|
||||
return 1.005 * static_cast<double>(_limit) + _dependencies.at(0)->getCost();
|
||||
//FIXME improve this estimate . . .
|
||||
// FIXME: improve this estimate . . .
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1381,7 +1397,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return CALCULATION;
|
||||
}
|
||||
|
||||
|
@ -1517,7 +1533,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SUBQUERY;
|
||||
}
|
||||
|
||||
|
@ -1650,7 +1666,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return FILTER;
|
||||
}
|
||||
|
||||
|
@ -1675,9 +1691,9 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
double estimateCost () const override final {
|
||||
return _dependencies.at(0)->getCost() * 0.105;
|
||||
//FIXME! 0.105 is the cost of doing the filter node under the
|
||||
//assumption that it returns 10% of the results of its dependency
|
||||
return _dependencies.at(0)->getCost() * 1.105;
|
||||
// FIXME! 1.105 is the cost of doing the filter node under the
|
||||
// assumption that it returns 10% of the results of its dependency
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1798,7 +1814,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SORT;
|
||||
}
|
||||
|
||||
|
@ -1937,7 +1953,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return AGGREGATE;
|
||||
}
|
||||
|
||||
|
@ -2057,7 +2073,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return RETURN;
|
||||
}
|
||||
|
||||
|
@ -2244,7 +2260,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REMOVE;
|
||||
}
|
||||
|
||||
|
@ -2357,7 +2373,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return INSERT;
|
||||
}
|
||||
|
||||
|
@ -2472,7 +2488,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return UPDATE;
|
||||
}
|
||||
|
||||
|
@ -2597,7 +2613,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REPLACE;
|
||||
}
|
||||
|
||||
|
@ -2709,7 +2725,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return NORESULTS;
|
||||
}
|
||||
|
||||
|
@ -2786,7 +2802,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return REMOTE;
|
||||
}
|
||||
|
||||
|
@ -2969,7 +2985,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return SCATTER;
|
||||
}
|
||||
|
||||
|
@ -3072,7 +3088,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return DISTRIBUTE;
|
||||
}
|
||||
|
||||
|
@ -3180,7 +3196,7 @@ namespace triagens {
|
|||
/// @brief return the type of the node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NodeType getType () const override {
|
||||
NodeType getType () const override final {
|
||||
return GATHER;
|
||||
}
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
|
|||
{ "NEAR", Function("NEAR", "AQL_NEAR", "h,n,n|nz,s", false, true, false) },
|
||||
{ "WITHIN", Function("WITHIN", "AQL_WITHIN", "h,n,n,n|s", false, true, false) },
|
||||
{ "WITHIN_RECTANGLE", Function("WITHIN_RECTANGLE", "AQL_WITHIN_RECTANGLE", "h,d,d,d,d", false, true, false) },
|
||||
{ "IS_IN_POLYGON", Function("IS_IN_POLYGON", "AQL_IS_IN_POLYGON", "d,d,l", true, false, true) },
|
||||
{ "IS_IN_POLYGON", Function("IS_IN_POLYGON", "AQL_IS_IN_POLYGON", "l,ln|nb", true, false, true) },
|
||||
|
||||
// fulltext functions
|
||||
{ "FULLTEXT", Function("FULLTEXT", "AQL_FULLTEXT", "h,s,s", false, true, false) },
|
||||
|
@ -724,7 +724,7 @@ void Executor::generateCodeExpand (AstNode const* node) {
|
|||
auto variable = static_cast<Variable*>(iterator->getMember(0)->getData());
|
||||
_buffer->appendText("vars[\"");
|
||||
_buffer->appendText(variable->name);
|
||||
_buffer->appendText("\"] = v; ");
|
||||
_buffer->appendText("\"]=v; ");
|
||||
|
||||
_buffer->appendText("r.push(");
|
||||
generateCodeNode(node->getMember(1));
|
||||
|
@ -812,7 +812,7 @@ void Executor::generateCodeNode (AstNode const* node) {
|
|||
|
||||
switch (node->type) {
|
||||
case NODE_TYPE_VALUE:
|
||||
node->append(_buffer, true);
|
||||
node->appendValue(_buffer);
|
||||
break;
|
||||
|
||||
case NODE_TYPE_LIST:
|
||||
|
|
|
@ -142,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);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -555,19 +555,19 @@ QueryResult Query::prepare (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -625,19 +625,19 @@ QueryResult Query::execute (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -697,23 +697,22 @@ QueryResultV8 Query::executeV8 (QueryRegistry* registry) {
|
|||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
cleanupPlanAndEngine(ex.code());
|
||||
return QueryResultV8(ex.code(), getStateString() + ex.message());
|
||||
return QueryResultV8(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_OUT_OF_MEMORY);
|
||||
return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResultV8(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResultV8(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResultV8(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
cleanupPlanAndEngine(TRI_ERROR_INTERNAL);
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief parse an AQL query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -811,16 +810,16 @@ QueryResult Query::explain () {
|
|||
return result;
|
||||
}
|
||||
catch (triagens::arango::Exception const& ex) {
|
||||
return QueryResult(ex.code(), getStateString() + ex.message());
|
||||
return QueryResult(ex.code(), ex.message() + getStateString());
|
||||
}
|
||||
catch (std::bad_alloc const&) {
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, getStateString() + TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY));
|
||||
return QueryResult(TRI_ERROR_OUT_OF_MEMORY, TRI_errno_string(TRI_ERROR_OUT_OF_MEMORY) + getStateString());
|
||||
}
|
||||
catch (std::exception const& ex) {
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + ex.what());
|
||||
return QueryResult(TRI_ERROR_INTERNAL, ex.what() + getStateString());
|
||||
}
|
||||
catch (...) {
|
||||
return QueryResult(TRI_ERROR_INTERNAL, getStateString() + TRI_errno_string(TRI_ERROR_INTERNAL));
|
||||
return QueryResult(TRI_ERROR_INTERNAL, TRI_errno_string(TRI_ERROR_INTERNAL) + getStateString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1051,7 +1050,7 @@ QueryResult Query::transactionError (int errorCode) const {
|
|||
err += std::string(" (") + detail + std::string(")");
|
||||
}
|
||||
|
||||
if (_queryString != nullptr) {
|
||||
if (_queryString != nullptr && verboseErrors()) {
|
||||
err += std::string("\nwhile executing:\n") + _queryString + std::string("\n");
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1144,7 @@ void Query::enterState (ExecutionState state) {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string Query::getStateString () const {
|
||||
return "while " + StateNames[_state] + ": ";
|
||||
return std::string(" (while " + StateNames[_state] + ")");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -382,6 +382,14 @@ namespace triagens {
|
|||
return _plan;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief whether or not the query returns verbose error messages
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool verboseErrors () const {
|
||||
return getBooleanOption("verboseErrors", false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set the plan for the query
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -618,6 +626,7 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool const _contextOwnedByExterior;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -338,7 +338,10 @@ namespace triagens {
|
|||
RangeInfoBound low,
|
||||
RangeInfoBound high,
|
||||
bool equality)
|
||||
: _var(var), _attr(attr), _valid(true), _defined(true),
|
||||
: _var(var),
|
||||
_attr(attr),
|
||||
_valid(true),
|
||||
_defined(true),
|
||||
_equality(equality) {
|
||||
|
||||
if (low.isConstant()) {
|
||||
|
@ -347,6 +350,7 @@ namespace triagens {
|
|||
else {
|
||||
_lows.emplace_back(low);
|
||||
}
|
||||
|
||||
if (high.isConstant()) {
|
||||
_highConst.assign(high);
|
||||
}
|
||||
|
@ -373,13 +377,19 @@ namespace triagens {
|
|||
}
|
||||
}
|
||||
|
||||
RangeInfo ( std::string var,
|
||||
std::string attr)
|
||||
: _var(var), _attr(attr), _valid(true), _defined(true),
|
||||
RangeInfo (std::string const& var,
|
||||
std::string const& attr)
|
||||
: _var(var),
|
||||
_attr(attr),
|
||||
_valid(true),
|
||||
_defined(true),
|
||||
_equality(false) {
|
||||
}
|
||||
|
||||
RangeInfo () : _valid(false), _defined(false), _equality(false) {
|
||||
RangeInfo ()
|
||||
: _valid(false),
|
||||
_defined(false),
|
||||
_equality(false) {
|
||||
}
|
||||
|
||||
RangeInfo (basics::Json const& json);
|
||||
|
|
|
@ -424,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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -543,6 +543,7 @@ int createDocumentOnCoordinator (
|
|||
shared_ptr<CollectionInfo> collinfo = ci->getCollection(dbname, collname);
|
||||
|
||||
if (collinfo->empty()) {
|
||||
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
||||
return TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@
|
|||
using namespace std;
|
||||
using namespace triagens::arango;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief controls if backtraces are printed with exceptions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool WithBackTrace = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor, without format string
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -46,9 +52,11 @@ Exception::Exception (int code,
|
|||
_code(code) {
|
||||
#ifdef TRI_ENABLE_MAINTAINER_MODE
|
||||
#if HAVE_BACKTRACE
|
||||
_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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define THROW_ARANGO_EXCEPTION_PARAMS(code, ...) \
|
||||
throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__, true)
|
||||
throw triagens::arango::Exception(code, triagens::arango::Exception::FillExceptionString(code, __VA_ARGS__), __FILE__, __LINE__)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief throws an arango exception with an error code and an already-built
|
||||
|
@ -60,7 +60,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define THROW_ARANGO_EXCEPTION_MESSAGE(code, message) \
|
||||
throw triagens::arango::Exception(code, message, __FILE__, __LINE__, false)
|
||||
throw triagens::arango::Exception(code, message, __FILE__, __LINE__)
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public types
|
||||
|
@ -84,8 +84,7 @@ namespace triagens {
|
|||
Exception (int code,
|
||||
std::string const& errorMessage,
|
||||
char const* file,
|
||||
int line,
|
||||
bool errnoStringResolved);
|
||||
int line);
|
||||
|
||||
~Exception () throw ();
|
||||
|
||||
|
@ -107,6 +106,12 @@ namespace triagens {
|
|||
|
||||
static std::string FillExceptionString (int, ...);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief controls whether a backtrace is created for each exception
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void SetVerbose (bool);
|
||||
|
||||
protected:
|
||||
std::string _errorMessage;
|
||||
char const* _file;
|
||||
|
|
|
@ -533,6 +533,22 @@ static v8::Handle<v8::Value> JS_normalize_string (v8::Arguments const& argv) {
|
|||
return scope.Close(TRI_normalize_V8_Obj(argv[0]));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enables or disables native backtrace
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static v8::Handle<v8::Value> JS_EnableNativeBacktraces (v8::Arguments const& argv) {
|
||||
v8::HandleScope scope;
|
||||
|
||||
if (argv.Length() != 1) {
|
||||
TRI_V8_EXCEPTION_USAGE(scope, "ENABLE_NATIVE_BACKTRACES(<value>)");
|
||||
}
|
||||
|
||||
triagens::arango::Exception::SetVerbose(TRI_ObjectToBoolean(argv[0]));
|
||||
|
||||
return scope.Close(v8::Undefined());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief compare two UTF 16 strings
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -92,6 +92,14 @@ var internal = require("internal");
|
|||
/// be present in the result if the query has a LIMIT clause and the LIMIT clause is
|
||||
/// actually used in the query.
|
||||
///
|
||||
/// - *maxPlans*: limits the maximum number of plans that are created by the AQL
|
||||
/// query optimizer.
|
||||
///
|
||||
/// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules
|
||||
/// can be put into this attribute, telling the optimizer to include or exclude
|
||||
/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it
|
||||
/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules.
|
||||
///
|
||||
/// If the result set can be created by the server, the server will respond with
|
||||
/// *HTTP 201*. The body of the response will contain a JSON object with the
|
||||
/// result set.
|
||||
|
@ -207,7 +215,7 @@ var internal = require("internal");
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Using a query option:
|
||||
/// Using query option "fullCount":
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestCursorCreateCursorOption}
|
||||
/// var url = "/_api/cursor";
|
||||
|
@ -226,6 +234,28 @@ var internal = require("internal");
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Enabling and disabling optimizer rules:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestCursorOptimizerRules}
|
||||
/// var url = "/_api/cursor";
|
||||
/// var body = {
|
||||
/// query: "FOR i IN 1..10 LET a = 1 LET b = 2 FILTER a + b == 3 RETURN i",
|
||||
/// count: true,
|
||||
/// options: {
|
||||
/// maxPlans: 1,
|
||||
/// optimizer: {
|
||||
/// rules: [ "-all", "+remove-unnecessary-filters" ]
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// var response = logCurlRequest('POST', url, JSON.stringify(body));
|
||||
///
|
||||
/// assert(response.code === 201);
|
||||
///
|
||||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Executes a data-modification query and retrieves the number of
|
||||
/// modified documents:
|
||||
///
|
||||
|
|
|
@ -51,12 +51,15 @@ var ERRORS = require("internal").errors;
|
|||
/// The currently supported options are:
|
||||
/// - *allPlans*: if set to *true*, all possible execution plans will be returned.
|
||||
/// The default is *false*, meaning only the optimal plan will be returned.
|
||||
///
|
||||
/// - *maxPlans*: an optional maximum number of plans that the optimizer is
|
||||
/// allowed to generate. Setting this attribute to a low value allows to put a
|
||||
/// cap on the amount of work the optimizer does.
|
||||
///
|
||||
/// - *optimizer.rules*: a list of to-be-included or to-be-excluded optimizer rules
|
||||
/// can be put into this attribute, telling the optimizer to include or exclude
|
||||
/// specific rules.
|
||||
/// specific rules. To disable a rule, prefix its name with a `-`, to enable a rule, prefix it
|
||||
/// with a `+`. There is also a pseudo-rule `all`, which will match all optimizer rules.
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
///
|
||||
|
@ -84,11 +87,15 @@ var ERRORS = require("internal").errors;
|
|||
/// Each plan in the result is a JSON object with the following attributes:
|
||||
/// - *nodes*: the list of execution nodes of the plan. The list of available node types
|
||||
/// can be found [here](.../Aql/Optimizer.html)
|
||||
///
|
||||
/// - *estimatedCost*: the total estimated cost for the plan. If there are multiple
|
||||
/// plans, the optimizer will choose the plan with the lowest total cost.
|
||||
///
|
||||
/// - *collections*: a list of collections used in the query
|
||||
///
|
||||
/// - *rules*: a list of rules the optimizer applied. The list of rules can be
|
||||
/// found [here](../Aql/Optimizer.html)
|
||||
///
|
||||
/// - *variables*: list of variables used in the query (note: this may contain
|
||||
/// internal variables created by the optimizer)
|
||||
///
|
||||
|
@ -152,6 +159,33 @@ var ERRORS = require("internal").errors;
|
|||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Using some options:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestExplainOptions}
|
||||
/// var url = "/_api/explain";
|
||||
/// var cn = "products";
|
||||
/// db._drop(cn);
|
||||
/// db._create(cn);
|
||||
/// db.products.ensureSkiplist("id");
|
||||
/// for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); }
|
||||
/// body = {
|
||||
/// query : "FOR p IN products LET a = p.id FILTER a == 4 LET name = p.name SORT p.id LIMIT 1 RETURN name",
|
||||
/// options : {
|
||||
/// maxPlans : 2,
|
||||
/// allPlans : true,
|
||||
/// optimizer : {
|
||||
/// rules: [ "-all", "+use-index-for-sort", "+use-index-range" ]
|
||||
/// }
|
||||
/// }
|
||||
/// };
|
||||
///
|
||||
/// var response = logCurlRequest('POST', url, JSON.stringify(body));
|
||||
///
|
||||
/// assert(response.code === 200);
|
||||
///
|
||||
/// logJsonResponse(response);
|
||||
/// @END_EXAMPLE_ARANGOSH_RUN
|
||||
///
|
||||
/// Returning all plans:
|
||||
///
|
||||
/// @EXAMPLE_ARANGOSH_RUN{RestExplainAllPlans}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -113,6 +113,9 @@
|
|||
content: [{
|
||||
label: "Insert",
|
||||
letter: "Ctrl + Insert"
|
||||
},{
|
||||
label: "Save",
|
||||
letter: "Ctrl + Return, CMD + Return"
|
||||
},{
|
||||
label: "Append",
|
||||
letter: "Ctrl + Shift + Insert"
|
||||
|
|
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
<% if(isSystem) { %>
|
||||
|
||||
<option value="#logs">Logs</option>
|
||||
<option value="#databases">Databases</option>
|
||||
|
||||
<% } %>
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>');
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -122,6 +122,12 @@
|
|||
width: 115px;
|
||||
}
|
||||
|
||||
.queryline {
|
||||
.fa-info-circle:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.ace_error {
|
||||
background: none !important;
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
background: $c-neutral;
|
||||
border: 0;
|
||||
color: $c-black;
|
||||
height: 20px;
|
||||
height: 21px;
|
||||
position: relative;
|
||||
width: 14px;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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 = [ ];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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" ];
|
||||
}
|
||||
|
|
|
@ -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:
|
|
@ -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)",
|
||||
|
|
|
@ -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:
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -182,6 +182,18 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual Job* createJob (AsyncJobServer*, bool) = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- protected variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
protected:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief current dispatcher thread
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DispatcherThread* _dispatcherThread;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -84,6 +84,17 @@ namespace triagens {
|
|||
|
||||
~SimpleHttpClient ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief invalidates the connection used by the client
|
||||
/// this may be called from other objects that are responsible for managing
|
||||
/// connections. after this method has been called, the client must not be
|
||||
/// used for any further HTTP operations, but should be destroyed instantly.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void invalidateConnection () {
|
||||
_connection = nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief close connection
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue