mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel
This commit is contained in:
commit
ab99e17876
|
@ -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 \
|
||||
|
|
|
@ -140,6 +140,7 @@ AM_LDFLAGS = \
|
|||
################################################################################
|
||||
|
||||
LIBS = \
|
||||
@RT_LIBS@ \
|
||||
@LIBEV_LIBS@ \
|
||||
@MATH_LIBS@ \
|
||||
@OPENSSL_LIBS@ \
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
@ -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:
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue