mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'aql2' of ssh://github.com/triAGENS/ArangoDB into aql2
This commit is contained in:
commit
a35f066477
|
@ -76,8 +76,7 @@ void AstNode::toJson (TRI_json_t* json,
|
|||
// dump node type
|
||||
TRI_Insert3ArrayJson(zone, node, "type", TRI_CreateStringCopyJson(zone, typeString().c_str()));
|
||||
|
||||
if (type == NODE_TYPE_REFERENCE ||
|
||||
type == NODE_TYPE_COLLECTION ||
|
||||
if (type == NODE_TYPE_COLLECTION ||
|
||||
type == NODE_TYPE_PARAMETER ||
|
||||
type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||
type == NODE_TYPE_FCALL ||
|
||||
|
@ -109,7 +108,8 @@ void AstNode::toJson (TRI_json_t* json,
|
|||
}
|
||||
}
|
||||
|
||||
if (type == NODE_TYPE_VARIABLE) {
|
||||
if (type == NODE_TYPE_VARIABLE ||
|
||||
type == NODE_TYPE_REFERENCE) {
|
||||
auto variable = static_cast<Variable*>(getData());
|
||||
|
||||
TRI_Insert3ArrayJson(zone, node, "name", TRI_CreateStringCopyJson(zone, variable->name.c_str()));
|
||||
|
@ -301,8 +301,6 @@ std::string AstNode::typeString () const {
|
|||
return "user function call";
|
||||
case NODE_TYPE_RANGE:
|
||||
return "range";
|
||||
case NODE_TYPE_NOP:
|
||||
return "no-op";
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,8 +123,7 @@ namespace triagens {
|
|||
NODE_TYPE_PARAMETER,
|
||||
NODE_TYPE_FCALL,
|
||||
NODE_TYPE_FCALL_USER,
|
||||
NODE_TYPE_RANGE,
|
||||
NODE_TYPE_NOP,
|
||||
NODE_TYPE_RANGE
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -270,7 +269,15 @@ namespace triagens {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the int value of a node
|
||||
/// @brief return the int value of a node, without asserting the node type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline int64_t getIntValue (bool) const {
|
||||
return value.value._int;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the int value of a node, with asserting the node type
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline int64_t getIntValue () const {
|
||||
|
|
|
@ -65,8 +65,7 @@ QueryAst::QueryAst (Query* query,
|
|||
_collectionNames(),
|
||||
_root(nullptr),
|
||||
_writeCollection(nullptr),
|
||||
_writeOptions(nullptr),
|
||||
_nopNode() {
|
||||
_writeOptions(nullptr) {
|
||||
|
||||
TRI_ASSERT(_query != nullptr);
|
||||
TRI_ASSERT(_parser != nullptr);
|
||||
|
@ -200,6 +199,7 @@ AstNode* QueryAst::createNodeLet (char const* variableName,
|
|||
|
||||
AstNode* QueryAst::createNodeFilter (AstNode const* expression) {
|
||||
AstNode* node = createNode(NODE_TYPE_FILTER);
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_UNKNOWN));
|
||||
node->addMember(expression);
|
||||
|
||||
return node;
|
||||
|
@ -403,7 +403,14 @@ AstNode* QueryAst::createNodeCollection (char const* name) {
|
|||
|
||||
AstNode* QueryAst::createNodeReference (char const* name) {
|
||||
AstNode* node = createNode(NODE_TYPE_REFERENCE);
|
||||
node->setStringValue(name);
|
||||
|
||||
auto variable = _scopes.getVariable(name);
|
||||
|
||||
if (variable == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
node->setData(variable);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -666,20 +673,6 @@ AstNode* QueryAst::createNodeRange (AstNode const* start,
|
|||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a no-op node
|
||||
/// note: the same no-op node can be returned multiple times
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* QueryAst::createNodeNop () {
|
||||
// the nop node is a singleton inside a query
|
||||
if (_nopNode == nullptr) {
|
||||
_nopNode = createNode(NODE_TYPE_NOP);
|
||||
}
|
||||
|
||||
return _nopNode;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief injects bind parameters into the AST
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -794,10 +787,27 @@ void QueryAst::optimize () {
|
|||
return optimizeTernaryOperator(node);
|
||||
}
|
||||
|
||||
// reference to a variable
|
||||
if (node->type == NODE_TYPE_REFERENCE) {
|
||||
return optimizeReference(node);
|
||||
}
|
||||
|
||||
// range
|
||||
if (node->type == NODE_TYPE_RANGE) {
|
||||
return optimizeRange(node);
|
||||
}
|
||||
|
||||
// LET
|
||||
if (node->type == NODE_TYPE_LET) {
|
||||
return optimizeLet(node);
|
||||
}
|
||||
|
||||
return node;
|
||||
};
|
||||
|
||||
_root = traverse(_root, func, nullptr);
|
||||
|
||||
optimizeRoot();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -853,12 +863,16 @@ AstNode* QueryAst::optimizeFilter (AstNode* node) {
|
|||
return node;
|
||||
}
|
||||
|
||||
if (operand->getBoolValue()) {
|
||||
// FILTER is always true, optimize it away
|
||||
return createNodeNop();
|
||||
}
|
||||
TRI_ASSERT(node->getIntValue(true) == static_cast<int64_t>(FILTER_UNKNOWN));
|
||||
|
||||
// TODO: optimize FILTERs that are always false
|
||||
if (operand->getBoolValue()) {
|
||||
// FILTER is always true
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_TRUE));
|
||||
}
|
||||
else {
|
||||
// FILTER is always false
|
||||
node->setIntValue(static_cast<int64_t>(FILTER_FALSE));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -1164,6 +1178,130 @@ AstNode* QueryAst::optimizeTernaryOperator (AstNode* node) {
|
|||
return falsePart;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes a reference to a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* QueryAst::optimizeReference (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_REFERENCE);
|
||||
|
||||
auto variable = static_cast<Variable*>(node->getData());
|
||||
|
||||
if (variable == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
if (variable->constValue() == nullptr) {
|
||||
return node;
|
||||
}
|
||||
|
||||
return static_cast<AstNode*>(variable->constValue());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the range operator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* QueryAst::optimizeRange (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_RANGE);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
AstNode* lhs = node->getMember(0);
|
||||
AstNode* rhs = node->getMember(1);
|
||||
|
||||
if (lhs == nullptr || rhs == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
if (! lhs->isConstant() || ! rhs->isConstant()) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (! lhs->isNumericValue() || ! rhs->isNumericValue()) {
|
||||
_parser->registerError(TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE);
|
||||
return node;
|
||||
}
|
||||
|
||||
double const l = lhs->getDoubleValue();
|
||||
double const r = rhs->getDoubleValue();
|
||||
|
||||
if (l < r) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// x..y with x > y, replace with empty list
|
||||
|
||||
return createNodeList();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the LET statement
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* QueryAst::optimizeLet (AstNode* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_LET);
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
|
||||
AstNode* variable = node->getMember(0);
|
||||
AstNode* expression = node->getMember(1);
|
||||
|
||||
if (expression->isConstant()) {
|
||||
// if the expression assigned to the LET variable is constant, we'll store
|
||||
// a pointer to the const value in the variable
|
||||
// further optimizations can then use this pointer and optimize further, e.g.
|
||||
// LET a = 1 LET b = a + 1, c = b + a can be optimized to LET a = 1 LET b = 2 LET c = 4
|
||||
auto v = static_cast<Variable*>(variable->getData());
|
||||
|
||||
TRI_ASSERT(v != nullptr);
|
||||
v->constValue(static_cast<void*>(expression));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the top-level statements
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void QueryAst::optimizeRoot() {
|
||||
TRI_ASSERT(_root != nullptr);
|
||||
TRI_ASSERT(_root->type == NODE_TYPE_ROOT);
|
||||
|
||||
size_t const n = _root->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
AstNode* member = _root->getMember(i);
|
||||
|
||||
if (member == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (member->type == NODE_TYPE_FOR) {
|
||||
// peek forward and check if the for contains a FILTER that is always false
|
||||
bool isEmpty = false;
|
||||
|
||||
for (size_t j = i + 1; j < n; ++j) {
|
||||
AstNode* sub = _root->getMember(j);
|
||||
|
||||
if (sub == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sub->type == NODE_TYPE_FILTER) {
|
||||
// TODO: found a FILTER that is always false
|
||||
isEmpty = true;
|
||||
}
|
||||
|
||||
if (sub->type == NODE_TYPE_RETURN) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -56,6 +56,12 @@ namespace triagens {
|
|||
|
||||
class QueryAst {
|
||||
|
||||
enum FilterType {
|
||||
FILTER_UNKNOWN,
|
||||
FILTER_TRUE,
|
||||
FILTER_FALSE
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -81,8 +87,6 @@ namespace triagens {
|
|||
|
||||
public:
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a copy of our own bind parameters
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -387,13 +391,6 @@ namespace triagens {
|
|||
AstNode* createNodeRange (AstNode const*,
|
||||
AstNode const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a no-op node
|
||||
/// note: the same no-op node can be returned multiple times
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* createNodeNop ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief injects bind parameters into the AST
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -464,6 +461,30 @@ namespace triagens {
|
|||
|
||||
AstNode* optimizeTernaryOperator (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes a reference to a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeReference (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the range operator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeRange (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the LET statement
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* optimizeLet (AstNode*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief optimizes the top-level statements
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void optimizeRoot ();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an AST node from JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -556,12 +577,6 @@ namespace triagens {
|
|||
|
||||
AstNode const* _writeOptions;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief a reusable no-operation node
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AstNode* _nopNode;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief AQL function names
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -99,19 +99,28 @@ Variable* Scope::addVariable (std::string const& name,
|
|||
return variable;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief end a scope
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks if a variable exists in the scope
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Scope::existsVariable (char const* name) const {
|
||||
TRI_ASSERT(name != nullptr);
|
||||
auto it = _variables.find(std::string(name));
|
||||
return (getVariable(name) != nullptr);
|
||||
}
|
||||
|
||||
return (it != _variables.end());
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* Scope::getVariable (char const* name) const {
|
||||
std::string const varname(name);
|
||||
|
||||
auto it = _variables.find(varname);
|
||||
|
||||
if (it == _variables.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -203,6 +212,7 @@ void Scopes::endNested () {
|
|||
Variable* Scopes::addVariable (char const* name,
|
||||
bool isUserDefined) {
|
||||
TRI_ASSERT(! _activeScopes.empty());
|
||||
TRI_ASSERT(name != nullptr);
|
||||
|
||||
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
|
||||
auto scope = (*it);
|
||||
|
@ -223,17 +233,25 @@ Variable* Scopes::addVariable (char const* name,
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Scopes::existsVariable (char const* name) const {
|
||||
return (getVariable(name) != nullptr);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* Scopes::getVariable (char const* name) const {
|
||||
TRI_ASSERT(! _activeScopes.empty());
|
||||
|
||||
for (auto it = _activeScopes.rbegin(); it != _activeScopes.rend(); ++it) {
|
||||
auto scope = (*it);
|
||||
auto variable = (*it)->getVariable(name);
|
||||
|
||||
if (scope->existsVariable(name)) {
|
||||
return true;
|
||||
if (variable != nullptr) {
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace triagens {
|
|||
size_t id,
|
||||
bool isUserDefined)
|
||||
: name(name),
|
||||
value(nullptr),
|
||||
id(id),
|
||||
isUserDefined(isUserDefined) {
|
||||
}
|
||||
|
@ -51,7 +52,29 @@ namespace triagens {
|
|||
~Variable () {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief registers a constant value for the variable
|
||||
/// this constant value is used for constant propagation in optimizations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void constValue (void* node) {
|
||||
value = node;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief returns a constant value registered for this variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void* constValue () const {
|
||||
return value;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
std::string const name;
|
||||
void* value;
|
||||
size_t const id;
|
||||
bool const isUserDefined;
|
||||
};
|
||||
|
@ -125,6 +148,12 @@ namespace triagens {
|
|||
|
||||
bool existsVariable (char const*) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* getVariable (char const*) const;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -209,6 +238,12 @@ namespace triagens {
|
|||
|
||||
bool existsVariable (char const*) const;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return a variable
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Variable* getVariable (char const*) const;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -443,11 +443,7 @@ expression:
|
|||
auto subQuery = parser->ast()->createNodeSubquery(tempName);
|
||||
parser->ast()->addOperation(subQuery);
|
||||
|
||||
auto nameNode = subQuery->getMember(0);
|
||||
auto result = parser->ast()->createNodeReference(nameNode->getStringValue());
|
||||
|
||||
// return the result
|
||||
$$ = result;
|
||||
$$ = parser->ast()->createNodeReference(tempName);
|
||||
}
|
||||
| operator_unary {
|
||||
$$ = $1;
|
||||
|
@ -683,6 +679,7 @@ reference:
|
|||
parser->pushStack($1);
|
||||
|
||||
// create a temporary variable for the row iterator (will be popped by "expansion" rule")
|
||||
// TODO:
|
||||
auto node = parser->ast()->createNodeReference(varname);
|
||||
|
||||
// push the variable
|
||||
|
@ -700,6 +697,7 @@ reference:
|
|||
auto nameNode = expand->getMember(1);
|
||||
|
||||
// return a reference only
|
||||
// TODO:
|
||||
$$ = parser->ast()->createNodeReference(nameNode->getStringValue());
|
||||
}
|
||||
;
|
||||
|
|
Loading…
Reference in New Issue