mirror of https://gitee.com/bigwinds/arangodb
490 lines
14 KiB
C++
490 lines
14 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Ahuacatl, scopes
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 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 ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Jan Steemann
|
|
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
|
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Ahuacatl/ahuacatl-scope.h"
|
|
#include "Ahuacatl/ahuacatl-access-optimiser.h"
|
|
#include "Ahuacatl/ahuacatl-variable.h"
|
|
|
|
#include "Basics/logging.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the current scope
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline TRI_aql_scope_t* CurrentScope (TRI_aql_context_t* const context) {
|
|
TRI_aql_scope_t* scope;
|
|
size_t n;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
n = context->_currentScopes._length;
|
|
TRI_ASSERT(n > 0);
|
|
|
|
scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, n - 1);
|
|
TRI_ASSERT(scope);
|
|
|
|
return scope;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the current type of a scope
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline TRI_aql_scope_e CurrentType (TRI_aql_context_t* const context) {
|
|
return CurrentScope(context)->_type;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the type for the next scope
|
|
///
|
|
/// this returns TRI_AQL_SCOPE_FOR_NESTED for a nested for scope, and the
|
|
/// originally requested type in all other cases
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_scope_e NextType (TRI_aql_context_t* const context,
|
|
const TRI_aql_scope_e requestedType) {
|
|
TRI_aql_scope_e current;
|
|
|
|
// we're only interested in TRI_AQL_SCOPE_FOR
|
|
if (requestedType != TRI_AQL_SCOPE_FOR) {
|
|
return requestedType;
|
|
}
|
|
|
|
current = CurrentType(context);
|
|
|
|
// if we are in a TRI_AQL_SCOPE_FOR scope, we'll return TRI_AQL_SCOPE_FOR_NESTED
|
|
if (current == TRI_AQL_SCOPE_FOR || current == TRI_AQL_SCOPE_FOR_NESTED) {
|
|
return TRI_AQL_SCOPE_FOR_NESTED;
|
|
}
|
|
|
|
return requestedType;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create a scope
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_scope_t* CreateScope (TRI_aql_context_t* const context,
|
|
const TRI_aql_scope_e type) {
|
|
TRI_aql_scope_t* scope;
|
|
int res;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
scope = (TRI_aql_scope_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_scope_t), false);
|
|
|
|
if (scope == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
scope->_type = NextType(context, type);
|
|
scope->_ranges = NULL;
|
|
scope->_selfContained = true;
|
|
scope->_empty = false;
|
|
scope->_level = 0; // init with a dummy value
|
|
|
|
scope->_limit._offset = 0;
|
|
scope->_limit._limit = INT64_MAX;
|
|
scope->_limit._status = TRI_AQL_LIMIT_UNDEFINED;
|
|
scope->_limit._hasFilter = false;
|
|
scope->_limit._found = 0;
|
|
|
|
if (context->_fullCount) {
|
|
// if option "fullCount" is specified, we must ignore all limit optimisations
|
|
scope->_limit._status = TRI_AQL_LIMIT_IGNORE;
|
|
}
|
|
|
|
|
|
TRI_InitVectorPointer(&scope->_sorts, TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
res = TRI_InitAssociativePointer(&scope->_variables,
|
|
TRI_UNKNOWN_MEM_ZONE,
|
|
&TRI_HashStringKeyAssociativePointer,
|
|
&TRI_HashVariableAql,
|
|
&TRI_EqualVariableAql,
|
|
0);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_DestroyVectorPointer(&scope->_sorts);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, scope);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return scope;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free a scope
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void FreeScope (TRI_aql_scope_t* const scope) {
|
|
size_t i, n;
|
|
|
|
// free variables lookup hash
|
|
n = scope->_variables._nrAlloc;
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_variable_t* variable
|
|
= static_cast<TRI_aql_variable_t*>(scope->_variables._table[i]);
|
|
|
|
if (variable) {
|
|
TRI_FreeVariableAql(variable);
|
|
}
|
|
}
|
|
|
|
TRI_DestroyAssociativePointer(&scope->_variables);
|
|
|
|
if (scope->_ranges) {
|
|
// free ranges if set
|
|
TRI_FreeAccessesAql(scope->_ranges);
|
|
}
|
|
|
|
for (i = 0; i < scope->_sorts._length; ++i) {
|
|
char* criterion = (char*) TRI_AtVectorPointer(&scope->_sorts, i);
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, criterion);
|
|
}
|
|
|
|
TRI_DestroyVectorPointer(&scope->_sorts);
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, scope);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns a variable if it is defined in the current scope or above
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_variable_t* GetVariableInScope (TRI_aql_context_t* const context,
|
|
const char* const name) {
|
|
size_t n;
|
|
|
|
if (name == nullptr) {
|
|
TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, nullptr);
|
|
return nullptr;
|
|
}
|
|
|
|
n = context->_currentScopes._length;
|
|
TRI_ASSERT(n > 0);
|
|
|
|
while (n > 0) {
|
|
void* result;
|
|
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_currentScopes, --n);
|
|
TRI_ASSERT(scope);
|
|
|
|
result = TRI_LookupByKeyAssociativePointer(&scope->_variables, (void*) name);
|
|
|
|
if (result != nullptr) {
|
|
// duplicate variable
|
|
return static_cast<TRI_aql_variable_t*>(result);
|
|
}
|
|
|
|
if (n == 0) {
|
|
// reached the outermost scope
|
|
break;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create a hint for an AQL for
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_for_hint_t* TRI_CreateForHintScopeAql (TRI_aql_context_t* const context) {
|
|
TRI_aql_for_hint_t* hint;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
hint = (TRI_aql_for_hint_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_for_hint_t), false);
|
|
if (hint == NULL) {
|
|
TRI_SetErrorContextAql(__FILE__, __LINE__, context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
// initialise
|
|
hint->_limit._offset = 0;
|
|
hint->_limit._limit = 0;
|
|
hint->_limit._status = TRI_AQL_LIMIT_UNDEFINED;
|
|
hint->_isIncremental = false;
|
|
|
|
return hint;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free a hint for an AQL for
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeForHintScopeAql (TRI_aql_for_hint_t* const hint) {
|
|
TRI_ASSERT(hint);
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, hint);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the name of a scope type
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const char* TRI_TypeNameScopeAql (const TRI_aql_scope_e type) {
|
|
switch (type) {
|
|
case TRI_AQL_SCOPE_FOR:
|
|
return "for";
|
|
case TRI_AQL_SCOPE_FOR_NESTED:
|
|
return "for nested";
|
|
case TRI_AQL_SCOPE_SUBQUERY:
|
|
return "subquery";
|
|
case TRI_AQL_SCOPE_EXPAND:
|
|
return "expand";
|
|
case TRI_AQL_SCOPE_MAIN:
|
|
return "main";
|
|
case TRI_AQL_SCOPE_FUNCTION:
|
|
return "function";
|
|
}
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief init scopes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_InitScopesAql (TRI_aql_context_t* const context) {
|
|
TRI_ASSERT(context);
|
|
|
|
TRI_InitVectorPointer(&context->_memory._scopes, TRI_UNKNOWN_MEM_ZONE);
|
|
TRI_InitVectorPointer(&context->_currentScopes, TRI_UNKNOWN_MEM_ZONE);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free all scopes
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeScopesAql (TRI_aql_context_t* const context) {
|
|
size_t i, n;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
n = context->_memory._scopes._length;
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&context->_memory._scopes, i);
|
|
|
|
FreeScope(scope);
|
|
}
|
|
|
|
TRI_DestroyVectorPointer(&context->_memory._scopes);
|
|
TRI_DestroyVectorPointer(&context->_currentScopes);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief add a new scope
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_StartScopeAql (TRI_aql_context_t* const context, const TRI_aql_scope_e type) {
|
|
TRI_aql_scope_t* scope;
|
|
TRI_aql_node_t* node;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
scope = CreateScope(context, type);
|
|
|
|
if (scope == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// set real scope level
|
|
scope->_level = (size_t) context->_memory._scopes._length;
|
|
|
|
LOG_TRACE("starting scope of type %s", TRI_TypeNameScopeAql(scope->_type));
|
|
|
|
if (TRI_ERROR_NO_ERROR != TRI_PushBackVectorPointer(&context->_memory._scopes, (void*) scope)) {
|
|
return false;
|
|
}
|
|
|
|
if (TRI_ERROR_NO_ERROR != TRI_PushBackVectorPointer(&context->_currentScopes, (void*) scope)) {
|
|
return false;
|
|
}
|
|
|
|
node = TRI_CreateNodeScopeStartAql(context, scope);
|
|
|
|
if (node == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (! TRI_AppendStatementListAql(context->_statements, node)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief end the current scope, only 1
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_EndScopeAql (TRI_aql_context_t* const context) {
|
|
TRI_aql_scope_t* scope;
|
|
TRI_aql_node_t* node;
|
|
size_t n;
|
|
|
|
TRI_ASSERT(context);
|
|
|
|
n = context->_currentScopes._length;
|
|
TRI_ASSERT(n > 0);
|
|
|
|
scope = static_cast<TRI_aql_scope_t*>
|
|
(TRI_RemoveVectorPointer(&context->_currentScopes, --n));
|
|
LOG_TRACE("closing scope of type %s", TRI_TypeNameScopeAql(scope->_type));
|
|
|
|
node = TRI_CreateNodeScopeEndAql(context, scope);
|
|
if (node == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (! TRI_AppendStatementListAql(context->_statements, node)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief end the current scopes, 0 .. n
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_EndScopeByReturnAql (TRI_aql_context_t* const context) {
|
|
TRI_aql_scope_e type;
|
|
size_t n;
|
|
|
|
TRI_ASSERT(context);
|
|
type = CurrentType(context);
|
|
|
|
if (type == TRI_AQL_SCOPE_MAIN || type == TRI_AQL_SCOPE_SUBQUERY) {
|
|
return true;
|
|
}
|
|
|
|
n = context->_currentScopes._length;
|
|
TRI_ASSERT(n > 0);
|
|
|
|
while (n > 0) {
|
|
TRI_aql_scope_t* scope;
|
|
TRI_aql_node_t* node;
|
|
|
|
scope = (TRI_aql_scope_t*) TRI_RemoveVectorPointer(&context->_currentScopes, --n);
|
|
TRI_ASSERT(scope);
|
|
|
|
LOG_TRACE("closing scope of type %s", TRI_TypeNameScopeAql(scope->_type));
|
|
|
|
node = TRI_CreateNodeScopeEndAql(context, scope);
|
|
if (node == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (! TRI_AppendStatementListAql(context->_statements, node)) {
|
|
return false;
|
|
}
|
|
|
|
if (scope->_type != TRI_AQL_SCOPE_FOR_NESTED) {
|
|
// removed enough scopes
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief checks if a variable is defined in the current scope or above
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_VariableExistsScopeAql (TRI_aql_context_t* const context,
|
|
const char* const name) {
|
|
return (GetVariableInScope(context, name) != nullptr);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief push a variable into the current scope's symbol table
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_AddVariableScopeAql (TRI_aql_context_t* const context,
|
|
const char* name,
|
|
TRI_aql_node_t* const definingNode,
|
|
bool allowDuplicateName) {
|
|
TRI_aql_variable_t* variable;
|
|
TRI_aql_scope_t* scope;
|
|
void* result;
|
|
|
|
TRI_ASSERT(context);
|
|
TRI_ASSERT(name);
|
|
|
|
result = GetVariableInScope(context, name);
|
|
|
|
if (result != nullptr && allowDuplicateName) {
|
|
scope = CurrentScope(context);
|
|
|
|
TRI_aql_variable_t* var = static_cast<TRI_aql_variable_t*>(result);
|
|
var->_isUpdated = true;
|
|
return true;
|
|
}
|
|
|
|
if (result != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
variable = TRI_CreateVariableAql(name, definingNode);
|
|
|
|
if (variable == nullptr) {
|
|
return false;
|
|
}
|
|
|
|
scope = CurrentScope(context);
|
|
|
|
result = TRI_InsertKeyAssociativePointer(&scope->_variables, variable->_name, (void*) variable, false);
|
|
TRI_ASSERT(result == nullptr);
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|