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