1
0
Fork 0

issue #248: allow AQL optimiser to pull out completely uncorrelated subqueries to the top level, resulting in less repeated evaluation of the subquery

This commit is contained in:
Jan Steemann 2012-10-22 09:23:58 +02:00
parent 0499d8b4e2
commit 6d1201b2ec
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),
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
* issue #247: added AQL function MERGE_RECURSIVE

View File

@ -258,12 +258,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-refaccess-variable.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-variables.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-noncollection.js \
@top_srcdir@/js/server/tests/ahuacatl-subquery.js \
@top_srcdir@/js/server/tests/ahuacatl-operators.js
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,
const char* const value) {
if (!buffer) {
if (buffer == NULL) {
return true;
}
if (!value) {
if (value == NULL) {
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,
const char value) {
if (!buffer) {
if (buffer == NULL) {
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,
const uint64_t value) {
if (!buffer) {
if (buffer == NULL) {
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,
const int64_t value) {
if (!buffer) {
if (buffer == NULL) {
return true;
}
@ -201,7 +201,7 @@ static inline void ScopeOutput (TRI_aql_codegen_js_t* const generator,
const char* const value) {
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;
}
}
@ -214,7 +214,7 @@ static inline void ScopeOutputInt (TRI_aql_codegen_js_t* const generator,
const int64_t value) {
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;
}
}
@ -227,7 +227,7 @@ static inline void ScopeOutputUInt (TRI_aql_codegen_js_t* const generator,
const uint64_t value) {
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;
}
}
@ -240,13 +240,13 @@ static inline void ScopeOutputQuoted (TRI_aql_codegen_js_t* const generator,
const char* const value) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!OutputChar(scope->_buffer, '\'')) {
if (! OutputChar(scope->_buffer, '\'')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
if (!OutputString(scope->_buffer, value)) {
if (! OutputString(scope->_buffer, value)) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
if (!OutputChar(scope->_buffer, '\'')) {
if (! OutputChar(scope->_buffer, '\'')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
}
@ -261,13 +261,13 @@ static inline void ScopeOutputQuoted2 (TRI_aql_codegen_js_t* const generator,
char* escaped;
size_t outLength;
if (!OutputChar(scope->_buffer, '"')) {
if (! OutputChar(scope->_buffer, '"')) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
escaped = TRI_EscapeUtf8StringZ(TRI_UNKNOWN_MEM_ZONE, value, strlen(value), false, &outLength);
if (escaped) {
if (!OutputString(scope->_buffer, escaped)) {
if (! OutputString(scope->_buffer, escaped)) {
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;
}
if (!OutputChar(scope->_buffer, '"')) {
if (! OutputChar(scope->_buffer, '"')) {
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) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (!scope->_buffer) {
if (! scope->_buffer) {
return;
}
@ -321,10 +321,10 @@ static inline void ScopeOutputFunction (TRI_aql_codegen_js_t* const generator,
const TRI_aql_codegen_register_t functionIndex) {
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;
}
if (!OutputInt(scope->_buffer, (int64_t) functionIndex)) {
if (! OutputInt(scope->_buffer, (int64_t) functionIndex)) {
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) {
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;
}
if (!OutputInt(scope->_buffer, (int64_t) registerIndex)) {
if (! OutputInt(scope->_buffer, (int64_t) registerIndex)) {
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;
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;
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_variable_t* variable = CreateVariable(name, registerIndex);
if (!variable) {
if (variable == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return;
}
@ -2368,7 +2368,8 @@ char* TRI_GenerateCodeAql (TRI_aql_context_t* const context) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
}
}
// errorCode might have changed, no else!
if (generator->_errorCode != TRI_ERROR_NO_ERROR) {
// register the error
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;
for (i = 0; i < n; ++i) {
TRI_aql_collection_t* collection = context->_collections._buffer[i];
char* name;
assert(collection);
assert(collection->_name);
@ -155,11 +154,10 @@ bool OpenCollections (TRI_aql_context_t* const context) {
assert(!collection->_readLocked);
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;
}
}
@ -341,7 +339,7 @@ void TRI_UnlockCollectionsAql (TRI_aql_context_t* const context) {
assert(collection);
assert(collection->_name);
if (!collection->_collection) {
if (collection->_collection == NULL) {
// collection not yet opened
continue;
}

View File

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

View File

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

View File

@ -212,7 +212,7 @@ for_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -225,7 +225,7 @@ filter_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -238,7 +238,7 @@ let_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -259,7 +259,7 @@ collect_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -310,7 +310,7 @@ sort_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -359,11 +359,11 @@ limit_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -373,7 +373,7 @@ limit_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
}
@ -386,7 +386,7 @@ return_statement:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (!TRI_AppendStatementListAql(context->_statements, node)) {
ABORT_OOM
}
@ -422,7 +422,7 @@ expression:
ABORT_OOM
}
if (!TRI_AddStatementListAql(context->_statements, subQuery)) {
if (!TRI_AppendStatementListAql(context->_statements, subQuery)) {
ABORT_OOM
}
@ -767,7 +767,7 @@ reference:
// push the actual expand node into the statement list
expand = TRI_CreateNodeExpandAql(context, varname, expanded, $4);
if (!TRI_AddStatementListAql(context->_statements, expand)) {
if (!TRI_AppendStatementListAql(context->_statements, expand)) {
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
@ -69,6 +92,34 @@ static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const,
/// @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
@ -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,
TRI_aql_node_t* node) {
TRI_aql_context_t* context;
aql_optimiser_t* optimiser;
if (node->_type != TRI_AQL_NODE_COLLECTION) {
return node;
}
context = (TRI_aql_context_t*) walker->_data;
optimiser = (aql_optimiser_t*) walker->_data;
AttachCollectionHint(context, node);
AttachCollectionHint(optimiser->_context, 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,
@ -342,9 +393,9 @@ static TRI_aql_node_t* OptimiseFor (TRI_aql_statement_walker_t* const walker,
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,
@ -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) {
@ -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,
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);
while (true) {
@ -431,7 +482,7 @@ static TRI_aql_node_t* OptimiseFilter (TRI_aql_statement_walker_t* const walker,
oldRanges = TRI_GetCurrentRangesStatementWalkerAql(walker);
changed = false;
newRanges = TRI_OptimiseRangesAql(context, expression, &changed, oldRanges);
newRanges = TRI_OptimiseRangesAql(optimiser->_context, expression, &changed, oldRanges);
if (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,
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);
@ -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) {
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;
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
////////////////////////////////////////////////////////////////////////////////
static bool OptimiseAst (TRI_aql_context_t* const context) {
static bool OptimiseAst (aql_optimiser_t* const optimiser) {
TRI_aql_statement_walker_t* walker;
walker = TRI_CreateStatementWalkerAql((void*) context,
walker = TRI_CreateStatementWalkerAql((void*) optimiser,
true,
&OptimiseNode,
NULL,
&ProcessStatement);
if (walker == NULL) {
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
TRI_SetErrorContextAql(optimiser->_context, TRI_ERROR_OUT_OF_MEMORY, NULL);
return false;
}
TRI_WalkStatementsAql(walker, context->_statements);
TRI_WalkStatementsAql(walker, optimiser->_context->_statements);
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
////////////////////////////////////////////////////////////////////////////////
static bool DetermineIndexes (TRI_aql_context_t* const context) {
static bool DetermineIndexes (aql_optimiser_t* const optimiser) {
TRI_aql_statement_walker_t* walker;
walker = TRI_CreateStatementWalkerAql((void*) context,
walker = TRI_CreateStatementWalkerAql((void*) optimiser,
false,
&AnnotateNode,
NULL,
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;
}
TRI_WalkStatementsAql(walker, context->_statements);
TRI_WalkStatementsAql(walker, optimiser->_context->_statements);
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) {
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;
}
if (!DetermineIndexes(context)) {
return false;
}
result = (OptimiseAst(optimiser) && DetermineIndexes(optimiser));
FreeOptimiser(optimiser);
return true;
return result;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -46,19 +46,6 @@
extern "C" {
#endif
// -----------------------------------------------------------------------------
// --SECTION-- public types
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Ahuacatl
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --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->_type = NextType(context, type);
scope->_ranges = NULL;
scope->_selfContained = true;
TRI_InitAssociativePointer(&scope->_variables,
TRI_UNKNOWN_MEM_ZONE,
@ -250,7 +251,7 @@ bool TRI_StartScopeAql (TRI_aql_context_t* const context, const TRI_aql_scope_e
return false;
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false;
}
@ -279,7 +280,7 @@ bool TRI_EndScopeAql (TRI_aql_context_t* const context) {
return false;
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false;
}
@ -318,7 +319,7 @@ bool TRI_EndScopeByReturnAql (TRI_aql_context_t* const context) {
return false;
}
if (!TRI_AddStatementListAql(context->_statements, node)) {
if (! TRI_AppendStatementListAql(context->_statements, node)) {
return false;
}
@ -388,7 +389,7 @@ bool TRI_AddVariableScopeAql (TRI_aql_context_t* const 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;
}

View File

@ -68,6 +68,7 @@ typedef struct TRI_aql_scope_s {
TRI_associative_pointer_t _variables;
TRI_vector_pointer_t* _ranges;
TRI_aql_scope_e _type;
bool _selfContained;
size_t _id;
}
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
///
/// 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,
@ -253,7 +259,14 @@ TRI_aql_variable_t* TRI_GetVariableStatementWalkerAql (TRI_aql_statement_walker_
if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// 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

View File

@ -26,6 +26,9 @@
////////////////////////////////////////////////////////////////////////////////
#include "Ahuacatl/ahuacatl-statementlist.h"
#include "BasicsC/logging.h"
#include "Ahuacatl/ahuacatl-ast-node.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,
TRI_aql_node_t* const node) {
void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const list) {
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;
int result;

View File

@ -114,6 +114,13 @@ TRI_aql_node_t* TRI_GetDummyNopNodeAql (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
///
@ -131,11 +138,19 @@ size_t TRI_InvalidateStatementListAql (TRI_aql_statement_list_t* const,
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,
TRI_aql_node_t* const);
bool TRI_InsertStatementListAql (TRI_aql_statement_list_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: