1
0
Fork 0

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

This commit is contained in:
Frank Celler 2014-12-17 09:32:02 +01:00
commit ab99e17876
21 changed files with 1186 additions and 712 deletions

View File

@ -8,7 +8,6 @@ make setup || exit 1
echo
echo "$0: configuring ArangoDB"
# V8 needs lib realtime:
export LDFLAGS='-lrt -pthread'
./configure \
--enable-relative \
--enable-all-in-one-libev \

View File

@ -140,6 +140,7 @@ AM_LDFLAGS = \
################################################################################
LIBS = \
@RT_LIBS@ \
@LIBEV_LIBS@ \
@MATH_LIBS@ \
@OPENSSL_LIBS@ \

View File

@ -555,6 +555,7 @@ SHELL_SERVER_AQL = @top_srcdir@/js/server/tests/aql-arithmetic.js \
@top_srcdir@/js/server/tests/aql-modify-noncluster.js \
@top_srcdir@/js/server/tests/aql-modify-noncluster-serializetest.js \
@top_srcdir@/js/server/tests/aql-operators.js \
@top_srcdir@/js/server/tests/aql-optimizer-collect-into.js \
@top_srcdir@/js/server/tests/aql-optimizer-count.js \
@top_srcdir@/js/server/tests/aql-optimizer-dynamic-bounds.js \
@top_srcdir@/js/server/tests/aql-optimizer-filters.js \

View File

@ -786,6 +786,33 @@ AqlValue AqlValue::CreateFromBlocks (triagens::arango::AqlTransaction* trx,
return AqlValue(json.release());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AqlValue from a vector of AqlItemBlock*s
////////////////////////////////////////////////////////////////////////////////
AqlValue AqlValue::CreateFromBlocks (triagens::arango::AqlTransaction* trx,
std::vector<AqlItemBlock*> const& src,
triagens::aql::RegisterId expressionRegister) {
size_t totalSize = 0;
for (auto it = src.begin(); it != src.end(); ++it) {
totalSize += (*it)->size();
}
std::unique_ptr<Json> json(new Json(Json::List, totalSize));
for (auto it = src.begin(); it != src.end(); ++it) {
auto current = (*it);
auto document = current->getDocumentCollection(expressionRegister);
for (size_t i = 0; i < current->size(); ++i) {
json->add(current->getValueReference(i, expressionRegister).toJson(trx, document));
}
}
return AqlValue(json.release());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief 3-way comparison for AqlValue objects
////////////////////////////////////////////////////////////////////////////////

View File

@ -30,6 +30,7 @@
#include "Basics/Common.h"
#include "Aql/Range.h"
#include "Aql/types.h"
#include "Basics/JsonHelper.h"
#include "Utils/V8TransactionContext.h"
#include "Utils/AqlTransaction.h"
@ -257,6 +258,14 @@ namespace triagens {
std::vector<AqlItemBlock*> const&,
std::vector<std::string> const&);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AqlValue from a vector of AqlItemBlock*s
////////////////////////////////////////////////////////////////////////////////
static AqlValue CreateFromBlocks (triagens::arango::AqlTransaction*,
std::vector<AqlItemBlock*> const&,
triagens::aql::RegisterId);
////////////////////////////////////////////////////////////////////////////////
/// @brief 3-way comparison for AqlValue objects
////////////////////////////////////////////////////////////////////////////////

View File

@ -353,11 +353,29 @@ AstNode* Ast::createNodeCollect (AstNode const* list,
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, COUNT
/// @brief create an AST collect node, INTO var = expr
////////////////////////////////////////////////////////////////////////////////
AstNode* Ast::createNodeCollect (AstNode const* list,
char const* name) {
AstNode* Ast::createNodeCollectExpression (AstNode const* list,
char const* name,
AstNode const* expression) {
AstNode* node = createNode(NODE_TYPE_COLLECT_EXPRESSION);
node->addMember(list);
AstNode* variable = createNodeVariable(name, true);
node->addMember(variable);
node->addMember(expression);
return node;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, COUNT INTO
////////////////////////////////////////////////////////////////////////////////
AstNode* Ast::createNodeCollectCount (AstNode const* list,
char const* name) {
AstNode* node = createNode(NODE_TYPE_COLLECT_COUNT);
node->addMember(list);

View File

@ -273,11 +273,19 @@ namespace triagens {
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, COUNT
/// @brief create an AST collect node
////////////////////////////////////////////////////////////////////////////////
AstNode* createNodeCollect (AstNode const*,
char const*);
AstNode* createNodeCollectExpression (AstNode const*,
char const*,
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST collect node, COUNT INTO
////////////////////////////////////////////////////////////////////////////////
AstNode* createNodeCollectCount (AstNode const*,
char const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an AST sort node

View File

@ -119,7 +119,8 @@ std::unordered_map<int, std::string const> const AstNode::TypeNames{
{ static_cast<int>(NODE_TYPE_FCALL_USER), "user function call" },
{ static_cast<int>(NODE_TYPE_RANGE), "range" },
{ static_cast<int>(NODE_TYPE_NOP), "no-op" },
{ static_cast<int>(NODE_TYPE_COLLECT_COUNT), "collect count" }
{ static_cast<int>(NODE_TYPE_COLLECT_COUNT), "collect count" },
{ static_cast<int>(NODE_TYPE_COLLECT_EXPRESSION), "collect expression" }
};
std::unordered_map<int, std::string const> const AstNode::valueTypeNames{
@ -492,6 +493,7 @@ AstNode::AstNode (Ast* ast,
case NODE_TYPE_REPLACE:
case NODE_TYPE_COLLECT:
case NODE_TYPE_COLLECT_COUNT:
case NODE_TYPE_COLLECT_EXPRESSION:
case NODE_TYPE_SORT:
case NODE_TYPE_SORT_ELEMENT:
case NODE_TYPE_LIMIT:

View File

@ -158,7 +158,8 @@ namespace triagens {
NODE_TYPE_FCALL_USER = 48,
NODE_TYPE_RANGE = 49,
NODE_TYPE_NOP = 50,
NODE_TYPE_COLLECT_COUNT = 51
NODE_TYPE_COLLECT_COUNT = 51,
NODE_TYPE_COLLECT_EXPRESSION = 52
};
static_assert(NODE_TYPE_VALUE < NODE_TYPE_LIST, "incorrect node types");

View File

@ -2709,6 +2709,7 @@ AggregateBlock::AggregateBlock (ExecutionEngine* engine,
: ExecutionBlock(engine, en),
_aggregateRegisters(),
_currentGroup(en->_countOnly),
_expressionRegister(ExecutionNode::MaxRegisterId),
_groupRegister(ExecutionNode::MaxRegisterId),
_variableNames() {
@ -2732,6 +2733,12 @@ AggregateBlock::AggregateBlock (ExecutionEngine* engine,
_groupRegister = (*it).second.registerId;
TRI_ASSERT(_groupRegister > 0 && _groupRegister < ExecutionNode::MaxRegisterId);
if (en->_expressionVariable != nullptr) {
auto it = registerPlan.find(en->_expressionVariable->id);
TRI_ASSERT(it != registerPlan.end());
_expressionRegister = (*it).second.registerId;
}
// construct a mapping of all register ids to variable names
// we need this mapping to generate the grouped output
@ -2978,9 +2985,18 @@ void AggregateBlock::emitGroup (AqlItemBlock const* cur,
_currentGroup.addValues(cur, _groupRegister);
if (static_cast<AggregateNode const*>(_exeNode)->_countOnly) {
// only set group count in result register
res->setValue(row, _groupRegister, AqlValue(new Json(static_cast<double>(_currentGroup.groupLength))));
}
else if (static_cast<AggregateNode const*>(_exeNode)->_expressionVariable != nullptr) {
// copy expression result into result register
res->setValue(row, _groupRegister,
AqlValue::CreateFromBlocks(_trx,
_currentGroup.groupBlocks,
_expressionRegister));
}
else {
// copy variables / keep variables into result register
res->setValue(row, _groupRegister,
AqlValue::CreateFromBlocks(_trx,
_currentGroup.groupBlocks,

View File

@ -1084,6 +1084,13 @@ namespace triagens {
AggregatorGroup _currentGroup;
////////////////////////////////////////////////////////////////////////////////
/// @brief the optional register that contains the input expression values for
/// each group
////////////////////////////////////////////////////////////////////////////////
RegisterId _expressionRegister;
////////////////////////////////////////////////////////////////////////////////
/// @brief the optional register that contains the values for each group
/// if no values should be returned, then this has a value of 0

View File

@ -151,6 +151,7 @@ ExecutionNode* ExecutionNode::fromJsonFactory (ExecutionPlan* plan,
return new SortNode(plan, oneNode, elements, stable);
}
case AGGREGATE: {
Variable* expressionVariable = varFromJson(plan->getAst(), oneNode, "expressionVariable", Optional);
Variable* outVariable = varFromJson(plan->getAst(), oneNode, "outVariable", Optional);
triagens::basics::Json jsonAggregates = oneNode.get("aggregates");
@ -175,10 +176,10 @@ ExecutionNode* ExecutionNode::fromJsonFactory (ExecutionPlan* plan,
aggregateVariables.reserve(len);
for (size_t i = 0; i < len; i++) {
triagens::basics::Json oneJsonAggregate = jsonAggregates.at(static_cast<int>(i));
Variable* outVariable = varFromJson(plan->getAst(), oneJsonAggregate, "outVariable");
Variable* inVariable = varFromJson(plan->getAst(), oneJsonAggregate, "inVariable");
Variable* outVar = varFromJson(plan->getAst(), oneJsonAggregate, "outVariable");
Variable* inVar = varFromJson(plan->getAst(), oneJsonAggregate, "inVariable");
aggregateVariables.emplace_back(std::make_pair(outVariable, inVariable));
aggregateVariables.emplace_back(std::make_pair(outVar, inVar));
}
triagens::basics::Json jsonCount = oneNode.get("count");
@ -186,6 +187,7 @@ ExecutionNode* ExecutionNode::fromJsonFactory (ExecutionPlan* plan,
return new AggregateNode(plan,
oneNode,
expressionVariable,
outVariable,
keepVariables,
plan->getAst()->variables()->variables(false),
@ -1980,6 +1982,7 @@ double SortNode::estimateCost (size_t& nrItems) const {
AggregateNode::AggregateNode (ExecutionPlan* plan,
triagens::basics::Json const& base,
Variable const* expressionVariable,
Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
@ -1987,6 +1990,7 @@ AggregateNode::AggregateNode (ExecutionPlan* plan,
bool countOnly)
: ExecutionNode(plan, base),
_aggregateVariables(aggregateVariables),
_expressionVariable(expressionVariable),
_outVariable(outVariable),
_keepVariables(keepVariables),
_variableMap(variableMap),
@ -2015,6 +2019,11 @@ void AggregateNode::toJsonHelper (triagens::basics::Json& nodes,
}
json("aggregates", values);
// expression variable might be empty
if (_expressionVariable != nullptr) {
json("expressionVariable", _expressionVariable->toJson());
}
// output variable might be empty
if (_outVariable != nullptr) {
json("outVariable", _outVariable->toJson());
@ -2044,9 +2053,14 @@ ExecutionNode* AggregateNode::clone (ExecutionPlan* plan,
bool withDependencies,
bool withProperties) const {
auto outVariable = _outVariable;
auto expressionVariable = _expressionVariable;
auto aggregateVariables = _aggregateVariables;
if (withProperties) {
if (expressionVariable != nullptr) {
expressionVariable = plan->getAst()->variables()->createVariable(expressionVariable);
}
if (outVariable != nullptr) {
outVariable = plan->getAst()->variables()->createVariable(outVariable);
}
@ -2059,7 +2073,14 @@ ExecutionNode* AggregateNode::clone (ExecutionPlan* plan,
}
auto c = new AggregateNode(plan, _id, aggregateVariables, outVariable, _keepVariables, _variableMap, _countOnly);
auto c = new AggregateNode(plan,
_id,
aggregateVariables,
expressionVariable,
outVariable,
_keepVariables,
_variableMap,
_countOnly);
CloneHelper(c, plan, withDependencies, withProperties);
@ -2110,6 +2131,10 @@ std::vector<Variable const*> AggregateNode::getVariablesUsedHere () const {
v.insert(p.second);
}
if (_expressionVariable != nullptr) {
v.insert(_expressionVariable);
}
if (_outVariable != nullptr && ! _countOnly) {
if (_keepVariables.empty()) {
// Here we have to find all user defined variables in this query

View File

@ -1930,12 +1930,14 @@ namespace triagens {
AggregateNode (ExecutionPlan* plan,
size_t id,
std::vector<std::pair<Variable const*, Variable const*>> const& aggregateVariables,
Variable const* expressionVariable,
Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
bool countOnly)
: ExecutionNode(plan, id),
_aggregateVariables(aggregateVariables),
_expressionVariable(expressionVariable),
_outVariable(outVariable),
_keepVariables(keepVariables),
_variableMap(variableMap),
@ -1945,6 +1947,7 @@ namespace triagens {
AggregateNode (ExecutionPlan*,
triagens::basics::Json const& base,
Variable const* expressionVariable,
Variable const* outVariable,
std::vector<Variable const*> const& keepVariables,
std::unordered_map<VariableId, std::string const> const& variableMap,
@ -2042,6 +2045,12 @@ namespace triagens {
std::vector<std::pair<Variable const*, Variable const*>> _aggregateVariables;
////////////////////////////////////////////////////////////////////////////////
/// @brief input expression variable (might be null)
////////////////////////////////////////////////////////////////////////////////
Variable const* _expressionVariable;
////////////////////////////////////////////////////////////////////////////////
/// @brief output variable to write to (might be null)
////////////////////////////////////////////////////////////////////////////////

View File

@ -562,7 +562,8 @@ ExecutionNode* ExecutionPlan::fromNodeSort (ExecutionNode* previous,
ExecutionNode* ExecutionPlan::fromNodeCollect (ExecutionNode* previous,
AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_COLLECT);
TRI_ASSERT(node != nullptr &&
node->type == NODE_TYPE_COLLECT);
size_t const n = node->numMembers();
TRI_ASSERT(n >= 1);
@ -635,12 +636,101 @@ ExecutionNode* ExecutionPlan::fromNodeCollect (ExecutionNode* previous,
}
}
auto en = registerNode(new AggregateNode(this, nextId(), aggregateVariables,
auto en = registerNode(new AggregateNode(this, nextId(), aggregateVariables, nullptr,
outVariable, keepVariables, _ast->variables()->variables(false), false));
return addDependency(previous, en);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node
/// note that also a sort plan node will be added in front of the collect plan
/// node
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* ExecutionPlan::fromNodeCollectExpression (ExecutionNode* previous,
AstNode const* node) {
TRI_ASSERT(node != nullptr &&
node->type == NODE_TYPE_COLLECT_EXPRESSION);
size_t const n = node->numMembers();
TRI_ASSERT(n == 3);
auto list = node->getMember(0);
size_t const numVars = list->numMembers();
std::vector<std::pair<Variable const*, bool>> sortElements;
std::vector<std::pair<Variable const*, Variable const*>> aggregateVariables;
aggregateVariables.reserve(numVars);
for (size_t i = 0; i < numVars; ++i) {
auto assigner = list->getMember(i);
if (assigner == nullptr) {
continue;
}
TRI_ASSERT(assigner->type == NODE_TYPE_ASSIGN);
auto out = assigner->getMember(0);
TRI_ASSERT(out != nullptr);
auto v = static_cast<Variable*>(out->getData());
TRI_ASSERT(v != nullptr);
auto expression = assigner->getMember(1);
if (expression->type == NODE_TYPE_REFERENCE) {
// operand is a variable
auto e = static_cast<Variable*>(expression->getData());
aggregateVariables.push_back(std::make_pair(v, e));
sortElements.push_back(std::make_pair(e, true));
}
else {
// operand is some misc expression
auto calc = createTemporaryCalculation(expression);
calc->addDependency(previous);
previous = calc;
aggregateVariables.emplace_back(std::make_pair(v, calc->outVariable()));
sortElements.emplace_back(std::make_pair(calc->outVariable(), true));
}
}
Variable const* expressionVariable = nullptr;
auto expression = node->getMember(2);
if (expression->type == NODE_TYPE_REFERENCE) {
// expression is already a variable
auto variable = static_cast<Variable*>(expression->getData());
TRI_ASSERT(variable != nullptr);
expressionVariable = variable;
}
else {
// expression is some misc expression
auto calc = createTemporaryCalculation(expression);
calc->addDependency(previous);
previous = calc;
expressionVariable = calc->outVariable();
}
// inject a sort node for all expressions / variables that we just picked up...
// note that this sort is stable
auto sort = registerNode(new SortNode(this, nextId(), sortElements, true));
sort->addDependency(previous);
previous = sort;
// output variable
auto v = node->getMember(1);
Variable* outVariable = static_cast<Variable*>(v->getData());
std::unordered_map<VariableId, std::string const> variableMap;
auto en = registerNode(new AggregateNode(this, nextId(), aggregateVariables,
expressionVariable, outVariable, std::vector<Variable const*>(), variableMap, false));
return addDependency(previous, en);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node, COUNT
/// note that also a sort plan node will be added in front of the collect plan
@ -649,7 +739,8 @@ ExecutionNode* ExecutionPlan::fromNodeCollect (ExecutionNode* previous,
ExecutionNode* ExecutionPlan::fromNodeCollectCount (ExecutionNode* previous,
AstNode const* node) {
TRI_ASSERT(node != nullptr && node->type == NODE_TYPE_COLLECT_COUNT);
TRI_ASSERT(node != nullptr &&
node->type == NODE_TYPE_COLLECT_COUNT);
size_t const n = node->numMembers();
TRI_ASSERT(n == 2);
@ -705,7 +796,7 @@ ExecutionNode* ExecutionPlan::fromNodeCollectCount (ExecutionNode* previous,
// handle out variable
Variable* outVariable = static_cast<Variable*>(v->getData());
auto en = registerNode(new AggregateNode(this, nextId(), aggregateVariables,
auto en = registerNode(new AggregateNode(this, nextId(), aggregateVariables, nullptr,
outVariable, std::vector<Variable const*>(), _ast->variables()->variables(false), true));
return addDependency(previous, en);
@ -997,6 +1088,11 @@ ExecutionNode* ExecutionPlan::fromNode (AstNode const* node) {
break;
}
case NODE_TYPE_COLLECT_EXPRESSION: {
en = fromNodeCollectExpression(en, member);
break;
}
case NODE_TYPE_COLLECT_COUNT: {
en = fromNodeCollectCount(en, member);
break;

View File

@ -381,6 +381,13 @@ namespace triagens {
ExecutionNode* fromNodeCollect (ExecutionNode*,
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node, var = expr
////////////////////////////////////////////////////////////////////////////////
ExecutionNode* fromNodeCollectExpression (ExecutionNode*,
AstNode const*);
////////////////////////////////////////////////////////////////////////////////
/// @brief create an execution plan element from an AST COLLECT node, COUNT
////////////////////////////////////////////////////////////////////////////////

File diff suppressed because it is too large Load Diff

View File

@ -309,7 +309,7 @@ collect_statement:
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
}
auto node = parser->ast()->createNodeCollect(parser->ast()->createNodeList(), $2);
auto node = parser->ast()->createNodeCollectCount(parser->ast()->createNodeList(), $2);
parser->ast()->addOperation(node);
}
| collect_variable_list count_into {
@ -336,7 +336,7 @@ collect_statement:
}
}
auto node = parser->ast()->createNodeCollect($1, $2);
auto node = parser->ast()->createNodeCollectCount($1, $2);
parser->ast()->addOperation(node);
}
| collect_variable_list optional_into optional_keep {
@ -370,6 +370,33 @@ collect_statement:
auto node = parser->ast()->createNodeCollect($1, $2, $3);
parser->ast()->addOperation(node);
}
| collect_variable_list T_INTO variable_name T_ASSIGN expression {
auto scopes = parser->ast()->scopes();
// check if we are in the main scope
bool reRegisterVariables = (scopes->type() != triagens::aql::AQL_SCOPE_MAIN);
if (reRegisterVariables) {
// end the active scopes
scopes->endNested();
// start a new scope
scopes->start(triagens::aql::AQL_SCOPE_COLLECT);
size_t const n = $1->numMembers();
for (size_t i = 0; i < n; ++i) {
auto member = $1->getMember(i);
if (member != nullptr) {
TRI_ASSERT(member->type == NODE_TYPE_ASSIGN);
auto v = static_cast<Variable*>(member->getMember(0)->getData());
scopes->addVariable(v);
}
}
}
auto node = parser->ast()->createNodeCollectExpression($1, $3, $5);
parser->ast()->addOperation(node);
}
;
collect_list:

View File

@ -1,6 +1,6 @@
/*jshint browser: true */
/*jshint strict: false, unused: false */
/*global Backbone, $, window, arangoHelper, templateEngine, Joi, alert, _*/
/*global require, Backbone, $, window, arangoHelper, templateEngine, Joi, alert, _*/
(function() {
"use strict";

View File

@ -0,0 +1,161 @@
/*jshint strict: false, maxlen: 500 */
/*global require, assertEqual, AQL_EXECUTE */
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for COLLECT w/ INTO var = expr
///
/// @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 db = require("org/arangodb").db;
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function optimizerCollectExpressionTestSuite () {
var c;
return {
setUp : function () {
db._drop("UnitTestsCollection");
c = db._create("UnitTestsCollection");
for (var i = 0; i < 1000; ++i) {
c.save({ gender: (i % 2 === 0 ? "m" : "f"), age: 11 + (i % 71), value: i });
}
},
tearDown : function () {
db._drop("UnitTestsCollection");
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test expression
////////////////////////////////////////////////////////////////////////////////
testReference : function () {
var query = "FOR i IN " + c.name() + " COLLECT gender = i.gender INTO docs = i RETURN { gender: gender, age: MIN(docs[*].age) }";
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertEqual("f", results.json[0].gender);
assertEqual(11, results.json[0].age);
assertEqual("m", results.json[1].gender);
assertEqual(11, results.json[1].age);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test expression
////////////////////////////////////////////////////////////////////////////////
testSubAttribute : function () {
var query = "FOR i IN " + c.name() + " COLLECT gender = i.gender INTO ages = i.age RETURN { gender: gender, age: MIN(ages) }";
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertEqual("f", results.json[0].gender);
assertEqual(11, results.json[0].age);
assertEqual("m", results.json[1].gender);
assertEqual(11, results.json[1].age);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test expression
////////////////////////////////////////////////////////////////////////////////
testConst : function () {
var query = "FOR i IN " + c.name() + " COLLECT gender = i.gender INTO values = 1 RETURN { gender: gender, values: values }";
var values = [ ];
for (var i = 0; i < 500; ++i) {
values.push(1);
}
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertEqual("f", results.json[0].gender);
assertEqual(values, results.json[0].values);
assertEqual("m", results.json[1].gender);
assertEqual(values, results.json[1].values);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test expression
////////////////////////////////////////////////////////////////////////////////
testDocAttribute : function () {
var query = "FOR i IN " + c.name() + " COLLECT gender = i.gender INTO values = i.value RETURN { gender: gender, values: values }";
var f = [ ], m = [ ];
for (var i = 0; i < 500; ++i) {
m.push(i * 2);
f.push((i * 2) + 1);
}
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertEqual("f", results.json[0].gender);
assertEqual(f, results.json[0].values.sort(function (l, r) { return l - r; }));
assertEqual("m", results.json[1].gender);
assertEqual(m, results.json[1].values.sort(function (l, r) { return l - r; }));
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test expression
////////////////////////////////////////////////////////////////////////////////
testCalculation : function () {
var query = "FOR i IN " + c.name() + " COLLECT gender = i.gender INTO names = (i.gender == 'f' ? 'female' : 'male') RETURN { gender: gender, names: names }";
var m = [ ], f = [ ];
for (var i = 0; i < 500; ++i) {
m.push('male');
f.push('female');
}
var results = AQL_EXECUTE(query);
assertEqual(2, results.json.length);
assertEqual("f", results.json[0].gender);
assertEqual(f, results.json[0].names);
assertEqual("m", results.json[1].gender);
assertEqual(m, results.json[1].names);
}
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(optimizerCollectExpressionTestSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:

View File

@ -5,7 +5,7 @@ AC_DEFUN([AC_CLOCK],
[
have_clock_gettime=no
AC_MSG_CHECKING([for clock_gettime oooooooooooooooooooooooooooooo])
AC_MSG_CHECKING([for clock_gettime])
AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
have_clock_gettime=yes
@ -17,7 +17,7 @@ AC_DEFUN([AC_CLOCK],
if test "$have_clock_gettime" = "no"; then
AC_MSG_CHECKING([for clock_gettime in -lrt])
SAVED_LIBS="$LIBS"
SAVED_LIBS=$LIBS
LIBS="$LIBS -lrt"
AC_TRY_LINK([ #include <time.h> ], [struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts);], [
@ -69,6 +69,9 @@ AC_DEFUN([AC_CLOCK],
if test "$have_clock_get_time" = "yes"; then
AC_DEFINE([HAVE_CLOCK_GET_TIME], 1, [do we have clock_get_time?])
fi
RT_LIBS=$LIBS
AC_SUBST(RT_LIBS)
SAVED_LIBS=$LIBS
])
dnl Local Variables:

View File

@ -253,3 +253,30 @@ AC_DEFUN([TR_LIBRARY],[
esac
done
])
dnl ----------------------------------------------------------------------------
dnl check for std::unordered_map::emplace()
dnl ----------------------------------------------------------------------------
AC_DEFUN([AX_CXX_CHECK_UNORDERED_MAP_EMPLACE], [
AC_LANG_PUSH([C++])
AC_MSG_CHECKING([whether C++ has support for std::unordered_map::emplace()])
AC_COMPILE_IFELSE(
[AC_LANG_SOURCE[
#include <unordered_map>
void test () {
std::unordered_map<int, int> x;
x.emplace(1, 1);
}
]],
[eval unordered_map_emplace=yes],
[eval unordered_map_emplace=no]
)
AC_MSG_RESULT([$unordered_map_emplace])
AC_LANG_POP([C++])
if test x$unordered_map_emplace = xno; then
AC_MSG_ERROR([C++ has no support for std::unordered_map::emplace()])
fi
])
AX_CXX_CHECK_UNORDERED_MAP_EMPLACE