1
0
Fork 0

do not remove constant sorts when creating plans, but during optimization phase

less utf8 comparisons
This commit is contained in:
Jan Steemann 2015-04-20 10:09:17 +02:00
parent a27953cf3b
commit 61499432b7
21 changed files with 567 additions and 237 deletions

View File

@ -56,10 +56,8 @@ AqlItemBlock::AqlItemBlock (size_t nrItems,
// this compare value is arbitrary, but having so many registers in a single query seems unlikely
TRI_ASSERT(nrRegs <= ExecutionNode::MaxRegisterId);
_data.reserve(nrItems * nrRegs);
for (size_t i = 0; i < nrItems * nrRegs; ++i) {
_data.emplace_back();
}
_data.resize(nrItems * nrRegs);
_docColls.reserve(nrRegs);
for (size_t i = 0; i < nrRegs; ++i) {
_docColls.emplace_back(nullptr);
@ -84,10 +82,7 @@ AqlItemBlock::AqlItemBlock (Json const& json) {
// Initialize the data vector:
if (_nrRegs > 0) {
_data.reserve(_nrItems * _nrRegs);
for (size_t i = 0; i < _nrItems * _nrRegs; ++i) {
_data.emplace_back();
}
_data.resize(_nrItems * _nrRegs);
_docColls.reserve(_nrRegs);
for (size_t i = 0; i < _nrRegs; ++i) {
_docColls.emplace_back(nullptr);
@ -178,17 +173,17 @@ AqlItemBlock::AqlItemBlock (Json const& json) {
////////////////////////////////////////////////////////////////////////////////
void AqlItemBlock::destroy () {
for (size_t i = 0; i < _nrItems * _nrRegs; i++) {
if (! _data[i].isEmpty()) {
for (auto& it : _data) {
if (! it.isEmpty()) {
try { // can find() really throw???
auto it = _valueCount.find(_data[i]);
if (it != _valueCount.end()) { // if we know it, we are still responsible
TRI_ASSERT_EXPENSIVE(it->second > 0);
if (--(it->second) == 0) {
_data[i].destroy();
auto it2 = _valueCount.find(it);
if (it2 != _valueCount.end()) { // if we know it, we are still responsible
TRI_ASSERT_EXPENSIVE(it2->second > 0);
if (--(it2->second) == 0) {
it.destroy();
try {
_valueCount.erase(it);
_valueCount.erase(it2);
}
catch (...) {
}

View File

@ -117,7 +117,7 @@ namespace triagens {
/// @brief setValue, set the current value of a register
////////////////////////////////////////////////////////////////////////////////
void setValue (size_t index, RegisterId varNr, AqlValue value) {
void setValue (size_t index, RegisterId varNr, AqlValue const& value) {
TRI_ASSERT_EXPENSIVE(_data.capacity() > index * _nrRegs + varNr);
TRI_ASSERT_EXPENSIVE(_data.at(index * _nrRegs + varNr).isEmpty());
@ -132,7 +132,7 @@ namespace triagens {
}
else {
TRI_ASSERT_EXPENSIVE(it->second > 0);
it->second++;
++(it->second);
}
}

View File

@ -75,10 +75,8 @@ bool AqlValue::isTrue () const {
void AqlValue::destroy () {
switch (_type) {
case JSON: {
if (_json != nullptr) {
delete _json;
_json = nullptr;
}
delete _json;
_json = nullptr;
break;
}
case DOCVEC: {
@ -701,6 +699,100 @@ Json AqlValue::toJson (triagens::arango::AqlTransaction* trx,
THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief toJson method
////////////////////////////////////////////////////////////////////////////////
uint64_t AqlValue::hash (triagens::arango::AqlTransaction* trx,
TRI_document_collection_t const* document) const {
switch (_type) {
case JSON: {
return TRI_HashJson(_json->json());
}
case SHAPED: {
TRI_ASSERT(document != nullptr);
TRI_ASSERT(_marker != nullptr);
TRI_shaper_t* shaper = document->getShaper();
TRI_shaped_json_t shaped;
TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, _marker);
Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped));
// append the internal attributes
// _id, _key, _rev
char const* key = TRI_EXTRACT_MARKER_KEY(_marker);
std::string id(trx->resolver()->getCollectionName(document->_info._cid));
id.push_back('/');
id.append(key);
json(TRI_VOC_ATTRIBUTE_ID, Json(id));
json(TRI_VOC_ATTRIBUTE_REV, Json(std::to_string(TRI_EXTRACT_MARKER_RID(_marker))));
json(TRI_VOC_ATTRIBUTE_KEY, Json(key));
if (TRI_IS_EDGE_MARKER(_marker)) {
// _from
std::string from(trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_FROM_CID(_marker)));
from.push_back('/');
from.append(TRI_EXTRACT_MARKER_FROM_KEY(_marker));
json(TRI_VOC_ATTRIBUTE_FROM, Json(from));
// _to
std::string to(trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(_marker)));
to.push_back('/');
to.append(TRI_EXTRACT_MARKER_TO_KEY(_marker));
json(TRI_VOC_ATTRIBUTE_TO, Json(to));
}
return TRI_HashJson(json.json());
}
case DOCVEC: {
TRI_ASSERT(_vector != nullptr);
// calculate the result array length
size_t totalSize = 0;
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
totalSize += (*it)->size();
}
// allocate the result array
Json json(Json::Array, static_cast<size_t>(totalSize));
for (auto it = _vector->begin(); it != _vector->end(); ++it) {
auto current = (*it);
size_t const n = current->size();
auto vecCollection = current->getDocumentCollection(0);
for (size_t i = 0; i < n; ++i) {
json.add(current->getValue(i, 0).toJson(trx, vecCollection));
}
}
return TRI_HashJson(json.json());
}
case RANGE: {
TRI_ASSERT(_range != nullptr);
// allocate the buffer for the result
size_t const n = _range->size();
Json json(Json::Array, n);
for (size_t i = 0; i < n; ++i) {
// is it safe to use a double here (precision loss)?
json.add(Json(static_cast<double>(_range->at(i))));
}
return TRI_HashJson(json.json());
}
case EMPTY: {
}
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief extract an attribute value from the AqlValue
/// this will return an empty Json if the value is not an object
@ -709,7 +801,8 @@ Json AqlValue::toJson (triagens::arango::AqlTransaction* trx,
Json AqlValue::extractObjectMember (triagens::arango::AqlTransaction* trx,
TRI_document_collection_t const* document,
char const* name,
bool copy) const {
bool copy,
triagens::basics::StringBuffer& buffer) const {
switch (_type) {
case JSON: {
TRI_ASSERT(_json != nullptr);
@ -744,30 +837,35 @@ Json AqlValue::extractObjectMember (triagens::arango::AqlTransaction* trx,
return Json(TRI_UNKNOWN_MEM_ZONE, TRI_EXTRACT_MARKER_KEY(_marker));
}
else if (strcmp(name, TRI_VOC_ATTRIBUTE_ID) == 0) {
std::string id(trx->resolver()->getCollectionName(document->_info._cid));
id.push_back('/');
id.append(TRI_EXTRACT_MARKER_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, id);
buffer.reset();
trx->resolver()->getCollectionName(document->_info._cid, buffer);
buffer.appendChar('/');
buffer.appendText(TRI_EXTRACT_MARKER_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length());
}
else if (strcmp(name, TRI_VOC_ATTRIBUTE_REV) == 0) {
TRI_voc_rid_t rid = TRI_EXTRACT_MARKER_RID(_marker);
return Json(TRI_UNKNOWN_MEM_ZONE, JsonHelper::uint64String(TRI_UNKNOWN_MEM_ZONE, rid));
buffer.reset();
buffer.appendInteger(rid);
return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length());
}
else if (strcmp(name, TRI_VOC_ATTRIBUTE_FROM) == 0 &&
(_marker->_type == TRI_DOC_MARKER_KEY_EDGE ||
_marker->_type == TRI_WAL_MARKER_EDGE)) {
std::string from(trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_FROM_CID(_marker)));
from.push_back('/');
from.append(TRI_EXTRACT_MARKER_FROM_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, from);
buffer.reset();
trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_FROM_CID(_marker), buffer);
buffer.appendChar('/');
buffer.appendText(TRI_EXTRACT_MARKER_FROM_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length());
}
else if (strcmp(name, TRI_VOC_ATTRIBUTE_TO) == 0 &&
(_marker->_type == TRI_DOC_MARKER_KEY_EDGE ||
_marker->_type == TRI_WAL_MARKER_EDGE)) {
std::string to(trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(_marker)));
to.push_back('/');
to.append(TRI_EXTRACT_MARKER_TO_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, to);
buffer.reset();
trx->resolver()->getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(_marker), buffer);
buffer.appendChar('/');
buffer.appendText(TRI_EXTRACT_MARKER_TO_KEY(_marker));
return Json(TRI_UNKNOWN_MEM_ZONE, buffer.c_str(), buffer.length());
}
}
@ -967,7 +1065,8 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
AqlValue const& left,
TRI_document_collection_t const* leftcoll,
AqlValue const& right,
TRI_document_collection_t const* rightcoll) {
TRI_document_collection_t const* rightcoll,
bool compareUtf8) {
if (left._type != right._type) {
if (left._type == AqlValue::EMPTY) {
return -1;
@ -983,7 +1082,7 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
right._type == AqlValue::RANGE ||
right._type == AqlValue::DOCVEC)) {
triagens::basics::Json rjson = right.toJson(trx, rightcoll);
return TRI_CompareValuesJson(left._json->json(), rjson.json(), true);
return TRI_CompareValuesJson(left._json->json(), rjson.json(), compareUtf8);
}
// SHAPED against x
@ -991,12 +1090,12 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
triagens::basics::Json ljson = left.toJson(trx, leftcoll);
if (right._type == AqlValue::JSON) {
return TRI_CompareValuesJson(ljson.json(), right._json->json(), true);
return TRI_CompareValuesJson(ljson.json(), right._json->json(), compareUtf8);
}
else if (right._type == AqlValue::RANGE ||
right._type == AqlValue::DOCVEC) {
triagens::basics::Json rjson = right.toJson(trx, rightcoll);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), true);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), compareUtf8);
}
}
@ -1005,12 +1104,12 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
triagens::basics::Json ljson = left.toJson(trx, leftcoll);
if (right._type == AqlValue::JSON) {
return TRI_CompareValuesJson(ljson.json(), right._json->json(), true);
return TRI_CompareValuesJson(ljson.json(), right._json->json(), compareUtf8);
}
else if (right._type == AqlValue::SHAPED ||
right._type == AqlValue::DOCVEC) {
triagens::basics::Json rjson = right.toJson(trx, rightcoll);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), true);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), compareUtf8);
}
}
@ -1019,12 +1118,12 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
triagens::basics::Json ljson = left.toJson(trx, leftcoll);
if (right._type == AqlValue::JSON) {
return TRI_CompareValuesJson(ljson.json(), right._json->json(), true);
return TRI_CompareValuesJson(ljson.json(), right._json->json(), compareUtf8);
}
else if (right._type == AqlValue::SHAPED ||
right._type == AqlValue::RANGE) {
triagens::basics::Json rjson = right.toJson(trx, rightcoll);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), true);
return TRI_CompareValuesJson(ljson.json(), rjson.json(), compareUtf8);
}
}
@ -1040,7 +1139,7 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
}
case AqlValue::JSON: {
return TRI_CompareValuesJson(left._json->json(), right._json->json(), true);
return TRI_CompareValuesJson(left._json->json(), right._json->json(), compareUtf8);
}
case AqlValue::SHAPED: {
@ -1065,11 +1164,16 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
rblock < right._vector->size()) {
AqlValue lval = left._vector->at(lblock)->getValue(litem, 0);
AqlValue rval = right._vector->at(rblock)->getValue(ritem, 0);
int cmp = Compare(trx,
lval,
left._vector->at(lblock)->getDocumentCollection(0),
rval,
right._vector->at(rblock)->getDocumentCollection(0));
int cmp = Compare(
trx,
lval,
left._vector->at(lblock)->getDocumentCollection(0),
rval,
right._vector->at(rblock)->getDocumentCollection(0),
compareUtf8
);
if (cmp != 0) {
return cmp;
}
@ -1090,6 +1194,7 @@ int AqlValue::Compare (triagens::arango::AqlTransaction* trx,
return (lblock < left._vector->size() ? -1 : 1);
}
case AqlValue::RANGE: {
if (left._range->_low < right._range->_low) {
return -1;

View File

@ -32,6 +32,7 @@
#include "Aql/Range.h"
#include "Aql/types.h"
#include "Basics/JsonHelper.h"
#include "Basics/StringBuffer.h"
#include "Utils/V8TransactionContext.h"
#include "Utils/AqlTransaction.h"
#include "VocBase/document-collection.h"
@ -118,7 +119,7 @@ namespace triagens {
/// @brief return the value type
////////////////////////////////////////////////////////////////////////////////
inline AqlValueType type () const {
inline AqlValueType type () const throw() {
return _type;
}
@ -126,7 +127,7 @@ namespace triagens {
/// @brief a quick method to decide whether a value is empty
////////////////////////////////////////////////////////////////////////////////
inline bool isEmpty () const {
inline bool isEmpty () const throw() {
return _type == EMPTY;
}
@ -134,7 +135,7 @@ namespace triagens {
/// @brief whether or not the AqlValue is a shape
////////////////////////////////////////////////////////////////////////////////
inline bool isShaped () const {
inline bool isShaped () const throw() {
return _type == SHAPED;
}
@ -276,6 +277,13 @@ namespace triagens {
triagens::basics::Json toJson (triagens::arango::AqlTransaction*,
TRI_document_collection_t const*) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief creates a hash value for the AqlValue
////////////////////////////////////////////////////////////////////////////////
uint64_t hash (triagens::arango::AqlTransaction*,
TRI_document_collection_t const*) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief extract an attribute value from the AqlValue
/// this will return null if the value is not an object
@ -284,7 +292,8 @@ namespace triagens {
triagens::basics::Json extractObjectMember (triagens::arango::AqlTransaction*,
TRI_document_collection_t const*,
char const*,
bool) const;
bool,
triagens::basics::StringBuffer&) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief extract a value from an array AqlValue
@ -323,7 +332,8 @@ namespace triagens {
AqlValue const&,
TRI_document_collection_t const*,
AqlValue const&,
TRI_document_collection_t const*);
TRI_document_collection_t const*,
bool compareUtf8);
// -----------------------------------------------------------------------------
// --SECTION-- public variables

View File

@ -551,6 +551,14 @@ namespace triagens {
return static_cast<AstNode*>(members._buffer[i]);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return a member of the node
////////////////////////////////////////////////////////////////////////////////
inline AstNode* getMemberUnchecked (size_t i) const throw() {
return static_cast<AstNode*>(members._buffer[i]);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief return an optional member of the node
////////////////////////////////////////////////////////////////////////////////

View File

@ -57,7 +57,6 @@ using StringBuffer = triagens::basics::StringBuffer;
#define LEAVE_BLOCK
#endif
// -----------------------------------------------------------------------------
// --SECTION-- struct AggregatorGroup
// -----------------------------------------------------------------------------
@ -189,6 +188,7 @@ ExecutionBlock::~ExecutionBlock () {
for (auto it = _buffer.begin(); it != _buffer.end(); ++it) {
delete *it;
}
_buffer.clear();
}
@ -452,20 +452,23 @@ void ExecutionBlock::inheritRegisters (AqlItemBlock const* src,
bool ExecutionBlock::getBlock (size_t atLeast, size_t atMost) {
throwIfKilled(); // check if we were aborted
AqlItemBlock* docs = _dependencies[0]->getSome(atLeast, atMost);
std::unique_ptr<AqlItemBlock> docs(_dependencies[0]->getSome(atLeast, atMost));
if (docs == nullptr) {
return false;
}
try {
TRI_IF_FAILURE("ExecutionBlock::getBlock") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
}
_buffer.emplace_back(docs);
_buffer.emplace_back(docs.get());
docs.release();
}
catch (...) {
delete docs;
throw;
}
return true;
}
@ -2697,7 +2700,7 @@ void CalculationBlock::fillBlockWithReference (AqlItemBlock* result) {
void CalculationBlock::executeExpression (AqlItemBlock* result) {
std::vector<AqlValue>& data(result->getData());
std::vector<TRI_document_collection_t const*> docColls(result->getDocumentCollections());
std::vector<TRI_document_collection_t const*>& docColls(result->getDocumentCollections());
RegisterId nrRegs = result->getNrRegs();
result->setDocumentCollection(_outReg, nullptr);
@ -2719,10 +2722,11 @@ void CalculationBlock::executeExpression (AqlItemBlock* result) {
continue;
}
}
// execute the expression
TRI_document_collection_t const* myCollection = nullptr;
AqlValue a = _expression->execute(_trx, docColls, data, nrRegs * i, _inVars, _inRegs, &myCollection);
try {
TRI_IF_FAILURE("CalculationBlock::executeExpression") {
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
@ -3297,11 +3301,15 @@ int SortedAggregateBlock::getOrSkipSome (size_t atLeast,
size_t i = 0;
for (auto it = _aggregateRegisters.begin(); it != _aggregateRegisters.end(); ++it) {
int cmp = AqlValue::Compare(_trx,
_currentGroup.groupValues[i],
_currentGroup.collections[i],
cur->getValue(_pos, (*it).second),
cur->getDocumentCollection((*it).second));
int cmp = AqlValue::Compare(
_trx,
_currentGroup.groupValues[i],
_currentGroup.collections[i],
cur->getValue(_pos, (*it).second),
cur->getDocumentCollection((*it).second),
false
);
if (cmp != 0) {
// group change
newGroup = true;
@ -3530,6 +3538,7 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
bool skipping,
AqlItemBlock*& result,
size_t& skipped) {
TRI_ASSERT(result == nullptr && skipped == 0);
if (_done) {
return TRI_ERROR_NO_ERROR;
@ -3544,7 +3553,7 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
}
_pos = 0; // this is in the first block
}
// If we get here, we do have _buffer.front()
AqlItemBlock* cur = _buffer.front();
@ -3616,6 +3625,7 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
groupValues.emplace_back(cur->getValueReference(_pos, _aggregateRegisters[i].second));
}
auto it = allGroups.find(groupValues);
if (it == allGroups.end()) {
@ -3627,7 +3637,7 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
group.emplace_back(cur->getValue(_pos, _aggregateRegisters[i].second).clone());
}
allGroups.emplace(std::make_pair(group, 1));
allGroups.emplace(group, 1);
}
else {
// existing group
@ -3677,7 +3687,6 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
}
}
if (! skipping) {
TRI_ASSERT(skipped > 0);
}
@ -3692,13 +3701,10 @@ int HashedAggregateBlock::getOrSkipSome (size_t atLeast,
////////////////////////////////////////////////////////////////////////////////
size_t HashedAggregateBlock::GroupKeyHash::operator() (std::vector<AqlValue> const& value) const {
size_t const n = value.size();
uint64_t hash = 0x12345678;
for (size_t i = 0; i < n; ++i) {
auto bound = value[i].toJson(_trx, _colls[i]);
hash ^= TRI_HashJson(bound.json());
for (size_t i = 0; i < _num; ++i) {
hash ^= value[i].hash(_trx, _colls[i]);
}
return static_cast<size_t>(hash);
@ -3713,7 +3719,7 @@ bool HashedAggregateBlock::GroupKeyEqual::operator() (std::vector<AqlValue> cons
size_t const n = lhs.size();
for (size_t i = 0; i < n; ++i) {
int res = AqlValue::Compare(_trx, lhs[i], _colls[i], rhs[i], _colls[i]);
int res = AqlValue::Compare(_trx, lhs[i], _colls[i], rhs[i], _colls[i], false);
if (res != 0) {
return false;
@ -3945,11 +3951,15 @@ bool SortBlock::OurLessThan::operator() (std::pair<size_t, size_t> const& a,
size_t i = 0;
for (auto reg : _sortRegisters) {
int cmp = AqlValue::Compare(_trx,
_buffer[a.first]->getValue(a.second, reg.first),
_colls[i],
_buffer[b.first]->getValue(b.second, reg.first),
_colls[i]);
int cmp = AqlValue::Compare(
_trx,
_buffer[a.first]->getValue(a.second, reg.first),
_colls[i],
_buffer[b.first]->getValue(b.second, reg.first),
_colls[i],
true
);
if (cmp == -1) {
return reg.second;
}
@ -4133,7 +4143,8 @@ ModificationBlock::ModificationBlock (ExecutionEngine* engine,
_outRegNew(ExecutionNode::MaxRegisterId),
_collection(ep->_collection),
_isDBServer(false),
_usesDefaultSharding(true) {
_usesDefaultSharding(true),
_buffer(TRI_UNKNOWN_MEM_ZONE) {
auto trxCollection = _trx->trxCollection(_collection->cid());
if (trxCollection != nullptr) {
@ -4246,14 +4257,14 @@ AqlItemBlock* ModificationBlock::getSome (size_t atLeast,
int ModificationBlock::extractKey (AqlValue const& value,
TRI_document_collection_t const* document,
std::string& key) const {
std::string& key) {
if (value.isShaped()) {
key = TRI_EXTRACT_MARKER_KEY(value.getMarker());
return TRI_ERROR_NO_ERROR;
}
if (value.isObject()) {
Json member(value.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_KEY, false));
Json member(value.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_KEY, false, _buffer));
TRI_json_t const* json = member.json();
@ -4539,7 +4550,7 @@ AqlItemBlock* InsertBlock::work (std::vector<AqlItemBlock*>& blocks) {
// array must have _from and _to attributes
TRI_json_t const* json;
Json member(a.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_FROM, false));
Json member(a.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_FROM, false, _buffer));
json = member.json();
if (TRI_IsStringJson(json)) {
@ -4550,7 +4561,7 @@ AqlItemBlock* InsertBlock::work (std::vector<AqlItemBlock*>& blocks) {
}
if (errorCode == TRI_ERROR_NO_ERROR) {
Json member(a.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_TO, false));
Json member(a.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_TO, false, _buffer));
json = member.json();
if (TRI_IsStringJson(json)) {
errorCode = resolve(json->_value._string.data, edge._toCid, to);
@ -4950,7 +4961,7 @@ AqlItemBlock* UpsertBlock::work (std::vector<AqlItemBlock*>& blocks) {
if (insertDoc.isObject()) {
if (isEdgeCollection) {
// array must have _from and _to attributes
Json member(insertDoc.extractObjectMember(_trx, insertDocument, TRI_VOC_ATTRIBUTE_FROM, false));
Json member(insertDoc.extractObjectMember(_trx, insertDocument, TRI_VOC_ATTRIBUTE_FROM, false, _buffer));
TRI_json_t const* json = member.json();
if (TRI_IsStringJson(json)) {
@ -4961,7 +4972,7 @@ AqlItemBlock* UpsertBlock::work (std::vector<AqlItemBlock*>& blocks) {
}
if (errorCode == TRI_ERROR_NO_ERROR) {
Json member(insertDoc.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_TO, false));
Json member(insertDoc.extractObjectMember(_trx, document, TRI_VOC_ATTRIBUTE_TO, false, _buffer));
json = member.json();
if (TRI_IsStringJson(json)) {
errorCode = resolve(json->_value._string.data, edge._toCid, to);
@ -5645,11 +5656,13 @@ bool GatherBlock::OurLessThan::operator() (std::pair<size_t, size_t> const& a,
for (auto reg : _sortRegisters) {
int cmp = AqlValue::Compare(
_trx,
_gatherBlockBuffer.at(a.first).front()->getValue(a.second, reg.first),
_colls[i],
_gatherBlockBuffer.at(b.first).front()->getValue(b.second, reg.first),
_colls[i]);
_trx,
_gatherBlockBuffer.at(a.first).front()->getValue(a.second, reg.first),
_colls[i],
_gatherBlockBuffer.at(b.first).front()->getValue(b.second, reg.first),
_colls[i],
true
);
if (cmp == -1) {
return reg.second;

View File

@ -29,8 +29,6 @@
#define ARANGODB_AQL_EXECUTION_BLOCK_H 1
#include "Basics/JsonHelper.h"
#include "ShapedJson/shaped-json.h"
#include "Aql/AqlItemBlock.h"
#include "Aql/Collection.h"
#include "Aql/CollectionScanner.h"
@ -38,7 +36,9 @@
#include "Aql/Range.h"
#include "Aql/WalkerWorker.h"
#include "Aql/ExecutionStats.h"
#include "Basics/StringBuffer.h"
#include "Cluster/ClusterComm.h"
#include "ShapedJson/shaped-json.h"
#include "Utils/AqlTransaction.h"
#include "Utils/transactions.h"
#include "Utils/V8TransactionContext.h"
@ -499,7 +499,7 @@ namespace triagens {
/// @brief getSome
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override;
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override final;
////////////////////////////////////////////////////////////////////////////////
// skip between atLeast and atMost, returns the number actually skipped . . .
@ -577,7 +577,7 @@ namespace triagens {
int initializeCursor (AqlItemBlock* items, size_t pos) override;
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override;
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override final;
////////////////////////////////////////////////////////////////////////////////
// skip between atLeast and atMost, returns the number actually skipped . . .
@ -864,7 +864,7 @@ namespace triagens {
int initializeCursor (AqlItemBlock* items, size_t pos) override;
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override;
AqlItemBlock* getSome (size_t atLeast, size_t atMost) override final;
////////////////////////////////////////////////////////////////////////////////
// skip between atLeast and atMost returns the number actually skipped . . .
@ -983,7 +983,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* getSome (size_t atLeast,
size_t atMost) override;
size_t atMost) override final;
private:
@ -1054,7 +1054,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* getSome (size_t atLeast,
size_t atMost) override;
size_t atMost) override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief shutdown, tell dependency and the subquery
@ -1226,7 +1226,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
std::vector<std::string> _variableNames;
};
// -----------------------------------------------------------------------------
@ -1267,7 +1267,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
RegisterId _groupRegister;
////////////////////////////////////////////////////////////////////////////////
/// @brief hasher for a vector of AQL values
////////////////////////////////////////////////////////////////////////////////
@ -1276,13 +1276,15 @@ namespace triagens {
GroupKeyHash (triagens::arango::AqlTransaction* trx,
std::vector<TRI_document_collection_t const*>& colls)
: _trx(trx),
_colls(colls) {
_colls(colls),
_num(colls.size()) {
}
size_t operator() (std::vector<AqlValue> const& value) const;
triagens::arango::AqlTransaction* _trx;
std::vector<TRI_document_collection_t const*>& _colls;
size_t const _num;
};
////////////////////////////////////////////////////////////////////////////////
@ -1463,7 +1465,7 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
AqlItemBlock* getSome (size_t atLeast,
size_t atMost) override;
size_t atMost) override final;
};
@ -1513,7 +1515,7 @@ namespace triagens {
int extractKey (AqlValue const&,
TRI_document_collection_t const*,
std::string&) const;
std::string&);
////////////////////////////////////////////////////////////////////////////////
/// @brief constructs a master pointer from the marker passed
@ -1580,6 +1582,12 @@ namespace triagens {
bool _usesDefaultSharding;
////////////////////////////////////////////////////////////////////////////////
/// @brief temporary string buffer for extracting system attributes
////////////////////////////////////////////////////////////////////////////////
triagens::basics::StringBuffer _buffer;
};
// -----------------------------------------------------------------------------

View File

@ -2201,6 +2201,39 @@ std::vector<std::pair<ExecutionNode*, bool>> SortNode::getCalcNodePairs () {
return findExp._myVars;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief simplifies the expressions of the sort node
/// this will sort expressions if they are constant
/// the method will return true if all sort expressions were removed after
/// simplification, and false otherwise
////////////////////////////////////////////////////////////////////////////////
bool SortNode::simplify (ExecutionPlan* plan) {
for (auto it = _elements.begin(); it != _elements.end(); /* no hoisting */) {
auto variable = (*it).first;
TRI_ASSERT(variable != nullptr);
auto setter = _plan->getVarSetBy(variable->id);
if (setter != nullptr) {
if (setter->getType() == ExecutionNode::CALCULATION) {
// variable introduced by a calculation
auto expression = static_cast<CalculationNode*>(setter)->expression();
if (expression->isConstant()) {
// constant expression, remove it!
it = _elements.erase(it);
continue;
}
}
}
++it;
}
return _elements.empty();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief returns all sort information
////////////////////////////////////////////////////////////////////////////////
@ -2214,7 +2247,7 @@ SortInformation SortNode::getSortInformation (ExecutionPlan* plan,
auto variable = (*it).first;
TRI_ASSERT(variable != nullptr);
auto setter = _plan->getVarSetBy(variable->id);
if (setter == nullptr) {
result.isValid = false;
break;
@ -2233,7 +2266,8 @@ SortInformation SortNode::getSortInformation (ExecutionPlan* plan,
}
if (! expression->isAttributeAccess() &&
! expression->isReference()) {
! expression->isReference() &&
! expression->isConstant()) {
result.isComplex = true;
break;
}

View File

@ -1948,6 +1948,15 @@ namespace triagens {
std::vector<std::pair<ExecutionNode*, bool>> getCalcNodePairs ();
////////////////////////////////////////////////////////////////////////////////
/// @brief simplifies the expressions of the sort node
/// this will sort expressions if they are constant
/// the method will return true if all sort expressions were removed after
/// simplification, and false otherwise
////////////////////////////////////////////////////////////////////////////////
bool simplify (ExecutionPlan*);
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------

View File

@ -527,12 +527,6 @@ ExecutionNode* ExecutionPlan::fromNodeSort (ExecutionNode* previous,
TRI_ASSERT(element->numMembers() == 2);
auto expression = element->getMember(0);
if (expression->isConstant()) {
// expression is constant, so sorting with not provide any benefit
continue;
}
auto ascending = element->getMember(1);
// get sort order

View File

@ -87,7 +87,9 @@ Expression::Expression (Ast* ast,
_canRunOnDBServer(false),
_isDeterministic(false),
_hasDeterminedAttributes(false),
_built(false) {
_built(false),
_attributes(),
_buffer(TRI_UNKNOWN_MEM_ZONE) {
TRI_ASSERT(_ast != nullptr);
TRI_ASSERT(_executor != nullptr);
@ -258,7 +260,7 @@ bool Expression::findInList (AqlValue const& left,
auto listItem = right.extractArrayMember(trx, rightCollection, m, false);
AqlValue listItemValue(&listItem);
int compareResult = AqlValue::Compare(trx, left, leftCollection, listItemValue, nullptr);
int compareResult = AqlValue::Compare(trx, left, leftCollection, listItemValue, nullptr, false);
if (compareResult == 0) {
// item found in the list
@ -287,7 +289,7 @@ bool Expression::findInList (AqlValue const& left,
auto listItem = right.extractArrayMember(trx, rightCollection, i, false);
AqlValue listItemValue(&listItem);
int compareResult = AqlValue::Compare(trx, left, leftCollection, listItemValue, nullptr);
int compareResult = AqlValue::Compare(trx, left, leftCollection, listItemValue, nullptr, false);
if (compareResult == 0) {
// item found in the list
@ -397,15 +399,15 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
std::vector<RegisterId> const& regs) {
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
// object lookup, e.g. users.name
TRI_ASSERT(node->numMembers() == 1);
TRI_ASSERT_EXPENSIVE(node->numMembers() == 1);
auto member = node->getMember(0);
auto member = node->getMemberUnchecked(0);
auto name = static_cast<char const*>(node->getData());
TRI_document_collection_t const* myCollection = nullptr;
AqlValue result = executeSimpleExpression(member, &myCollection, trx, docColls, argv, startPos, vars, regs);
auto j = result.extractObjectMember(trx, myCollection, name, true);
auto j = result.extractObjectMember(trx, myCollection, name, true, _buffer);
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
@ -458,7 +460,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
if (indexResult.isNumber()) {
auto&& indexString = std::to_string(indexResult.toInt64());
auto j = result.extractObjectMember(trx, myCollection, indexString.c_str(), true);
auto j = result.extractObjectMember(trx, myCollection, indexString.c_str(), true, _buffer);
indexResult.destroy();
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
@ -467,7 +469,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
auto&& value = indexResult.toString();
indexResult.destroy();
auto j = result.extractObjectMember(trx, myCollection, value.c_str(), true);
auto j = result.extractObjectMember(trx, myCollection, value.c_str(), true, _buffer);
result.destroy();
return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, j.steal()));
}
@ -559,7 +561,7 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
}
else if (node->type == NODE_TYPE_REFERENCE) {
auto v = static_cast<Variable*>(node->getData());
auto v = static_cast<Variable const*>(node->getData());
size_t i = 0;
for (auto it = vars.begin(); it != vars.end(); ++it, ++i) {
@ -685,8 +687,12 @@ AqlValue Expression::executeSimpleExpression (AstNode const* node,
return AqlValue(new triagens::basics::Json(result));
}
// all other comparison operators
int compareResult = AqlValue::Compare(trx, left, leftCollection, right, rightCollection);
// all other comparison operators...
// for equality and non-equality we can use a binary comparison
bool compareUtf8 = (node->type != NODE_TYPE_OPERATOR_BINARY_EQ && node->type != NODE_TYPE_OPERATOR_BINARY_NE);
int compareResult = AqlValue::Compare(trx, left, leftCollection, right, rightCollection, compareUtf8);
left.destroy();
right.destroy();
@ -755,6 +761,14 @@ bool Expression::isAttributeAccess () const {
bool Expression::isReference () const {
return (_node->type == triagens::aql::NODE_TYPE_REFERENCE);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief check whether this is a constant node
////////////////////////////////////////////////////////////////////////////////
bool Expression::isConstant () const {
return _node->isConstant();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief this gives you ("variable.access", "Reference")

View File

@ -35,6 +35,7 @@
#include "Aql/Variable.h"
#include "Aql/types.h"
#include "Basics/JsonHelper.h"
#include "Basics/StringBuffer.h"
#include "Utils/AqlTransaction.h"
struct TRI_json_t;
@ -42,7 +43,6 @@ struct TRI_json_t;
namespace triagens {
namespace basics {
class Json;
class StringBuffer;
}
namespace aql {
@ -237,6 +237,12 @@ namespace triagens {
bool isReference () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief check whether this is a constant node
////////////////////////////////////////////////////////////////////////////////
bool isConstant () const;
////////////////////////////////////////////////////////////////////////////////
/// @brief this gives you ("variable.access", "Reference")
/// call isSimpleAccessReference in advance to ensure no exceptions.
@ -387,6 +393,12 @@ namespace triagens {
std::unordered_map<Variable const*, std::unordered_set<std::string>> _attributes;
////////////////////////////////////////////////////////////////////////////////
/// @brief buffer for temporary strings
////////////////////////////////////////////////////////////////////////////////
triagens::basics::StringBuffer _buffer;
// -----------------------------------------------------------------------------
// --SECTION-- public static members
// -----------------------------------------------------------------------------

View File

@ -74,7 +74,6 @@ int triagens::aql::removeRedundantSortsRule (Optimizer* opt,
if (sortInfo.isValid && ! sortInfo.criteria.empty()) {
// we found a sort that we can understand
std::vector<ExecutionNode*> stack;
for (auto dep : sortNode->getDependencies()) {
stack.push_back(dep);
@ -155,7 +154,7 @@ int triagens::aql::removeRedundantSortsRule (Optimizer* opt,
}
else {
// abort at all other type of nodes. we cannot remove a sort beyond them
// this include COLLECT and LIMIT
// this includes COLLECT and LIMIT
break;
}
@ -171,9 +170,16 @@ int triagens::aql::removeRedundantSortsRule (Optimizer* opt,
stack.push_back(dep);
}
}
if (toUnlink.find(n) == toUnlink.end() &&
sortNode->simplify(plan)) {
// sort node had only constant expressions. it will make no difference if we execute it or not
// so we can remove it
toUnlink.insert(n);
}
}
}
if (! toUnlink.empty()) {
plan->unlinkNodes(toUnlink);
plan->findVarUsage();

View File

@ -31,12 +31,11 @@
#define ARANGODB_UTILS_COLLECTION_NAME_RESOLVER_H 1
#include "Basics/Common.h"
#include "Basics/StringBuffer.h"
#include "Basics/StringUtils.h"
#include "VocBase/vocbase.h"
#include "Cluster/ServerState.h"
#include "Cluster/ClusterInfo.h"
#include "VocBase/vocbase.h"
namespace triagens {
namespace arango {
@ -83,7 +82,7 @@ namespace triagens {
TRI_voc_cid_t getCollectionId (std::string const& name) const {
if (name[0] >= '0' && name[0] <= '9') {
// name is a numeric id
return (TRI_voc_cid_t) triagens::basics::StringUtils::uint64(name);
return static_cast<TRI_voc_cid_t>(triagens::basics::StringUtils::uint64(name));
}
TRI_vocbase_col_t const* collection = getCollectionStruct(name);
@ -99,12 +98,10 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
TRI_vocbase_col_t const* getCollectionStruct (std::string const& name) const {
if (! _resolvedNames.empty()) {
auto it = _resolvedNames.find(name);
auto it = _resolvedNames.find(name);
if (it != _resolvedNames.end()) {
return (*it).second;
}
if (it != _resolvedNames.end()) {
return (*it).second;
}
TRI_vocbase_col_t const* collection = TRI_LookupCollectionByNameVocBase(_vocbase, name.c_str());
@ -147,12 +144,10 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
std::string getCollectionName (TRI_voc_cid_t cid) const {
if (! _resolvedIds.empty()) {
auto it = _resolvedIds.find(cid);
auto it = _resolvedIds.find(cid);
if (it != _resolvedIds.end()) {
return (*it).second;
}
if (it != _resolvedIds.end()) {
return (*it).second;
}
std::string name;
@ -168,7 +163,7 @@ namespace triagens {
if (found->_planId == 0) {
// DBserver local case
char* n = TRI_GetCollectionNameByIdVocBase(_vocbase, cid);
if (0 != n) {
if (n != nullptr) {
name = n;
TRI_Free(TRI_UNKNOWN_MEM_ZONE, n);
}
@ -213,13 +208,11 @@ namespace triagens {
size_t getCollectionName (char* buffer,
TRI_voc_cid_t cid) const {
if (! _resolvedIds.empty()) {
auto it = _resolvedIds.find(cid);
auto it = _resolvedIds.find(cid);
if (it != _resolvedIds.end()) {
memcpy(buffer, (*it).second.c_str(), (*it).second.size());
return (*it).second.size();
}
if (it != _resolvedIds.end()) {
memcpy(buffer, (*it).second.c_str(), (*it).second.size());
return (*it).second.size();
}
std::string&& name(getCollectionName(cid));
@ -228,6 +221,28 @@ namespace triagens {
return name.size();
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a collection name for a collection id, this implements
/// some magic in the cluster case: a DBserver in a cluster will automatically
/// translate the local collection ID into a cluster wide collection name.
///
/// the name is copied into <buffer>. the caller is responsible for allocating
/// a big-enough buffer (that is, at least 64 bytes). no NUL byte is appended
/// to the buffer. the length of the collection name is returned.
////////////////////////////////////////////////////////////////////////////////
void getCollectionName (TRI_voc_cid_t cid,
triagens::basics::StringBuffer& buffer) const {
auto const& it = _resolvedIds.find(cid);
if (it != _resolvedIds.end()) {
buffer.appendText((*it).second.c_str(), (*it).second.size());
return;
}
std::string&& name(getCollectionName(cid));
buffer.appendText(name.c_str(), name.size());
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a cluster-wide collection name for a cluster-wide
@ -292,6 +307,36 @@ namespace triagens {
return strlen("_unknown");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief look up a cluster-wide collection name for a cluster-wide
/// collection id
////////////////////////////////////////////////////////////////////////////////
void getCollectionNameCluster (TRI_voc_cid_t cid,
triagens::basics::StringBuffer& buffer) const {
if (! ServerState::instance()->isRunningInCluster()) {
return getCollectionName(cid, buffer);
}
int tries = 0;
while (tries++ < 2) {
std::shared_ptr<CollectionInfo> ci
= ClusterInfo::instance()->getCollection(_vocbase->_name,
triagens::basics::StringUtils::itoa(cid));
std::string name = ci->name();
if (name.empty()) {
ClusterInfo::instance()->flush();
continue;
}
buffer.appendText(name);
return;
}
buffer.appendText("_unknown");
}
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------

View File

@ -122,9 +122,7 @@ TRI_document_collection_t::TRI_document_collection_t ()
////////////////////////////////////////////////////////////////////////////////
TRI_document_collection_t::~TRI_document_collection_t () {
if (_keyGenerator != nullptr) {
delete _keyGenerator;
}
delete _keyGenerator;
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -1528,7 +1528,10 @@ int TRI_CompareShapeTypes (char const* leftDocument,
attribute_entry_t const* l = static_cast<attribute_entry_t const*>(TRI_AtVector(&leftSorted, i));
attribute_entry_t const* r = static_cast<attribute_entry_t const*>(TRI_AtVector(&rightSorted, i));
result = TRI_compare_utf8(l->_attribute, r->_attribute);
// a binary comparison is sufficient here as we're only interested in if the attribute names are
// identical. the attribute names are from ShapedJson, so they have been normalized already
result = strcmp(l->_attribute, r->_attribute);
// result = TRI_compare_utf8(l->_attribute, r->_attribute);
if (result != 0) {
break;

View File

@ -124,12 +124,19 @@ function optimizerCollectMethodsTestSuite () {
var plan = AQL_EXPLAIN(query[0]).plan;
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
++aggregateNodes;
assertEqual("hash", node.method);
}
if (node.type === "SortNode") {
++sortNodes;
}
});
assertEqual(1, aggregateNodes);
assertEqual(1, sortNodes);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);
@ -157,12 +164,19 @@ function optimizerCollectMethodsTestSuite () {
var plan = AQL_EXPLAIN(query[0]).plan;
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
++aggregateNodes;
assertEqual("hash", node.method);
}
if (node.type === "SortNode") {
++sortNodes;
}
});
assertEqual(1, aggregateNodes);
assertEqual(1, sortNodes);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);
@ -188,12 +202,56 @@ function optimizerCollectMethodsTestSuite () {
var plan = AQL_EXPLAIN(query[0]).plan;
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
++aggregateNodes;
assertEqual("sorted", node.method);
}
if (node.type === "SortNode") {
++sortNodes;
}
});
assertEqual(1, aggregateNodes);
assertEqual(0, sortNodes);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);
});
},
////////////////////////////////////////////////////////////////////////////////
/// @brief expect hash COLLECT w/ sort node removed
////////////////////////////////////////////////////////////////////////////////
testSortRemoval : function () {
var queries = [
[ "FOR j IN " + c.name() + " COLLECT value = j SORT null RETURN value", 1500 ],
[ "FOR j IN " + c.name() + " COLLECT value = j._key SORT null RETURN value", 1500 ],
[ "FOR j IN " + c.name() + " COLLECT value = j.group SORT null RETURN value", 10 ],
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value SORT null RETURN [ value1, value2 ]", 1500 ],
[ "FOR j IN " + c.name() + " COLLECT value = j.group WITH COUNT INTO l SORT null RETURN [ value, l ]", 10 ],
[ "FOR j IN " + c.name() + " COLLECT value1 = j.group, value2 = j.value WITH COUNT INTO l SORT null RETURN [ value1, value2, l ]", 1500 ]
];
queries.forEach(function(query) {
var plan = AQL_EXPLAIN(query[0]).plan;
var aggregateNodes = 0;
var sortNodes = 0;
plan.nodes.map(function(node) {
if (node.type === "AggregateNode") {
++aggregateNodes;
assertEqual("hash", node.method);
}
if (node.type === "SortNode") {
++sortNodes;
}
});
assertEqual(1, aggregateNodes);
assertEqual(0, sortNodes);
var results = AQL_EXECUTE(query[0]);
assertEqual(query[1], results.json.length);

View File

@ -516,6 +516,19 @@ namespace triagens {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor for a char const*
////////////////////////////////////////////////////////////////////////////////
explicit Json (char const* x, size_t length, autofree_e autofree = AUTOFREE)
: _zone(TRI_UNKNOWN_MEM_ZONE), _json(nullptr), _autofree(autofree) {
_json = TRI_CreateStringCopyJson(_zone, x, length);
if (_json == nullptr) {
throw JsonException("Json: out of memory");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor for a memzone and a char const*
////////////////////////////////////////////////////////////////////////////////
@ -529,6 +542,19 @@ namespace triagens {
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor for a memzone and a char const*
////////////////////////////////////////////////////////////////////////////////
explicit Json (TRI_memory_zone_t* z, char const* x, size_t length, autofree_e autofree = AUTOFREE)
: _zone(z), _json(nullptr), _autofree(autofree) {
_json = TRI_CreateStringCopyJson(_zone, x, length);
if (_json == nullptr) {
throw JsonException("Json: out of memory");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor for a string
////////////////////////////////////////////////////////////////////////////////

View File

@ -54,12 +54,14 @@ Utf8Helper Utf8Helper::DefaultUtf8Helper;
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
Utf8Helper::Utf8Helper () : _coll(0) {
setCollatorLanguage("");
Utf8Helper::Utf8Helper (std::string const& lang) :
_coll(nullptr) {
setCollatorLanguage(lang);
}
Utf8Helper::Utf8Helper (std::string const& lang) : _coll(0) {
setCollatorLanguage(lang);
Utf8Helper::Utf8Helper () :
Utf8Helper("") {
}
Utf8Helper::~Utf8Helper () {
@ -71,15 +73,15 @@ Utf8Helper::~Utf8Helper () {
}
}
int Utf8Helper::compareUtf8 (const char* left, const char* right) {
if (!_coll) {
int Utf8Helper::compareUtf8 (const char* left, const char* right) const {
if (! _coll) {
LOG_ERROR("no Collator in Utf8Helper::compareUtf8()!");
return (strcmp(left, right));
}
UErrorCode status = U_ZERO_ERROR;
int result = _coll->compareUTF8(StringPiece(left), StringPiece(right), status);
if(U_FAILURE(status)) {
if (U_FAILURE(status)) {
LOG_ERROR("error in Collator::compareUTF8(...): %s", u_errorName(status));
return (strcmp(left, right));
}
@ -87,7 +89,7 @@ int Utf8Helper::compareUtf8 (const char* left, const char* right) {
return result;
}
int Utf8Helper::compareUtf16 (const uint16_t* left, size_t leftLength, const uint16_t* right, size_t rightLength) {
int Utf8Helper::compareUtf16 (const uint16_t* left, size_t leftLength, const uint16_t* right, size_t rightLength) const {
if (!_coll) {
LOG_ERROR("no Collator in Utf8Helper::compareUtf16()!");
@ -208,7 +210,7 @@ std::string Utf8Helper::getCollatorCountry () {
std::string Utf8Helper::toLowerCase (std::string const& src) {
int32_t utf8len = 0;
char* utf8 = tolower(TRI_UNKNOWN_MEM_ZONE, src.c_str(), (int32_t) src.length(), utf8len);
if (utf8 == 0) {
if (utf8 == nullptr) {
return string("");
}
@ -224,9 +226,9 @@ std::string Utf8Helper::toLowerCase (std::string const& src) {
char* Utf8Helper::tolower (TRI_memory_zone_t* zone, const char *src, int32_t srcLength, int32_t& dstLength) {
char* utf8_dest = 0;
if (src == 0 || srcLength == 0) {
if (src == nullptr || srcLength == 0) {
utf8_dest = (char*) TRI_Allocate(zone, sizeof(char), false);
if (utf8_dest != 0) {
if (utf8_dest != nullptr) {
utf8_dest[0] = '\0';
}
dstLength = 0;
@ -244,8 +246,8 @@ char* Utf8Helper::tolower (TRI_memory_zone_t* zone, const char *src, int32_t src
}
else {
utf8_dest = (char*) TRI_Allocate(zone, (srcLength + 1) * sizeof(char), false);
if (utf8_dest == 0) {
return 0;
if (utf8_dest == nullptr) {
return nullptr;
}
dstLength = ucasemap_utf8ToLower(csm.getAlias(),
@ -259,8 +261,8 @@ char* Utf8Helper::tolower (TRI_memory_zone_t* zone, const char *src, int32_t src
status = U_ZERO_ERROR;
TRI_Free(zone, utf8_dest);
utf8_dest = (char*) TRI_Allocate(zone, (dstLength + 1) * sizeof(char), false);
if (utf8_dest == 0) {
return 0;
if (utf8_dest == nullptr) {
return nullptr;
}
dstLength = ucasemap_utf8ToLower(csm.getAlias(),
@ -281,7 +283,7 @@ char* Utf8Helper::tolower (TRI_memory_zone_t* zone, const char *src, int32_t src
}
utf8_dest = TRI_LowerAsciiStringZ(zone, src);
if (utf8_dest != 0) {
if (utf8_dest != nullptr) {
dstLength = (int32_t) strlen(utf8_dest);
}
return utf8_dest;
@ -294,7 +296,7 @@ char* Utf8Helper::tolower (TRI_memory_zone_t* zone, const char *src, int32_t src
std::string Utf8Helper::toUpperCase (std::string const& src) {
int32_t utf8len = 0;
char* utf8 = toupper(TRI_UNKNOWN_MEM_ZONE, src.c_str(), (int32_t) src.length(), utf8len);
if (utf8 == 0) {
if (utf8 == nullptr) {
return string("");
}
@ -308,9 +310,9 @@ std::string Utf8Helper::toUpperCase (std::string const& src) {
////////////////////////////////////////////////////////////////////////////////
char* Utf8Helper::toupper (TRI_memory_zone_t* zone, const char *src, int32_t srcLength, int32_t& dstLength) {
char* utf8_dest = 0;
char* utf8_dest = nullptr;
if (src == 0 || srcLength == 0) {
if (src == nullptr || srcLength == 0) {
utf8_dest = (char*) TRI_Allocate(zone, sizeof(char), false);
if (utf8_dest != 0) {
utf8_dest[0] = '\0';
@ -330,8 +332,8 @@ char* Utf8Helper::toupper (TRI_memory_zone_t* zone, const char *src, int32_t src
}
else {
utf8_dest = (char*) TRI_Allocate(zone, (srcLength+1) * sizeof(char), false);
if (utf8_dest == 0) {
return 0;
if (utf8_dest == nullptr) {
return nullptr;
}
dstLength = ucasemap_utf8ToUpper(csm.getAlias(),
@ -345,8 +347,8 @@ char* Utf8Helper::toupper (TRI_memory_zone_t* zone, const char *src, int32_t src
status = U_ZERO_ERROR;
TRI_Free(zone, utf8_dest);
utf8_dest = (char*) TRI_Allocate(zone, (dstLength + 1) * sizeof(char), false);
if (utf8_dest == 0) {
return 0;
if (utf8_dest == nullptr) {
return nullptr;
}
dstLength = ucasemap_utf8ToUpper(csm.getAlias(),
@ -367,7 +369,7 @@ char* Utf8Helper::toupper (TRI_memory_zone_t* zone, const char *src, int32_t src
}
utf8_dest = TRI_UpperAsciiStringZ(zone, src);
if (utf8_dest != NULL) {
if (utf8_dest != nullptr) {
dstLength = (int32_t) strlen(utf8_dest);
}
return utf8_dest;
@ -388,30 +390,30 @@ TRI_vector_string_t* Utf8Helper::getWords (const char* const text,
if (textLength == 0) {
// input text is empty
return NULL;
return nullptr;
}
if (textLength < minimalLength) {
// input text is shorter than required minimum length
return NULL;
return nullptr;
}
size_t textUtf16Length = 0;
UChar* textUtf16 = NULL;
UChar* textUtf16 = nullptr;
if (lowerCase) {
// lower case string
int32_t lowerLength = 0;
char* lower = tolower(TRI_UNKNOWN_MEM_ZONE, text, (int32_t) textLength, lowerLength);
if (lower == NULL) {
if (lower == nullptr) {
// out of memory
return NULL;
return nullptr;
}
if (lowerLength == 0) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, lower);
return NULL;
return nullptr;
}
textUtf16 = TRI_Utf8ToUChar(TRI_UNKNOWN_MEM_ZONE, lower, lowerLength, &textUtf16Length);
@ -421,8 +423,8 @@ TRI_vector_string_t* Utf8Helper::getWords (const char* const text,
textUtf16 = TRI_Utf8ToUChar(TRI_UNKNOWN_MEM_ZONE, text, (int32_t) textLength, &textUtf16Length);
}
if (textUtf16 == NULL) {
return NULL;
if (textUtf16 == nullptr) {
return nullptr;
}
ULocDataLocaleType type = ULOC_VALID_LOCALE;
@ -431,22 +433,22 @@ TRI_vector_string_t* Utf8Helper::getWords (const char* const text,
if (U_FAILURE(status)) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, textUtf16);
LOG_ERROR("error in Collator::getLocale(...): %s", u_errorName(status));
return NULL;
return nullptr;
}
UChar* tempUtf16 = (UChar *) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (textUtf16Length + 1) * sizeof(UChar), false);
if (tempUtf16 == NULL) {
if (tempUtf16 == nullptr) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, textUtf16);
return NULL;
return nullptr;
}
words = (TRI_vector_string_t*) TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_vector_string_t), false);
if (words == NULL) {
if (words == nullptr) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, textUtf16);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tempUtf16);
return NULL;
return nullptr;
}
// estimate an initial vector size. this is not accurate, but setting the initial size to some
@ -496,7 +498,7 @@ TRI_vector_string_t* Utf8Helper::getWords (const char* const text,
if (words->_length == 0) {
// no words found
TRI_FreeVectorString(TRI_UNKNOWN_MEM_ZONE, words);
return NULL;
return nullptr;
}
return words;

View File

@ -57,19 +57,19 @@ namespace triagens {
public:
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
Utf8Helper();
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
/// @param lang Lowercase two-letter or three-letter ISO-639 code.
/// This parameter can instead be an ICU style C locale (e.g. "en_US")
////////////////////////////////////////////////////////////////////////////////
Utf8Helper (std::string const& lang);
explicit Utf8Helper (std::string const& lang);
////////////////////////////////////////////////////////////////////////////////
/// @brief constructor
////////////////////////////////////////////////////////////////////////////////
Utf8Helper();
////////////////////////////////////////////////////////////////////////////////
/// @brief destructor
@ -90,7 +90,7 @@ namespace triagens {
/// 1 : left > right
////////////////////////////////////////////////////////////////////////////////
int compareUtf8 (const char* left, const char* right);
int compareUtf8 (const char* left, const char* right) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief compare utf16 strings
@ -99,7 +99,7 @@ namespace triagens {
/// 1 : left > right
////////////////////////////////////////////////////////////////////////////////
int compareUtf16 (const uint16_t* left, size_t leftLength, const uint16_t* right, size_t rightLength);
int compareUtf16 (const uint16_t* left, size_t leftLength, const uint16_t* right, size_t rightLength) const;
////////////////////////////////////////////////////////////////////////////////
/// @brief set collator by language

View File

@ -202,7 +202,7 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
return 1;
}
TRI_ASSERT(lWeight == rWeight);
TRI_ASSERT_EXPENSIVE(lWeight == rWeight);
// lhs and rhs have equal weights
if (lhs == nullptr) {
@ -213,21 +213,23 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
switch (lhs->_type) {
case TRI_JSON_UNUSED:
case TRI_JSON_NULL:
case TRI_JSON_NULL: {
return 0; // null == null;
}
case TRI_JSON_BOOLEAN:
case TRI_JSON_BOOLEAN: {
if (lhs->_value._boolean == rhs->_value._boolean) {
return 0;
}
if (!lhs->_value._boolean && rhs->_value._boolean) {
if (! lhs->_value._boolean && rhs->_value._boolean) {
return -1;
}
return 1;
}
case TRI_JSON_NUMBER:
case TRI_JSON_NUMBER: {
if (lhs->_value._number == rhs->_value._number) {
return 0;
}
@ -237,9 +239,10 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
}
return 1;
}
case TRI_JSON_STRING:
case TRI_JSON_STRING_REFERENCE:
case TRI_JSON_STRING_REFERENCE: {
// same for STRING and STRING_REFERENCE
int res;
if (useUTF8) {
@ -255,14 +258,13 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
else if (res > 0) {
return 1;
}
else {
return 0;
}
return 0;
}
case TRI_JSON_ARRAY: {
size_t nl = lhs->_value._objects._length;
size_t nr = rhs->_value._objects._length;
size_t i, n;
size_t n;
if (nl > nr) {
n = nl;
@ -271,14 +273,12 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
n = nr;
}
for (i = 0; i < n; ++i) {
TRI_json_t* lhsValue;
TRI_json_t* rhsValue;
int result;
for (size_t i = 0; i < n; ++i) {
auto lhsValue = (i >= nl) ? nullptr : static_cast<TRI_json_t const*>(TRI_AtVector(&lhs->_value._objects, i));
auto rhsValue = (i >= nr) ? nullptr : static_cast<TRI_json_t const*>(TRI_AtVector(&rhs->_value._objects, i));
int result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8);
lhsValue = (i >= nl) ? nullptr : reinterpret_cast<TRI_json_t*>(TRI_AtVector(&lhs->_value._objects, i));
rhsValue = (i >= nr) ? nullptr : reinterpret_cast<TRI_json_t*>(TRI_AtVector(&rhs->_value._objects, i));
result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8);
if (result != 0) {
return result;
}
@ -288,30 +288,21 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
}
case TRI_JSON_OBJECT: {
TRI_json_t* keys;
TRI_ASSERT(lhs->_type == TRI_JSON_OBJECT);
TRI_ASSERT(rhs->_type == TRI_JSON_OBJECT);
keys = GetMergedKeyList(lhs, rhs);
TRI_json_t* keys = GetMergedKeyList(lhs, rhs);
if (keys != nullptr) {
size_t i, n;
n = keys->_value._objects._length;
for (i = 0; i < n; ++i) {
TRI_json_t* keyElement;
TRI_json_t* lhsValue;
TRI_json_t* rhsValue;
int result;
keyElement = reinterpret_cast<TRI_json_t*>(TRI_AtVector(&keys->_value._objects, i));
size_t const n = keys->_value._objects._length;
for (size_t i = 0; i < n; ++i) {
auto keyElement = static_cast<TRI_json_t const*>(TRI_AtVector(&keys->_value._objects, i));
TRI_ASSERT(TRI_IsStringJson(keyElement));
lhsValue = TRI_LookupObjectJson((TRI_json_t*) lhs, keyElement->_value._string.data); // may be NULL
rhsValue = TRI_LookupObjectJson((TRI_json_t*) rhs, keyElement->_value._string.data); // may be NULL
TRI_json_t const* lhsValue = TRI_LookupObjectJson(lhs, keyElement->_value._string.data); // may be NULL
TRI_json_t const* rhsValue = TRI_LookupObjectJson(rhs, keyElement->_value._string.data); // may be NULL
result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8);
int result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8);
if (result != 0) {
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, keys);
@ -321,13 +312,12 @@ int TRI_CompareValuesJson (TRI_json_t const* lhs,
TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, keys);
}
return 0;
// fall-through to returning 0
}
default:
return 0;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////