diff --git a/Ahuacatl/ahuacatl-access-optimizer.c b/Ahuacatl/ahuacatl-access-optimizer.c new file mode 100644 index 0000000000..490fff59ab --- /dev/null +++ b/Ahuacatl/ahuacatl-access-optimizer.c @@ -0,0 +1,1509 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Ahuacatl, access optimizer +/// +/// @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-access-optimizer.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief hash a field name +//////////////////////////////////////////////////////////////////////////////// + +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 field name 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); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the name of an access type +//////////////////////////////////////////////////////////////////////////////// + +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_RANGE_SINGLE: + return "single range"; + case TRI_AQL_ACCESS_RANGE_DOUBLE: + return "double range"; + default: + return "unknown"; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief free access member data, but do not free the access struct itself +//////////////////////////////////////////////////////////////////////////////// + +static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) { + assert(fieldAccess); + + switch (fieldAccess->_type) { + case TRI_AQL_ACCESS_EXACT: + case TRI_AQL_ACCESS_LIST: + if (fieldAccess->_value._value) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value); + } + break; + case TRI_AQL_ACCESS_RANGE_SINGLE: + if (fieldAccess->_value._singleRange._value) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._singleRange._value); + } + break; + case TRI_AQL_ACCESS_RANGE_DOUBLE: + if (fieldAccess->_value._between._lower._value) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._lower._value); + } + if (fieldAccess->_value._between._upper._value) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._upper._value); + } + break; + + case TRI_AQL_ACCESS_ALL: + case TRI_AQL_ACCESS_IMPOSSIBLE: + default: { + // nada + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief free access structure with its members and the pointer +//////////////////////////////////////////////////////////////////////////////// + +static void FreeAccess (TRI_aql_context_t* const context, + TRI_aql_field_access_t* const fieldAccess) { + assert(fieldAccess); + + FreeAccessMembers(fieldAccess); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_fieldName); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is an impossible range, so the result is also the +/// impossible range +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndImpossible (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + // impossible merged with anything just returns impossible + assert(lhs->_type == TRI_AQL_ACCESS_IMPOSSIBLE); + FreeAccess(context, rhs); + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is all items, so the result is the other operand +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndAll (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + // all merged with anything just returns the other side + assert(lhs->_type == TRI_AQL_ACCESS_ALL); + FreeAccess(context, lhs); + + return rhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is the exact match, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndExact (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_EXACT); + + if (rhs->_type == TRI_AQL_ACCESS_EXACT) { + // check if values are identical + bool isSame = TRI_CheckSameValueJson(lhs->_value._value, rhs->_value._value); + + FreeAccess(context, rhs); + + if (!isSame) { + // lhs and rhs values are non-identical, return impossible + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_LIST) { + // check if lhs is contained in rhs list + bool inList = TRI_CheckInListJson(lhs->_value._value, rhs->_value._value); + + FreeAccess(context, rhs); + + if (!inList) { + // lhs value is not in rhs list, return impossible + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + // check if value is in range + int result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + + // lhs value is contained in rhs range, simply return rhs + FreeAccess(context, lhs); + + return rhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { + // check if value is in range + int result; + bool contained; + + // compare lower end + result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + + // compare upper end + result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + + // lhs value is contained in rhs range, return rhs + FreeAccess(context, lhs); + + return rhs; + } + + assert(false); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is a value list, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndList (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_LIST); + + if (rhs->_type == TRI_AQL_ACCESS_LIST) { + // make a list of both + TRI_json_t* merged = TRI_IntersectListsJson(lhs->_value._value, rhs->_value._value, true); + if (!merged) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return lhs; + } + + FreeAccessMembers(lhs); + FreeAccess(context, rhs); + + if (merged->_value._objects._length > 0) { + // merged list is not empty + lhs->_value._value = merged; + } + else { + // merged list is empty + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, merged); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + TRI_json_t* listInRange; + + if (rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED || + rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_EXCLUDED) { + listInRange = TRI_BetweenListJson(lhs->_value._singleRange._value, + rhs->_value._singleRange._value, + rhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED, + NULL, + true); + } + else { + listInRange = TRI_BetweenListJson(lhs->_value._singleRange._value, + NULL, + true, + rhs->_value._singleRange._value, + rhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_INCLUDED); + } + + if (!listInRange) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + FreeAccessMembers(rhs); + return lhs; + } + + FreeAccessMembers(lhs); + FreeAccess(context, rhs); + + if (listInRange->_value._objects._length > 0) { + // merged list is not empty + lhs->_value._value = listInRange; + } + else { + // merged list is empty + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, listInRange); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { + // check if value in range + TRI_json_t* listInRange; + + listInRange = TRI_BetweenListJson(lhs->_value._singleRange._value, + rhs->_value._between._lower._value, + rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_INCLUDED, + rhs->_value._between._upper._value, + rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_INCLUDED); + + if (!listInRange) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + FreeAccessMembers(rhs); + return lhs; + } + + FreeAccessMembers(lhs); + FreeAccess(context, rhs); + + if (listInRange->_value._objects._length > 0) { + // merged list is not empty + lhs->_value._value = listInRange; + } + else { + // merged list is empty + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, listInRange); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + assert(false); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is a single range, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndRangeSingle (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE); + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + TRI_json_t* lhsValue; + TRI_json_t* rhsValue; + TRI_aql_range_e lhsType; + TRI_aql_range_e rhsType; + int compareResult; + + if (lhs->_value._singleRange._type > rhs->_value._singleRange._type) { + // swap operands so they are always sorted + TRI_aql_field_access_t* tmp = lhs; + lhs = rhs; + rhs = tmp; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._singleRange._value); + lhsType = lhs->_value._singleRange._type; + rhsType = rhs->_value._singleRange._type; + lhsValue = lhs->_value._singleRange._value; + rhsValue = rhs->_value._singleRange._value; + + // check if ranges overlap + if ((lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_EXCLUDED) || + (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED)) { + // > && > + // >= && >= + if (compareResult > 0) { + // lhs > rhs + FreeAccess(context, rhs); + + return lhs; + } + else { + FreeAccess(context, lhs); + + return rhs; + } + } + else if ((lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED) || + (lhsType == TRI_AQL_RANGE_UPPER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED)) { + // < && < + // <= && <= + if (compareResult > 0) { + // lhs > rhs + FreeAccess(context, lhs); + + return rhs; + } + else { + FreeAccess(context, rhs); + + return lhs; + } + } + else if (lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED) { + // > && >= + if (compareResult >= 0) { + // lhs > rhs + FreeAccess(context, rhs); + + return lhs; + } + else { + FreeAccess(context, lhs); + + return rhs; + } + } + else if (lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED) { + // > && < + if (compareResult < 0) { + // save pointers + lhsValue = lhs->_value._singleRange._value; + rhsValue = rhs->_value._singleRange._value; + rhs->_value._singleRange._value = NULL; + FreeAccess(context, rhs); + + lhs->_type = TRI_AQL_ACCESS_RANGE_DOUBLE; + lhs->_value._between._lower._type = lhsType; + lhs->_value._between._lower._value = lhsValue; + lhs->_value._between._upper._type = rhsType; + lhs->_value._between._upper._value = rhsValue; + + return lhs; + } + else { + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + } + else if ((lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) || + (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED)) { + // > && <= + // >= && < + if (compareResult < 0) { + // save pointers + lhsValue = lhs->_value._singleRange._value; + rhsValue = rhs->_value._singleRange._value; + rhs->_value._singleRange._value = NULL; + FreeAccess(context, rhs); + + lhs->_type = TRI_AQL_ACCESS_RANGE_DOUBLE; + lhs->_value._between._lower._type = lhsType; + lhs->_value._between._lower._value = lhsValue; + lhs->_value._between._upper._type = rhsType; + lhs->_value._between._upper._value = rhsValue; + + return lhs; + } + else { + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + } + else if (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) { + // >= && <= + if (compareResult < 0) { + // save pointers + lhsValue = lhs->_value._singleRange._value; + rhsValue = rhs->_value._singleRange._value; + rhs->_value._singleRange._value = NULL; + FreeAccess(context, rhs); + + lhs->_type = TRI_AQL_ACCESS_RANGE_DOUBLE; + lhs->_value._between._lower._type = lhsType; + lhs->_value._between._lower._value = lhsValue; + lhs->_value._between._upper._type = rhsType; + lhs->_value._between._upper._value = rhsValue; + + return lhs; + } + else if (compareResult == 0) { + FreeAccess(context, rhs); + + // save pointer + lhsValue = lhs->_value._singleRange._value; + lhs->_type = TRI_AQL_ACCESS_EXACT; + lhs->_value._value = lhsValue; + return lhs; + } + else { + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + } + else if (lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) { + // < && <= + if (compareResult <= 0) { + FreeAccess(context, rhs); + + return lhs; + } + else { + FreeAccess(context, lhs); + + return rhs; + } + } + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { + int compareResult; + + if (lhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_EXCLUDED) { + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._upper._value); + if (compareResult >= 0) { + // lhs value is bigger than rhs upper bound + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._lower._value); + if (compareResult > 0) { + // lhs value is bigger than rhs lower bound + rhs->_value._between._lower._type = lhs->_value._singleRange._type; + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, rhs->_value._between._lower._value); + rhs->_value._between._lower._value = lhs->_value._singleRange._value; + lhs->_value._singleRange._value = NULL; + } + else if (compareResult == 0) { + // lhs value is equal to rhs lower bound + if (rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_INCLUDED) { + rhs->_value._between._lower._type = TRI_AQL_RANGE_LOWER_EXCLUDED; + } + } + // else intentionally left out + + FreeAccess(context, lhs); + + return rhs; + } + + if (lhs->_value._singleRange._type == TRI_AQL_RANGE_LOWER_INCLUDED) { + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._upper._value); + if (compareResult > 0 || (compareResult == 0 && rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_EXCLUDED)) { + // lhs value is bigger than rhs upper bound + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + else if (compareResult == 0) { + // save pointer + TRI_json_t* value = lhs->_value._singleRange._value; + + FreeAccess(context, rhs); + lhs->_value._singleRange._value = NULL; + FreeAccessMembers(lhs); + + lhs->_value._singleRange._type = TRI_AQL_ACCESS_EXACT; + lhs->_value._singleRange._value = value; + + return lhs; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._lower._value); + if (compareResult > 0) { + // lhs value is bigger than rhs lower bound + rhs->_value._between._lower._type = lhs->_value._singleRange._type; + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, rhs->_value._between._lower._value); + rhs->_value._between._lower._value = lhs->_value._singleRange._value; + lhs->_value._singleRange._value = NULL; + } + // else intentionally left out + + FreeAccess(context, lhs); + + return rhs; + } + + if (lhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_EXCLUDED) { + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._lower._value); + if (compareResult <= 0) { + // lhs value is smaller than rhs lower bound + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._upper._value); + if (compareResult < 0) { + // lhs value is smaller than rhs upper bound + rhs->_value._between._upper._type = lhs->_value._singleRange._type; + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, rhs->_value._between._upper._value); + rhs->_value._between._upper._value = lhs->_value._singleRange._value; + lhs->_value._singleRange._value = NULL; + } + else if (compareResult == 0) { + // lhs value is equal to rhs lower bound + if (rhs->_value._between._upper._type == TRI_AQL_RANGE_UPPER_INCLUDED) { + rhs->_value._between._upper._type = TRI_AQL_RANGE_UPPER_EXCLUDED; + } + } + // else intentionally left out + + FreeAccess(context, lhs); + + return rhs; + } + + if (lhs->_value._singleRange._type == TRI_AQL_RANGE_UPPER_INCLUDED) { + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._lower._value); + if (compareResult < 0 || (compareResult == 0 && rhs->_value._between._lower._type == TRI_AQL_RANGE_LOWER_EXCLUDED)) { + // lhs value is smaller than rhs lower bound + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + + return lhs; + } + else if (compareResult == 0) { + // save pointer + TRI_json_t* value = lhs->_value._singleRange._value; + + FreeAccess(context, rhs); + lhs->_value._singleRange._value = NULL; + FreeAccessMembers(lhs); + + lhs->_value._singleRange._type = TRI_AQL_ACCESS_EXACT; + lhs->_value._singleRange._value = value; + + return lhs; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._between._upper._value); + if (compareResult < 0) { + // lhs value is smaller than rhs upper bound + rhs->_value._between._upper._type = lhs->_value._singleRange._type; + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, rhs->_value._between._upper._value); + rhs->_value._between._upper._value = lhs->_value._singleRange._value; + lhs->_value._singleRange._value = NULL; + } + // else intentionally left out + + FreeAccess(context, lhs); + + return rhs; + } + + return lhs; + } + + assert(false); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical AND +/// +/// left hand operand is a double range, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAndRangeDouble (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(false); + /* this should never be called. let's see if this assumption is true or not */ + assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE); + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is an impossible range, so the result is the right hand +/// operator +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrImpossible (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + // impossible merged with anything just returns the other side + assert(lhs->_type == TRI_AQL_ACCESS_IMPOSSIBLE); + FreeAccess(context, lhs); + + return rhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is all items, so the result is also all +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrAll (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + // all merged with anything just returns all + assert(lhs->_type == TRI_AQL_ACCESS_ALL); + FreeAccess(context, rhs); + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is the exact match, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrExact (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_EXACT); + + if (rhs->_type == TRI_AQL_ACCESS_EXACT) { + TRI_json_t* result; + + // check if values are identical + if ( TRI_CheckSameValueJson(lhs->_value._value, rhs->_value._value)) { + // lhs and rhs values are identical, return lhs + FreeAccess(context, rhs); + + return lhs; + } + + // make a list with both values + result = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + if (!result) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + FreeAccess(context, rhs); + + return lhs; + } + + TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, result, lhs->_value._value); + TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, result, rhs->_value._value); + TRI_SortListJson(result); + + FreeAccess(context, lhs); + FreeAccessMembers(rhs); + + rhs->_type = TRI_AQL_ACCESS_LIST; + rhs->_value._value = result; + + return rhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_LIST) { + // check if lhs is contained in rhs list + if (TRI_CheckInListJson(lhs->_value._value, rhs->_value._value)) { + // lhs is contained in rhs, we can return rhs + FreeAccess(context, lhs); + + return rhs; + } + + // lhs is not contained, we need to add it to the list + TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, rhs->_value._value, lhs->_value._value); + TRI_SortListJson(rhs->_value._value); + + FreeAccess(context, lhs); + + return rhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + // check if value is in range + int result = TRI_CompareValuesJson(lhs->_value._value, 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, return all + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; + } + + // lhs value is contained in rhs range, simply return rhs + FreeAccess(context, lhs); + + return rhs; + } + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { + // check if value is in range + int result; + bool contained; + + // compare lower end + result = TRI_CompareValuesJson(lhs->_value._value, 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, return all + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; + } + + // compare upper end + result = TRI_CompareValuesJson(lhs->_value._value, 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, return all + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; + } + + // lhs value is contained in rhs range, return rhs + FreeAccess(context, lhs); + + return rhs; + } + + assert(false); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is a value list, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrList (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_LIST); + + if (rhs->_type == TRI_AQL_ACCESS_LIST) { + // make a list of both + TRI_json_t* merged = TRI_UnionizeListsJson(lhs->_value._value, rhs->_value._value, true); + if (!merged) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return lhs; + } + + FreeAccessMembers(lhs); + FreeAccess(context, rhs); + + if (merged->_value._objects._length > 0) { + // merged list is not empty + lhs->_value._value = merged; + } + else { + // merged list is empty + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, merged); + lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; + } + + return lhs; + } + + // for all other combinations, we give up + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is a single range, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrRangeSingle (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(lhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE); + + if (rhs->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + TRI_aql_range_e lhsType; + TRI_aql_range_e rhsType; + int compareResult; + + if (lhs->_value._singleRange._type > rhs->_value._singleRange._type) { + // swap operands so they are always sorted + TRI_aql_field_access_t* tmp = lhs; + lhs = rhs; + rhs = tmp; + } + + compareResult = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._singleRange._value); + lhsType = lhs->_value._singleRange._type; + rhsType = rhs->_value._singleRange._type; + + // check if ranges overlap + if ((lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_EXCLUDED) || + (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED)) { + // > && > + // >= && >= + if (compareResult > 0) { + // lhs > rhs + FreeAccess(context, lhs); + + return rhs; + } + else { + FreeAccess(context, rhs); + + return lhs; + } + } + else if ((lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED) || + (lhsType == TRI_AQL_RANGE_UPPER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED)) { + // < && < + // <= && <= + if (compareResult > 0) { + // lhs > rhs + FreeAccess(context, rhs); + + return lhs; + } + else { + FreeAccess(context, lhs); + + return rhs; + } + } + else if (lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED) { + // > && >= + if (compareResult >= 0) { + // lhs > rhs + FreeAccess(context, lhs); + + return rhs; + } + else { + FreeAccess(context, rhs); + + return lhs; + } + } + else if (lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) { + // < && <= + if (compareResult <= 0) { + // lhs < rhs + FreeAccess(context, lhs); + + return rhs; + } + else { + FreeAccess(context, rhs); + + return lhs; + } + } + } + + // for all other combinations, we give up + FreeAccess(context, rhs); + FreeAccessMembers(lhs); + + lhs->_type = TRI_AQL_ACCESS_ALL; + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using a logical OR +/// +/// left hand operand is a double range, the result type depends on the right +/// hand operand type +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeOrRangeDouble (TRI_aql_context_t* const context, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(false); + /* this should never be called. let's see if this assumption is true or not */ + assert(lhs->_type == TRI_AQL_ACCESS_RANGE_DOUBLE); + + return lhs; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief merge two access structures using either logical AND or OR +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* MergeAccess (TRI_aql_context_t* const context, + const TRI_aql_logical_e logicalType, + TRI_aql_field_access_t* lhs, + TRI_aql_field_access_t* rhs) { + assert(context); + assert(lhs); + assert(rhs); + assert(logicalType == TRI_AQL_LOGICAL_AND || logicalType == TRI_AQL_LOGICAL_OR); + + 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; + } + + assert(lhs->_type <= rhs->_type); + + if (logicalType == TRI_AQL_LOGICAL_AND) { + // logical AND + switch (lhs->_type) { + case TRI_AQL_ACCESS_IMPOSSIBLE: + return MergeAndImpossible(context, lhs, rhs); + case TRI_AQL_ACCESS_ALL: + return MergeAndAll(context, lhs, rhs); + case TRI_AQL_ACCESS_EXACT: + return MergeAndExact(context, lhs, rhs); + case TRI_AQL_ACCESS_LIST: + return MergeAndList(context, lhs, rhs); + case TRI_AQL_ACCESS_RANGE_SINGLE: + return MergeAndRangeSingle(context, lhs, rhs); + case TRI_AQL_ACCESS_RANGE_DOUBLE: + return MergeAndRangeDouble(context, lhs, rhs); + } + } + else { + // logical OR + switch (lhs->_type) { + case TRI_AQL_ACCESS_IMPOSSIBLE: + return MergeOrImpossible(context, lhs, rhs); + case TRI_AQL_ACCESS_ALL: + return MergeOrAll(context, lhs, rhs); + case TRI_AQL_ACCESS_EXACT: + return MergeOrExact(context, lhs, rhs); + case TRI_AQL_ACCESS_LIST: + return MergeOrList(context, lhs, rhs); + case TRI_AQL_ACCESS_RANGE_SINGLE: + return MergeOrRangeSingle(context, lhs, rhs); + case TRI_AQL_ACCESS_RANGE_DOUBLE: + return MergeOrRangeDouble(context, lhs, rhs); + } + } + + assert(false); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an access structure for the given node and operator +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_field_access_t* CreateAccessForNode (TRI_aql_context_t* const context, + const TRI_aql_attribute_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; + + assert(context); + assert(field); + assert(field->_name._buffer); + assert(node); + + value = TRI_NodeJsonAql(context, node); + if (!value) { + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return NULL; + } + + fieldAccess = (TRI_aql_field_access_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_field_access_t), false); + if (fieldAccess == NULL) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return NULL; + } + + fieldAccess->_fieldName = TRI_DuplicateString(field->_name._buffer); + if (fieldAccess->_fieldName == NULL) { + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess); + return NULL; + } + + if (operator == AQL_NODE_OPERATOR_BINARY_EQ) { + // create an exact value access + fieldAccess->_type = TRI_AQL_ACCESS_EXACT; + fieldAccess->_value._value = value; + } + else if (operator == AQL_NODE_OPERATOR_BINARY_LT) { + // create a single range access + fieldAccess->_type = TRI_AQL_ACCESS_RANGE_SINGLE; + fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_EXCLUDED; + fieldAccess->_value._singleRange._value = value; + } + else if (operator == AQL_NODE_OPERATOR_BINARY_LE) { + // create a single range access + fieldAccess->_type = TRI_AQL_ACCESS_RANGE_SINGLE; + fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_UPPER_INCLUDED; + fieldAccess->_value._singleRange._value = value; + } + else if (operator == AQL_NODE_OPERATOR_BINARY_GT) { + // create a single range access + fieldAccess->_type = TRI_AQL_ACCESS_RANGE_SINGLE; + fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_EXCLUDED; + fieldAccess->_value._singleRange._value = value; + } + else if (operator == AQL_NODE_OPERATOR_BINARY_GE) { + // create a single range access + fieldAccess->_type = TRI_AQL_ACCESS_RANGE_SINGLE; + fieldAccess->_value._singleRange._type = TRI_AQL_RANGE_LOWER_INCLUDED; + fieldAccess->_value._singleRange._value = value; + } + else if (operator == AQL_NODE_OPERATOR_BINARY_IN) { + TRI_json_t* list; + + // create a list access + fieldAccess->_type = TRI_AQL_ACCESS_LIST; + fieldAccess->_value._value = value; + + // sort values in list + TRI_SortListJson(fieldAccess->_value._value); + // make list values unique + list = TRI_UniquifyListJson(fieldAccess->_value._value); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value); + fieldAccess->_value._value = list; + } + else { + assert(false); + } + + return fieldAccess; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief create an access structure for the given node and operator, +/// merge it with potential others already found for the same variable +//////////////////////////////////////////////////////////////////////////////// + +static void NoteAttributeAccess (TRI_aql_context_t* const context, + const TRI_aql_logical_e logicalType, + const TRI_aql_attribute_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; + + assert(logicalType == TRI_AQL_LOGICAL_AND || logicalType == TRI_AQL_LOGICAL_OR); + assert(node); + + if (!field || !field->_name._buffer) { + return; + } + + fieldAccess = CreateAccessForNode(context, field, operator, node); + if (!fieldAccess) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return; + } + + // look up previous range first + previous = (TRI_aql_field_access_t*) TRI_LookupByKeyAssociativePointer(context->_ranges, fieldAccess->_fieldName); + if (previous) { + TRI_aql_field_access_t* merged; + // previous range exists, now merge new access type with previous one + + // remove from hash first + TRI_RemoveKeyAssociativePointer(context->_ranges, fieldAccess->_fieldName); + + // MergeAccess() will free previous and/or fieldAccess + merged = MergeAccess(context, logicalType, fieldAccess, previous); + + if (!merged) { + // OOM + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return; + } + + TRI_InsertKeyAssociativePointer(context->_ranges, merged->_fieldName, merged, true); + } + else { + // no previous access exists, no need to merge + TRI_InsertKeyAssociativePointer(context->_ranges, fieldAccess->_fieldName, fieldAccess, false); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set up the name of an attribute, including the variable name and +/// '.'s (e.g. u.birthday.day) for a given node +//////////////////////////////////////////////////////////////////////////////// + +static TRI_aql_attribute_name_t* GetAttributeName (TRI_aql_context_t* const context, + const TRI_aql_node_t* const node) { + assert(context); + assert(node); + + if (node->_type == AQL_NODE_ATTRIBUTE_ACCESS) { + TRI_aql_attribute_name_t* field = GetAttributeName(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_attribute_name_t* field; + + field = (TRI_aql_attribute_name_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_aql_attribute_name_t), false); + + if (!field) { + // OOM + 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; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief init the optimizer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_InitOptimizerAql (TRI_aql_context_t* const context) { + assert(context); + assert(context->_ranges == NULL); + + context->_ranges = (TRI_associative_pointer_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_associative_pointer_t), false); + if (!context->_ranges) { + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return false; + } + + TRI_InitAssociativePointer(context->_ranges, + TRI_UNKNOWN_MEM_ZONE, + &TRI_HashStringKeyAssociativePointer, + &HashFieldAccess, + &EqualFieldAccess, + NULL); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shutdown the optimizer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeOptimizerAql (TRI_aql_context_t* const context) { + assert(context); + + if (context->_ranges) { + size_t i, n; + + // free all remaining access elements + n = context->_ranges->_nrAlloc; + for (i = 0; i < n; ++i) { + TRI_aql_field_access_t* fieldAccess = (TRI_aql_field_access_t*) context->_ranges->_table[i]; + if (!fieldAccess) { + continue; + } + + FreeAccess(context, fieldAccess); + } + + // free hash array + TRI_FreeAssociativePointer(TRI_UNKNOWN_MEM_ZONE, context->_ranges); + context->_ranges = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief dump ranges found for debugging purposes +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DumpRangesAql (TRI_aql_context_t* const context) { + size_t i; + + assert(context); + + 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 || fieldAccess->_type == TRI_AQL_ACCESS_LIST) { + TRI_string_buffer_t b; + TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); + TRI_StringifyJson(&b, fieldAccess->_value._value); + + printf("- VALUE: %s\n", b._buffer); + TRI_DestroyStringBuffer(&b); + } + else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_SINGLE) { + 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); + TRI_DestroyStringBuffer(&b); + } + else if (fieldAccess->_type == TRI_AQL_ACCESS_RANGE_DOUBLE) { + TRI_string_buffer_t b; + TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); + TRI_StringifyJson(&b, fieldAccess->_value._between._lower._value); + TRI_AppendStringStringBuffer(&b, ", "); + TRI_StringifyJson(&b, fieldAccess->_value._between._upper._value); + + printf("- VALUE: %s\n", b._buffer); + TRI_DestroyStringBuffer(&b); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inspect a condition and note all accesses found for it +//////////////////////////////////////////////////////////////////////////////// + +void TRI_InspectConditionAql (TRI_aql_context_t* const context, + const TRI_aql_logical_e type, + TRI_aql_node_t* node) { + 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); + + // recurse into next level + TRI_InspectConditionAql(context, TRI_AQL_LOGICAL_OR, lhs); + TRI_InspectConditionAql(context, TRI_AQL_LOGICAL_OR, 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); + + // recurse into next level + TRI_InspectConditionAql(context, TRI_AQL_LOGICAL_AND, lhs); + TRI_InspectConditionAql(context, TRI_AQL_LOGICAL_AND, 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 || + node->_type == AQL_NODE_OPERATOR_BINARY_IN) { + 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_attribute_name_t* field = GetAttributeName(context, lhs); + + if (field) { + NoteAttributeAccess(context, type, field, node->_type, rhs); + TRI_DestroyStringBuffer(&field->_name); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, field); + } + } + else if (rhs->_type == AQL_NODE_ATTRIBUTE_ACCESS) { + TRI_aql_attribute_name_t* field = GetAttributeName(context, rhs); + + if (field) { + NoteAttributeAccess(context, type, field, node->_type, lhs); + TRI_DestroyStringBuffer(&field->_name); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, field); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/Ahuacatl/ahuacatl-access-optimizer.h b/Ahuacatl/ahuacatl-access-optimizer.h new file mode 100644 index 0000000000..17d22c8551 --- /dev/null +++ b/Ahuacatl/ahuacatl-access-optimizer.h @@ -0,0 +1,198 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief Ahuacatl, access optimizer +/// +/// @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 +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TRIAGENS_DURHAM_AHUACATL_ACCESS_OPTIMIZER_H +#define TRIAGENS_DURHAM_AHUACATL_ACCESS_OPTIMIZER_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Ahuacatl/ahuacatl-ast-node.h" +#include "Ahuacatl/ahuacatl-conversions.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- public types +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief logical operator types +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + TRI_AQL_LOGICAL_AND, + TRI_AQL_LOGICAL_OR +} +TRI_aql_logical_e; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief access types +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + TRI_AQL_ACCESS_ALL = 0, // all values must be accessed + TRI_AQL_ACCESS_IMPOSSIBLE, // no values must be accessed + TRI_AQL_ACCESS_EXACT, // one value must be accessed + TRI_AQL_ACCESS_LIST, // a list of values must be accessed + TRI_AQL_ACCESS_RANGE_SINGLE, // a range with one bound must be accessed + TRI_AQL_ACCESS_RANGE_DOUBLE // a two bounded range must be accessed +} +TRI_aql_access_e; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief range access types +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + TRI_AQL_RANGE_LOWER_EXCLUDED, // x| ... inf + TRI_AQL_RANGE_LOWER_INCLUDED, // |x ... inf + TRI_AQL_RANGE_UPPER_EXCLUDED, // -inf ... |x + TRI_AQL_RANGE_UPPER_INCLUDED // -inf ... x| +} +TRI_aql_range_e; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief range type (consisting of range type & range bound value) +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_aql_range_s { + TRI_json_t* _value; + TRI_aql_range_e _type; +} +TRI_aql_range_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief attribute access container used during optimisation +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_aql_field_access_s { + char* _fieldName; + TRI_aql_access_e _type; + + union { + TRI_json_t* _value; // used for TRI_AQL_ACCESS_EXACT, TRI_AQL_ACCESS_LIST + TRI_aql_range_t _singleRange; // used for TRI_AQL_ACCESS_RANGE_SINGLE + struct { + TRI_aql_range_t _lower; // lower bound + TRI_aql_range_t _upper; // upper bound + } + _between; // used for TRI_AQL_ACCESS_RANGE_DOUBLE + } + _value; +} +TRI_aql_field_access_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief attribute name container +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_aql_attribute_name_s { + const char* _variable; // variable name/alias used + TRI_string_buffer_t _name; // complete attribute name (including variable and '.'s) +} +TRI_aql_attribute_name_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief init the optimizer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_InitOptimizerAql (TRI_aql_context_t* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief shutdown the optimizer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeOptimizerAql (TRI_aql_context_t* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Ahuacatl +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief dump ranges found for debugging purposes +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DumpRangesAql (TRI_aql_context_t* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief inspect a condition and note all accesses found for it +//////////////////////////////////////////////////////////////////////////////// + +void TRI_InspectConditionAql (TRI_aql_context_t* const, + const TRI_aql_logical_e, + TRI_aql_node_t*); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +#ifdef __cplusplus +} +#endif + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/Ahuacatl/ahuacatl-constant-folder.c b/Ahuacatl/ahuacatl-constant-folder.c index 677950fb02..e4d6d6c74d 100644 --- a/Ahuacatl/ahuacatl-constant-folder.c +++ b/Ahuacatl/ahuacatl-constant-folder.c @@ -31,614 +31,12 @@ #include "V8/v8-execution.h" +#undef RANGE_OPTIMIZER + // ----------------------------------------------------------------------------- // --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); -} - -#ifdef RANGE_OPTIMIZER -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"; - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief free access member data -//////////////////////////////////////////////////////////////////////////////// - -static void FreeAccessMembers (TRI_aql_field_access_t* const fieldAccess) { - assert(fieldAccess); - - switch (fieldAccess->_type) { - case TRI_AQL_ACCESS_EXACT: - case TRI_AQL_ACCESS_LIST: - if (fieldAccess->_value._value) { - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._value); - } - break; - case TRI_AQL_ACCESS_SINGLE_RANGE: - if (fieldAccess->_value._singleRange._value) { - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._singleRange._value); - } - break; - case TRI_AQL_ACCESS_DOUBLE_RANGE: - if (fieldAccess->_value._between._lower._value) { - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._lower._value); - } - if (fieldAccess->_value._between._upper._value) { - TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_value._between._upper._value); - } - break; - - case TRI_AQL_ACCESS_ALL: - case TRI_AQL_ACCESS_IMPOSSIBLE: - default: { - // nada - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief free access structure with its members -//////////////////////////////////////////////////////////////////////////////// - -static void FreeAccess (TRI_aql_field_access_t* const fieldAccess) { - assert(fieldAccess); - - FreeAccessMembers(fieldAccess); - TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess->_fieldName); - TRI_Free(TRI_UNKNOWN_MEM_ZONE, fieldAccess); -} - -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_IMPOSSIBLE) { - // impossible merged with anything just returns impossible - FreeAccess(rhs); - - return lhs; - } - - if (lhs->_type == TRI_AQL_ACCESS_ALL) { - // all merged with anything just returns the other side - FreeAccess(lhs); - - return rhs; - } - - - if (lhs->_type == TRI_AQL_ACCESS_EXACT) { - if (rhs->_type == TRI_AQL_ACCESS_EXACT) { - // check if values are identical - bool isSame = TRI_CheckSameValueJson(lhs->_value._value, rhs->_value._value); - - FreeAccess(rhs); - - if (!isSame) { - // lhs and rhs values are non-identical, return impossible - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - } - return lhs; - } - else if (rhs->_type == TRI_AQL_ACCESS_LIST) { - // check if lhs is contained in rhs list - bool inList = TRI_CheckInListJson(lhs->_value._value, rhs->_value._value); - - FreeAccess(rhs); - - if (!inList) { - // lhs value is not in rhs list, return impossible - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - } - - return lhs; - } - else if (rhs->_type == TRI_AQL_ACCESS_SINGLE_RANGE) { - // check if value is in range - int result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - - // lhs value is contained in rhs range, simply return rhs - FreeAccess(lhs); - - return rhs; - } - else if (rhs->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { - // check if value is in range - int result; - bool contained; - - // compare lower end - result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - - // compare upper end - result = TRI_CompareValuesJson(lhs->_value._value, 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, return impossible - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - - // lhs value is contained in rhs range, return rhs - FreeAccess(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) { - TRI_json_t* lhsValue; - TRI_json_t* rhsValue; - TRI_aql_range_e lhsType; - TRI_aql_range_e rhsType; - int result; - - if (lhs->_value._singleRange._type > rhs->_value._singleRange._type) { - // swap operands so they are always sorted - TRI_aql_field_access_t* tmp = lhs; - lhs = rhs; - rhs = tmp; - } - - result = TRI_CompareValuesJson(lhs->_value._singleRange._value, rhs->_value._singleRange._value); - lhsType = lhs->_value._singleRange._type; - rhsType = rhs->_value._singleRange._type; - lhsValue = lhs->_value._singleRange._value; - rhsValue = rhs->_value._singleRange._value; - - // check if ranges overlap - if ((lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_EXCLUDED) || - (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED)) { - // > && > - // >= && >= - if (result > 0) { - // lhs > rhs - FreeAccess(rhs); - - return lhs; - } - else { - FreeAccess(lhs); - - return rhs; - } - } - else if ((lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED) || - (lhsType == TRI_AQL_RANGE_UPPER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED)) { - // < && < - // <= && <= - if (result > 0) { - // lhs > rhs - FreeAccess(lhs); - - return rhs; - } - else { - FreeAccess(rhs); - - return lhs; - } - } - else if (lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_LOWER_INCLUDED) { - // > && >= - if (result >= 0) { - // lhs > rhs - FreeAccess(rhs); - - return lhs; - } - else { - FreeAccess(lhs); - - return rhs; - } - } - else if (lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED) { - // > && < - if (result < 0) { - // save pointers - lhsValue = lhs->_value._singleRange._value; - rhsValue = rhs->_value._singleRange._value; - rhs->_value._singleRange._value = NULL; - FreeAccess(rhs); - - lhs->_type = TRI_AQL_ACCESS_DOUBLE_RANGE; - lhs->_value._between._lower._type = lhsType; - lhs->_value._between._lower._value = lhsValue; - lhs->_value._between._upper._type = rhsType; - lhs->_value._between._upper._value = rhsValue; - - return lhs; - } - else { - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - } - else if ((lhsType == TRI_AQL_RANGE_LOWER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) || - (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_EXCLUDED)) { - // > && <= - // >= && < - if (result < 0) { - // save pointers - lhsValue = lhs->_value._singleRange._value; - rhsValue = rhs->_value._singleRange._value; - rhs->_value._singleRange._value = NULL; - FreeAccess(rhs); - - lhs->_type = TRI_AQL_ACCESS_DOUBLE_RANGE; - lhs->_value._between._lower._type = lhsType; - lhs->_value._between._lower._value = lhsValue; - lhs->_value._between._upper._type = rhsType; - lhs->_value._between._upper._value = rhsValue; - - return lhs; - } - else { - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - } - else if (lhsType == TRI_AQL_RANGE_LOWER_INCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) { - // >= && <= - if (result < 0) { - // save pointers - lhsValue = lhs->_value._singleRange._value; - rhsValue = rhs->_value._singleRange._value; - rhs->_value._singleRange._value = NULL; - FreeAccess(rhs); - - lhs->_type = TRI_AQL_ACCESS_DOUBLE_RANGE; - lhs->_value._between._lower._type = lhsType; - lhs->_value._between._lower._value = lhsValue; - lhs->_value._between._upper._type = rhsType; - lhs->_value._between._upper._value = rhsValue; - - return lhs; - } - else if (result == 0) { - FreeAccess(rhs); - - // save pointer - lhsValue = lhs->_value._singleRange._value; - lhs->_type = TRI_AQL_ACCESS_EXACT; - lhs->_value._value = lhsValue; - return lhs; - } - else { - FreeAccess(rhs); - FreeAccessMembers(lhs); - lhs->_type = TRI_AQL_ACCESS_IMPOSSIBLE; - - return lhs; - } - } - else if (lhsType == TRI_AQL_RANGE_UPPER_EXCLUDED && rhsType == TRI_AQL_RANGE_UPPER_INCLUDED) { - // < && <= - if (result <= 0) { - FreeAccess(rhs); - - return lhs; - } - else { - FreeAccess(lhs); - - return rhs; - } - } - } - 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._value = 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 if (operator == AQL_NODE_OPERATOR_BINARY_IN) { - fieldAccess->_type = TRI_AQL_ACCESS_LIST; - fieldAccess->_value._value = value; - TRI_SortListJson(fieldAccess->_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, merged->_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 || - node->_type == AQL_NODE_OPERATOR_BINARY_IN) { - 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); - } - } - } -} -#endif - //////////////////////////////////////////////////////////////////////////////// /// @brief create javascript function code for a relational operation //////////////////////////////////////////////////////////////////////////////// @@ -861,7 +259,6 @@ static TRI_aql_node_t* OptimiseSort (TRI_aql_context_t* const context, static TRI_aql_node_t* OptimiseFilter (TRI_aql_context_t* const context, TRI_aql_node_t* node) { -#ifdef RANGE_OPTIMIZER TRI_aql_node_t* expression = TRI_AQL_NODE_MEMBER(node, 0); bool result; @@ -870,7 +267,9 @@ static TRI_aql_node_t* OptimiseFilter (TRI_aql_context_t* const context, } if (!TRI_IsConstantValueNodeAql(expression)) { - InspectFilter(context, expression); +#ifdef RANGE_OPTIMIZER + TRI_InspectConditionAql(context, TRI_AQL_LOGICAL_AND, expression); +#endif return node; } @@ -881,7 +280,7 @@ static TRI_aql_node_t* OptimiseFilter (TRI_aql_context_t* const context, return NULL; } -#endif + return node; } @@ -1054,6 +453,10 @@ static TRI_aql_node_t* OptimiseBinaryRelationalOperation (TRI_aql_context_t* con else if (node->_type == AQL_NODE_OPERATOR_BINARY_IN) { func = "IN"; } + else { + // not what we expected, however, simply continue + return node; + } code = RelationCode(func, lhs, rhs); if (!code) { @@ -1190,7 +593,6 @@ static TRI_aql_node_t* MarkFor (TRI_aql_context_t* const context, return node; } - //////////////////////////////////////////////////////////////////////////////// /// @brief fold constants in a node //////////////////////////////////////////////////////////////////////////////// @@ -1260,15 +662,14 @@ static TRI_aql_node_t* ModifyNode (void* data, TRI_aql_node_t* node) { 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); - + +#ifdef RANGE_OPTIMIZER + if (!TRI_InitOptimizerAql(context)) { + TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); + return node; + } +#endif + walker = TRI_CreateModifyTreeWalkerAql((void*) context, &ModifyNode); if (!walker) { TRI_SetErrorContextAql(context, TRI_ERROR_OUT_OF_MEMORY, NULL); @@ -1279,41 +680,8 @@ TRI_aql_node_t* TRI_FoldConstantsAql (TRI_aql_context_t* const context, TRI_FreeModifyTreeWalkerAql(walker); -#ifdef RANGE_OPTIMIZER - 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 || fieldAccess->_type == TRI_AQL_ACCESS_LIST) { - TRI_string_buffer_t b; - TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); - TRI_StringifyJson(&b, fieldAccess->_value._value); - - 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); - } - else if (fieldAccess->_type == TRI_AQL_ACCESS_DOUBLE_RANGE) { - TRI_string_buffer_t b; - TRI_InitStringBuffer(&b, TRI_UNKNOWN_MEM_ZONE); - TRI_StringifyJson(&b, fieldAccess->_value._between._lower._value); - TRI_AppendStringStringBuffer(&b, ", "); - TRI_StringifyJson(&b, fieldAccess->_value._between._upper._value); - - printf("- VALUE: %s\n", b._buffer); - } - } +#ifdef RANGE_OPTIMIZER + TRI_DumpRangesAql(context); #endif return node; diff --git a/Ahuacatl/ahuacatl-constant-folder.h b/Ahuacatl/ahuacatl-constant-folder.h index 28aa6522b6..0c0725a5e5 100644 --- a/Ahuacatl/ahuacatl-constant-folder.h +++ b/Ahuacatl/ahuacatl-constant-folder.h @@ -37,6 +37,7 @@ #include #include +#include "Ahuacatl/ahuacatl-access-optimizer.h" #include "Ahuacatl/ahuacatl-ast-node.h" #include "Ahuacatl/ahuacatl-tree-walker.h" @@ -53,65 +54,6 @@ extern "C" { /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief access type -//////////////////////////////////////////////////////////////////////////////// - -typedef enum { - TRI_AQL_RANGE_LOWER_EXCLUDED, - TRI_AQL_RANGE_LOWER_INCLUDED, - TRI_AQL_RANGE_UPPER_EXCLUDED, - TRI_AQL_RANGE_UPPER_INCLUDED -} -TRI_aql_range_e; - -typedef enum { - TRI_AQL_ACCESS_ALL = 0, - TRI_AQL_ACCESS_IMPOSSIBLE, - TRI_AQL_ACCESS_EXACT, - TRI_AQL_ACCESS_LIST, - TRI_AQL_ACCESS_SINGLE_RANGE, - TRI_AQL_ACCESS_DOUBLE_RANGE, -} -TRI_aql_access_e; - -typedef struct TRI_aql_range_s { - TRI_json_t* _value; - TRI_aql_range_e _type; -} -TRI_aql_range_t; - - -//////////////////////////////////////////////////////////////////////////////// -/// @brief attribute access container used during optimisation -//////////////////////////////////////////////////////////////////////////////// - -typedef struct TRI_aql_field_access_s { - char* _fieldName; - TRI_aql_access_e _type; - union { - TRI_json_t* _value; - TRI_aql_range_t _singleRange; - struct { - TRI_aql_range_t _lower; - TRI_aql_range_t _upper; - } - _between; - } - _value; -} -TRI_aql_field_access_t; - - - -typedef struct TRI_aql_field_name_s { - const char* _variable; - TRI_string_buffer_t _name; -} -TRI_aql_field_name_t; - - - //////////////////////////////////////////////////////////////////////////////// /// @brief fold constants recursively //////////////////////////////////////////////////////////////////////////////// diff --git a/Ahuacatl/ahuacatl-context.c b/Ahuacatl/ahuacatl-context.c index 471685d90b..c97234917b 100644 --- a/Ahuacatl/ahuacatl-context.c +++ b/Ahuacatl/ahuacatl-context.c @@ -26,6 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// #include "Ahuacatl/ahuacatl-context.h" +#include "Ahuacatl/ahuacatl-access-optimizer.h" #include "Ahuacatl/ahuacatl-ast-node.h" #include "Ahuacatl/ahuacatl-bind-parameter.h" #include "Ahuacatl/ahuacatl-collections.h" @@ -77,6 +78,8 @@ TRI_aql_context_t* TRI_CreateContextAql (TRI_vocbase_t* vocbase, } context->_vocbase = vocbase; + context->_ranges = NULL; + context->_variableIndex = 0; // actual bind parameter values @@ -161,6 +164,9 @@ void TRI_FreeContextAql (TRI_aql_context_t* const context) { } TRI_DestroyVectorPointer(&context->_scopes); + // free range optimizer + TRI_FreeOptimizerAql(context); + // free all strings registered i = context->_strings._length; while (i--) { @@ -462,10 +468,8 @@ bool TRI_AddStatementAql (TRI_aql_context_t* const context, TRI_aql_scope_t* TRI_StartScopeContextAql (TRI_aql_context_t* const context) { TRI_aql_scope_t* scope; - size_t n; assert(context); - n = context->_scopes._length; scope = TRI_CreateScopeAql(); if (!scope) { ABORT_OOM diff --git a/Ahuacatl/ahuacatl-context.h b/Ahuacatl/ahuacatl-context.h index a069040839..3890f93f83 100644 --- a/Ahuacatl/ahuacatl-context.h +++ b/Ahuacatl/ahuacatl-context.h @@ -82,7 +82,7 @@ typedef struct TRI_aql_context_s { TRI_associative_pointer_t _parameterValues; TRI_associative_pointer_t _parameterNames; TRI_associative_pointer_t _collectionNames; - TRI_associative_pointer_t _ranges; + TRI_associative_pointer_t* _ranges; size_t _variableIndex; void* _first; char* _query; diff --git a/Ahuacatl/ahuacatl-conversions.c b/Ahuacatl/ahuacatl-conversions.c index cb3c8a39b6..64a0bd3660 100644 --- a/Ahuacatl/ahuacatl-conversions.c +++ b/Ahuacatl/ahuacatl-conversions.c @@ -89,7 +89,12 @@ TRI_json_t* TRI_NodeJsonAql (TRI_aql_context_t* const context, if (result) { n = node->_members._length; for (i = 0; i < n; ++i) { - TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, result, TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(node, i))); + TRI_json_t* subValue = TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(node, i)); + + if (subValue) { + TRI_PushBack2ListJson(result, subValue); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, subValue); + } } } return result; @@ -102,7 +107,15 @@ TRI_json_t* TRI_NodeJsonAql (TRI_aql_context_t* const context, n = node->_members._length; for (i = 0; i < n; ++i) { TRI_aql_node_t* element = TRI_AQL_NODE_MEMBER(node, i); - TRI_InsertArrayJson(TRI_UNKNOWN_MEM_ZONE, result, TRI_AQL_NODE_STRING(element), TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(element, 0))); + TRI_json_t* subValue = TRI_NodeJsonAql(context, TRI_AQL_NODE_MEMBER(element, 0)); + + if (subValue) { + TRI_Insert2ArrayJson(TRI_UNKNOWN_MEM_ZONE, + result, + TRI_AQL_NODE_STRING(element), + subValue); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, subValue); + } } } return result; diff --git a/Ahuacatl/ahuacatl-error.c b/Ahuacatl/ahuacatl-error.c index a442441161..649f070b07 100644 --- a/Ahuacatl/ahuacatl-error.c +++ b/Ahuacatl/ahuacatl-error.c @@ -141,6 +141,8 @@ char* TRI_GetContextErrorAql (const char* const query, const size_t line, const size_t currentLine = 1; size_t currentColumn = 1; const char* p = query; + char* temp; + char* result; size_t offset; char c; @@ -169,7 +171,16 @@ char* TRI_GetContextErrorAql (const char* const query, const size_t line, const return TRI_DuplicateString2(query + offset, strlen(query) - offset); } - return TRI_Concatenate2String(TRI_DuplicateString2(query + offset, SNIPPET_LENGTH), SNIPPET_SUFFIX); + temp = TRI_DuplicateString2(query + offset, SNIPPET_LENGTH); + if (!temp) { + // out of memory + return NULL; + } + + result = TRI_Concatenate2String(temp, SNIPPET_SUFFIX); + TRI_FreeString(TRI_CORE_MEM_ZONE, temp); + + return result; } //////////////////////////////////////////////////////////////////////////////// diff --git a/BasicsC/json-utilities.c b/BasicsC/json-utilities.c index bef3d3c85d..44518a9585 100644 --- a/BasicsC/json-utilities.c +++ b/BasicsC/json-utilities.c @@ -259,6 +259,58 @@ bool TRI_CheckInListJson (const TRI_json_t* const search, return false; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the elements of a list that are between the specified bounds +//////////////////////////////////////////////////////////////////////////////// + +TRI_json_t* TRI_BetweenListJson (const TRI_json_t* const list, + const TRI_json_t* const lower, + const bool includeLower, + const TRI_json_t* const upper, + const bool includeUpper) { + TRI_json_t* result; + size_t i, n; + + assert(list); + assert(list->_type == TRI_JSON_LIST); + assert(lower || upper); + + // create result list + result = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + if (!result) { + return NULL; + } + + n = list->_value._objects._length; + for (i = 0; i < n; ++i) { + TRI_json_t* p = TRI_AtVector(&list->_value._objects, i); + + if (lower) { + // lower bound is set + int compareResult = TRI_CompareValuesJson(lower, p); + if (compareResult > 0 || (compareResult == 0 && !includeLower)) { + // element is bigger than lower bound + continue; + } + } + + if (upper) { + // upper bound is set + int compareResult = TRI_CompareValuesJson(p, upper); + if (compareResult > 0 || (compareResult == 0 && !includeUpper)) { + // element is smaller than upper bound + continue; + } + } + + // element is between lower and upper bound + + TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, result, p); + } + + return result; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief uniquify a sorted json list into a new list //////////////////////////////////////////////////////////////////////////////// @@ -386,7 +438,7 @@ TRI_json_t* TRI_UnionizeListsJson (const TRI_json_t* const list1, } else if (i1 >= n1 && i2 < n2) { // only left list is exhausted - p2 = TRI_AtVector(&list1->_value._objects, i2); + p2 = TRI_AtVector(&list2->_value._objects, i2); if (!unique || !last || TRI_CompareValuesJson(p2, last) > 0) { TRI_PushBackListJson(TRI_UNKNOWN_MEM_ZONE, result, p2); diff --git a/BasicsC/json-utilities.h b/BasicsC/json-utilities.h index 6feac7676c..a98ebc7b32 100644 --- a/BasicsC/json-utilities.h +++ b/BasicsC/json-utilities.h @@ -67,6 +67,24 @@ bool TRI_CheckSameValueJson (const TRI_json_t* const, const TRI_json_t* const); bool TRI_CheckInListJson (const TRI_json_t* const, const TRI_json_t* const); +//////////////////////////////////////////////////////////////////////////////// +/// @brief return the elements of a list that are between the specified bounds +/// +/// lower and upper are the bounds values. if both lower and upper have a value, +/// then each list element is checked against the range (lower ... uppper). +/// if either lower or upper are null, then the comparison is done as either +/// (-inf ... upper) or (lower ... +inf). +/// +/// using the boolean flags includeLower and includeUpper it can be specified +/// whether the bounds values are part of the range (true) or not (false) +//////////////////////////////////////////////////////////////////////////////// + +TRI_json_t* TRI_BetweenListJson (const TRI_json_t* const, + const TRI_json_t* const, + const bool, + const TRI_json_t* const, + const bool); + //////////////////////////////////////////////////////////////////////////////// /// @brief uniquify a sorted json list into a new list /// diff --git a/Makefile.files b/Makefile.files index 2fd6ec5584..abb69f0f20 100644 --- a/Makefile.files +++ b/Makefile.files @@ -155,6 +155,7 @@ avocado_SOURCES = \ HttpsServer/HttpsServerImpl.cpp \ PriorityQueue/pqueueindex.c \ PriorityQueue/priorityqueue.c \ + Ahuacatl/ahuacatl-access-optimizer.c \ Ahuacatl/ahuacatl-ast-node.c \ Ahuacatl/ahuacatl-bind-parameter.c \ Ahuacatl/ahuacatl-codegen-js.c \ diff --git a/Makefile.in b/Makefile.in index ee9448db92..8ca70f57fc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,9 +1,9 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.11.3 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software +# Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -158,11 +158,11 @@ CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) AR = ar ARFLAGS = cru -AM_V_AR = $(am__v_AR_$(V)) -am__v_AR_ = $(am__v_AR_$(AM_DEFAULT_VERBOSITY)) +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; -AM_V_at = $(am__v_at_$(V)) -am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ libavocado_a_AR = $(AR) $(ARFLAGS) libavocado_a_LIBADD = @@ -288,10 +288,12 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" \ PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS) am__UnitTests_basics_suite_SOURCES_DIST = UnitTests/Runner.cpp \ UnitTests/Philadelphia/json-test.cpp \ + UnitTests/Philadelphia/json-utilities-test.cpp \ UnitTests/Philadelphia/hashes-test.cpp \ UnitTests/Philadelphia/associative-pointer-test.cpp \ UnitTests/Philadelphia/string-buffer-test.cpp \ UnitTests/Philadelphia/string-utf8-test.cpp \ + UnitTests/Philadelphia/string-test.cpp \ UnitTests/Philadelphia/vector-pointer-test.cpp \ UnitTests/Philadelphia/vector-test.cpp \ UnitTests/Jutland/StringBufferTest.cpp \ @@ -299,10 +301,12 @@ am__UnitTests_basics_suite_SOURCES_DIST = UnitTests/Runner.cpp \ @ENABLE_BOOST_TEST_TRUE@am_UnitTests_basics_suite_OBJECTS = \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Runner.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/json-test.$(OBJEXT) \ +@ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/json-utilities-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/hashes-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/associative-pointer-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-buffer-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-utf8-test.$(OBJEXT) \ +@ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/vector-pointer-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/vector-test.$(OBJEXT) \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Jutland/StringBufferTest.$(OBJEXT) \ @@ -345,6 +349,7 @@ am_avocado_OBJECTS = Admin/ApplicationAdminServer.$(OBJEXT) \ HttpsServer/HttpsServerImpl.$(OBJEXT) \ PriorityQueue/pqueueindex.$(OBJEXT) \ PriorityQueue/priorityqueue.$(OBJEXT) \ + Ahuacatl/ahuacatl-access-optimizer.$(OBJEXT) \ Ahuacatl/ahuacatl-ast-node.$(OBJEXT) \ Ahuacatl/ahuacatl-bind-parameter.$(OBJEXT) \ Ahuacatl/ahuacatl-codegen-js.$(OBJEXT) \ @@ -441,27 +446,27 @@ am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -AM_V_CC = $(am__v_CC_$(V)) -am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ -AM_V_CCLD = $(am__v_CCLD_$(V)) -am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -AM_V_CXX = $(am__v_CXX_$(V)) -am__v_CXX_ = $(am__v_CXX_$(AM_DEFAULT_VERBOSITY)) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) am__v_CXX_0 = @echo " CXX " $@; CXXLD = $(CXX) CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ -o $@ -AM_V_CXXLD = $(am__v_CXXLD_$(V)) -am__v_CXXLD_ = $(am__v_CXXLD_$(AM_DEFAULT_VERBOSITY)) +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; -AM_V_GEN = $(am__v_GEN_$(V)) -am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; SOURCES = $(libavocado_a_SOURCES) $(UnitTests_basics_suite_SOURCES) \ $(UnitTests_geo_suite_SOURCES) $(avocado_SOURCES) \ @@ -492,6 +497,12 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } DATA = $(nobase_pkgdata_DATA) ETAGS = etags CTAGS = ctags @@ -499,12 +510,16 @@ DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ - { test ! -d "$(distdir)" \ - || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ - && rm -fr "$(distdir)"; }; } + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ @@ -783,6 +798,7 @@ avocado_SOURCES = \ HttpsServer/HttpsServerImpl.cpp \ PriorityQueue/pqueueindex.c \ PriorityQueue/priorityqueue.c \ + Ahuacatl/ahuacatl-access-optimizer.c \ Ahuacatl/ahuacatl-ast-node.c \ Ahuacatl/ahuacatl-bind-parameter.c \ Ahuacatl/ahuacatl-codegen-js.c \ @@ -1034,10 +1050,12 @@ CLIENT_OPT := --startup.directory ./js --startup.modules-path ./js/client/module @ENABLE_BOOST_TEST_TRUE@UnitTests_basics_suite_SOURCES = \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Runner.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/json-test.cpp \ +@ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/json-utilities-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/hashes-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/associative-pointer-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-buffer-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-utf8-test.cpp \ +@ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/string-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/vector-pointer-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Philadelphia/vector-test.cpp \ @ENABLE_BOOST_TEST_TRUE@ UnitTests/Jutland/StringBufferTest.cpp \ @@ -1088,7 +1106,7 @@ all: $(BUILT_SOURCES) .SUFFIXES: .SUFFIXES: .c .cpp .o .obj -am--refresh: +am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Makefile.files $(srcdir)/Makefile.doxygen $(srcdir)/Makefile.javascript $(srcdir)/Makefile.unittests $(srcdir)/Makefile.flex $(srcdir)/Makefile.bison $(srcdir)/m4/Makefile.all-in-one-libev $(srcdir)/m4/Makefile.all-in-one-v8 $(srcdir)/m4/Makefile.all-in-one-mruby $(am__configure_deps) @for dep in $?; do \ @@ -1113,6 +1131,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; +$(srcdir)/Makefile.files $(srcdir)/Makefile.doxygen $(srcdir)/Makefile.javascript $(srcdir)/Makefile.unittests $(srcdir)/Makefile.flex $(srcdir)/Makefile.bison $(srcdir)/m4/Makefile.all-in-one-libev $(srcdir)/m4/Makefile.all-in-one-v8 $(srcdir)/m4/Makefile.all-in-one-mruby: $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck @@ -1124,10 +1143,8 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): config/config.h: config/stamp-h1 - @if test ! -f $@; then \ - rm -f config/stamp-h1; \ - $(MAKE) $(AM_MAKEFLAGS) config/stamp-h1; \ - else :; fi + @if test ! -f $@; then rm -f config/stamp-h1; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) config/stamp-h1; else :; fi config/stamp-h1: $(top_srcdir)/config/config.h.in $(top_builddir)/config.status @rm -f config/stamp-h1 @@ -1138,10 +1155,8 @@ $(top_srcdir)/config/config.h.in: $(am__configure_deps) touch $@ BasicsC/local-configuration.h: BasicsC/stamp-h2 - @if test ! -f $@; then \ - rm -f BasicsC/stamp-h2; \ - $(MAKE) $(AM_MAKEFLAGS) BasicsC/stamp-h2; \ - else :; fi + @if test ! -f $@; then rm -f BasicsC/stamp-h2; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) BasicsC/stamp-h2; else :; fi BasicsC/stamp-h2: $(top_srcdir)/BasicsC/local-configuration.h.in $(top_builddir)/config.status @rm -f BasicsC/stamp-h2 @@ -1445,7 +1460,7 @@ MRuby/MRLineEditor.$(OBJEXT): MRuby/$(am__dirstamp) \ MRuby/$(DEPDIR)/$(am__dirstamp) MRuby/mr-utils.$(OBJEXT): MRuby/$(am__dirstamp) \ MRuby/$(DEPDIR)/$(am__dirstamp) -libavocado.a: $(libavocado_a_OBJECTS) $(libavocado_a_DEPENDENCIES) +libavocado.a: $(libavocado_a_OBJECTS) $(libavocado_a_DEPENDENCIES) $(EXTRA_libavocado_a_DEPENDENCIES) $(AM_V_at)-rm -f libavocado.a $(AM_V_AR)$(libavocado_a_AR) libavocado.a $(libavocado_a_OBJECTS) $(libavocado_a_LIBADD) $(AM_V_at)$(RANLIB) libavocado.a @@ -1543,6 +1558,9 @@ UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp): UnitTests/Philadelphia/json-test.$(OBJEXT): \ UnitTests/Philadelphia/$(am__dirstamp) \ UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) +UnitTests/Philadelphia/json-utilities-test.$(OBJEXT): \ + UnitTests/Philadelphia/$(am__dirstamp) \ + UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) UnitTests/Philadelphia/hashes-test.$(OBJEXT): \ UnitTests/Philadelphia/$(am__dirstamp) \ UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) @@ -1555,6 +1573,9 @@ UnitTests/Philadelphia/string-buffer-test.$(OBJEXT): \ UnitTests/Philadelphia/string-utf8-test.$(OBJEXT): \ UnitTests/Philadelphia/$(am__dirstamp) \ UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) +UnitTests/Philadelphia/string-test.$(OBJEXT): \ + UnitTests/Philadelphia/$(am__dirstamp) \ + UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) UnitTests/Philadelphia/vector-pointer-test.$(OBJEXT): \ UnitTests/Philadelphia/$(am__dirstamp) \ UnitTests/Philadelphia/$(DEPDIR)/$(am__dirstamp) @@ -1573,7 +1594,7 @@ UnitTests/Jutland/StringBufferTest.$(OBJEXT): \ UnitTests/Jutland/StringUtilsTest.$(OBJEXT): \ UnitTests/Jutland/$(am__dirstamp) \ UnitTests/Jutland/$(DEPDIR)/$(am__dirstamp) -UnitTests/basics_suite$(EXEEXT): $(UnitTests_basics_suite_OBJECTS) $(UnitTests_basics_suite_DEPENDENCIES) UnitTests/$(am__dirstamp) +UnitTests/basics_suite$(EXEEXT): $(UnitTests_basics_suite_OBJECTS) $(UnitTests_basics_suite_DEPENDENCIES) $(EXTRA_UnitTests_basics_suite_DEPENDENCIES) UnitTests/$(am__dirstamp) @rm -f UnitTests/basics_suite$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(UnitTests_basics_suite_OBJECTS) $(UnitTests_basics_suite_LDADD) $(LIBS) UnitTests/Cambridge/$(am__dirstamp): @@ -1588,7 +1609,7 @@ UnitTests/Cambridge/Runner.$(OBJEXT): \ UnitTests/Cambridge/georeg.$(OBJEXT): \ UnitTests/Cambridge/$(am__dirstamp) \ UnitTests/Cambridge/$(DEPDIR)/$(am__dirstamp) -UnitTests/geo_suite$(EXEEXT): $(UnitTests_geo_suite_OBJECTS) $(UnitTests_geo_suite_DEPENDENCIES) UnitTests/$(am__dirstamp) +UnitTests/geo_suite$(EXEEXT): $(UnitTests_geo_suite_OBJECTS) $(UnitTests_geo_suite_DEPENDENCIES) $(EXTRA_UnitTests_geo_suite_DEPENDENCIES) UnitTests/$(am__dirstamp) @rm -f UnitTests/geo_suite$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(UnitTests_geo_suite_OBJECTS) $(UnitTests_geo_suite_LDADD) $(LIBS) Admin/$(am__dirstamp): @@ -1721,6 +1742,8 @@ Ahuacatl/$(am__dirstamp): Ahuacatl/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) Ahuacatl/$(DEPDIR) @: > Ahuacatl/$(DEPDIR)/$(am__dirstamp) +Ahuacatl/ahuacatl-access-optimizer.$(OBJEXT): \ + Ahuacatl/$(am__dirstamp) Ahuacatl/$(DEPDIR)/$(am__dirstamp) Ahuacatl/ahuacatl-ast-node.$(OBJEXT): Ahuacatl/$(am__dirstamp) \ Ahuacatl/$(DEPDIR)/$(am__dirstamp) Ahuacatl/ahuacatl-bind-parameter.$(OBJEXT): Ahuacatl/$(am__dirstamp) \ @@ -1940,7 +1963,7 @@ VocBase/voc-shaper.$(OBJEXT): VocBase/$(am__dirstamp) \ VocBase/$(DEPDIR)/$(am__dirstamp) VocBase/vocbase.$(OBJEXT): VocBase/$(am__dirstamp) \ VocBase/$(DEPDIR)/$(am__dirstamp) -avocado$(EXEEXT): $(avocado_OBJECTS) $(avocado_DEPENDENCIES) +avocado$(EXEEXT): $(avocado_OBJECTS) $(avocado_DEPENDENCIES) $(EXTRA_avocado_DEPENDENCIES) @rm -f avocado$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(avocado_OBJECTS) $(avocado_LDADD) $(LIBS) SimpleHttpClient/$(am__dirstamp): @@ -1967,7 +1990,7 @@ V8Client/V8ClientConnection.$(OBJEXT): V8Client/$(am__dirstamp) \ V8Client/$(DEPDIR)/$(am__dirstamp) V8Client/avocimp.$(OBJEXT): V8Client/$(am__dirstamp) \ V8Client/$(DEPDIR)/$(am__dirstamp) -avocimp$(EXEEXT): $(avocimp_OBJECTS) $(avocimp_DEPENDENCIES) +avocimp$(EXEEXT): $(avocimp_OBJECTS) $(avocimp_DEPENDENCIES) $(EXTRA_avocimp_DEPENDENCIES) @rm -f avocimp$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(avocimp_OBJECTS) $(avocimp_LDADD) $(LIBS) MRClient/$(am__dirstamp): @@ -1978,12 +2001,12 @@ MRClient/$(DEPDIR)/$(am__dirstamp): @: > MRClient/$(DEPDIR)/$(am__dirstamp) MRClient/avocirb.$(OBJEXT): MRClient/$(am__dirstamp) \ MRClient/$(DEPDIR)/$(am__dirstamp) -avocirb$(EXEEXT): $(avocirb_OBJECTS) $(avocirb_DEPENDENCIES) +avocirb$(EXEEXT): $(avocirb_OBJECTS) $(avocirb_DEPENDENCIES) $(EXTRA_avocirb_DEPENDENCIES) @rm -f avocirb$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(avocirb_OBJECTS) $(avocirb_LDADD) $(LIBS) V8Client/avocsh.$(OBJEXT): V8Client/$(am__dirstamp) \ V8Client/$(DEPDIR)/$(am__dirstamp) -avocsh$(EXEEXT): $(avocsh_OBJECTS) $(avocsh_DEPENDENCIES) +avocsh$(EXEEXT): $(avocsh_OBJECTS) $(avocsh_DEPENDENCIES) $(EXTRA_avocsh_DEPENDENCIES) @rm -f avocsh$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(avocsh_OBJECTS) $(avocsh_LDADD) $(LIBS) @@ -1995,6 +2018,7 @@ mostlyclean-compile: -rm -f Admin/RestAdminLogHandler.$(OBJEXT) -rm -f Admin/RestBaseHandler.$(OBJEXT) -rm -f Admin/RestVersionHandler.$(OBJEXT) + -rm -f Ahuacatl/ahuacatl-access-optimizer.$(OBJEXT) -rm -f Ahuacatl/ahuacatl-ast-node.$(OBJEXT) -rm -f Ahuacatl/ahuacatl-bind-parameter.$(OBJEXT) -rm -f Ahuacatl/ahuacatl-codegen-js.$(OBJEXT) @@ -2156,7 +2180,9 @@ mostlyclean-compile: -rm -f UnitTests/Philadelphia/associative-pointer-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/hashes-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/json-test.$(OBJEXT) + -rm -f UnitTests/Philadelphia/json-utilities-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/string-buffer-test.$(OBJEXT) + -rm -f UnitTests/Philadelphia/string-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/string-utf8-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/vector-pointer-test.$(OBJEXT) -rm -f UnitTests/Philadelphia/vector-test.$(OBJEXT) @@ -2241,6 +2267,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@Admin/$(DEPDIR)/RestAdminLogHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Admin/$(DEPDIR)/RestBaseHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Admin/$(DEPDIR)/RestVersionHandler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@Ahuacatl/$(DEPDIR)/ahuacatl-access-optimizer.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Ahuacatl/$(DEPDIR)/ahuacatl-ast-node.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Ahuacatl/$(DEPDIR)/ahuacatl-bind-parameter.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Ahuacatl/$(DEPDIR)/ahuacatl-codegen-js.Po@am__quote@ @@ -2403,7 +2430,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/associative-pointer-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/hashes-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/json-test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/json-utilities-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/string-buffer-test.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/string-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/string-utf8-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/vector-pointer-test.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@UnitTests/Philadelphia/$(DEPDIR)/vector-test.Po@am__quote@ @@ -2482,37 +2511,33 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .cpp.o: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $< +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< .cpp.obj: @am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po -@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@ -@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` install-nobase_pkgdataDATA: $(nobase_pkgdata_DATA) @$(NORMAL_INSTALL) test -z "$(pkgdatadir)" || $(MKDIR_P) "$(DESTDIR)$(pkgdatadir)" @@ -2533,9 +2558,7 @@ uninstall-nobase_pkgdataDATA: @$(NORMAL_UNINSTALL) @list='$(nobase_pkgdata_DATA)'; test -n "$(pkgdatadir)" || list=; \ $(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(pkgdatadir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(pkgdatadir)" && rm -f $$files + dir='$(DESTDIR)$(pkgdatadir)'; $(am__uninstall_files_from_dir) ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ @@ -2633,7 +2656,11 @@ dist-gzip: distdir $(am__remove_distdir) dist-bzip2: distdir - tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__remove_distdir) dist-lzma: distdir @@ -2641,7 +2668,7 @@ dist-lzma: distdir $(am__remove_distdir) dist-xz: distdir - tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__remove_distdir) dist-tarZ: distdir @@ -2672,6 +2699,8 @@ distcheck: dist bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lzma*) \ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ @@ -2691,6 +2720,7 @@ distcheck: dist && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ @@ -2719,8 +2749,16 @@ distcheck: dist list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: - @$(am__cd) '$(distuninstallcheck_dir)' \ - && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ @@ -2755,10 +2793,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -2926,8 +2969,8 @@ uninstall-am: uninstall-binPROGRAMS uninstall-nobase_pkgdataDATA \ .PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \ clean-binPROGRAMS clean-generic clean-local \ clean-noinstLIBRARIES clean-noinstPROGRAMS clean-sbinPROGRAMS \ - ctags dist dist-all dist-bzip2 dist-gzip dist-lzma dist-shar \ - dist-tarZ dist-xz dist-zip distcheck distclean \ + ctags dist dist-all dist-bzip2 dist-gzip dist-lzip dist-lzma \ + dist-shar dist-tarZ dist-xz dist-zip distcheck distclean \ distclean-compile distclean-generic distclean-hdr \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ diff --git a/Makefile.unittests b/Makefile.unittests index 4a0c5120bb..673d30212f 100644 --- a/Makefile.unittests +++ b/Makefile.unittests @@ -101,10 +101,12 @@ UnitTests_basics_suite_DEPENDENCIES = @top_builddir@/libavocado.a UnitTests_basics_suite_SOURCES = \ UnitTests/Runner.cpp \ UnitTests/Philadelphia/json-test.cpp \ + UnitTests/Philadelphia/json-utilities-test.cpp \ UnitTests/Philadelphia/hashes-test.cpp \ UnitTests/Philadelphia/associative-pointer-test.cpp \ UnitTests/Philadelphia/string-buffer-test.cpp \ UnitTests/Philadelphia/string-utf8-test.cpp \ + UnitTests/Philadelphia/string-test.cpp \ UnitTests/Philadelphia/vector-pointer-test.cpp \ UnitTests/Philadelphia/vector-test.cpp \ UnitTests/Jutland/StringBufferTest.cpp \ diff --git a/UnitTests/Philadelphia/json-test.cpp b/UnitTests/Philadelphia/json-test.cpp index e5e8cf2732..dbe54c1566 100644 --- a/UnitTests/Philadelphia/json-test.cpp +++ b/UnitTests/Philadelphia/json-test.cpp @@ -292,7 +292,7 @@ BOOST_AUTO_TEST_CASE (tst_json_list_empty) { } //////////////////////////////////////////////////////////////////////////////// -/// @brief test empty json list mixed +/// @brief test json list mixed //////////////////////////////////////////////////////////////////////////////// BOOST_AUTO_TEST_CASE (tst_json_list_mixed) { @@ -305,9 +305,40 @@ BOOST_AUTO_TEST_CASE (tst_json_list_mixed) { TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, -8093)); TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1.5)); TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, (char*) "the quick brown fox")); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE)); STRINGIFY - BOOST_CHECK_EQUAL("[null,true,false,-8093,1.5,\"the quick brown fox\"]", STRING_VALUE); + BOOST_CHECK_EQUAL("[null,true,false,-8093,1.5,\"the quick brown fox\",[],{}]", STRING_VALUE); + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test json lists nested +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_json_list_nested) { + INIT_BUFFER + + TRI_json_t* json = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* list1 = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* list2 = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* list3 = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* list4 = TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE); + + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list1, TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list1, TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, false)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list2, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, -8093)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list2, TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1.5)); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, list3, TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, (char*) "the quick brown fox")); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, list1); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, list2); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, list3); + TRI_PushBack3ListJson(TRI_UNKNOWN_MEM_ZONE, json, list4); + + STRINGIFY + BOOST_CHECK_EQUAL("[[true,false],[-8093,1.5],[\"the quick brown fox\"],[]]", STRING_VALUE); FREE_JSON FREE_BUFFER } @@ -327,6 +358,107 @@ BOOST_AUTO_TEST_CASE (tst_json_array_empty) { FREE_BUFFER } +//////////////////////////////////////////////////////////////////////////////// +/// @brief test json array mixed +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_json_array_mixed) { + INIT_BUFFER + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "one", TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "two", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "three", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, false)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "four", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, -8093)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "five", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1.5)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "six", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, (char*) "the quick brown fox")); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "seven", TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "eight", TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE)); + + STRINGIFY + BOOST_CHECK_EQUAL("{\"one\":null,\"two\":true,\"three\":false,\"four\":-8093,\"five\":1.5,\"six\":\"the quick brown fox\",\"seven\":[],\"eight\":{}}", STRING_VALUE); + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test nested json array +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_json_array_nested) { + INIT_BUFFER + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* array1 = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* array2 = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* array3 = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_json_t* array4 = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array1, "one", TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array1, "two", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, true)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array1, "three", TRI_CreateBooleanJson(TRI_UNKNOWN_MEM_ZONE, false)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array2, "four", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, -8093)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array2, "five", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1.5)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array2, "six", TRI_CreateStringCopyJson(TRI_UNKNOWN_MEM_ZONE, (char*) "the quick brown fox")); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array3, "seven", TRI_CreateListJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, array3, "eight", TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "one", array1); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "two", array2); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "three", array3); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "four", array4); + + STRINGIFY + BOOST_CHECK_EQUAL("{\"one\":{\"one\":null,\"two\":true,\"three\":false},\"two\":{\"four\":-8093,\"five\":1.5,\"six\":\"the quick brown fox\"},\"three\":{\"seven\":[],\"eight\":{}},\"four\":{}}", STRING_VALUE); + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test json array keys +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_json_array_keys) { + INIT_BUFFER + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "\"quoted\"", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "'quoted'", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 2)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "\\slashed\\\"", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 3)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "white spaced", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 4)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "line\\nbreak", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 5)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 6)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, " ", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 7)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "null", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 8)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "true", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 9)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "false", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 10)); + + STRINGIFY + BOOST_CHECK_EQUAL("{\"\\\"quoted\\\"\":1,\"'quoted'\":2,\"\\\\slashed\\\\\\\"\":3,\"white spaced\":4,\"line\\\\nbreak\":5,\"\":6,\" \":7,\"null\":8,\"true\":9,\"false\":10}", STRING_VALUE); + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test utf8 json array keys +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_json_array_keys_utf8) { + INIT_BUFFER + + TRI_json_t* json = TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "äöüÄÖÜß", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 1)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "코리아닷컴", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 2)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "ジャパン", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 3)); + TRI_Insert3ArrayJson(TRI_UNKNOWN_MEM_ZONE, json, "мадридского", TRI_CreateNumberJson(TRI_UNKNOWN_MEM_ZONE, 4)); + + STRINGIFY + BOOST_CHECK_EQUAL("{\"\\u00E4\\u00F6\\u00FC\\u00C4\\u00D6\\u00DC\\u00DF\":1,\"\\uCF54\\uB9AC\\uC544\\uB2F7\\uCEF4\":2,\"\\u30B8\\u30E3\\u30D1\\u30F3\":3,\"\\u043C\\u0430\\u0434\\u0440\\u0438\\u0434\\u0441\\u043A\\u043E\\u0433\\u043E\":4}", STRING_VALUE); + + FREE_JSON + FREE_BUFFER +} + +// TODO: add tests for lookup json array value etc. + //////////////////////////////////////////////////////////////////////////////// /// @brief generate tests //////////////////////////////////////////////////////////////////////////////// diff --git a/UnitTests/Philadelphia/json-utilities-test.cpp b/UnitTests/Philadelphia/json-utilities-test.cpp new file mode 100644 index 0000000000..64c418fba8 --- /dev/null +++ b/UnitTests/Philadelphia/json-utilities-test.cpp @@ -0,0 +1,431 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite for json-utilities.c +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 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 + +#include "BasicsC/json-utilities.h" +#include "BasicsC/string-buffer.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- private macros +// ----------------------------------------------------------------------------- + +#define JSON_CHECK(expected, func, lValue, rValue) \ + l = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, lValue); \ + r = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, rValue); \ + if (l && r) { \ + BOOST_CHECK_EQUAL(expected, func(l, r)); \ + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, l); \ + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, r); \ + } + +#define INIT_BUFFER TRI_string_buffer_t* sb = TRI_CreateStringBuffer(TRI_UNKNOWN_MEM_ZONE); +#define FREE_BUFFER TRI_FreeStringBuffer(TRI_UNKNOWN_MEM_ZONE, sb); +#define STRINGIFY TRI_StringifyJson(sb, json); +#define STRING_VALUE sb->_buffer +#define FREE_JSON TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + +// ----------------------------------------------------------------------------- +// --SECTION-- setup / tear-down +// ----------------------------------------------------------------------------- + +struct CJsonUtilitiesSetup { + CJsonUtilitiesSetup () { + BOOST_TEST_MESSAGE("setup json utilities test"); + } + + ~CJsonUtilitiesSetup () { + BOOST_TEST_MESSAGE("tear-down json utilities test"); + } +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief setup +//////////////////////////////////////////////////////////////////////////////// + +BOOST_FIXTURE_TEST_SUITE(CJsonUtilitiesTest, CJsonUtilitiesSetup) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test compare values with equal values +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_compare_values_equal) { + TRI_json_t* l; + TRI_json_t* r; + + JSON_CHECK(0, TRI_CompareValuesJson, "null", "null"); + JSON_CHECK(0, TRI_CompareValuesJson, "false", "false"); + JSON_CHECK(0, TRI_CompareValuesJson, "true", "true"); + JSON_CHECK(0, TRI_CompareValuesJson, "0", "0"); + JSON_CHECK(0, TRI_CompareValuesJson, "1", "1"); + JSON_CHECK(0, TRI_CompareValuesJson, "1.5", "1.5"); + JSON_CHECK(0, TRI_CompareValuesJson, "-43.2", "-43.2"); + JSON_CHECK(0, TRI_CompareValuesJson, "\"\"", "\"\""); + JSON_CHECK(0, TRI_CompareValuesJson, "\" \"", "\" \""); + JSON_CHECK(0, TRI_CompareValuesJson, "\"the quick brown fox\"", "\"the quick brown fox\""); + JSON_CHECK(0, TRI_CompareValuesJson, "[]", "[]"); + JSON_CHECK(0, TRI_CompareValuesJson, "[-1]", "[-1]"); + JSON_CHECK(0, TRI_CompareValuesJson, "[0]", "[0]"); + JSON_CHECK(0, TRI_CompareValuesJson, "[1]", "[1]"); + JSON_CHECK(0, TRI_CompareValuesJson, "[true]", "[true]"); + JSON_CHECK(0, TRI_CompareValuesJson, "{}", "{}"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test compare values with unequal values +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_compare_values_unequal) { + TRI_json_t* l; + TRI_json_t* r; + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "false"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "true"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "-1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "0"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "-10"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "\"\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "\"0\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "\" \""); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "[]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "[null]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "[false]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "[true]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "[0]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "null", "{}"); + + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "true"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "-1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "0"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "-10"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "\"\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "\"0\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "\" \""); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "[]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "[null]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "[false]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "[true]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "[0]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "false", "{}"); + + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "-1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "0"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "-10"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "\"\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "\"0\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "\" \""); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "[]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "[null]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "[false]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "[true]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "[0]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "{}"); + + JSON_CHECK(-1, TRI_CompareValuesJson, "-2", "-1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "-10", "-9"); + JSON_CHECK(-1, TRI_CompareValuesJson, "-20", "-5"); + JSON_CHECK(-1, TRI_CompareValuesJson, "-5", "-2"); + JSON_CHECK(-1, TRI_CompareValuesJson, "true", "1"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1.5", "1.6"); + JSON_CHECK(-1, TRI_CompareValuesJson, "10.5", "10.51"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "\"\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "\"0\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "\"-1\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "\"-1\""); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "\" \""); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[-1]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[0]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[1]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[null]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[false"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "[true]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "0", "{}"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[-1]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[0]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[1]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[null]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[false"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "[true]"); + JSON_CHECK(-1, TRI_CompareValuesJson, "1", "{}"); + + // TODO: add more tests +} + +// TODO: add tests for + // TRI_CheckSameValueJson + // TRI_BetweenListJson + // TRI_UniquifyListJson + // TRI_UnionizeListsJson + // TRI_IntersectListsJson + // TRI_SortListJson + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test check in list +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_check_in_list) { + TRI_json_t* l; + TRI_json_t* r; + + JSON_CHECK(true, TRI_CheckInListJson, "null", "[1,2,3,null]"); + JSON_CHECK(true, TRI_CheckInListJson, "false", "[false]"); + JSON_CHECK(true, TRI_CheckInListJson, "true", "[false,true]"); + JSON_CHECK(true, TRI_CheckInListJson, "0", "[0]"); + JSON_CHECK(true, TRI_CheckInListJson, "0", "[0,1]"); + JSON_CHECK(true, TRI_CheckInListJson, "0", "[0,1,2]"); + JSON_CHECK(true, TRI_CheckInListJson, "0", "[2,1,0]"); + JSON_CHECK(true, TRI_CheckInListJson, "1", "[1,0]"); + JSON_CHECK(true, TRI_CheckInListJson, "1", "[2,1,0]"); + JSON_CHECK(true, TRI_CheckInListJson, "1", "[12,12,12,12,1]"); + JSON_CHECK(true, TRI_CheckInListJson, "12", "[0,9,100,7,12,8]"); + JSON_CHECK(true, TRI_CheckInListJson, "15", "[12,13,14,16,17,15]"); + JSON_CHECK(true, TRI_CheckInListJson, "\"\"", "[1,2,3,\"\"]"); + JSON_CHECK(true, TRI_CheckInListJson, "\"a\"", "[1,2,3,\"a\"]"); + JSON_CHECK(true, TRI_CheckInListJson, "\"A\"", "[1,2,\"A\"]"); + JSON_CHECK(true, TRI_CheckInListJson, "\"the fox\"", "[1,\"the fox\"]"); + JSON_CHECK(true, TRI_CheckInListJson, "[]", "[[]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[]", "[2,3,[]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[null]", "[[null]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[false]", "[[false]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[true]", "[[true]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[true]", "[[false],[true]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[0]", "[1,2,3,[0]]"); + JSON_CHECK(true, TRI_CheckInListJson, "[\"a\"]", "[\"b\",\"\",[\"a\"]]"); + + JSON_CHECK(false, TRI_CheckInListJson, "null", "[0,1,2,3,\"\",false,\"null\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "null", "[[null]]"); + JSON_CHECK(false, TRI_CheckInListJson, "false", "[0,1,2,3,\"\",\"false\",\"null\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "false", "[[false]]"); + JSON_CHECK(false, TRI_CheckInListJson, "true", "[\"true\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "true", "[[true]]"); + JSON_CHECK(false, TRI_CheckInListJson, "0", "[null,false,\"\",\" \"]"); + JSON_CHECK(false, TRI_CheckInListJson, "0", "[[0]]"); + JSON_CHECK(false, TRI_CheckInListJson, "15", "[12,13,14,16,17]"); + JSON_CHECK(false, TRI_CheckInListJson, "15", "[[15]]"); + JSON_CHECK(false, TRI_CheckInListJson, "120", "[12,121,1200]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"a\"", "[\"A\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"A\"", "[\"a\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"a\"", "[\"abc\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"a\"", "[\"a \"]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"the fox\"", "[\"the\",\"fox\"]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"a\"", "[[\"a\"]]"); + JSON_CHECK(false, TRI_CheckInListJson, "[]", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "[]", "[5,4,3,2,1]"); + JSON_CHECK(false, TRI_CheckInListJson, "[0]", "[0,1,2,3]"); + JSON_CHECK(false, TRI_CheckInListJson, "[]", "[0,1,2,3]"); + JSON_CHECK(false, TRI_CheckInListJson, "[false]", "[false,true]"); + JSON_CHECK(false, TRI_CheckInListJson, "[\"a\"]", "[\"a\"]"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test check in list with an empty list +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_check_in_list_empty) { + TRI_json_t* l; + TRI_json_t* r; + + JSON_CHECK(false, TRI_CheckInListJson, "null", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "false", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "true", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "0", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "1", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "\"fox\"", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, " ", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "[]", "[]"); + JSON_CHECK(false, TRI_CheckInListJson, "{}", "[]"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists union +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_unionize_lists_empty) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + json = TRI_UnionizeListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists intersection +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_intersect_lists_empty1) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + json = TRI_IntersectListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists intersection +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_intersect_lists_empty2) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[1]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + json = TRI_IntersectListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists intersection +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_intersect_lists_empty3) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[0]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[]"); + json = TRI_IntersectListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists intersection +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_intersect_lists_values1) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[0,1,2,3]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[2,3,4]"); + json = TRI_IntersectListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[2,3]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lists intersection +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_intersect_lists_values2) { + INIT_BUFFER + + TRI_json_t* json; + TRI_json_t* list1; + TRI_json_t* list2; + + list1 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[null,false,true,0,1,2,3,99,99.5,\"fox\",\"zoo\"]"); + list2 = TRI_JsonString(TRI_UNKNOWN_MEM_ZONE, "[false,2,99,99.2,\"Fox\",\"zoo\"]"); + json = TRI_IntersectListsJson(list1, list2, true); + + STRINGIFY + BOOST_CHECK_EQUAL("[false,2,99,\"zoo\"]", STRING_VALUE); + + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list1); + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, list2); + + FREE_JSON + FREE_BUFFER +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief generate tests +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_SUITE_END () + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/UnitTests/Philadelphia/string-test.cpp b/UnitTests/Philadelphia/string-test.cpp new file mode 100644 index 0000000000..8435626921 --- /dev/null +++ b/UnitTests/Philadelphia/string-test.cpp @@ -0,0 +1,297 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite for string utility functions +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 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 + +#include "BasicsC/strings.h" + +// ----------------------------------------------------------------------------- +// --SECTION-- private macros +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- private constants +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- setup / tear-down +// ----------------------------------------------------------------------------- + +struct CStringSetup { + CStringSetup () { + BOOST_TEST_MESSAGE("setup string test"); + } + + ~CStringSetup () { + BOOST_TEST_MESSAGE("tear-down string test"); + } +}; + +// ----------------------------------------------------------------------------- +// --SECTION-- test suite +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief setup +//////////////////////////////////////////////////////////////////////////////// + +BOOST_FIXTURE_TEST_SUITE(CStringTest, CStringSetup) + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lower casing (no changes) +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_lower_ascii_no_change) { + char* result; + + result = TRI_LowerAsciiString("this is supposed to stay the same"); + BOOST_CHECK_EQUAL("this is supposed to stay the same", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_LowerAsciiString("this is also supposed to stay the same"); + BOOST_CHECK_EQUAL("this is also supposed to stay the same", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // punctuation should not change + result = TRI_LowerAsciiString("01234567890,.;:-_#'+*~!\"§$%&/()[]{}=?\\|<>"); + BOOST_CHECK_EQUAL("01234567890,.;:-_#'+*~!\"§$%&/()[]{}=?\\|<>", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // whitespace should not change + result = TRI_LowerAsciiString(" \t \n \r \n"); + BOOST_CHECK_EQUAL(" \t \n \r \n", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // test an empty string + result = TRI_LowerAsciiString(""); + BOOST_CHECK_EQUAL("", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lower casing +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_lower_ascii) { + char* result; + + result = TRI_LowerAsciiString("This MUST be converted into LOWER CASE!"); + BOOST_CHECK_EQUAL("this must be converted into lower case!", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_LowerAsciiString("SCREAMING OUT LOUD"); + BOOST_CHECK_EQUAL("screaming out loud", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test lower casing with non-ASCII +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_lower_ascii_non_ascii) { + char* result; + + result = TRI_LowerAsciiString("äöüÄÖÜß"); + BOOST_CHECK_EQUAL("äöüÄÖÜß", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_LowerAsciiString("코리아닷컴"); + BOOST_CHECK_EQUAL("코리아닷컴", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_LowerAsciiString("своих партнеров"); + BOOST_CHECK_EQUAL("своих партнеров", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test upper casing (no changes) +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_upper_ascii_no_change) { + char* result; + + result = TRI_UpperAsciiString("THIS IS SUPPOSED TO STAY THE SAME"); + BOOST_CHECK_EQUAL("THIS IS SUPPOSED TO STAY THE SAME", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_UpperAsciiString("THIS IS ALSO SUPPOSED TO STAY THE SAME"); + BOOST_CHECK_EQUAL("THIS IS ALSO SUPPOSED TO STAY THE SAME", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // punctuation should not change + result = TRI_UpperAsciiString("01234567890,.;:-_#'+*~!\"§$%&/()[]{}=?\\|<>"); + BOOST_CHECK_EQUAL("01234567890,.;:-_#'+*~!\"§$%&/()[]{}=?\\|<>", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // whitespace should not change + result = TRI_UpperAsciiString(" \t \n \r \n"); + BOOST_CHECK_EQUAL(" \t \n \r \n", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + // test an empty string + result = TRI_UpperAsciiString(""); + BOOST_CHECK_EQUAL("", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test upper casing +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_upper_ascii) { + char* result; + + result = TRI_UpperAsciiString("This must be converted into upper CASE!"); + BOOST_CHECK_EQUAL("THIS MUST BE CONVERTED INTO UPPER CASE!", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_UpperAsciiString("silently whispering"); + BOOST_CHECK_EQUAL("SILENTLY WHISPERING", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test upper casing with non-ASCII +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_upper_ascii_non_ascii) { + char* result; + + result = TRI_UpperAsciiString("äöüÄÖÜß"); + BOOST_CHECK_EQUAL("äöüÄÖÜß", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_UpperAsciiString("코리아닷컴"); + BOOST_CHECK_EQUAL("코리아닷컴", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); + + result = TRI_UpperAsciiString("своих партнеров"); + BOOST_CHECK_EQUAL("своих партнеров", result); + TRI_FreeString(TRI_CORE_MEM_ZONE, result); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test equal string +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_equal_string) { + BOOST_CHECK_EQUAL(true, TRI_EqualString("", "")); + BOOST_CHECK_EQUAL(true, TRI_EqualString(" ", " ")); + BOOST_CHECK_EQUAL(true, TRI_EqualString("a", "a")); + BOOST_CHECK_EQUAL(true, TRI_EqualString("the quick brown fox", "the quick brown fox")); + BOOST_CHECK_EQUAL(true, TRI_EqualString("The Quick Brown FOX", "The Quick Brown FOX")); + BOOST_CHECK_EQUAL(true, TRI_EqualString("\"\t\r\n ", "\"\t\r\n ")); + + BOOST_CHECK_EQUAL(false, TRI_EqualString("", " ")); + BOOST_CHECK_EQUAL(false, TRI_EqualString(" ", "")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("a", "")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("a", "a ")); + BOOST_CHECK_EQUAL(false, TRI_EqualString(" a", "a")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("A", "a")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("a", "A")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("", "0")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("0", "")); + BOOST_CHECK_EQUAL(false, TRI_EqualString(" ", "0")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("0", " ")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("case matters", "Case matters")); + BOOST_CHECK_EQUAL(false, TRI_EqualString("CASE matters", "CASE matterS")); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test case equal string +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_case_equal_string) { + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("", "")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString(" ", " ")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("a", "a")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("the quick brown fox", "the quick brown fox")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("The Quick Brown FOX", "The Quick Brown FOX")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("\"\t\r\n ", "\"\t\r\n ")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("A", "a")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("a", "A")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("case matters", "Case matters")); + BOOST_CHECK_EQUAL(true, TRI_CaseEqualString("CASE matters", "CASE matterS")); + + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("", " ")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString(" ", "")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("a", "")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("a", "a ")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString(" a", "a")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("", "0")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("0", "")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString(" ", "0")); + BOOST_CHECK_EQUAL(false, TRI_CaseEqualString("0", " ")); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test prefix string +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_CASE (tst_prefix_string) { + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the quick brown fox", "the")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the quick brown fox", "th")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the quick brown fox", "t")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the quick brown fox", "the q")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString(" the quick brown fox", " ")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the fox", "the fox")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("\t\r\n0", "\t")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("\t\r\n0", "\t\r")); + BOOST_CHECK_EQUAL(true, TRI_IsPrefixString("the fox", "")); + + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "The")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", " the")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "the quick")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "the q ")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "foo")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "a")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "quick")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "he quick")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "fox")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the quick brown fox", "T")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("The quick brown fox", "the")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("THE QUICK BROWN FOX", "The")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("THE QUICK BROWN FOX", "the")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("THE QUICK BROWN FOX", "THE quick")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString(" the quick brown fox", "the")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("the fox", " ")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("\r\n0", "\n")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("\r\n0", " ")); + BOOST_CHECK_EQUAL(false, TRI_IsPrefixString("", "the")); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief generate tests +//////////////////////////////////////////////////////////////////////////////// + +BOOST_AUTO_TEST_SUITE_END () + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/UnitTests/Philadelphia/string-utf8-test.cpp b/UnitTests/Philadelphia/string-utf8-test.cpp index 7651be8fac..6c71ff7b4d 100644 --- a/UnitTests/Philadelphia/string-utf8-test.cpp +++ b/UnitTests/Philadelphia/string-utf8-test.cpp @@ -53,7 +53,7 @@ struct CStringUtf8Setup { } ~CStringUtf8Setup () { - BOOST_TEST_MESSAGE("tear-downstring UTF8 test"); + BOOST_TEST_MESSAGE("tear-down string UTF8 test"); } }; diff --git a/V8/v8-vocbase.cpp b/V8/v8-vocbase.cpp index ecbffb8eae..89b89ec8e3 100644 --- a/V8/v8-vocbase.cpp +++ b/V8/v8-vocbase.cpp @@ -6359,7 +6359,6 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas v8::Handle SaveFuncName = v8::Persistent::New(v8::String::New("save")); v8::Handle StatusFuncName = v8::Persistent::New(v8::String::New("status")); v8::Handle UnloadFuncName = v8::Persistent::New(v8::String::New("unload")); - v8::Handle UseNextFuncName = v8::Persistent::New(v8::String::New("useNext")); v8::Handle _CollectionFuncName = v8::Persistent::New(v8::String::New("_collection")); v8::Handle _CollectionsFuncName = v8::Persistent::New(v8::String::New("_collections"));