1
0
Fork 0
arangodb/arangod/Ahuacatl/ahuacatl-statement-walker.cpp

417 lines
14 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Ahuacatl, statement list walking
///
/// @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-statement-walker.h"
#include "Basics/logging.h"
#include "Ahuacatl/ahuacatl-access-optimiser.h"
#include "Ahuacatl/ahuacatl-scope.h"
#include "Ahuacatl/ahuacatl-variable.h"
// -----------------------------------------------------------------------------
// --SECTION-- statement list walker
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief process a statement
////////////////////////////////////////////////////////////////////////////////
static void VisitStatement (TRI_aql_statement_walker_t* const walker,
const size_t position,
TRI_aql_visit_f func) {
TRI_aql_node_t* node;
TRI_aql_node_t* modified;
node = walker->_statements->_statements[position];
TRI_ASSERT(node);
modified = func(walker, node);
if (walker->_canModify && modified != node) {
if (modified == NULL) {
modified = TRI_GetDummyNopNodeAql();
}
walker->_statements->_statements[position] = modified;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief process a node's members
////////////////////////////////////////////////////////////////////////////////
static void VisitMembers (TRI_aql_statement_walker_t* const walker,
TRI_aql_node_t* const node) {
size_t i, n;
TRI_ASSERT(node);
n = node->_members._length;
for (i = 0; i < n; ++i) {
TRI_aql_node_t* member;
TRI_aql_node_t* modified;
member = (TRI_aql_node_t*) TRI_AtVectorPointer(&node->_members, i);
if (! member) {
continue;
}
VisitMembers(walker, member);
modified = walker->visitMember(walker, member);
if (walker->_canModify && modified != member) {
if (modified == NULL) {
modified = TRI_GetDummyNopNodeAql();
}
node->_members._buffer[i] = modified;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief actually walk the statements in the list
////////////////////////////////////////////////////////////////////////////////
static void RunWalk (TRI_aql_statement_walker_t* const walker) {
size_t i, n;
TRI_ASSERT(walker);
n = walker->_statements->_statements.size();
for (i = 0; i < n; ++i) {
TRI_aql_node_t* node;
TRI_aql_node_type_e nodeType;
node = walker->_statements->_statements[i];
if (! node) {
continue;
}
nodeType = node->_type;
// handle scopes
if (nodeType == TRI_AQL_NODE_SCOPE_START) {
TRI_PushBackVectorPointer(&walker->_currentScopes, TRI_AQL_NODE_DATA(node));
}
// preprocess node
if (walker->preVisitStatement != NULL) {
// this might change the node ptr
VisitStatement(walker, i, walker->preVisitStatement);
node = walker->_statements->_statements[i];
}
// process node's members
if (walker->visitMember != NULL) {
VisitMembers(walker, node);
}
// post process node
if (walker->postVisitStatement != NULL) {
VisitStatement(walker, i, walker->postVisitStatement);
}
if (nodeType == TRI_AQL_NODE_SCOPE_END) {
size_t len = walker->_currentScopes._length;
TRI_ASSERT(len > 0);
TRI_RemoveVectorPointer(&walker->_currentScopes, len - 1);
}
}
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief mark the scope so it ignores following offset/limit statement
/// optimisations.
/// this is necessary if we find a SORT statement, followed by a LIMIT
/// statement. we must not optimise that because we would modify results then.
////////////////////////////////////////////////////////////////////////////////
void TRI_IgnoreCurrentLimitStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
TRI_ASSERT(scope);
if (scope->_limit._status == TRI_AQL_LIMIT_UNDEFINED) {
scope->_limit._status = TRI_AQL_LIMIT_IGNORE;
LOG_TRACE("setting limit status to ignorable");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief restrict a offset/limit combination for the top scope to not apply
/// a limit optimisation on collection access, but to apply it on for loop
/// traversal
////////////////////////////////////////////////////////////////////////////////
void TRI_RestrictCurrentLimitStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
TRI_ASSERT(scope);
if (scope->_limit._hasFilter) {
scope->_limit._status = TRI_AQL_LIMIT_IGNORE;
LOG_TRACE("limit given up because of additional filter");
}
else if (scope->_limit._status == TRI_AQL_LIMIT_UNDEFINED) {
scope->_limit._hasFilter = true;
LOG_TRACE("setting limit status to use-in-for-loop");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief note an offset/limit combination for the top scope
////////////////////////////////////////////////////////////////////////////////
void TRI_SetCurrentLimitStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
const int64_t offset,
const int64_t limit) {
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
TRI_ASSERT(scope);
if (scope->_limit._status == TRI_AQL_LIMIT_UNDEFINED) {
// never overwrite a previously picked up limit
scope->_limit._limit = limit;
scope->_limit._offset = offset;
scope->_limit._status = TRI_AQL_LIMIT_USE;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get current ranges in top scope of statement walker
////////////////////////////////////////////////////////////////////////////////
TRI_vector_pointer_t* TRI_GetCurrentRangesStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
TRI_ASSERT(scope);
return scope->_ranges;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief set current ranges in top scope of statement walker
////////////////////////////////////////////////////////////////////////////////
void TRI_SetCurrentRangesStatementWalkerAql (TRI_aql_statement_walker_t* const walker,
TRI_vector_pointer_t* ranges) {
TRI_aql_scope_t* scope = TRI_GetCurrentScopeStatementWalkerAql(walker);
TRI_ASSERT(scope);
if (ranges != NULL) { // ranges may be NULL
TRI_vector_pointer_t* oldRanges = scope->_ranges;
if (oldRanges != NULL) {
// free old value
TRI_FreeAccessesAql(oldRanges);
}
// set to new value
scope->_ranges = ranges;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get current top scope of statement walker
////////////////////////////////////////////////////////////////////////////////
TRI_aql_scope_t* TRI_GetCurrentScopeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
size_t n;
TRI_ASSERT(walker);
n = walker->_currentScopes._length;
TRI_ASSERT(n > 0);
return (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, n - 1);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief mark a scope as being empty and infect surrounding scopes
////////////////////////////////////////////////////////////////////////////////
void TRI_EmptyScopeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
size_t n = walker->_currentScopes._length;
while (n > 0) {
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, n - 1);
if (scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
break;
}
scope->_empty = true;
if (scope->_type != TRI_AQL_SCOPE_FOR &&
scope->_type != TRI_AQL_SCOPE_FOR_NESTED &&
scope->_type != TRI_AQL_SCOPE_MAIN) {
break;
}
--n;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @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,
const char* const name,
size_t* scopeCount) {
size_t n;
// init scope counter to 0
*scopeCount = 0;
TRI_ASSERT(name != NULL);
n = walker->_currentScopes._length;
while (n > 0) {
TRI_aql_scope_t* scope;
TRI_aql_variable_t* variable;
scope = (TRI_aql_scope_t*) TRI_AtVectorPointer(&walker->_currentScopes, --n);
TRI_ASSERT(scope);
variable = static_cast<TRI_aql_variable_t*>
(TRI_LookupByKeyAssociativePointer(&scope->_variables,
(void*) name));
if (variable != NULL) {
return variable;
}
if (n == 0 || scope->_type == TRI_AQL_SCOPE_SUBQUERY) {
// reached the outermost scope
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
(*scopeCount)++;
}
// variable not found
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief create a statement walker
////////////////////////////////////////////////////////////////////////////////
TRI_aql_statement_walker_t* TRI_CreateStatementWalkerAql (void* data,
const bool canModify,
TRI_aql_visit_f visitMember,
TRI_aql_visit_f preVisitStatement,
TRI_aql_visit_f postVisitStatement) {
TRI_aql_statement_walker_t* walker;
int res;
walker = (TRI_aql_statement_walker_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_statement_walker_t), false);
if (walker == NULL) {
return NULL;
}
walker->_data = data;
walker->_canModify = canModify;
walker->visitMember = visitMember;
walker->preVisitStatement = preVisitStatement;
walker->postVisitStatement = postVisitStatement;
res = TRI_InitVectorPointer2(&walker->_currentScopes, TRI_UNKNOWN_MEM_ZONE, 4);
if (res != TRI_ERROR_NO_ERROR) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, walker);
return NULL;
}
return walker;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief free a statement walker
////////////////////////////////////////////////////////////////////////////////
void TRI_FreeStatementWalkerAql (TRI_aql_statement_walker_t* const walker) {
TRI_ASSERT(walker);
TRI_DestroyVectorPointer(&walker->_currentScopes);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, walker);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief run the statement list walk
////////////////////////////////////////////////////////////////////////////////
void TRI_WalkStatementsAql (TRI_aql_statement_walker_t* const walker,
TRI_aql_statement_list_t* list) {
TRI_ASSERT(walker);
TRI_ASSERT(list);
walker->_statements = list;
RunWalk(walker);
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: