1
0
Fork 0

defer evaluation of subqueries

This commit is contained in:
Jan Steemann 2014-01-10 16:42:46 +01:00
parent d5383d2be8
commit dc8fdf8ca0
8 changed files with 352 additions and 166 deletions

View File

@ -1,6 +1,8 @@
v1.4.5-rc1 (XXXX-XX-XX)
-------------------
* defer evaluation of AQL subqueries and logical operators (lazy evaluation)
* generally allow function return values as call parameters to AQL functions
* fixed potential deadlock in global context method execution

View File

@ -94,6 +94,124 @@ static void ProcessNode (TRI_aql_codegen_js_t* const,
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief free a scope variable
////////////////////////////////////////////////////////////////////////////////
static void FreeVariable (TRI_aql_codegen_variable_t* const variable) {
assert(variable);
assert(variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a scope variable
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_variable_t* CreateVariable (const char* const name,
const TRI_aql_codegen_register_t registerIndex,
bool isCachedSubquery) {
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_variable_t), false);
if (variable == NULL) {
return NULL;
}
variable->_register = registerIndex;
variable->_name = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, name);
variable->_isCachedSubquery = isCachedSubquery;
if (variable->_name == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
return NULL;
}
return variable;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the current scope
////////////////////////////////////////////////////////////////////////////////
static inline TRI_aql_codegen_scope_t* CurrentScope (TRI_aql_codegen_js_t* const generator) {
size_t n;
if (generator->_errorCode != TRI_ERROR_NO_ERROR) {
return NULL;
}
n = generator->_scopes._length;
if (n == 0) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return NULL;
}
return (TRI_aql_codegen_scope_t*) TRI_AtVectorPointer(&generator->_scopes, n - 1);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief enter a variable into the symbol table
////////////////////////////////////////////////////////////////////////////////
static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name,
const TRI_aql_codegen_register_t registerIndex,
bool isCachedSubquery) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_variable_t* variable;
if (scope == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return;
}
variable = CreateVariable(name, registerIndex, isCachedSubquery);
if (variable == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return;
}
if (TRI_InsertKeyAssociativePointer(&scope->_variables, name, (void*) variable, false) != NULL) {
// variable already exists in symbol table. this should never happen
LOG_TRACE("variable already registered: %s", name);
generator->_errorCode = TRI_ERROR_QUERY_VARIABLE_REDECLARED;
generator->_errorValue = (char*) name;
FreeVariable(variable);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a variable in the symbol table
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_variable_t* LookupVariable (TRI_aql_codegen_js_t* const generator,
const char* const name) {
size_t i = generator->_scopes._length;
assert(name);
// iterate from current scope to the top level scope
while (i-- > 0) {
TRI_aql_codegen_scope_t* scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_LookupByKeyAssociativePointer(&scope->_variables, name);
// variable found in scope
if (variable != NULL) {
return variable;
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief hash a variable
////////////////////////////////////////////////////////////////////////////////
@ -133,26 +251,6 @@ static inline TRI_aql_codegen_register_t IncFunction (TRI_aql_codegen_js_t* cons
return ++generator->_functionIndex;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the current scope
////////////////////////////////////////////////////////////////////////////////
static inline TRI_aql_codegen_scope_t* CurrentScope (TRI_aql_codegen_js_t* const generator) {
size_t n;
if (generator->_errorCode != TRI_ERROR_NO_ERROR) {
return NULL;
}
n = generator->_scopes._length;
if (n == 0) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return NULL;
}
return (TRI_aql_codegen_scope_t*) TRI_AtVectorPointer(&generator->_scopes, n - 1);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief append a string to the buffer
////////////////////////////////////////////////////////////////////////////////
@ -387,6 +485,41 @@ static inline void ScopeOutputRegister (TRI_aql_codegen_js_t* const generator,
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief print a register name in the current scope
////////////////////////////////////////////////////////////////////////////////
static void ScopeOutputSymbol (TRI_aql_codegen_js_t* const generator,
char const* name) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_variable_t* variable;
if (scope == NULL) {
return;
}
variable = LookupVariable(generator, name);
if (variable == NULL) {
generator->_errorCode = TRI_ERROR_QUERY_VARIABLE_NAME_UNKNOWN;
generator->_errorValue = (char*) name;
return;
}
if (variable->_isCachedSubquery) {
ScopeOutput(generator, "(");
ScopeOutputRegister(generator, variable->_register);
ScopeOutput(generator, ".executed ? ");
ScopeOutputRegister(generator, variable->_register);
ScopeOutput(generator, ".data : ");
ScopeOutputRegister(generator, variable->_register);
ScopeOutput(generator, ".execute())");
}
else {
ScopeOutputRegister(generator, variable->_register);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for indexed register access
////////////////////////////////////////////////////////////////////////////////
@ -400,44 +533,6 @@ static void ScopeOutputIndexedName (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, "]");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a scope variable
////////////////////////////////////////////////////////////////////////////////
static void FreeVariable (TRI_aql_codegen_variable_t* const variable) {
assert(variable);
assert(variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable->_name);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a scope variable
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_variable_t* CreateVariable (const char* const name,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_codegen_variable_t), false);
if (variable == NULL) {
return NULL;
}
variable->_register = registerIndex;
variable->_name = TRI_DuplicateStringZ(TRI_UNKNOWN_MEM_ZONE, name);
if (variable->_name == NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, variable);
return NULL;
}
return variable;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the type for the next scope
////////////////////////////////////////////////////////////////////////////////
@ -510,16 +605,17 @@ static void StartScope (TRI_aql_codegen_js_t* const generator,
}
}
scope->_buffer = buffer;
scope->_type = NextScopeType(generator, type);
scope->_listRegister = listRegister;
scope->_keyRegister = keyRegister;
scope->_ownRegister = ownRegister;
scope->_offsetRegister = offsetRegister;
scope->_limitRegister = limitRegister;
scope->_resultRegister = resultRegister;
scope->_prefix = NULL;
scope->_hint = hint; // for-loop hint
scope->_buffer = buffer;
scope->_type = NextScopeType(generator, type);
scope->_listRegister = listRegister;
scope->_keyRegister = keyRegister;
scope->_ownRegister = ownRegister;
scope->_offsetRegister = offsetRegister;
scope->_limitRegister = limitRegister;
scope->_resultRegister = resultRegister;
scope->_subqueryRegister = 0;
scope->_prefix = NULL;
scope->_hint = hint; // for-loop hint
// init symbol table
res = TRI_InitAssociativePointer(&scope->_variables,
@ -763,69 +859,6 @@ static void InitArray (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, " = { };\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief enter a variable into the symbol table
////////////////////////////////////////////////////////////////////////////////
static void EnterSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name,
const TRI_aql_codegen_register_t registerIndex) {
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
TRI_aql_codegen_variable_t* variable;
if (scope == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return;
}
variable = CreateVariable(name, registerIndex);
if (variable == NULL) {
generator->_errorCode = TRI_ERROR_OUT_OF_MEMORY;
return;
}
if (TRI_InsertKeyAssociativePointer(&scope->_variables, name, (void*) variable, false) != NULL) {
// variable already exists in symbol table. this should never happen
LOG_TRACE("variable already registered: %s", name);
generator->_errorCode = TRI_ERROR_QUERY_VARIABLE_REDECLARED;
generator->_errorValue = (char*) name;
FreeVariable(variable);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a variable in the symbol table
////////////////////////////////////////////////////////////////////////////////
static TRI_aql_codegen_register_t LookupSymbol (TRI_aql_codegen_js_t* const generator,
const char* const name) {
size_t i = generator->_scopes._length;
assert(name);
// iterate from current scope to the top level scope
while (i-- > 0) {
TRI_aql_codegen_scope_t* scope = (TRI_aql_codegen_scope_t*) generator->_scopes._buffer[i];
TRI_aql_codegen_variable_t* variable;
variable = (TRI_aql_codegen_variable_t*) TRI_LookupByKeyAssociativePointer(&scope->_variables, name);
// variable found in scope
if (variable) {
// return the variable register
return variable->_register;
}
}
// variable not found. this should never happen
LOG_TRACE("variable not found: %s", name);
generator->_errorCode = TRI_ERROR_QUERY_VARIABLE_NAME_UNKNOWN;
generator->_errorValue = (char*) name;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief start a for loop
////////////////////////////////////////////////////////////////////////////////
@ -861,7 +894,7 @@ static void StartFor (TRI_aql_codegen_js_t* const generator,
}
if (variableName) {
EnterSymbol(generator, variableName, ownRegister);
EnterSymbol(generator, variableName, ownRegister, false);
}
ScopeOutput(generator, "var ");
@ -1135,7 +1168,7 @@ static void RestoreSymbols (TRI_aql_codegen_js_t* const generator,
// re-enter symbols
if (variableName) {
EnterSymbol(generator, variableName, registerIndex);
EnterSymbol(generator, variableName, registerIndex, false);
}
// unpack variables from current scope's iterator register
@ -1188,7 +1221,7 @@ static void GeneratePrimaryAccess (TRI_aql_codegen_js_t* const generator,
fieldAccess->_value._reference._operator == TRI_AQL_NODE_OPERATOR_BINARY_IN);
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1240,7 +1273,7 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, ", ");
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1278,7 +1311,7 @@ static void GenerateHashAccess (TRI_aql_codegen_js_t* const generator,
fieldAccess->_value._reference._operator == TRI_AQL_NODE_OPERATOR_BINARY_IN);
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1331,7 +1364,7 @@ static void GenerateEdgeAccess (TRI_aql_codegen_js_t* const generator,
fieldAccess->_value._reference._operator == TRI_AQL_NODE_OPERATOR_BINARY_IN);
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1383,7 +1416,7 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1);
ScopeOutput(generator, ", ");
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1448,7 +1481,7 @@ static void GenerateSkiplistAccess (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, "\", ");
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1504,7 +1537,7 @@ static void GenerateBitarrayAccess (TRI_aql_codegen_js_t* const generator,
ScopeOutputQuoted2(generator, fieldAccess->_fullName + fieldAccess->_variableNameLength + 1);
ScopeOutput(generator, ", ");
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1545,7 +1578,7 @@ static void GenerateBitarrayAccess (TRI_aql_codegen_js_t* const generator,
case TRI_AQL_ACCESS_REFERENCE: {
if (fieldAccess->_value._reference._type == TRI_AQL_REFERENCE_VARIABLE) {
ScopeOutputRegister(generator, LookupSymbol(generator, fieldAccess->_value._reference._ref._name));
ScopeOutputSymbol(generator, fieldAccess->_value._reference._ref._name);
}
else {
ProcessAttributeAccess(generator, fieldAccess->_value._reference._ref._node);
@ -1589,8 +1622,7 @@ static void ProcessReference (TRI_aql_codegen_js_t* const generator,
}
else {
// regular scope, we are using a register
TRI_aql_codegen_register_t registerIndex = LookupSymbol(generator, name);
ScopeOutputRegister(generator, registerIndex);
ScopeOutputSymbol(generator, name);
}
}
@ -1887,7 +1919,7 @@ static void ProcessExpand (TRI_aql_codegen_js_t* const generator,
// for
StartFor(generator, scope->_buffer, sourceRegister, isList, NULL, NULL);
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode1), CurrentScope(generator)->_ownRegister);
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode1), CurrentScope(generator)->_ownRegister, false);
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, ".push(");
@ -1901,7 +1933,7 @@ static void ProcessExpand (TRI_aql_codegen_js_t* const generator,
EndScope(generator);
// register the variable for the result
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode2), resultRegister);
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode2), resultRegister, false);
}
////////////////////////////////////////////////////////////////////////////////
@ -2142,24 +2174,59 @@ static void ProcessTernary (TRI_aql_codegen_js_t* const generator,
static void ProcessSubquery (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_register_t subQueryRegister;
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
// get the subquery scope's result register
subQueryRegister = generator->_lastResultRegister;
if (scope == NULL) {
return;
}
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = ");
ScopeOutputRegister(generator, subQueryRegister);
if (scope->_subqueryRegister > 0) {
ScopeOutputRegister(generator, scope->_subqueryRegister);
#if AQL_VERBOSE
ScopeOutput(generator, "; /* subquery */\n");
ScopeOutput(generator, "(); /* subquery */\n");
#else
ScopeOutput(generator, ";\n");
ScopeOutput(generator, "();\n");
#endif
}
else {
// subquery has been optimised away
ScopeOutput(generator, "[ ];\n");
}
EnterSymbol(generator, nameNode->_value._value._string, resultRegister, false);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief generate code for cached subqueries (nested fors etc.)
////////////////////////////////////////////////////////////////////////////////
static void ProcessSubqueryCached (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_codegen_register_t resultRegister = IncRegister(generator);
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
TRI_aql_codegen_scope_t* scope = CurrentScope(generator);
if (scope == NULL) {
return;
}
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, " = { data: null, executed: false, fn: ");
ScopeOutputRegister(generator, scope->_subqueryRegister);
#if AQL_VERBOSE
ScopeOutput(generator, ", execute: function () { this.executed = true; return this.data = this.fn(); } }; /* subquery */\n");
#else
ScopeOutput(generator, ", execute: function () { this.executed = true; return this.data = this.fn(); } };\n");
#endif
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
EnterSymbol(generator, nameNode->_value._value._string, resultRegister, true);
}
////////////////////////////////////////////////////////////////////////////////
@ -2197,11 +2264,24 @@ static void ProcessScopeStart (TRI_aql_codegen_js_t* const generator,
TRI_aql_codegen_register_t resultRegister;
TRI_aql_codegen_register_t sourceRegister;
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
TRI_aql_codegen_scope_t* s;
if (scope->_type != TRI_AQL_SCOPE_SUBQUERY) {
return;
}
s = CurrentScope(generator);
if (s == NULL) {
return;
}
s->_subqueryRegister = IncRegister(generator);
ScopeOutput(generator, "var ");
ScopeOutputRegister(generator, s->_subqueryRegister);
ScopeOutput(generator, " = function() {\n");
resultRegister = IncRegister(generator);
InitList(generator, resultRegister);
StartScope(generator, &generator->_buffer, TRI_AQL_SCOPE_SUBQUERY, 0, 0, 0, resultRegister, NULL);
@ -2226,12 +2306,18 @@ static void ProcessScopeStart (TRI_aql_codegen_js_t* const generator,
static void ProcessScopeEnd (TRI_aql_codegen_js_t* const generator,
const TRI_aql_node_t* const node) {
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
TRI_aql_codegen_register_t resultRegister;
if (scope->_type != TRI_AQL_SCOPE_SUBQUERY) {
return;
}
resultRegister = CurrentScope(generator)->_resultRegister;
EndScope(generator);
ScopeOutput(generator, "return ");
ScopeOutputRegister(generator, resultRegister);
ScopeOutput(generator, ";\n};\n");
}
////////////////////////////////////////////////////////////////////////////////
@ -2444,7 +2530,7 @@ static void ProcessCollect (TRI_aql_codegen_js_t* const generator,
ScopeOutputIndexedName(generator, scope->_ownRegister, TRI_AQL_NODE_STRING(varNode));
ScopeOutput(generator, ";\n");
EnterSymbol(generator, TRI_AQL_NODE_STRING(varNode), varRegister);
EnterSymbol(generator, TRI_AQL_NODE_STRING(varNode), varRegister, false);
}
// re-enter symbol for into
@ -2459,7 +2545,7 @@ static void ProcessCollect (TRI_aql_codegen_js_t* const generator,
ScopeOutputIndexedName(generator, scope->_ownRegister, TRI_AQL_NODE_STRING(nameNode));
ScopeOutput(generator, ";\n");
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode), intoRegister);
EnterSymbol(generator, TRI_AQL_NODE_STRING(nameNode), intoRegister, false);
}
#if AQL_VERBOSE
@ -2560,8 +2646,6 @@ static void ProcessReturn (TRI_aql_codegen_js_t* const generator,
ScopeOutputRegister(generator, rowRegister);
ScopeOutput(generator, ");\n");
generator->_lastResultRegister = scope->_resultRegister;
// }
CloseLoops(generator);
}
@ -2583,8 +2667,6 @@ static void ProcessReturnEmpty (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, " = [ ];\n");
#endif
generator->_lastResultRegister = resultRegister;
// }
CloseLoops(generator);
}
@ -2606,7 +2688,7 @@ static void ProcessLet (TRI_aql_codegen_js_t* const generator,
ScopeOutput(generator, ";\n");
// enter the new variable in the symbol table
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
EnterSymbol(generator, nameNode->_value._value._string, resultRegister, false);
}
////////////////////////////////////////////////////////////////////////////////
@ -2643,7 +2725,7 @@ static void ProcessAssign (TRI_aql_codegen_js_t* const generator,
}
// enter the new variable in the symbol table
EnterSymbol(generator, nameNode->_value._value._string, resultRegister);
EnterSymbol(generator, nameNode->_value._value._string, resultRegister, false);
}
////////////////////////////////////////////////////////////////////////////////
@ -2827,6 +2909,9 @@ static void ProcessNode (TRI_aql_codegen_js_t* const generator, const TRI_aql_no
case TRI_AQL_NODE_SUBQUERY:
ProcessSubquery(generator, node);
break;
case TRI_AQL_NODE_SUBQUERY_CACHED:
ProcessSubqueryCached(generator, node);
break;
case TRI_AQL_NODE_SCOPE_START:
ProcessScopeStart(generator, node);
break;

View File

@ -66,6 +66,7 @@ typedef uint32_t TRI_aql_codegen_register_t;
typedef struct TRI_aql_codegen_variable_s {
char* _name; // variable name
TRI_aql_codegen_register_t _register; // the assigned register
bool _isCachedSubquery;
}
TRI_aql_codegen_variable_t;
@ -82,6 +83,7 @@ typedef struct TRI_aql_codegen_scope_s {
TRI_aql_codegen_register_t _resultRegister;
TRI_aql_codegen_register_t _offsetRegister; // limit offset, limit
TRI_aql_codegen_register_t _limitRegister; // limit offset, limit
TRI_aql_codegen_register_t _subqueryRegister;
TRI_associative_pointer_t _variables; // list of variables in scope
char* _prefix; // prefix for variable names, used in FUNCTION scopes only
TRI_aql_for_hint_t* _hint; // generic hint
@ -97,7 +99,6 @@ typedef struct TRI_aql_codegen_js_s {
TRI_string_buffer_t _buffer;
TRI_string_buffer_t _functionBuffer;
TRI_vector_pointer_t _scopes;
TRI_aql_codegen_register_t _lastResultRegister;
TRI_aql_codegen_register_t _registerIndex;
TRI_aql_codegen_register_t _functionIndex;
int _errorCode; // error number

View File

@ -292,7 +292,8 @@ static TRI_aql_node_t* ProcessStatement (TRI_aql_statement_walker_t* const walke
break;
}
case TRI_AQL_NODE_SUBQUERY: {
case TRI_AQL_NODE_SUBQUERY:
case TRI_AQL_NODE_SUBQUERY_CACHED: {
TRI_json_t* row;
row = GetRowProtoType(explain, node->_type);

View File

@ -40,6 +40,7 @@ bool TRI_IsTopLevelTypeAql (const TRI_aql_node_type_e type) {
if (type == TRI_AQL_NODE_SCOPE_START ||
type == TRI_AQL_NODE_SCOPE_END ||
type == TRI_AQL_NODE_SUBQUERY ||
type == TRI_AQL_NODE_SUBQUERY_CACHED ||
type == TRI_AQL_NODE_EXPAND ||
type == TRI_AQL_NODE_FOR ||
type == TRI_AQL_NODE_FILTER ||
@ -165,6 +166,8 @@ const char* TRI_NodeNameAql (const TRI_aql_node_type_e type) {
return "ternary";
case TRI_AQL_NODE_SUBQUERY:
return "subquery";
case TRI_AQL_NODE_SUBQUERY_CACHED:
return "subquery (cached)";
case TRI_AQL_NODE_ATTRIBUTE_ACCESS:
return "attribute access";
case TRI_AQL_NODE_BOUND_ATTRIBUTE_ACCESS:

View File

@ -171,6 +171,7 @@ typedef enum {
TRI_AQL_NODE_OPERATOR_BINARY_IN,
TRI_AQL_NODE_OPERATOR_TERNARY,
TRI_AQL_NODE_SUBQUERY,
TRI_AQL_NODE_SUBQUERY_CACHED,
TRI_AQL_NODE_ATTRIBUTE_ACCESS,
TRI_AQL_NODE_BOUND_ATTRIBUTE_ACCESS,
TRI_AQL_NODE_INDEXED,

View File

@ -228,6 +228,8 @@ void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const list) {
size_t j = moveStart;
size_t inserted = 0;
node->_type = TRI_AQL_NODE_SUBQUERY_CACHED;
// moving statements from the middle to the beginning of the list will also
// modify the positions we're moving from
while (j < i + 2) {

View File

@ -51,6 +51,97 @@ function ahuacatlQueryNonCollectionTestSuite () {
tearDown : function () {
},
////////////////////////////////////////////////////////////////////////////////
/// @brief multiple subqueries
////////////////////////////////////////////////////////////////////////////////
testMultipleSubqueries1 : function () {
var expected = [ [ [ 2, 4 ], [ 6, 8 ] ] ];
var actual = getQueryResults("LET a = (FOR i IN [ 1, 2 ] RETURN i * 2), b = (FOR i IN [ 3, 4 ] RETURN i * 2) RETURN [ a, b ]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief multiple subqueries
////////////////////////////////////////////////////////////////////////////////
testMultipleSubqueries2 : function () {
var expected = [ [ [ 2, 4 ], [ 6, 8 ] ] ];
var actual = getQueryResults("LET a = (FOR i IN [ 1, 2 ] RETURN i * 2) LET b = (FOR i IN [ 3, 4 ] RETURN i * 2) RETURN [ a, b ]");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval1 : function () {
var expected = [ ];
var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year > 2013 && FAIL() RETURN year");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval2 : function () {
var expected = [ ];
var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year > 2013 RETURN FAIL()");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval3 : function () {
var expected = [ ];
var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year > 2013 && FAIL() RETURN FAIL()");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval4 : function () {
var expected = [ ];
var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year > 2020 LET x = (FOR i IN [ 1, 2 ] RETURN FAIL()) RETURN year");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval5 : function () {
var expected = [ ];
var actual = getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year > 2020 LET x = (FOR i IN [ 1, 2 ] RETURN FAIL()) RETURN FAIL()");
assertEqual(expected, actual);
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval6 : function () {
assertException(function() { getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year < 2020 RETURN FAIL()"); });
},
////////////////////////////////////////////////////////////////////////////////
/// @brief test deferred evaluation
////////////////////////////////////////////////////////////////////////////////
testDeferredEval7 : function () {
assertException(function() { getQueryResults("FOR year IN [ 2010, 2011, 2012 ] FILTER year < 2020 LET x = (FOR i IN [ 1, 2 ] RETURN FAIL()) RETURN year") });
},
////////////////////////////////////////////////////////////////////////////////
/// @brief return a single value from a list
////////////////////////////////////////////////////////////////////////////////