1
0
Fork 0
arangodb/arangod/Ahuacatl/ahuacatl-scope.cpp

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: