1
0
Fork 0

issue #248: pull out some sort of subqueries

This commit is contained in:
Jan Steemann 2012-10-22 09:14:38 +02:00
parent d27ba1cf30
commit d117f2916a
15 changed files with 450 additions and 100 deletions

View File

@ -32,6 +32,9 @@ v1.1.beta1 (2012-XX-XX)
- if the length of the HTTP headers is greated than the maximum allowed size (1 MB), - if the length of the HTTP headers is greated than the maximum allowed size (1 MB),
the server will fail with HTTP 431 (request header fields too large) the server will fail with HTTP 431 (request header fields too large)
* issue #248: allow AQL optimiser to pull out completely uncorrelated subqueries to the
top level, resulting in less repeated evaluation of the subquery
* upgraded to Doxygen 1.8.0 * upgraded to Doxygen 1.8.0
* issue #247: added AQL function MERGE_RECURSIVE * issue #247: added AQL function MERGE_RECURSIVE

View File

@ -259,12 +259,12 @@ SHELL_SERVER_AHUACATL = @top_srcdir@/js/server/tests/ahuacatl-ranges.js \
@top_srcdir@/js/server/tests/ahuacatl-skiplist.js \ @top_srcdir@/js/server/tests/ahuacatl-skiplist.js \
@top_srcdir@/js/server/tests/ahuacatl-refaccess-variable.js \ @top_srcdir@/js/server/tests/ahuacatl-refaccess-variable.js \
@top_srcdir@/js/server/tests/ahuacatl-refaccess-attribute.js \ @top_srcdir@/js/server/tests/ahuacatl-refaccess-attribute.js \
@top_srcdir@/js/server/tests/ahuacatl-skiplist.js \
@top_srcdir@/js/server/tests/ahuacatl-queries-simple.js \ @top_srcdir@/js/server/tests/ahuacatl-queries-simple.js \
@top_srcdir@/js/server/tests/ahuacatl-queries-variables.js \ @top_srcdir@/js/server/tests/ahuacatl-queries-variables.js \
@top_srcdir@/js/server/tests/ahuacatl-queries-geo.js \ @top_srcdir@/js/server/tests/ahuacatl-queries-geo.js \
@top_srcdir@/js/server/tests/ahuacatl-queries-collection.js \ @top_srcdir@/js/server/tests/ahuacatl-queries-collection.js \
@top_srcdir@/js/server/tests/ahuacatl-queries-noncollection.js \ @top_srcdir@/js/server/tests/ahuacatl-queries-noncollection.js \
@top_srcdir@/js/server/tests/ahuacatl-subquery.js \
@top_srcdir@/js/server/tests/ahuacatl-operators.js @top_srcdir@/js/server/tests/ahuacatl-operators.js
SHELL_SERVER_AHUACATL_EXTENDED = \ SHELL_SERVER_AHUACATL_EXTENDED = \

View File

@ -143,11 +143,11 @@ static inline TRI_aql_codegen_scope_t* CurrentScope (TRI_aql_codegen_js_t* const
static inline bool OutputString (TRI_string_buffer_t* const buffer, static inline bool OutputString (TRI_string_buffer_t* const buffer,
const char* const value) { const char* const value) {
if (!buffer) { if (buffer == NULL) {
return true; return true;
} }
if (!value) { if (value == NULL) {
return false; return false;
} }
@ -160,7 +160,7 @@ static inline bool OutputString (TRI_string_buffer_t* const buffer,
static inline bool OutputChar (TRI_string_buffer_t* const buffer, static inline bool OutputChar (TRI_string_buffer_t* const buffer,
const char value) { const char value) {
if (!buffer) { if (buffer == NULL) {
return true; return true;
} }
@ -173,7 +173,7 @@ static inline bool OutputChar (TRI_string_buffer_t* const buffer,
static inline bool OutputUInt (TRI_string_buffer_t* const buffer, static inline bool OutputUInt (TRI_string_buffer_t* const buffer,
const uint64_t value) { const uint64_t value) {
if (!buffer) { if (buffer == NULL) {
return true; return true;
} }
@ -186,7 +186,7 @@ static inline bool OutputUInt (TRI_string_buffer_t* const buffer,
static inline bool OutputInt (TRI_string_buffer_t* const buffer, static inline bool OutputInt (TRI_string_buffer_t* const buffer,
const int64_t value) { const int64_t value) {
if (!buffer) { if (buffer == NULL) {
return true; return true;
} }
@ -201,7 +201,7 @@ static inline void ScopeOutput (TRI_aql_codegen_js_t* const generator,
const char* const value) { const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, value)) { if (! OutputString(scope->_buffer, value)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -214,7 +214,7 @@ static inline void ScopeOutputInt (TRI_aql_codegen_js_t* const generator,
const int64_t value) { const int64_t value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputInt(scope->_buffer, value)) { if (! OutputInt(scope->_buffer, value)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -227,7 +227,7 @@ static inline void ScopeOutputUInt (TRI_aql_codegen_js_t* const generator,
const uint64_t value) { const uint64_t value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputUInt(scope->_buffer, value)) { if (! OutputUInt(scope->_buffer, value)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -240,13 +240,13 @@ static inline void ScopeOutputQuoted (TRI_aql_codegen_js_t* const generator,
const char* const value) { const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputChar(scope->_buffer, '\'')) { if (! OutputChar(scope->_buffer, '\'')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
if (!OutputString(scope->_buffer, value)) { if (! OutputString(scope->_buffer, value)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
if (!OutputChar(scope->_buffer, '\'')) { if (! OutputChar(scope->_buffer, '\'')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -261,13 +261,13 @@ static inline void ScopeOutputQuoted2 (TRI_aql_codegen_js_t* const generator,
char* escaped; char* escaped;
size_t outLength; size_t outLength;
if (!OutputChar(scope->_buffer, '"')) { if (! OutputChar(scope->_buffer, '"')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
escaped = TRI_EscapeUtf8StringZ(TRI_UNKNOWN_MEM_ZONE, value, strlen(value), false, &outLength); escaped = TRI_EscapeUtf8StringZ(TRI_UNKNOWN_MEM_ZONE, value, strlen(value), false, &outLength);
if (escaped) { if (escaped) {
if (!OutputString(scope->_buffer, escaped)) { if (! OutputString(scope->_buffer, escaped)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
@ -277,7 +277,7 @@ static inline void ScopeOutputQuoted2 (TRI_aql_codegen_js_t* const generator,
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
if (!OutputChar(scope->_buffer, '"')) { if (! OutputChar(scope->_buffer, '"')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -290,7 +290,7 @@ static inline void ScopeOutputJson (TRI_aql_codegen_js_t* const generator,
const TRI_json_t* const json) { const TRI_json_t* const json) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!scope->_buffer) { if (! scope->_buffer) {
return; return;
} }
@ -321,10 +321,10 @@ static inline void ScopeOutputFunction (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t functionIndex) { const TRI_aql_codegen_register_t functionIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, FUNCTION_PREFIX)) { if (! OutputString(scope->_buffer, FUNCTION_PREFIX)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
if (!OutputInt(scope->_buffer, (int64_t) functionIndex)) { if (! OutputInt(scope->_buffer, (int64_t) functionIndex)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -337,10 +337,10 @@ static inline void ScopeOutputRegister (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t registerIndex) { const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputString(scope->_buffer, REGISTER_PREFIX)) { if (! OutputString(scope->_buffer, REGISTER_PREFIX)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
if (!OutputInt(scope->_buffer, (int64_t) registerIndex)) { if (! OutputInt(scope->_buffer, (int64_t) registerIndex)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
@ -432,7 +432,7 @@ static void StartScope (TRI_aql_codegen_js_t* const generator,
TRI_aql_codegen_scope_t* scope; TRI_aql_codegen_scope_t* scope;
scope = (TRI_aql_codegen_scope_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_scope_t), false); scope = (TRI_aql_codegen_scope_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_scope_t), false);
if (!scope) { if (scope == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return; return;
} }
@ -633,7 +633,7 @@ static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
TRI_aql_codegen_scope_t* scope = CurrentScope(generator); TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_variable_t* variable = CreateVariable(name, registerIndex); TRI_aql_codegen_variable_t* variable = CreateVariable(name, registerIndex);
if (!variable) { if (variable == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return; return;
} }
@ -2368,7 +2368,8 @@ char* TRI_GenerateCodeAql (TRI_aql_context_t* const context) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY; generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
} }
} }
// errorCode might have changed, no else!
if (generator->_errorCode != TRI_ERROR_NO_ERROR) { if (generator->_errorCode != TRI_ERROR_NO_ERROR) {
// register the error // register the error
TRI_SetErrorContextAql(context, generator->_errorCode, generator->_errorValue); TRI_SetErrorContextAql(context, generator->_errorCode, generator->_errorValue);

View File

@ -146,7 +146,6 @@ bool OpenCollections (TRI_aql_context_t* const context) {
n = context->_collections._length; n = context->_collections._length;
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
TRI_aql_collection_t* collection = context->_collections._buffer[i]; TRI_aql_collection_t* collection = context->_collections._buffer[i];
char* name;
assert(collection); assert(collection);
assert(collection->_name); assert(collection->_name);
@ -155,11 +154,10 @@ bool OpenCollections (TRI_aql_context_t* const context) {
assert(!collection->_readLocked); assert(!collection->_readLocked);
LOG_TRACE("locking collection '%s'", collection->_name); LOG_TRACE("locking collection '%s'", collection->_name);
collection->_collection = TRI_UseCollectionByNameVocBase(context->_vocbase, collection->_name);
if (collection->_collection == NULL) {
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_COLLECTION_NOT_FOUND, collection->_name);
name = collection->_name;
collection->_collection = TRI_UseCollectionByNameVocBase(context->_vocbase, name);
if (!collection->_collection) {
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_COLLECTION_NOT_FOUND, name);
return false; return false;
} }
} }
@ -341,7 +339,7 @@ void TRI_UnlockCollectionsAql (TRI_aql_context_t* const context) {
assert(collection); assert(collection);
assert(collection->_name); assert(collection->_name);
if (!collection->_collection) { if (collection->_collection == NULL) {
// collection not yet opened // collection not yet opened
continue; continue;
} }

View File

@ -156,6 +156,8 @@ TRI_aql_context_t* TRI_CreateContextAql (TRI_vocbase_t* vocbase,
assert(vocbase); assert(vocbase);
assert(query); assert(query);
LOG_TRACE("creating context");
context = (TRI_aql_context_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_context_t), false); context = (TRI_aql_context_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_context_t), false);
if (context == NULL) { if (context == NULL) {
@ -235,6 +237,8 @@ TRI_aql_context_t* TRI_CreateContextAql (TRI_vocbase_t* vocbase,
void TRI_FreeContextAql (TRI_aql_context_t* const context) { void TRI_FreeContextAql (TRI_aql_context_t* const context) {
assert(context); assert(context);
LOG_TRACE("freeing context");
// remove barriers for all collections used // remove barriers for all collections used
TRI_RemoveBarrierCollectionsAql(context); TRI_RemoveBarrierCollectionsAql(context);
@ -347,6 +351,7 @@ bool TRI_OptimiseQueryContextAql (TRI_aql_context_t* const context) {
} }
TRI_CompactStatementListAql(context->_statements); TRI_CompactStatementListAql(context->_statements);
TRI_PulloutStatementListAql(context->_statements);
// TRI_DumpStatementsAql(context->_statements); // TRI_DumpStatementsAql(context->_statements);

View File

@ -1769,7 +1769,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -1785,7 +1785,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -1801,7 +1801,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -1832,7 +1832,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -1914,7 +1914,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -1993,11 +1993,11 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -2013,7 +2013,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
;} ;}
@ -2029,7 +2029,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
@ -2080,7 +2080,7 @@ yyreduce:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, subQuery)) { if (!TRI_AppendStatementListAql(context->_statements, subQuery)) {
ABORT_OOM ABORT_OOM
} }
@ -2674,7 +2674,7 @@ yyreduce:
// push the actual expand node into the statement list // push the actual expand node into the statement list
expand = TRI_CreateNodeExpandAql(context, varname, expanded, (yyvsp[(4) - (4)].node)); expand = TRI_CreateNodeExpandAql(context, varname, expanded, (yyvsp[(4) - (4)].node));
if (!TRI_AddStatementListAql(context->_statements, expand)) { if (!TRI_AppendStatementListAql(context->_statements, expand)) {
ABORT_OOM ABORT_OOM
} }

View File

@ -212,7 +212,7 @@ for_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -225,7 +225,7 @@ filter_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -238,7 +238,7 @@ let_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -259,7 +259,7 @@ collect_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -310,7 +310,7 @@ sort_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -359,11 +359,11 @@ limit_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -373,7 +373,7 @@ limit_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
} }
@ -386,7 +386,7 @@ return_statement:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM ABORT_OOM
} }
@ -422,7 +422,7 @@ expression:
ABORT_OOM ABORT_OOM
} }
if (!TRI_AddStatementListAql(context->_statements, subQuery)) { if (!TRI_AppendStatementListAql(context->_statements, subQuery)) {
ABORT_OOM ABORT_OOM
} }
@ -767,7 +767,7 @@ reference:
// push the actual expand node into the statement list // push the actual expand node into the statement list
expand = TRI_CreateNodeExpandAql(context, varname, expanded, $4); expand = TRI_CreateNodeExpandAql(context, varname, expanded, $4);
if (!TRI_AddStatementListAql(context->_statements, expand)) { if (!TRI_AppendStatementListAql(context->_statements, expand)) {
ABORT_OOM ABORT_OOM
} }

View File

@ -60,6 +60,29 @@ static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const,
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @} /// @}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief a local optimiser structure that is used temporarily during the
/// AST traversal
////////////////////////////////////////////////////////////////////////////////
typedef struct aql_optimiser_s {
TRI_aql_context_t* _context;
}
aql_optimiser_t;
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private functions // --SECTION-- private functions
@ -69,6 +92,34 @@ static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const,
/// @addtogroup Ahuacatl /// @addtogroup Ahuacatl
/// @{ /// @{
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief create an optimiser structure
////////////////////////////////////////////////////////////////////////////////
static aql_optimiser_t* CreateOptimiser (TRI_aql_context_t* const context) {
aql_optimiser_t* optimiser;
optimiser = (aql_optimiser_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(aql_optimiser_t), false);
if (optimiser == NULL) {
return NULL;
}
optimiser->_context = context;
return optimiser;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free an optimiser structure
////////////////////////////////////////////////////////////////////////////////
static void FreeOptimiser (aql_optimiser_t* const optimiser) {
assert(optimiser);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, optimiser);
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief pick an index for the ranges found /// @brief pick an index for the ranges found
@ -133,15 +184,15 @@ static void AttachCollectionHint (TRI_aql_context_t* const context,
static TRI_aql_node_t* AnnotateNode (TRI_aql_statement_walker_t* const walker, static TRI_aql_node_t* AnnotateNode (TRI_aql_statement_walker_t* const walker,
TRI_aql_node_t* node) { TRI_aql_node_t* node) {
TRI_aql_context_t* context; aql_optimiser_t* optimiser;
if (node->_type != TRI_AQL_NODE_COLLECTION) { if (node->_type != TRI_AQL_NODE_COLLECTION) {
return node; return node;
} }
context = (TRI_aql_context_t*) walker->_data; optimiser = (aql_optimiser_t*) walker->_data;
AttachCollectionHint(context, node); AttachCollectionHint(optimiser->_context, node);
return node; return node;
} }
@ -323,7 +374,7 @@ static TRI_aql_node_t* OptimiseFcall (TRI_aql_context_t* const context,
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief optimise a for statement /// @brief optimise a FOR statement
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* OptimiseFor (TRI_aql_statement_walker_t* const walker, static TRI_aql_node_t* OptimiseFor (TRI_aql_statement_walker_t* const walker,
@ -342,9 +393,9 @@ static TRI_aql_node_t* OptimiseFor (TRI_aql_statement_walker_t* const walker,
return node; return node;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief optimise a sort statement /// @brief optimise a SORT statement
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* OptimiseSort (TRI_aql_statement_walker_t* const walker, static TRI_aql_node_t* OptimiseSort (TRI_aql_statement_walker_t* const walker,
@ -387,7 +438,7 @@ static TRI_aql_node_t* OptimiseSort (TRI_aql_statement_walker_t* const walker,
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief optimise a constant filter expression /// @brief optimise a constant FILTER expression
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* OptimiseConstantFilter (TRI_aql_node_t* const node) { static TRI_aql_node_t* OptimiseConstantFilter (TRI_aql_node_t* const node) {
@ -405,12 +456,12 @@ static TRI_aql_node_t* OptimiseConstantFilter (TRI_aql_node_t* const node) {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief optimise a filter statement /// @brief optimise a FILTER statement
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static TRI_aql_node_t* OptimiseFilter (TRI_aql_statement_walker_t* const walker, static TRI_aql_node_t* OptimiseFilter (TRI_aql_statement_walker_t* const walker,
TRI_aql_node_t* node) { TRI_aql_node_t* node) {
TRI_aql_context_t* context = (TRI_aql_context_t*) walker->_data; aql_optimiser_t* optimiser = (aql_optimiser_t*) walker->_data;
TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0); TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0);
while (true) { while (true) {
@ -431,7 +482,7 @@ static TRI_aql_node_t* OptimiseFilter (TRI_aql_statement_walker_t* const walker,
oldRanges = TRI_GetCurrentRangesStatementWalkerAql(walker); oldRanges = TRI_GetCurrentRangesStatementWalkerAql(walker);
changed = false; changed = false;
newRanges = TRI_OptimiseRangesAql(context, expression, &changed, oldRanges); newRanges = TRI_OptimiseRangesAql(optimiser->_context, expression, &changed, oldRanges);
if (newRanges) { if (newRanges) {
TRI_SetCurrentRangesStatementWalkerAql(walker, newRanges); TRI_SetCurrentRangesStatementWalkerAql(walker, newRanges);
@ -840,7 +891,7 @@ static TRI_aql_node_t* OptimiseTernaryOperation (TRI_aql_context_t* const contex
static TRI_aql_node_t* OptimiseNode (TRI_aql_statement_walker_t* const walker, static TRI_aql_node_t* OptimiseNode (TRI_aql_statement_walker_t* const walker,
TRI_aql_node_t* node) { TRI_aql_node_t* node) {
TRI_aql_context_t* context = (TRI_aql_context_t*) walker->_data; TRI_aql_context_t* context = ((aql_optimiser_t*) walker->_data)->_context;
assert(node); assert(node);
@ -910,7 +961,7 @@ static TRI_aql_node_t* OptimiseStatement (TRI_aql_statement_walker_t* const walk
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void PatchVariables (TRI_aql_statement_walker_t* const walker) { static void PatchVariables (TRI_aql_statement_walker_t* const walker) {
TRI_aql_context_t* context = (TRI_aql_context_t*) walker->_data; TRI_aql_context_t* context = ((aql_optimiser_t*) walker->_data)->_context;
TRI_vector_pointer_t* ranges; TRI_vector_pointer_t* ranges;
size_t i, n; size_t i, n;
@ -1012,21 +1063,21 @@ static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const walke
/// @brief optimise the AST, first iteration /// @brief optimise the AST, first iteration
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static bool OptimiseAst (TRI_aql_context_t* const context) { static bool OptimiseAst (aql_optimiser_t* const optimiser) {
TRI_aql_statement_walker_t* walker; TRI_aql_statement_walker_t* walker;
walker = TRI_CreateStatementWalkerAql((void*) context, walker = TRI_CreateStatementWalkerAql((void*) optimiser,
true, true,
&OptimiseNode, &OptimiseNode,
NULL, NULL,
&ProcessStatement); &ProcessStatement);
if (walker == NULL) { if (walker == NULL) {
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); TRI_SetErrorContextAql(optimiser->_context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return false; return false;
} }
TRI_WalkStatementsAql(walker, context->_statements); TRI_WalkStatementsAql(walker, optimiser->_context->_statements);
TRI_FreeStatementWalkerAql(walker); TRI_FreeStatementWalkerAql(walker);
@ -1037,21 +1088,21 @@ static bool OptimiseAst (TRI_aql_context_t* const context) {
/// @brief determine which indexes to use in the query /// @brief determine which indexes to use in the query
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static bool DetermineIndexes (TRI_aql_context_t* const context) { static bool DetermineIndexes (aql_optimiser_t* const optimiser) {
TRI_aql_statement_walker_t* walker; TRI_aql_statement_walker_t* walker;
walker = TRI_CreateStatementWalkerAql((void*) context, walker = TRI_CreateStatementWalkerAql((void*) optimiser,
false, false,
&AnnotateNode, &AnnotateNode,
NULL, NULL,
NULL); NULL);
if (walker == NULL) { if (walker == NULL) {
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); TRI_SetErrorContextAql(optimiser->_context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return false; return false;
} }
TRI_WalkStatementsAql(walker, context->_statements); TRI_WalkStatementsAql(walker, optimiser->_context->_statements);
TRI_FreeStatementWalkerAql(walker); TRI_FreeStatementWalkerAql(walker);
@ -1076,15 +1127,19 @@ static bool DetermineIndexes (TRI_aql_context_t* const context) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool TRI_OptimiseAql (TRI_aql_context_t* const context) { bool TRI_OptimiseAql (TRI_aql_context_t* const context) {
if (!OptimiseAst(context)) { aql_optimiser_t* optimiser;
bool result;
optimiser = CreateOptimiser(context);
if (optimiser == NULL) {
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return false; return false;
} }
if (!DetermineIndexes(context)) { result = (OptimiseAst(optimiser) && DetermineIndexes(optimiser));
return false; FreeOptimiser(optimiser);
}
return true; return result;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -46,19 +46,6 @@
extern "C" { extern "C" {
#endif #endif
// -----------------------------------------------------------------------------
// --SECTION-- public types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- public functions // --SECTION-- public functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -118,6 +118,7 @@ static TRI_aql_scope_t* CreateScope (TRI_aql_context_t* const context,
scope->_id = NextId(context); scope->_id = NextId(context);
scope->_type = NextType(context, type); scope->_type = NextType(context, type);
scope->_ranges = NULL; scope->_ranges = NULL;
scope->_selfContained = true;
TRI_InitAssociativePointer(&scope->_variables, TRI_InitAssociativePointer(&scope->_variables,
TRI_UNKNOWN_MEM_ZONE, TRI_UNKNOWN_MEM_ZONE,
@ -250,7 +251,7 @@ bool TRI_StartScopeAql (TRI_aql_context_t* const context, const TRI_aql_scope_e
return false; return false;
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false; return false;
} }
@ -279,7 +280,7 @@ bool TRI_EndScopeAql (TRI_aql_context_t* const context) {
return false; return false;
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false; return false;
} }
@ -318,7 +319,7 @@ bool TRI_EndScopeByReturnAql (TRI_aql_context_t* const context) {
return false; return false;
} }
if (!TRI_AddStatementListAql(context->_statements, node)) { if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false; return false;
} }
@ -388,7 +389,7 @@ bool TRI_AddVariableScopeAql (TRI_aql_context_t* const context,
} }
scope = CurrentScope(context); scope = CurrentScope(context);
assert(!TRI_InsertKeyAssociativePointer(&scope->_variables, variable->_name, (void*) variable, false)); assert(! TRI_InsertKeyAssociativePointer(&scope->_variables, variable->_name, (void*) variable, false));
return true; return true;
} }

View File

@ -68,6 +68,7 @@ typedef struct TRI_aql_scope_s {
TRI_associative_pointer_t _variables; TRI_associative_pointer_t _variables;
TRI_vector_pointer_t* _ranges; TRI_vector_pointer_t* _ranges;
TRI_aql_scope_e _type; TRI_aql_scope_e _type;
bool _selfContained;
size_t _id; size_t _id;
} }
TRI_aql_scope_t; TRI_aql_scope_t;

View File

@ -226,6 +226,12 @@ TRI_vector_pointer_t* TRI_GetScopesStatementWalkerAql (TRI_aql_statement_walker_
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief return a pointer to a variable, identified by its name /// @brief return a pointer to a variable, identified by its name
///
/// The variable will be searched in the current and the surrounding scopes.
/// If the variable is not found and the reference to it is coming from a
/// subquery scope, we'll mark the subquery as not self-contained. This prevents
/// moving of the subquery to some other position (which would break the query
/// in this case)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker, TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
@ -253,7 +259,14 @@ TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_
if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) { if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// reached the outermost scope // reached the outermost scope
break; if (scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// variable not found but we reached the end of the scope
// we must mark the scope as not self-contained so it is not moved to
// some other position
scope->_selfContained = false;
}
return NULL;
} }
// increase the scope counter // increase the scope counter

View File

@ -26,6 +26,9 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "Ahuacatl/ahuacatl-statementlist.h" #include "Ahuacatl/ahuacatl-statementlist.h"
#include "BasicsC/logging.h"
#include "Ahuacatl/ahuacatl-ast-node.h" #include "Ahuacatl/ahuacatl-ast-node.h"
#include "Ahuacatl/ahuacatl-scope.h" #include "Ahuacatl/ahuacatl-scope.h"
@ -177,11 +180,106 @@ TRI_aql_node_t* TRI_GetDummyReturnEmptyNodeAql (void) {
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief add a statement to the statement list /// @brief pull out uncorrelated subqueries from the middle of the statement
/// list to the beginning
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool TRI_AddStatementListAql (TRI_aql_statement_list_t* const list, void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const list) {
TRI_aql_node_t* const node) { size_t i, n;
size_t scopes = 0;
size_t targetScope = 0;
size_t moveStart = 0;
bool watch = false;
assert(list);
i = 0;
n = list->_statements._length;
while (i < n) {
TRI_aql_node_t* node = StatementAt(list, i);
TRI_aql_node_type_e type = node->_type;
if (type == TRI_AQL_NODE_SCOPE_START) {
// node is a scope start
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
if (scope->_type == TRI_AQL_SCOPE_SUBQUERY && scope->_selfContained) {
if (! watch && scopes > 0) {
watch = true;
targetScope = scopes;
moveStart = i;
}
}
++scopes;
}
else if (type == TRI_AQL_NODE_SCOPE_END) {
// node is a scope end
--scopes;
if (watch && scopes == targetScope) {
watch = false;
node = StatementAt(list, i + 1);
// check if next statement is a subquery statement
if (i + 1 < n && node->_type == TRI_AQL_NODE_SUBQUERY) {
size_t j = moveStart;
size_t inserted = 0;
// moving statements from the middle to the beginning of the list will also
// modify the positions we're moving from
while (j < i + 2) {
node = StatementAt(list, j + inserted);
if (! TRI_InsertStatementListAql(list, node, inserted + 0)) {
return;
}
// insert a dummy node in place of the moved node
list->_statements._buffer[j + inserted + 1] = TRI_GetDummyNopNodeAql();
// next
++j;
++inserted;
}
// moving statements from the middle to the beginning of the list will also
// change the list length and the position we'll be continuing from
n += inserted;
i = j + inserted;
}
}
}
++i;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add a statement to the front of the statement list
////////////////////////////////////////////////////////////////////////////////
bool TRI_InsertStatementListAql (TRI_aql_statement_list_t* const list,
TRI_aql_node_t* const node,
const size_t position) {
int result;
assert(list);
assert(node);
result = TRI_InsertVectorPointer(&list->_statements, node, position);
return result == TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief add a statement to the end of the statement list
////////////////////////////////////////////////////////////////////////////////
bool TRI_AppendStatementListAql (TRI_aql_statement_list_t* const list,
TRI_aql_node_t* const node) {
TRI_aql_node_type_e type; TRI_aql_node_type_e type;
int result; int result;

View File

@ -114,6 +114,13 @@ TRI_aql_node_t* TRI_GetDummyNopNodeAql (void);
TRI_aql_node_t* TRI_GetDummyReturnEmptyNodeAql (void); TRI_aql_node_t* TRI_GetDummyReturnEmptyNodeAql (void);
////////////////////////////////////////////////////////////////////////////////
/// @brief pull out subqueries in the statement list from the middle to the
/// beginning
////////////////////////////////////////////////////////////////////////////////
void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief remove all non-ops from the statement list /// @brief remove all non-ops from the statement list
/// ///
@ -131,11 +138,19 @@ size_t TRI_InvalidateStatementListAql (TRI_aql_statement_list_t* const,
const size_t); const size_t);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief add a statement to the statement list /// @brief insert a statement into the statement list
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool TRI_AddStatementListAql (TRI_aql_statement_list_t* const list, bool TRI_InsertStatementListAql (TRI_aql_statement_list_t* const,
TRI_aql_node_t* const); TRI_aql_node_t* const,
const size_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief add a statement to the end of the statement list
////////////////////////////////////////////////////////////////////////////////
bool TRI_AppendStatementListAql (TRI_aql_statement_list_t* const,
TRI_aql_node_t* const);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @} /// @}

View File

@ -0,0 +1,173 @@
////////////////////////////////////////////////////////////////////////////////
/// @brief tests for Ahuacatl, subqueries
///
/// @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 internal = require("internal");
var jsunity = require("jsunity");
////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////
function ahuacatlSubqueryTestSuite () {
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query
////////////////////////////////////////////////////////////////////////////////
function executeQuery (query, bindVars) {
var cursor = AHUACATL_RUN(query, bindVars);
return cursor;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief execute a given query and return the results as an array
////////////////////////////////////////////////////////////////////////////////
function getQueryResults (query, bindVars) {
var result = executeQuery(query, bindVars).getRows();
var results = [ ];
for (var i in result) {
if (!result.hasOwnProperty(i)) {
continue;
}
results.push(result[i]);
}
return results;
}
return {
////////////////////////////////////////////////////////////////////////////////
/// @brief set up
////////////////////////////////////////////////////////////////////////////////
setUp : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief tear down
////////////////////////////////////////////////////////////////////////////////
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery evaluation
////////////////////////////////////////////////////////////////////////////////
testSubqueryDependent1 : function () {
var expected = [ 2, 4, 6 ];
var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 1, 2, 3 ] RETURN i * 2) RETURN s[i - 1]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery evaluation
////////////////////////////////////////////////////////////////////////////////
testSubqueryDependent2 : function () {
var expected = [ 2, 4, 6 ];
var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET t = (FOR k IN [ 1, 2, 3 ] RETURN k) LET s = (FOR j IN [ 1, 2, 3 ] RETURN t[i - 1] * 2) RETURN s[i - 1]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery evaluation
////////////////////////////////////////////////////////////////////////////////
testSubqueryDependent3 : function () {
var expected = [ 4, 5, 6, 8, 10, 12, 12, 15, 18 ];
var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] FOR j IN [ 4, 5, 6 ] LET s = (FOR k IN [ 1 ] RETURN i * j) RETURN s[0]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery optimisation
////////////////////////////////////////////////////////////////////////////////
testSubqueryIndependent1 : function () {
var expected = [ 9, 8, 7 ];
var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 9, 8, 7 ] RETURN j) RETURN s[i - 1]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery optimisation
////////////////////////////////////////////////////////////////////////////////
testSubqueryIndependent2 : function () {
var expected = [ [ 9, 8, 7 ] ];
var actual = getQueryResults("LET s = (FOR j IN [ 9, 8, 7 ] RETURN j) RETURN s");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery optimisation
////////////////////////////////////////////////////////////////////////////////
testSubqueryIndependent3 : function () {
var expected = [ [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ], [ 25 ] ];
var actual = getQueryResults("FOR i IN [ 1, 2, 3 ] FOR j IN [ 1, 2, 3 ] LET s = (FOR k IN [ 25 ] RETURN k) RETURN s");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test subquery optimisation
////////////////////////////////////////////////////////////////////////////////
testSubqueryIndependent4 : function () {
var expected = [ [ 2, 4, 6 ] ];
var actual = getQueryResults("LET a = (FOR i IN [ 1, 2, 3 ] LET s = (FOR j IN [ 1, 2 ] RETURN j) RETURN i * s[1]) RETURN a");
assertEqual(expected, actual);
},
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////
jsunity.run(ahuacatlSubqueryTestSuite);
return jsunity.done();
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End: