10000 optimization for AQL CONCAT() · ezhangle/arangodb@50b5316 · GitHub
[go: up one dir, main page]

Skip to content

Commit 50b5316

Browse files
committed
optimization for AQL CONCAT()
Conflicts: arangod/Aql/AqlValue.cpp arangod/Aql/AqlValue.h arangod/Aql/Ast.cpp
1 parent 9c3419f commit 50b5316

File tree

14 files changed

+326
-67
lines changed

14 files changed

+326
-67
lines changed

arangod/Aql/AqlValue.cpp

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ bool AqlValue::isTrue () const {
5858
}
5959
}
6060
else if (_type == RANGE || _type == DOCVEC) {
61-
// a range or a docvec is equivalent to a list
61+
// a range or a docvec is equivalent to an array
6262
return true;
6363
}
6464
else if (_type == EMPTY) {
@@ -244,7 +244,7 @@ bool AqlValue::isBoolean () const {
244244
}
245245

246246
////////////////////////////////////////////////////////////////////////////////
247-
/// @brief whether or not the AqlValue contains a list value
247+
/// @brief whether or not the AqlValue contains an array value
248248
////////////////////////////////////////////////////////////////////////////////
249249

250250
bool AqlValue::isArray () const {
@@ -272,7 +272,7 @@ bool AqlValue::isArray () const {
272272
}
273273

274274
////////////////////////////////////////////////////////////////////////////////
275-
/// @brief whether or not the AqlValue contains an array value
275+
/// @brief whether or not the AqlValue contains an object value
276276
////////////////////////////////////////////////////////////////////////////////
277277

278278
bool AqlValue::isObject () const {
@@ -296,6 +296,80 @@ bool AqlValue::isObject () const {
296296
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
297297
}
298298

299+
////////////////////////////////////////////////////////////////////////////////
300+
/// @brief whether or not the AqlValue contains a null value
301+
////////////////////////////////////////////////////////////////////////////////
302+
303+
bool AqlValue::isNull (bool emptyIsNull) const {
304+
switch (_type) {
305+
case JSON: {
306+
TRI_json_t const* json = _json->json();
307+
return json == nullptr || json->_type == TRI_JSON_NULL;
308+
}
309+
310+
case SHAPED: {
311+
return false;
312+
}
313+
314+
case DOCVEC:
315+
case RANGE: {
316+
return false;
317+
}
318+
319+
case EMPTY: {
320+
return emptyIsNull;
321+
}
322+
}
323+
324+
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
325+
}
326+
327+
////////////////////////////////////////////////////////////////////////////////
328+
/// @brief returns the array member at position i
329+
////////////////////////////////////////////////////////////////////////////////
330+
331+
triagens::basics::Json AqlValue::at (triagens::arango::AqlTransaction* trx,
332+
size_t i) const {
333+
switch (_type) {
334+
case JSON: {
335+
TRI_json_t const* json = _json->json();
336+
if (TRI_IsArrayJson(json)) {
337+
if (i < TRI_LengthArrayJson(json)) {
338+
return triagens::basics::Json(TRI_UNKNOWN_MEM_ZONE, static_cast<TRI_json_t*>(TRI_AtVector(&json->_value._objects, i)), triagens::basics::Json::NOFREE);
339+
}
340+
}
341+
break; // fall-through to exception
342+
}
343+
344+
case DOCVEC: {
345+
TRI_ASSERT(_vector != nullptr);
346+
// calculate the result list length
347+
size_t offset = 0;
348+
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
349+
auto current = (*it);
350+
size_t const n = current->size();
351+
if (offset + i < n) {
352+
auto vecCollection = current->getDocumentCollection(0);
353+
return current->getValue(i - offset, 0).toJson(trx, vecCollection);
354+
}
355+
offset += (*it)->size();
356+
}
357+
break; // fall-through to exception
358+
}
359+
360+
case RANGE: {
361+
TRI_ASSERT(_range != nullptr);
362+
return triagens::basics::Json(static_cast<double>(_range->at(i)));
363+
}
364+
365+
case SHAPED:
366+
case EMPTY: {
367+
}
368+
}
369+
370+
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
371+
}
372+
299373
////////////////////////////////////////////////////////////////////////////////
300374
/// @brief returns the length of an AqlValue containing an array
301375
////////////////////////////////////////////////////////////////////////////////
@@ -487,13 +561,13 @@ v8::Handle<v8::Value> AqlValue::toV8 (v8::Isolate* isolate,
487561
case DOCVEC: {
488562
TRI_ASSERT(_vector != nullptr);
489563

490-
// calculate the result list length
564+
// calculate the result array length
491565
size_t totalSize = 0;
492566
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
493567
totalSize += (*it)->size();
494568
}
495569

496-
// allocate the result list
570+
// allocate the result array
497571
v8::Handle<v8::Array> result = v8::Array::New(isolate, static_cast<int>(totalSize));
498572
uint32_t j = 0; // output row count
499573

@@ -583,13 +657,13 @@ Json AqlValue::toJson (triagens::arango::AqlTransaction* trx,
583657
case DOCVEC: {
584658
TRI_ASSERT(_vector != nullptr);
585659

586-
// calculate the result list length
660+
// calculate the result array length
587661
size_t totalSize = 0;
588662
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
589663
totalSize += (*it)->size();
590664
}
591665

592-
// allocate the result list
666+
// allocate the result array
593667
Json json(Json::Array, static_cast<size_t>(totalSize));
594668

595669
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
@@ -755,7 +829,7 @@ Json AqlValue::extractArrayMember (triagens::arango::AqlTransaction* trx,
755829
}
756830

757831
if (position >= 0 && position < static_cast<int64_t>(length)) {
758-
// only look up the value if it is within list bounds
832+
// only look up the value if it is within array bounds
759833
TRI_json_t const* found = TRI_LookupArrayJson(json, static_cast<size_t>(position));
760834

761835
if (found != nullptr) {
@@ -784,7 +858,7 @@ Json AqlValue::extractArrayMember (triagens::arango::AqlTransaction* trx,
784858
}
785859

786860
if (position >= 0 && position < static_cast<int64_t>(n)) {
787-
// only look up the value if it is within list bounds
861+
// only look up the value if it is within array bounds
788862
return Json(static_cast<double>(_range->at(static_cast<size_t>(position))));
789863
}
790864
break; // fall-through to returning null
@@ -794,7 +868,7 @@ Json AqlValue::extractArrayMember (triagens::arango::AqlTransaction* trx,
794868
TRI_ASSERT(_vector != nullptr);
795869
size_t const p = static_cast<size_t>(position);
796870

797-
// calculate the result list length
871+
// calculate the result array length
798872
size_t totalSize = 0;
799873
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
800874
if (p < totalSize + (*it)->size()) {

arangod/Aql/AqlValue.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ namespace triagens {
187187
////////////////////////////////////////////////////////////////////////////////
188188

189189
bool isString () const;
190-
190+
191191
////////////////////////////////////////////////////////////////////////////////
192192
/// @brief whether or not the AqlValue contains a numeric value
193193
////////////////////////////////////////////////////////////////////////////////
@@ -212,6 +212,19 @@ namespace triagens {
212212

213213
bool isObject () const;
214214

215+
////////////////////////////////////////////////////////////////////////////////
216+
/// @brief whether or not the AqlValue contains a null value
217+
////////////////////////////////////////////////////////////////////////////////
218+
219+
bool isNull (bool emptyIsNull) const;
220+
221+
////////////////////////////////////////////////////////////////////////////////
222+
/// @brief returns the array member at position i
223+
////////////////////////////////////////////////////////////////////////////////
224+
225+
triagens::basics::Json at (triagens::arango::AqlTransaction*,
226+
size_t i) const;
227+
215228
////////////////////////////////////////////////////////////////////////////////
216229
/// @brief returns the length of an AqlValue containing an array
217230
////////////////////////////////////////////////////////////////////////////////

arangod/Aql/Ast.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1149,12 +1149,22 @@ void Ast::validateAndOptimize () {
11491149
struct TraversalContext {
11501150
bool isInFilter = false;
11511151
bool hasSeenWriteNode = false;
1152+
int64_t stopOptimizationRequests = 0;
11521153
};
11531154

11541155
auto preVisitor = [&](AstNode const* node, void* data) -> void {
11551156
if (node->type == NODE_TYPE_FILTER) {
11561157
static_cast<TraversalContext*>(data)->isInFilter = true;
11571158
}
1159+
else if (node->type == NODE_TYPE_FCALL) {
1160+
auto func = static_cast<Function*>(node->getData());
1161+
TRI_ASSERT(func != nullptr);
1162+
1163+
if (func->externalName == "NOOPT") {
1164+
// NOOPT will turn all function optimizations off
1165+
++(static_cast<TraversalContext*>(data)->stopOptimizationRequests);
1166+
}
1167+
}
11581168
};
11591169

11601170
auto postVisitor = [&](AstNode const* node, void* data) -> void {
@@ -1169,6 +1179,15 @@ void Ast::validateAndOptimize () {
11691179
node->type == NODE_TYPE_UPSERT) {
11701180
static_cast<TraversalContext*>(data)->hasSeenWriteNode = true;
11711181
}
1182+
else if (node->type == NODE_TYPE_FCALL) {
1183+
auto func = static_cast<Function*>(node->getData());
1184+
TRI_ASSERT(func != nullptr);
1185+
1186+
if (func->externalName == "NOOPT") {
1187+
// NOOPT will turn all function optimizations off
1188+
--(static_cast<TraversalContext*>(data)->stopOptimizationRequests);
1189+
}
1190+
}
11721191
};
11731192

11741193
auto visitor = [&](AstNode* node, void* data) -> AstNode* {
@@ -1225,7 +1244,12 @@ void Ast::validateAndOptimize () {
12251244
THROW_ARANGO_EXCEPTION(TRI_ERROR_QUERY_ACCESS_AFTER_MODIFICATION);
12261245
}
12271246

1228-
return this->optimizeFunctionCall(node);
1247+
if (static_cast<TraversalContext*>(data)->stopOptimizationRequests == 0) {
1248+
// optimization allowed
1249+
return this->optimizeFunctionCall(node);
1250+
}
1251+
// optimization not allowed
1252+
return node;
12291253
}
12301254

12311255
// reference to a variable, may be able to insert the variable value directly

arangod/Aql/Executor.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
110110
{ "TO_LIST", Function("TO_LIST", "AQL_TO_LIST", ".", true, false, true) },
111111

112112
// string functions
113-
{ "CONCAT", Function("CONCAT", "AQL_CONCAT", "szl|+", true, false, true) },
113+
{ "CONCAT", Function("CONCAT", "AQL_CONCAT", "szl|+", true, false, true, &Functions::Concat) },
114114
{ "CONCAT_SEPARATOR", Function("CONCAT_SEPARATOR", "AQL_CONCAT_SEPARATOR", "s,szl|+", true, false, true) },
115115
{ "CHAR_LENGTH", Function("CHAR_LENGTH", "AQL_CHAR_LENGTH", "s", true, false, true) },
116116
{ "LOWER", Function("LOWER", "AQL_LOWER", "s", true, false, true) },
@@ -238,7 +238,8 @@ std::unordered_map<std::string, Function const> const Executor::FunctionNames{
238238

239239
// misc functions
240240
{ "FAIL", Function("FAIL", "AQL_FAIL", "|s", false, true, true) },
241-
{ "PASSTHRU", Function("PASSTHRU", "AQL_PASSTHRU", ".", false, false, true) },
241+
{ "PASSTHRU", Function("PASSTHRU", "AQL_PASSTHRU", ".", false, false, true, &Functions::Passthru ) },
242+
{ "NOOPT", Function("NOOPT", "AQL_PASSTHRU", ".", false, false, true, &Functions::Passthru ) },
242243
{ "SLEEP", Function("SLEEP", "AQL_SLEEP", "n", false, true, true) },
243244
{ "COLLECTIONS", Function("COLLECTIONS", "AQL_COLLECTIONS", "", false, true, false) },
244245
{ "NOT_NULL", Function("NOT_NULL", "AQL_NOT_NULL", ".|+", true, false, true) },
@@ -297,6 +298,8 @@ V8Expression* Executor::generateExpression (AstNode const* node) {
297298

298299
////////////////////////////////////////////////////////////////////////////////
299300
/// @brief executes an expression directly
301+
/// this method is called during AST optimization and will be used to calculate
302+
/// values for constant expressions
300303
////////////////////////////////////////////////////////////////////////////////
301304

302305
TRI_json_t* Executor::executeExpression (Query* query,

arangod/Aql/Expression.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
391391
TRI_document_collection_t const** collection,
392392
triagens::arango::AqlTransaction* trx,
393393
std::vector<TRI_document_collection_t const*>& docColls,
394-
std::vector<AqlValue>& argv,
394+
std::vector<AqlValue> const& argv,
395395
size_t startPos,
396396
std::vector<Variable*> const& vars,
397397
std::vector<RegisterId> const& regs) {
@@ -582,8 +582,10 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
582582

583583
TRI_document_collection_t const* myCollection = nullptr;
584584
auto member = node->getMember(0);
585-
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
585+
TRI_ASSERT(member->type == NODE_TYPE_ARRAY);
586586

587+
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
588+
587589
auto res2 = func->implementation(trx, myCollection, result);
588590
result.destroy();
589591
return res2;

arangod/Aql/Expression.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,8 @@ namespace triagens {
282282
TRI_document_collection_t const**,
283283
triagens::arango::AqlTransaction*,
284284
std::vector<TRI_document_collection_t const*>&,
285-
std::vector<AqlValue>&, size_t,
285+
std::vector<AqlValue> const&,
286+
size_t,
286287
std::vector<Variable*> const&,
287288
std::vector<RegisterId> const&);
288289

0 commit comments

Comments
 (0)
0