1
0
Fork 0
arangodb/arangod/Aql/Scopes.cpp

376 lines
12 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief Aql, 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 "Aql/Scopes.h"
#include "Basics/Exceptions.h"
using namespace triagens::aql;
// -----------------------------------------------------------------------------
// --SECTION-- constructors / destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief create the scope
////////////////////////////////////////////////////////////////////////////////
Scope::Scope (ScopeType type)
: _type(type),
_variables() {
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the scope
////////////////////////////////////////////////////////////////////////////////
Scope::~Scope () {
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief return the name of a scope type
////////////////////////////////////////////////////////////////////////////////
std::string Scope::typeName () const {
return std::move(typeName(_type));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return the name of a scope type
////////////////////////////////////////////////////////////////////////////////
std::string Scope::typeName (ScopeType type) {
switch (type) {
case AQL_SCOPE_MAIN:
return "main";
case AQL_SCOPE_SUBQUERY:
return "subquery";
case AQL_SCOPE_FOR:
return "for";
case AQL_SCOPE_COLLECT:
return "collection";
}
TRI_ASSERT(false);
return "unknown";
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a variable to the scope
////////////////////////////////////////////////////////////////////////////////
void Scope::addVariable (Variable* variable) {
_variables.emplace(std::make_pair(variable->name, variable));
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if a variable exists in the scope
////////////////////////////////////////////////////////////////////////////////
bool Scope::existsVariable (char const* name,
size_t nameLength) const {
return (getVariable(name, nameLength) != nullptr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks if a variable exists in the scope
////////////////////////////////////////////////////////////////////////////////
bool Scope::existsVariable (std::string const& name) const {
return (getVariable(name) != nullptr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a variable
////////////////////////////////////////////////////////////////////////////////
Variable const* Scope::getVariable (char const* name,
size_t nameLength) const {
std::string const varname(name, nameLength);
auto it = _variables.find(varname);
if (it == _variables.end()) {
return nullptr;
}
return (*it).second;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns a variable
////////////////////////////////////////////////////////////////////////////////
Variable const* Scope::getVariable (std::string const& name) const {
auto it = _variables.find(name);
if (it == _variables.end()) {
return nullptr;
}
return (*it).second;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a variable, allowing usage of special pseudo vars such
/// as OLD and NEW
////////////////////////////////////////////////////////////////////////////////
Variable const* Scope::getVariable (char const* name,
size_t nameLength,
bool allowSpecial) const {
auto variable = getVariable(name, nameLength);
if (variable == nullptr && allowSpecial) {
// variable does not exist
// now try variable aliases OLD (= $OLD) and NEW (= $NEW)
if (strcmp(name, "OLD") == 0) {
variable = getVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_OLD));
}
else if (strcmp(name, "NEW") == 0) {
variable = getVariable(TRI_CHAR_LENGTH_PAIR(Variable::NAME_NEW));
}
}
return variable;
}
// -----------------------------------------------------------------------------
// --SECTION-- constructors / destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief create the scopes
////////////////////////////////////////////////////////////////////////////////
Scopes::Scopes ()
: _activeScopes(),
_currentVariables() {
_activeScopes.reserve(4);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the scopes
////////////////////////////////////////////////////////////////////////////////
Scopes::~Scopes () {
for (auto& it : _activeScopes) {
delete it;
}
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief start a new scope
////////////////////////////////////////////////////////////////////////////////
void Scopes::start (ScopeType type) {
std::unique_ptr<Scope> scope(new Scope(type));
_activeScopes.emplace_back(scope.get());
scope.release();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief end the current scope
////////////////////////////////////////////////////////////////////////////////
void Scopes::endCurrent () {
TRI_ASSERT(! _activeScopes.empty());
Scope* scope = _activeScopes.back();
TRI_ASSERT(scope != nullptr);
_activeScopes.pop_back();
delete scope;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief end the current scope plus any FOR scopes it is nested in
////////////////////////////////////////////////////////////////////////////////
void Scopes::endNested () {
TRI_ASSERT(! _activeScopes.empty());
int iterations = 0;
while (! _activeScopes.empty()) {
++iterations;
auto scope = _activeScopes.back();
TRI_ASSERT(scope != nullptr);
ScopeType type = scope->type();
if (type == AQL_SCOPE_MAIN || type == AQL_SCOPE_SUBQUERY) {
// main and subquery scopes cannot be closed here
break;
}
if (type != AQL_SCOPE_FOR && iterations >= 2) {
// if nested, do not close anything but for scopes
break;
}
endCurrent();
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a variable to the current scope
////////////////////////////////////////////////////////////////////////////////
void Scopes::addVariable (Variable* variable) {
TRI_ASSERT(! _activeScopes.empty());
TRI_ASSERT(variable != nullptr);
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
auto scope = (*it);
if (scope->existsVariable(variable->name)) {
// duplicate variable name
THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_VARIABLE_REDECLARED, variable->name.c_str());
}
}
// if this fails, there won't be a memleak
_activeScopes.back()->addVariable(variable);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks whether a variable exists in any scope
////////////////////////////////////////////////////////////////////////////////
bool Scopes::existsVariable (char const* name,
size_t nameLength) const {
return (getVariable(name, nameLength) != nullptr);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a variable by name - this respects the current scopes
////////////////////////////////////////////////////////////////////////////////
Variable const* Scopes::getVariable (char const* name,
size_t nameLength) const {
TRI_ASSERT(! _activeScopes.empty());
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
auto variable = (*it)->getVariable(name, nameLength);
if (variable != nullptr) {
return variable;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a variable by name - this respects the current scopes
////////////////////////////////////////////////////////////////////////////////
Variable const* Scopes::getVariable (std::string const& name) const {
TRI_ASSERT(! _activeScopes.empty());
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
auto variable = (*it)->getVariable(name);
if (variable != nullptr) {
return variable;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a variable by name - this respects the current scopes
////////////////////////////////////////////////////////////////////////////////
Variable const* Scopes::getVariable (char const* name,
size_t nameLength,
bool allowSpecial) const {
TRI_ASSERT(! _activeScopes.empty());
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
auto variable = (*it)->getVariable(name, nameLength, allowSpecial);
if (variable != nullptr) {
return variable;
}
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief get the $CURRENT variable
////////////////////////////////////////////////////////////////////////////////
Variable const* Scopes::getCurrentVariable () const {
if (_currentVariables.empty()) {
THROW_ARANGO_EXCEPTION_PARAMS(TRI_ERROR_QUERY_VARIABLE_NAME_UNKNOWN, Variable::NAME_CURRENT);
}
auto result = _currentVariables.back();
TRI_ASSERT(result != nullptr);
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief stack a $CURRENT variable from the stack
////////////////////////////////////////////////////////////////////////////////
void Scopes::stackCurrentVariable (Variable const* variable) {
_currentVariables.emplace_back(variable);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief unregister the $CURRENT variable from the stack
////////////////////////////////////////////////////////////////////////////////
void Scopes::unstackCurrentVariable () {
TRI_ASSERT(! _currentVariables.empty());
_currentVariables.pop_back();
}
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: