mirror of https://gitee.com/bigwinds/arangodb
1259 lines
38 KiB
C
1259 lines
38 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Ahuacatl, constant folding
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2010-2012 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 triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Jan Steemann
|
|
/// @author Copyright 2012, triagens GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Ahuacatl/ahuacatl-constant-folder.h"
|
|
#include "Ahuacatl/ahuacatl-conversions.h"
|
|
#include "Ahuacatl/ahuacatl-functions.h"
|
|
|
|
#include "V8/v8-execution.h"
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief hash a variable
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static uint64_t HashFieldAccess (TRI_associative_pointer_t* array,
|
|
void const* element) {
|
|
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) element;
|
|
|
|
return TRI_FnvHashString(fieldAccess->_fieldName);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief comparison function used to determine variable equality
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool EqualFieldAccess (TRI_associative_pointer_t* array,
|
|
void const* key,
|
|
void const* element) {
|
|
TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) element;
|
|
|
|
return TRI_EqualString(key, fieldAccess->_fieldName);
|
|
}
|
|
|
|
static int TypeWeight (const TRI_json_t* const value) {
|
|
switch (value->_type) {
|
|
case TRI_JSON_BOOLEAN:
|
|
return 1;
|
|
case TRI_JSON_NUMBER:
|
|
return 2;
|
|
case TRI_JSON_STRING:
|
|
return 3;
|
|
case TRI_JSON_LIST:
|
|
return 4;
|
|
case TRI_JSON_ARRAY:
|
|
return 5;
|
|
case TRI_JSON_NULL:
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int Compare (const TRI_json_t* const lhs, const TRI_json_t* const rhs) {
|
|
int lWeight = TypeWeight(lhs);
|
|
int rWeight = TypeWeight(rhs);
|
|
|
|
if (lWeight < rWeight) {
|
|
return -1;
|
|
}
|
|
if (lWeight > rWeight) {
|
|
return 1;
|
|
}
|
|
|
|
// equal weight
|
|
switch (lhs->_type) {
|
|
case TRI_JSON_NULL:
|
|
return 0; // null == null;
|
|
case TRI_JSON_BOOLEAN:
|
|
if (lhs->_value._boolean == rhs->_value._boolean) {
|
|
return 0;
|
|
}
|
|
if (!lhs->_value._boolean && rhs->_value._boolean) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
case TRI_JSON_NUMBER:
|
|
if (lhs->_value._number == rhs->_value._number) {
|
|
return 0;
|
|
}
|
|
if (lhs->_value._number < rhs->_value._number) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
case TRI_JSON_STRING:
|
|
return strcmp(lhs->_value._string.data, rhs->_value._string.data);
|
|
case TRI_JSON_LIST: {
|
|
size_t nl = lhs->_value._objects._length;
|
|
size_t nr = rhs->_value._objects._length;
|
|
size_t i;
|
|
|
|
for (i = 0; i < nl; ++i) {
|
|
int result;
|
|
|
|
if (i >= nr) {
|
|
// left list is longer
|
|
return 1;
|
|
}
|
|
|
|
result = Compare(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i));
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// right list is longer
|
|
if (nr > nl) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
case TRI_JSON_ARRAY: {
|
|
size_t nl = lhs->_value._objects._length;
|
|
size_t nr = rhs->_value._objects._length;
|
|
size_t i;
|
|
|
|
for (i = 0; i < nl; i += 2) {
|
|
int result;
|
|
|
|
if (i > nr) {
|
|
// left list is longer
|
|
return 1;
|
|
}
|
|
|
|
// compare key
|
|
result = Compare(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i));
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
|
|
// compare value
|
|
result = Compare(TRI_AtVector(&lhs->_value._objects, i + 1), TRI_AtVector(&rhs->_value._objects, i + 1));
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// right list is longer
|
|
if (nr > nl) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static bool IsSameValue (const TRI_json_t* const lhs, const TRI_json_t* const rhs) {
|
|
if (!lhs || !rhs) {
|
|
return false;
|
|
}
|
|
|
|
if (lhs->_type != rhs->_type) {
|
|
return false;
|
|
}
|
|
|
|
switch (lhs->_type) {
|
|
case TRI_JSON_NULL:
|
|
return true;
|
|
case TRI_JSON_BOOLEAN:
|
|
return (lhs->_value._boolean == rhs->_value._boolean);
|
|
case TRI_JSON_NUMBER:
|
|
return (lhs->_value._number == rhs->_value._number); // TODO: handle epsilon
|
|
case TRI_JSON_STRING:
|
|
return TRI_EqualString(lhs->_value._string.data, rhs->_value._string.data);
|
|
case TRI_JSON_LIST:
|
|
case TRI_JSON_ARRAY: {
|
|
// lists and arrays can be treated the same here
|
|
size_t n = lhs->_value._objects._length;
|
|
size_t i;
|
|
|
|
if (n != rhs->_value._objects._length) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < n; ++i) {
|
|
if (!IsSameValue(TRI_AtVector(&lhs->_value._objects, i), TRI_AtVector(&rhs->_value._objects, i))) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool IsInList (const TRI_json_t* const lhs, const TRI_json_t* const rhs) {
|
|
size_t n;
|
|
size_t i;
|
|
|
|
if (!lhs || !rhs || !rhs->_type == TRI_JSON_LIST) {
|
|
return false;
|
|
}
|
|
|
|
n = rhs->_value._objects._length;
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_json_t* r = (TRI_json_t*) TRI_AtVector(&rhs->_value._objects, i);
|
|
|
|
if (IsSameValue(lhs, r)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static char* AccessName (const TRI_aql_access_e type) {
|
|
switch (type) {
|
|
case TRI_AQL_ACCESS_ALL:
|
|
return "all";
|
|
case TRI_AQL_ACCESS_IMPOSSIBLE:
|
|
return "impossible";
|
|
case TRI_AQL_ACCESS_EXACT:
|
|
return "exact";
|
|
case TRI_AQL_ACCESS_LIST:
|
|
return "list";
|
|
case TRI_AQL_ACCESS_SINGLE_RANGE:
|
|
return "single range";
|
|
case TRI_AQL_ACCESS_DOUBLE_RANGE:
|
|
return "double range";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static TRI_aql_field_access_t* MergeAccess (TRI_aql_context_t* const context,
|
|
TRI_aql_field_access_t* lhs,
|
|
TRI_aql_field_access_t* rhs) {
|
|
assert(lhs);
|
|
assert(rhs);
|
|
|
|
assert(lhs->_fieldName != NULL);
|
|
assert(rhs->_fieldName != NULL);
|
|
|
|
if (lhs->_type > rhs->_type) {
|
|
// swap operands so they are always sorted
|
|
TRI_aql_field_access_t* tmp = lhs;
|
|
lhs = rhs;
|
|
rhs = tmp;
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_ALL) {
|
|
// TODO: free lhs
|
|
return rhs;
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_IMPOSSIBLE) {
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_EXACT) {
|
|
if (rhs->_type == TRI_AQL_ACCESS_EXACT) {
|
|
if (!IsSameValue(lhs->_value._exactValue, rhs->_value._exactValue)) {
|
|
// lhs and rhs values are non-identical
|
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue);
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
// lhs and rhs values are identical
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_LIST) {
|
|
if (!IsInList(lhs->_value._exactValue, rhs->_value._list)) {
|
|
// lhs value is not in rhs list
|
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue);
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
// lhs value is contained in rhs
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) {
|
|
// check if value is in range
|
|
int result = Compare(lhs->_value._exactValue, rhs->_value._singleRange._value);
|
|
|
|
bool contained = ((rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_EXCLUDED && result > 0) ||
|
|
(rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED && result >= 0) ||
|
|
(rhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_EXCLUDED && result < 0) ||
|
|
(rhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_INCLUDED && result <= 0));
|
|
|
|
if (!contained) {
|
|
// lhs value is not contained in rhs range
|
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue);
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
// lhs value is contained in rhs range
|
|
// TODO: free lhs
|
|
return rhs;
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) {
|
|
// check if value is in range
|
|
int result;
|
|
bool contained;
|
|
|
|
result = Compare(lhs->_value._exactValue, rhs->_value._between._lower._value);
|
|
|
|
contained = ((rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_EXCLUDED && result > 0) ||
|
|
(rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_INCLUDED && result >= 0));
|
|
|
|
if (!contained) {
|
|
// lhs value is not contained in rhs range
|
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue);
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
result = Compare(lhs->_value._exactValue, rhs->_value._between._upper._value);
|
|
|
|
contained = ((rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_EXCLUDED && result < 0) ||
|
|
(rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_INCLUDED && result <= 0));
|
|
|
|
if (!contained) {
|
|
// lhs value is not contained in rhs range
|
|
lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE;
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, lhs->_value._exactValue);
|
|
// TODO: free rhs
|
|
return lhs;
|
|
}
|
|
|
|
// lhs value is contained in rhs range
|
|
// TODO: free lhs
|
|
return rhs;
|
|
}
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_LIST) {
|
|
if (rhs->_type == TRI_AQL_ACCESS_LIST) {
|
|
// make a list of both
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) {
|
|
// check if value in range
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) {
|
|
// check if value in range
|
|
}
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) {
|
|
if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) {
|
|
// check if value in range
|
|
}
|
|
else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) {
|
|
// check if value in range
|
|
}
|
|
}
|
|
|
|
if (lhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) {
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static TRI_aql_field_access_t* CreateAccess (TRI_aql_context_t* const context,
|
|
const TRI_aql_field_name_t* const field,
|
|
const TRI_aql_node_type_e operator,
|
|
const TRI_aql_node_t* const node) {
|
|
TRI_aql_field_access_t* fieldAccess;
|
|
TRI_json_t* value;
|
|
|
|
value = TRI_NodeJsonAql(context, node);
|
|
if (!value) {
|
|
return NULL;
|
|
}
|
|
|
|
fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false);
|
|
if (fieldAccess == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
fieldAccess->_fieldName = TRI_DuplicateString(field->_name._buffer);
|
|
if (fieldAccess->_fieldName == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess);
|
|
return NULL;
|
|
}
|
|
|
|
if (operator == AQL_NODE_OPERATOR_BINARY_EQ) {
|
|
fieldAccess->_type = TRI_AQL_ACCESS_EXACT;
|
|
fieldAccess->_value._exactValue = value;
|
|
}
|
|
else if (operator == AQL_NODE_OPERATOR_BINARY_LT) {
|
|
fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE;
|
|
fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_EXCLUDED;
|
|
fieldAccess->_value._singleRange._value = value;
|
|
}
|
|
else if (operator == AQL_NODE_OPERATOR_BINARY_LE) {
|
|
fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE;
|
|
fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_INCLUDED;
|
|
fieldAccess->_value._singleRange._value = value;
|
|
}
|
|
else if (operator == AQL_NODE_OPERATOR_BINARY_GT) {
|
|
fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE;
|
|
fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_EXCLUDED;
|
|
fieldAccess->_value._singleRange._value = value;
|
|
}
|
|
else if (operator == AQL_NODE_OPERATOR_BINARY_GE) {
|
|
fieldAccess->_type = TRI_AQL_ACCESS_SINGLE_RANGE;
|
|
fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_INCLUDED;
|
|
fieldAccess->_value._singleRange._value = value;
|
|
}
|
|
else {
|
|
assert(false);
|
|
}
|
|
|
|
return fieldAccess;
|
|
}
|
|
|
|
|
|
static void CreateFieldAccess (TRI_aql_context_t* const context,
|
|
const TRI_aql_field_name_t* const field,
|
|
const TRI_aql_node_type_e operator,
|
|
const TRI_aql_node_t* const node) {
|
|
TRI_aql_field_access_t* previous;
|
|
TRI_aql_field_access_t* fieldAccess;
|
|
|
|
if (!field || !field->_name._buffer) {
|
|
return;
|
|
}
|
|
|
|
fieldAccess = CreateAccess(context, field, operator, node);
|
|
if (!fieldAccess) {
|
|
// TODO: mark failure
|
|
return;
|
|
}
|
|
|
|
previous = (TRI_aql_field_access_t*) TRI_LookupByKeyAssociativePointer(&context->_ranges, field->_name._buffer);
|
|
if (previous) {
|
|
printf("ENTER MERGE\n");
|
|
TRI_aql_field_access_t* merged = MergeAccess(context, fieldAccess, previous);
|
|
printf("LEAVE MERGE\n");
|
|
|
|
// TODO:
|
|
// free previous
|
|
// free fieldAccess
|
|
if (merged) {
|
|
TRI_InsertKeyAssociativePointer(&context->_ranges, fieldAccess->_fieldName, merged, true);
|
|
printf("MERGE1\n");
|
|
}
|
|
|
|
// previous access exists, must merge
|
|
// merge(previous, new)
|
|
// TRI_InsertKeyAssociativePointer(&context
|
|
}
|
|
else {
|
|
// no previous access exists, no need to merge
|
|
TRI_InsertKeyAssociativePointer(&context->_ranges, fieldAccess->_fieldName, fieldAccess, false);
|
|
printf("INSERT1\n");
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup Ahuacatl
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_field_name_t* GetFieldName (TRI_aql_context_t* const context,
|
|
const TRI_aql_node_t* const node) {
|
|
if (node->_type == AQL_NODE_ATTRIBUTE_ACCESS) {
|
|
TRI_aql_field_name_t* field = GetFieldName(context, TRI_AQL_NODE_MEMBER(node, 0));
|
|
|
|
if (!field) {
|
|
return NULL;
|
|
}
|
|
|
|
TRI_AppendCharStringBuffer(&field->_name, '.');
|
|
TRI_AppendStringStringBuffer(&field->_name, TRI_AQL_NODE_STRING(node));
|
|
return field;
|
|
}
|
|
else if (node->_type == AQL_NODE_REFERENCE) {
|
|
TRI_aql_field_name_t* field;
|
|
|
|
field = (TRI_aql_field_name_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_name_t), false);
|
|
|
|
if (!field) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
field->_variable = TRI_AQL_NODE_STRING(node);
|
|
TRI_InitStringBuffer(&field->_name, TRI_UNKNOWN_MEM_ZONE);
|
|
TRI_AppendStringStringBuffer(&field->_name, TRI_AQL_NODE_STRING(node));
|
|
|
|
return field;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inspect a filter expression
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void InspectFilter (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
printf("ITERATION SOMETHING %s\n", TRI_NodeNameAql(node->_type));
|
|
if (node->_type == AQL_NODE_OPERATOR_UNARY_NOT) {
|
|
return;
|
|
}
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_OR) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
|
|
InspectFilter(context, lhs);
|
|
InspectFilter(context, rhs);
|
|
}
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_AND) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
|
|
InspectFilter(context, lhs);
|
|
InspectFilter(context, rhs);
|
|
}
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_EQ ||
|
|
// node->_type == AQL_NODE_OPERATOR_BINARY_NE ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_LT ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_LE ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_GT ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_GE) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
|
|
if (lhs->_type == AQL_NODE_ATTRIBUTE_ACCESS) {
|
|
TRI_aql_field_name_t* field = GetFieldName(context, lhs);
|
|
|
|
if (field) {
|
|
CreateFieldAccess(context, field, node->_type, rhs);
|
|
TRI_DestroyStringBuffer(&field->_name);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, field);
|
|
}
|
|
}
|
|
else if (lhs->_type == AQL_NODE_ATTRIBUTE_ACCESS) {
|
|
TRI_aql_field_name_t* field = GetFieldName(context, rhs);
|
|
|
|
if (field) {
|
|
CreateFieldAccess(context, field, node->_type, lhs);
|
|
TRI_DestroyStringBuffer(&field->_name);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, field);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create javascript function code for a relational operation
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_string_buffer_t* RelationCode (const char* const name,
|
|
const TRI_aql_node_t* const lhs,
|
|
const TRI_aql_node_t* const rhs) {
|
|
TRI_string_buffer_t* buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE);
|
|
|
|
if (!lhs || !rhs) {
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, "(function(){return AHUACATL_RELATIONAL_") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, name) != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, "(") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (!TRI_NodeJavascriptAql(buffer, lhs)) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendCharStringBuffer(buffer, ',') != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (!TRI_NodeJavascriptAql(buffer, rhs)) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, ");})") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief create javascript function code for a function call
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_string_buffer_t* FcallCode (const char* const name,
|
|
const TRI_aql_node_t* const args) {
|
|
TRI_string_buffer_t* buffer = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE);
|
|
size_t i;
|
|
size_t n;
|
|
|
|
if (!buffer) {
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, "(function(){return AHUACATL_FCALL(") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, name) != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, ",[") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
n = args->_members._length;
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_node_t* arg = (TRI_aql_node_t*) args->_members._buffer[i];
|
|
if (i > 0) {
|
|
if (TRI_AppendCharStringBuffer(buffer, ',') != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!TRI_NodeJavascriptAql(buffer, arg)) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (TRI_AppendStringStringBuffer(buffer, "]);})") != TRI_ERROR_NO_ERROR) {
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, buffer);
|
|
return NULL;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a function call
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseFcall (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* args = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_function_t* function;
|
|
TRI_js_exec_context_t* execContext;
|
|
TRI_string_buffer_t* code;
|
|
TRI_json_t* json;
|
|
size_t i;
|
|
size_t n;
|
|
|
|
function = (TRI_aql_function_t*) TRI_AQL_NODE_DATA(node);
|
|
assert(function);
|
|
|
|
// check if function is deterministic
|
|
if (!function->_isDeterministic) {
|
|
return node;
|
|
}
|
|
|
|
// check if function call arguments are deterministic
|
|
n = args->_members._length;
|
|
for (i = 0; i < n; ++i) {
|
|
TRI_aql_node_t* arg = (TRI_aql_node_t*) args->_members._buffer[i];
|
|
|
|
if (!arg || !TRI_IsConstantValueNodeAql(arg)) {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
// all arguments are constants
|
|
// create the function code
|
|
code = FcallCode(function->_internalName, args);
|
|
if (!code) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return node;
|
|
}
|
|
|
|
// execute the function code
|
|
execContext = TRI_CreateExecutionContext(code->_buffer);
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, code);
|
|
|
|
if (!execContext) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return node;
|
|
}
|
|
|
|
json = TRI_ExecuteResultContext(execContext);
|
|
TRI_FreeExecutionContext(execContext);
|
|
if (!json) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_SCRIPT, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
// use the constant values instead of the function call node
|
|
node = TRI_JsonNodeAql(context, json);
|
|
if (!node) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
}
|
|
|
|
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json);
|
|
|
|
LOG_TRACE("optimised function call");
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a sort expression
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseSort (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* list = TRI_AQL_NODE_MEMBER(node, 0);
|
|
size_t i, n;
|
|
|
|
if (!list) {
|
|
return node;
|
|
}
|
|
|
|
i = 0;
|
|
n = list->_members._length;
|
|
while (i < n) {
|
|
// sort element
|
|
TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(list, i);
|
|
TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(element, 0);
|
|
|
|
// check if the sort element is constant
|
|
if (!expression || !TRI_IsConstantValueNodeAql(expression)) {
|
|
++i;
|
|
continue;
|
|
}
|
|
|
|
// sort element is constant so it can be removed
|
|
TRI_RemoveVectorPointer(&list->_members, i);
|
|
--n;
|
|
|
|
LOG_TRACE("optimised away sort element");
|
|
}
|
|
|
|
if (n == 0) {
|
|
// no members left => sort removed
|
|
LOG_TRACE("optimised away sort");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a filter expression
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseFilter (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0);
|
|
bool result;
|
|
|
|
if (!expression) {
|
|
return node;
|
|
}
|
|
|
|
if (!TRI_IsConstantValueNodeAql(expression)) {
|
|
//InspectFilter(context, expression);
|
|
return node;
|
|
}
|
|
|
|
result = TRI_GetBooleanNodeValueAql(expression);
|
|
if (result) {
|
|
// filter expression is always true => remove it
|
|
LOG_TRACE("optimised away constant filter");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise an arithmetic operation with one operand
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseUnaryArithmeticOperation (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* operand = TRI_AQL_NODE_MEMBER(node, 0);
|
|
|
|
if (!operand || !TRI_IsConstantValueNodeAql(operand)) {
|
|
return node;
|
|
}
|
|
|
|
if (!TRI_IsNumericValueNodeAql(operand)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
assert(node->_type == AQL_NODE_OPERATOR_UNARY_PLUS ||
|
|
node->_type == AQL_NODE_OPERATOR_UNARY_MINUS);
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_UNARY_PLUS) {
|
|
// + number => number
|
|
node = operand;
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_UNARY_MINUS) {
|
|
// - number => eval!
|
|
node = TRI_CreateNodeValueDoubleAql(context, - TRI_GetNumericNodeValueAql(operand));
|
|
if (!node) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a boolean operation with one operand
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseUnaryLogicalOperation (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* operand = TRI_AQL_NODE_MEMBER(node, 0);
|
|
|
|
if (!operand || !TRI_IsConstantValueNodeAql(operand)) {
|
|
return node;
|
|
}
|
|
|
|
if (!TRI_IsBooleanValueNodeAql(operand)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
assert(node->_type == AQL_NODE_OPERATOR_UNARY_NOT);
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_UNARY_NOT) {
|
|
// ! bool => evaluate
|
|
node = TRI_CreateNodeValueBoolAql(context, ! TRI_GetBooleanNodeValueAql(operand));
|
|
if (!node) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
}
|
|
|
|
LOG_TRACE("optimised away unary logical operation");
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a boolean operation with two operands
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseBinaryLogicalOperation (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
bool isEligibleLhs;
|
|
bool isEligibleRhs;
|
|
bool lhsValue;
|
|
|
|
if (!lhs || !rhs) {
|
|
return node;
|
|
}
|
|
|
|
isEligibleLhs = TRI_IsConstantValueNodeAql(lhs);
|
|
isEligibleRhs = TRI_IsConstantValueNodeAql(rhs);
|
|
|
|
if (isEligibleLhs && !TRI_IsBooleanValueNodeAql(lhs)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
if (isEligibleRhs && !TRI_IsBooleanValueNodeAql(rhs)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_LOGICAL_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
if (!isEligibleLhs || !isEligibleRhs) {
|
|
return node;
|
|
}
|
|
|
|
lhsValue = TRI_GetBooleanNodeValueAql(lhs);
|
|
|
|
assert(node->_type == AQL_NODE_OPERATOR_BINARY_AND ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_OR);
|
|
|
|
LOG_TRACE("optimised away binary logical operation");
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_AND) {
|
|
if (lhsValue) {
|
|
// if (true && rhs) => rhs
|
|
return rhs;
|
|
}
|
|
|
|
// if (false && rhs) => false
|
|
return lhs;
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_OR) {
|
|
if (lhsValue) {
|
|
// if (true || rhs) => true
|
|
return lhs;
|
|
}
|
|
|
|
// if (false || rhs) => rhs
|
|
return rhs;
|
|
}
|
|
|
|
assert(false);
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise a relational operation with two operands
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseBinaryRelationalOperation (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
TRI_js_exec_context_t* execContext;
|
|
TRI_string_buffer_t* code;
|
|
TRI_json_t* json;
|
|
char* func;
|
|
|
|
if (!lhs || !TRI_IsConstantValueNodeAql(lhs) || !rhs || !TRI_IsConstantValueNodeAql(rhs)) {
|
|
return node;
|
|
}
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_EQ) {
|
|
func = "EQUAL";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_NE) {
|
|
func = "UNEQUAL";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_GT) {
|
|
func = "GREATER";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_GE) {
|
|
func = "GREATER_EQUAL";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_LT) {
|
|
func = "LESS";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_LE) {
|
|
func = "LESS_EQUAL";
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_IN) {
|
|
func = "IN";
|
|
}
|
|
|
|
code = RelationCode(func, lhs, rhs);
|
|
if (!code) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return node;
|
|
}
|
|
|
|
// execute the function code
|
|
execContext = TRI_CreateExecutionContext(code->_buffer);
|
|
TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, code);
|
|
|
|
if (!execContext) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return node;
|
|
}
|
|
|
|
json = TRI_ExecuteResultContext(execContext);
|
|
TRI_FreeExecutionContext(execContext);
|
|
if (!json) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_SCRIPT, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
// use the constant values instead of the function call node
|
|
node = TRI_JsonNodeAql(context, json);
|
|
if (!node) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
}
|
|
|
|
LOG_TRACE("optimised away binary relational operation");
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief optimise an arithmetic operation with two operands
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* OptimiseBinaryArithmeticOperation (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* lhs = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* rhs = TRI_AQL_NODE_MEMBER(node, 1);
|
|
bool isEligibleLhs;
|
|
bool isEligibleRhs;
|
|
double value;
|
|
|
|
if (!lhs || !rhs) {
|
|
return node;
|
|
}
|
|
|
|
isEligibleLhs = TRI_IsConstantValueNodeAql(lhs);
|
|
isEligibleRhs = TRI_IsConstantValueNodeAql(rhs);
|
|
|
|
if (isEligibleLhs && !TRI_IsNumericValueNodeAql(lhs)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
|
|
if (isEligibleRhs && !TRI_IsNumericValueNodeAql(rhs)) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_INVALID_ARITHMETIC_VALUE, NULL);
|
|
return node;
|
|
}
|
|
|
|
if (!isEligibleLhs || !isEligibleRhs) {
|
|
return node;
|
|
}
|
|
|
|
assert(node->_type == AQL_NODE_OPERATOR_BINARY_PLUS ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_MINUS ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_TIMES ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_DIV ||
|
|
node->_type == AQL_NODE_OPERATOR_BINARY_MOD);
|
|
|
|
if (node->_type == AQL_NODE_OPERATOR_BINARY_PLUS) {
|
|
value = TRI_GetNumericNodeValueAql(lhs) + TRI_GetNumericNodeValueAql(rhs);
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_MINUS) {
|
|
value = TRI_GetNumericNodeValueAql(lhs) - TRI_GetNumericNodeValueAql(rhs);
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_TIMES) {
|
|
value = TRI_GetNumericNodeValueAql(lhs) * TRI_GetNumericNodeValueAql(rhs);
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_DIV) {
|
|
if (TRI_GetNumericNodeValueAql(rhs) == 0.0) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISON_BY_ZERO, NULL);
|
|
return node;
|
|
}
|
|
value = TRI_GetNumericNodeValueAql(lhs) / TRI_GetNumericNodeValueAql(rhs);
|
|
}
|
|
else if (node->_type == AQL_NODE_OPERATOR_BINARY_MOD) {
|
|
if (TRI_GetNumericNodeValueAql(rhs) == 0.0) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_QUERY_DIVISON_BY_ZERO, NULL);
|
|
return node;
|
|
}
|
|
value = fmod(TRI_GetNumericNodeValueAql(lhs), TRI_GetNumericNodeValueAql(rhs));
|
|
}
|
|
|
|
node = TRI_CreateNodeValueDoubleAql(context, value);
|
|
if (!node) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
LOG_TRACE("optimised away binary arithmetic operation");
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief mark a for node
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* MarkFor (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_node_t* nameNode = TRI_AQL_NODE_MEMBER(node, 0);
|
|
TRI_aql_node_t* expressionNode = TRI_AQL_NODE_MEMBER(node, 1);
|
|
|
|
if (!nameNode || !expressionNode) {
|
|
return node;
|
|
}
|
|
|
|
if (expressionNode->_type == AQL_NODE_COLLECTION) {
|
|
char* varName = TRI_AQL_NODE_STRING(nameNode);
|
|
char* collectionName = TRI_AQL_NODE_STRING(expressionNode);
|
|
|
|
if (!varName || !collectionName) {
|
|
return node;
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief fold constants in a node
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static TRI_aql_node_t* ModifyNode (void* data, TRI_aql_node_t* node) {
|
|
TRI_aql_context_t* context = (TRI_aql_context_t*) data;
|
|
|
|
if (!node) {
|
|
return NULL;
|
|
}
|
|
|
|
switch (node->_type) {
|
|
case AQL_NODE_OPERATOR_UNARY_PLUS:
|
|
case AQL_NODE_OPERATOR_UNARY_MINUS:
|
|
return OptimiseUnaryArithmeticOperation(context, node);
|
|
case AQL_NODE_OPERATOR_UNARY_NOT:
|
|
return OptimiseUnaryLogicalOperation(context, node);
|
|
case AQL_NODE_OPERATOR_BINARY_AND:
|
|
case AQL_NODE_OPERATOR_BINARY_OR:
|
|
return OptimiseBinaryLogicalOperation(context, node);
|
|
case AQL_NODE_OPERATOR_BINARY_EQ:
|
|
case AQL_NODE_OPERATOR_BINARY_NE:
|
|
case AQL_NODE_OPERATOR_BINARY_LT:
|
|
case AQL_NODE_OPERATOR_BINARY_LE:
|
|
case AQL_NODE_OPERATOR_BINARY_GT:
|
|
case AQL_NODE_OPERATOR_BINARY_GE:
|
|
case AQL_NODE_OPERATOR_BINARY_IN:
|
|
return OptimiseBinaryRelationalOperation(context, node);
|
|
case AQL_NODE_OPERATOR_BINARY_PLUS:
|
|
case AQL_NODE_OPERATOR_BINARY_MINUS:
|
|
case AQL_NODE_OPERATOR_BINARY_TIMES:
|
|
case AQL_NODE_OPERATOR_BINARY_DIV:
|
|
case AQL_NODE_OPERATOR_BINARY_MOD:
|
|
return OptimiseBinaryArithmeticOperation(context, node);
|
|
case AQL_NODE_SORT:
|
|
return OptimiseSort(context, node);
|
|
case AQL_NODE_FILTER:
|
|
return OptimiseFilter(context, node);
|
|
case AQL_NODE_FCALL:
|
|
return OptimiseFcall(context, node);
|
|
case AQL_NODE_FOR:
|
|
// return MarkFor(context, node);
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup Ahuacatl
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief fold constants recursively
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_aql_node_t* TRI_FoldConstantsAql (TRI_aql_context_t* const context,
|
|
TRI_aql_node_t* node) {
|
|
TRI_aql_modify_tree_walker_t* walker;
|
|
|
|
// todo: must free
|
|
TRI_InitAssociativePointer(&context->_ranges,
|
|
TRI_UNKNOWN_MEM_ZONE,
|
|
&TRI_HashStringKeyAssociativePointer,
|
|
&HashFieldAccess,
|
|
&EqualFieldAccess,
|
|
NULL);
|
|
|
|
walker = TRI_CreateModifyTreeWalkerAql((void*) context, &ModifyNode);
|
|
if (!walker) {
|
|
TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL);
|
|
return node;
|
|
}
|
|
|
|
node = TRI_ModifyWalkTreeAql(walker, node);
|
|
|
|
TRI_FreeModifyTreeWalkerAql(walker);
|
|
/*
|
|
size_t i;
|
|
for (i = 0; i < context->_ranges._nrAlloc; ++i) {
|
|
TRI_aql_field_access_t* fieldAccess = context->_ranges._table[i];
|
|
|
|
if (!fieldAccess) {
|
|
continue;
|
|
}
|
|
|
|
printf("\nFIELD ACCESS\n- FIELD: %s\n",fieldAccess->_fieldName);
|
|
printf("- TYPE: %s\n", AccessName(fieldAccess->_type));
|
|
if (fieldAccess->_type == TRI_AQL_ACCESS_EXACT) {
|
|
TRI_string_buffer_t b;
|
|
TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE);
|
|
TRI_StringifyJson(&b, fieldAccess->_value._exactValue);
|
|
|
|
printf("- VALUE: %s\n", b._buffer);
|
|
}
|
|
else if (fieldAccess->_type == TRI_AQL_ACCESS_SINGLE_RANGE) {
|
|
TRI_string_buffer_t b;
|
|
TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE);
|
|
TRI_StringifyJson(&b, fieldAccess->_value._singleRange._value);
|
|
|
|
printf("- VALUE: %s\n", b._buffer);
|
|
}
|
|
}
|
|
*/
|
|
return node;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
|
// End:
|