mirror of https://gitee.com/bigwinds/arangodb
417 lines
14 KiB
C++
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:
|