mirror of https://gitee.com/bigwinds/arangodb
373 lines
11 KiB
C++
373 lines
11 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Ahuacatl, statement list
|
|
///
|
|
/// @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-statementlist.h"
|
|
|
|
#include "Basics/logging.h"
|
|
|
|
#include "Ahuacatl/ahuacatl-ast-node.h"
|
|
#include "Ahuacatl/ahuacatl-scope.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private members
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief dummy no-operations node (re-used for ALL non-operations)}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* DummyNopNode;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief dummy empty return node (re-used for multiple operations)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* DummyReturnEmptyNode;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the statement at a certain position
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static inline TRI_aql_node_t* StatementAt (const TRI_aql_statement_list_t* const list,
|
|
const size_t position) {
|
|
return list->_statements[position];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief invalidates all statements in a scope marked as "empty"
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static size_t InvalidateEmptyScope (TRI_aql_statement_list_t* const list,
|
|
const size_t position) {
|
|
size_t i, n;
|
|
size_t scopes;
|
|
|
|
TRI_ASSERT(list);
|
|
n = list->_statements.size();
|
|
i = position;
|
|
scopes = 0;
|
|
|
|
while (i < n) {
|
|
TRI_aql_node_t* node = StatementAt(list, i);
|
|
TRI_aql_node_type_e type = node->_type;
|
|
|
|
if (i == position) {
|
|
TRI_ASSERT(type == TRI_AQL_NODE_SCOPE_START);
|
|
list->_statements[i] = DummyReturnEmptyNode;
|
|
}
|
|
else {
|
|
list->_statements[i] = TRI_GetDummyNopNodeAql();
|
|
}
|
|
++i;
|
|
|
|
if (type == TRI_AQL_NODE_SCOPE_START) {
|
|
++scopes;
|
|
}
|
|
else if (type == TRI_AQL_NODE_SCOPE_END) {
|
|
TRI_ASSERT(scopes > 0);
|
|
--scopes;
|
|
|
|
if (scopes == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create and initialize a statement list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_statement_list_t* TRI_CreateStatementListAql (void) {
|
|
TRI_aql_statement_list_t* list;
|
|
|
|
try {
|
|
list = new TRI_aql_statement_list_t();
|
|
}
|
|
catch (std::exception&) {
|
|
list = nullptr;
|
|
}
|
|
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
list->_currentLevel = 0;
|
|
|
|
return list;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free a statement list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_FreeStatementListAql (TRI_aql_statement_list_t* const list) {
|
|
if (list == NULL) {
|
|
return;
|
|
}
|
|
|
|
delete list;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief init the global nodes at program start
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_GlobalInitStatementListAql (void) {
|
|
DummyNopNode = TRI_CreateNodeNopAql();
|
|
DummyReturnEmptyNode = TRI_CreateNodeReturnEmptyAql();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief free the global nodes at program end
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_GlobalFreeStatementListAql (void) {
|
|
if (DummyNopNode != NULL) {
|
|
TRI_DestroyVectorPointer(&DummyNopNode->_members);
|
|
delete DummyNopNode;
|
|
}
|
|
|
|
if (DummyReturnEmptyNode != NULL) {
|
|
TRI_aql_node_t* list = TRI_AQL_NODE_MEMBER(DummyReturnEmptyNode, 0);
|
|
|
|
TRI_DestroyVectorPointer(&list->_members);
|
|
delete list;
|
|
|
|
TRI_DestroyVectorPointer(&DummyReturnEmptyNode->_members);
|
|
delete DummyReturnEmptyNode;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief get the address of the dummy non-op node
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_node_t* TRI_GetDummyNopNodeAql (void) {
|
|
return DummyNopNode;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief pull out uncorrelated subqueries from the middle of the statement
|
|
/// list to the beginning
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_PulloutStatementListAql (TRI_aql_statement_list_t* const list) {
|
|
size_t i, n;
|
|
size_t scopes = 0;
|
|
size_t targetScope = 0;
|
|
size_t moveStart = 0;
|
|
bool watch = false;
|
|
|
|
TRI_ASSERT(list);
|
|
|
|
i = 0;
|
|
n = list->_statements.size();
|
|
|
|
while (i < n) {
|
|
TRI_aql_node_t* node = StatementAt(list, i);
|
|
TRI_aql_node_type_e type = node->_type;
|
|
|
|
if (type == TRI_AQL_NODE_SCOPE_START) {
|
|
// node is a scope start
|
|
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
|
|
|
|
if (scope->_type == TRI_AQL_SCOPE_SUBQUERY && scope->_selfContained) {
|
|
if (! watch && scopes > 0) {
|
|
watch = true;
|
|
targetScope = scopes;
|
|
moveStart = i;
|
|
}
|
|
}
|
|
|
|
++scopes;
|
|
}
|
|
else if (type == TRI_AQL_NODE_SCOPE_END) {
|
|
// node is a scope end
|
|
--scopes;
|
|
|
|
if (watch && scopes == targetScope) {
|
|
watch = false;
|
|
|
|
node = StatementAt(list, i + 1);
|
|
|
|
// check if next statement is a subquery statement
|
|
if (i + 1 < n && node->_type == TRI_AQL_NODE_SUBQUERY) {
|
|
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) {
|
|
node = StatementAt(list, j + inserted);
|
|
if (! TRI_InsertStatementListAql(list, node, inserted + 0)) {
|
|
return;
|
|
}
|
|
|
|
// insert a dummy node in place of the moved node
|
|
list->_statements[j + inserted + 1] = TRI_GetDummyNopNodeAql();
|
|
|
|
// next
|
|
++j;
|
|
++inserted;
|
|
}
|
|
|
|
// moving statements from the middle to the beginning of the list will also
|
|
// change the list length and the position we'll be continuing from
|
|
n += inserted;
|
|
i = j + inserted;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief add a statement to the front of the statement list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_InsertStatementListAql (TRI_aql_statement_list_t* const list,
|
|
TRI_aql_node_t* const node,
|
|
const size_t position) {
|
|
TRI_ASSERT(list != NULL);
|
|
TRI_ASSERT(node != NULL);
|
|
|
|
try {
|
|
list->_statements.insert(list->_statements.begin() + position, node);
|
|
}
|
|
catch (std::exception&) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief add a statement to the end of the statement list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool TRI_AppendStatementListAql (TRI_aql_statement_list_t* const list,
|
|
TRI_aql_node_t* const node) {
|
|
TRI_aql_node_type_e type;
|
|
|
|
TRI_ASSERT(list != NULL);
|
|
TRI_ASSERT(node != NULL);
|
|
|
|
type = node->_type;
|
|
TRI_ASSERT(TRI_IsTopLevelTypeAql(type));
|
|
|
|
if (type == TRI_AQL_NODE_SCOPE_START) {
|
|
++list->_currentLevel;
|
|
}
|
|
else if (type == TRI_AQL_NODE_SCOPE_END) {
|
|
TRI_ASSERT(list->_currentLevel > 0);
|
|
--list->_currentLevel;
|
|
}
|
|
|
|
try {
|
|
list->_statements.push_back(node);
|
|
}
|
|
catch (std::exception&) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief remove all non-ops from the statement list
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void TRI_CompactStatementListAql (TRI_aql_statement_list_t* const list) {
|
|
size_t i, j, n;
|
|
|
|
TRI_ASSERT(list);
|
|
|
|
i = 0;
|
|
j = 0;
|
|
n = list->_statements.size();
|
|
|
|
while (i < n) {
|
|
TRI_aql_node_t* node = StatementAt(list, i);
|
|
|
|
if (node->_type == TRI_AQL_NODE_NOP) {
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
if (node->_type == TRI_AQL_NODE_SCOPE_START) {
|
|
TRI_aql_scope_t* scope = (TRI_aql_scope_t*) TRI_AQL_NODE_DATA(node);
|
|
|
|
if (scope->_empty) {
|
|
i = InvalidateEmptyScope(list, i);
|
|
j = i;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* should not happen anymore
|
|
if (node->_type == TRI_AQL_NODE_RETURN_EMPTY) {
|
|
TRI_ASSERT(false);
|
|
}
|
|
*/
|
|
|
|
list->_statements[j++] = node;
|
|
++i;
|
|
}
|
|
|
|
list->_statements.resize(j);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|