//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany /// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Max Neunhoeffer //////////////////////////////////////////////////////////////////////////////// #include "AqlValue.h" #include "Aql/AqlItemBlock.h" #include "Basics/VelocyPackHelper.h" #include "Utils/AqlTransaction.h" #include "V8/v8-conv.h" #include "V8/v8-vpack.h" #include "VocBase/document-collection.h" #include #include #include #include using namespace arangodb::aql; #if 0 case DOCVEC: { TRI_ASSERT(_vector != nullptr); size_t const p = static_cast(position); // calculate the result array length size_t totalSize = 0; for (auto it = _vector->begin(); it != _vector->end(); ++it) { if (p < totalSize + (*it)->size()) { // found the correct vector auto vecCollection = (*it)->getDocumentCollection(0); return (*it) ->getValueReference(p - totalSize, 0) .toJson(trx, vecCollection, copy); } totalSize += (*it)->size(); } break; // fall-through to returning null } #endif #if 0 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(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->getValueReference(i, 0) .toJson(trx, vecCollection, true)); } } return TRI_FastHashJson(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(_range->at(i)))); } return TRI_FastHashJson(json.json()); } case EMPTY: { } } return 0; } #endif #if 0 void AqlValue::destroy() { switch (_type) { case JSON: { delete _json; _json = nullptr; break; } case DOCVEC: { if (_vector != nullptr) { for (auto it = _vector->begin(); it != _vector->end(); ++it) { delete *it; } delete _vector; _vector = nullptr; } break; } case RANGE: { delete _range; _range = nullptr; break; } case SHAPED: { // do nothing here, since data pointers need not be freed break; } case EMPTY: { // do nothing break; } } // to avoid double freeing _type = EMPTY; } #endif #if 0 AqlValue AqlValue::clone() const { switch (_type) { case JSON: { TRI_ASSERT(_json != nullptr); return AqlValue(new Json(_json->copy())); } case SHAPED: { return AqlValue(_marker); } case DOCVEC: { auto c = new std::vector; try { c->reserve(_vector->size()); for (auto it = _vector->begin(); it != _vector->end(); ++it) { c->emplace_back((*it)->slice(0, (*it)->size())); } } catch (...) { for (auto& x : *c) { delete x; } delete c; throw; } return AqlValue(c); } case RANGE: { return AqlValue(_range->_low, _range->_high); } case EMPTY: { return AqlValue(); } } THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } #endif #if 0 at: case DOCVEC: { TRI_ASSERT(_vector != nullptr); // calculate the result list length size_t offset = 0; for (auto it = _vector->begin(); it != _vector->end(); ++it) { auto current = (*it); size_t const n = current->size(); if (offset + i < n) { auto vecCollection = current->getDocumentCollection(0); return current->getValue(i - offset, 0) .toJson(trx, vecCollection, true); } offset += (*it)->size(); } break; // fall-through to exception } #endif #if 0 size_t AqlValue::arraySize() const { switch (_type) { case JSON: { TRI_ASSERT(_json != nullptr); TRI_json_t const* json = _json->json(); if (TRI_IsArrayJson(json)) { return TRI_LengthArrayJson(json); } return 0; } case DOCVEC: { TRI_ASSERT(_vector != nullptr); // calculate the result list length size_t totalSize = 0; for (auto it = _vector->begin(); it != _vector->end(); ++it) { totalSize += (*it)->size(); } return totalSize; } #endif //////////////////////////////////////////////////////////////////////////////// /// @brief construct a V8 value as input for the expression execution in V8 //////////////////////////////////////////////////////////////////////////////// #if 0 v8::Handle AqlValue::toV8( v8::Isolate* isolate, arangodb::AqlTransaction* trx, TRI_document_collection_t const* document) const { switch (_type) { case JSON: { TRI_ASSERT(_json != nullptr); return TRI_ObjectJson(isolate, _json->json()); } case SHAPED: { TRI_ASSERT(document != nullptr); TRI_ASSERT(_marker != nullptr); return TRI_WrapShapedJson( isolate, *trx, document->_info.id(), _marker); } 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 v8::Handle result = v8::Array::New(isolate, static_cast(totalSize)); uint32_t j = 0; // output row count 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) { result->Set(j++, current->getValueReference(i, 0) .toV8(isolate, trx, vecCollection)); } } return result; } case RANGE: { TRI_ASSERT(_range != nullptr); // allocate the buffer for the result size_t const n = _range->size(); v8::Handle result = v8::Array::New(isolate, static_cast(n)); for (uint32_t i = 0; i < n; ++i) { // is it safe to use a double here (precision loss)? result->Set(i, v8::Number::New(isolate, static_cast(_range->at( static_cast(i))))); } return result; } case EMPTY: { return v8::Null(isolate); } } // should never get here THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } #endif #if 0 Json AqlValue::toJson(arangodb::AqlTransaction* trx, TRI_document_collection_t const* document, bool copy) const { switch (_type) { case JSON: { TRI_ASSERT(_json != nullptr); if (copy) { return _json->copy(); } return Json(_json->zone(), _json->json(), Json::NOFREE); } case SHAPED: { TRI_ASSERT(document != nullptr); TRI_ASSERT(_marker != nullptr); auto shaper = document->getShaper(); return TRI_ExpandShapedJson(shaper, trx->resolver(), document->_info.id(), _marker); } 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(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->getValueReference(i, 0) .toJson(trx, vecCollection, true)); } } return 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(_range->at(i)))); } return json; } case EMPTY: { return Json(Json::Null); } } THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); } #endif //////////////////////////////////////////////////////////////////////////////// /// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$() { invalidate(*this); } //////////////////////////////////////////////////////////////////////////////// /// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackBuilder const& data) { TRI_ASSERT(data.isClosed()); VPackValueLength length = data.size(); if (length < 16) { // Use internal memcpy(_data.internal, data.data(), length); setType(AqlValueType::INTERNAL); } else { // Use external _data.external = new VPackBuffer(length); memcpy(_data.external->data(), data.data(), length); setType(AqlValueType::EXTERNAL); } } //////////////////////////////////////////////////////////////////////////////// /// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackBuilder const* data) : AqlValue$(*data) {} //////////////////////////////////////////////////////////////////////////////// /// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackSlice const& data) { VPackValueLength length = data.byteSize(); if (length < 16) { // Use internal memcpy(_data.internal, data.begin(), length); setType(AqlValueType::INTERNAL); } else { // Use external _data.external = new VPackBuffer(length); memcpy(_data.external->data(), data.begin(), length); setType(AqlValueType::EXTERNAL); } } //////////////////////////////////////////////////////////////////////////////// /// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackSlice const& data, AqlValueType type) { if (type == AqlValueType::REFERENCE || type == AqlValueType::REFERENCE_STICKY) { // simply copy the slice _data.reference = data.begin(); setType(type); return; } // everything else as usual VPackValueLength length = data.byteSize(); if (length < 16) { // Use internal memcpy(_data.internal, data.begin(), length); setType(AqlValueType::INTERNAL); } else { // Use external _data.external = new VPackBuffer(length); memcpy(_data.external->data(), data.begin(), length); setType(AqlValueType::EXTERNAL); } } //////////////////////////////////////////////////////////////////////////////// /// @brief constructor with range value //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(int64_t low, int64_t high) { Range* range = new Range(low, high); _data.internal[0] = 0xf4; // custom type for range _data.internal[1] = sizeof(Range*); memcpy(&_data.internal[2], &range, sizeof(Range*)); setType(AqlValueType::RANGE); } AqlValue$::AqlValue$(bool value) : AqlValue$(value ? arangodb::basics::VelocyPackHelper::TrueValue() : arangodb::basics::VelocyPackHelper::FalseValue()) { } //////////////////////////////////////////////////////////////////////////////// /// @brief destructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::~AqlValue$() { destroyQuick(); } //////////////////////////////////////////////////////////////////////////////// /// @brief copy //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(AqlValue$ const& other) { copy(other); } //////////////////////////////////////////////////////////////////////////////// /// @brief copy //////////////////////////////////////////////////////////////////////////////// AqlValue$& AqlValue$::operator=(AqlValue$ const& other) { if (this != &other) { destroyQuick(); copy(other); } return *this; } //////////////////////////////////////////////////////////////////////////////// /// @brief move //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(AqlValue$&& other) { move(other); } //////////////////////////////////////////////////////////////////////////////// /// @brief move //////////////////////////////////////////////////////////////////////////////// AqlValue$& AqlValue$::operator=(AqlValue$&& other) { if (this != &other) { destroyQuick(); move(other); } return *this; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the value //////////////////////////////////////////////////////////////////////////////// uint64_t AqlValue$::hash() const { switch (type()) { case INTERNAL: case EXTERNAL: case REFERENCE: case REFERENCE_STICKY: { return slice().hash(); } case RANGE: { // build the range values temporarily Range* r = range(); size_t const n = r->size(); VPackBuilder builder; builder.openArray(); for (size_t i = 0; i < n; ++i) { builder.add(VPackValue(r->at(i))); } builder.close(); return builder.slice().hash(); } } return 0; } ////////////////////////////////////////////////////////////////////////////// /// @brief return a slice for the contained value ////////////////////////////////////////////////////////////////////////////// VPackSlice AqlValue$::slice() const { switch (type()) { case EXTERNAL: return VPackSlice(_data.external->data()); case REFERENCE: case REFERENCE_STICKY: return VPackSlice(_data.reference); case INTERNAL: case RANGE: return VPackSlice(_data.internal); } return VPackSlice(); // we should not get here } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the value is empty / none ////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isNone() const { if (type() == RANGE) { return false; } return slice().isNone(); } //////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the value contains a null value //////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isNull(bool emptyIsNull) const { if (type() == RANGE) { // special handling for ranges return false; } if (slice().isNone()) { return emptyIsNull; } return slice().isNull(); } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the value is a number ////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isNumber() const { if (type() == RANGE) { // special handling for ranges return false; } return slice().isNumber(); } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the value is an object ////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isObject() const { if (type() == RANGE) { // special handling for ranges return true; } // all other cases return slice().isObject(); } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the value is an array (note: this treats ranges /// as arrays, too!) ////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isArray() const { if (type() == RANGE) { // special handling for ranges return true; } // all other cases return slice().isArray(); } ////////////////////////////////////////////////////////////////////////////// /// @brief get the (array) length (note: this treats ranges as arrays, too!) ////////////////////////////////////////////////////////////////////////////// size_t AqlValue$::length() const { if (type() == RANGE) { // special handling for ranges return range()->size(); } // all other cases VPackSlice const s = slice(); if (s.isArray()) { return s.length(); } return 0; } ////////////////////////////////////////////////////////////////////////////// /// @brief get the (array) element at position ////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::at(int64_t position) const { if (type() == RANGE) { // special handling for ranges Range* r = range(); size_t const n = r->size(); if (position < 0) { // a negative position is allowed position = static_cast(n) + position; } if (position >= 0 && position < static_cast(n)) { // only look up the value if it is within array bounds VPackBuilder builder; builder.add(VPackValue(r->at(static_cast(position)))); return AqlValue$(builder); } // fall-through intentional } else { VPackSlice const s = slice(); if (s.isArray()) { size_t const n = static_cast(s.length()); if (position < 0) { // a negative position is allowed position = static_cast(n) + position; } if (position >= 0 && position < static_cast(n)) { // only look up the value if it is within array bounds if (type() == AqlValueType::REFERENCE_STICKY) { return AqlValue$(s.at(position), AqlValueType::REFERENCE_STICKY); } return AqlValue$(s.at(position)); } } } // default is to return null return AqlValue$(arangodb::basics::VelocyPackHelper::NullValue(), REFERENCE_STICKY); } ////////////////////////////////////////////////////////////////////////////// /// @brief get the (object) element by name ////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::get(std::string const& name) const { VPackSlice const s = slice(); if (s.isObject()) { VPackSlice const value = s.get(name); if (!value.isNone()) { if (type() == AqlValueType::REFERENCE_STICKY) { return AqlValue$(value, AqlValueType::REFERENCE_STICKY); } return AqlValue$(value); } } // default is to return null return AqlValue$(arangodb::basics::VelocyPackHelper::NullValue(), REFERENCE_STICKY); } ////////////////////////////////////////////////////////////////////////////// /// @brief get the (object) element(s) by name ////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::get(std::vector const& names) const { VPackSlice const s = slice(); if (s.isObject()) { VPackSlice const value = s.get(names); if (!value.isNone()) { if (type() == AqlValueType::REFERENCE_STICKY) { return AqlValue$(value, AqlValueType::REFERENCE_STICKY); } return AqlValue$(value); } } // default is to return null return AqlValue$(arangodb::basics::VelocyPackHelper::NullValue(), REFERENCE_STICKY); } ////////////////////////////////////////////////////////////////////////////// /// @brief get the numeric value of an AqlValue ////////////////////////////////////////////////////////////////////////////// double AqlValue$::toDouble() const { if (type() == RANGE) { // special handling for ranges Range* r = range(); size_t rangeSize = r->size(); if (rangeSize == 1) { return static_cast(r->at(0)); } return 0.0; } VPackSlice const s = slice(); if (s.isBoolean()) { return s.getBoolean() ? 1.0 : 0.0; } if (s.isNumber()) { return s.getNumber(); } if (s.isString()) { try { return std::stod(s.copyString()); } catch (...) { // conversion failed } return 0.0; } if (s.isArray()) { if (s.length() == 1) { AqlValue$ tmp(s.at(0)); return tmp.toDouble(); } return 0.0; } return 0.0; } ////////////////////////////////////////////////////////////////////////////// /// @brief get the numeric value of an AqlValue ////////////////////////////////////////////////////////////////////////////// int64_t AqlValue$::toInt64() const { if (type() == RANGE) { // special handling for ranges Range* r = range(); size_t rangeSize = r->size(); if (rangeSize == 1) { return static_cast(r->at(0)); } return 0.0; } VPackSlice const s = slice(); if (s.isBoolean()) { return s.getBoolean() ? 1 : 0; } if (s.isNumber()) { return s.getNumber(); } if (s.isString()) { try { return static_cast(std::stoll(s.copyString())); } catch (...) { // conversion failed } return 0; } if (s.isArray()) { if (s.length() == 1) { AqlValue$ tmp(s.at(0)); return tmp.toInt64(); } return 0; } return 0; } ////////////////////////////////////////////////////////////////////////////// /// @brief whether or not the contained value evaluates to true ////////////////////////////////////////////////////////////////////////////// bool AqlValue$::isTrue() const { if (type() == RANGE) { return true; } VPackSlice const s = slice(); if (s.isBoolean()) { return s.getBoolean(); } if (s.isNumber()) { return (s.getNumber() != 0.0); } if (s.isString()) { VPackValueLength length; s.getString(length); return length > 0; } if (s.isArray() || s.isObject()) { return true; } // all other cases, include Null and None return false; } //////////////////////////////////////////////////////////////////////////////// /// @brief construct a V8 value as input for the expression execution in V8 /// only construct those attributes that are needed in the expression //////////////////////////////////////////////////////////////////////////////// v8::Handle AqlValue$::toV8Partial( v8::Isolate* isolate, arangodb::AqlTransaction* trx, std::unordered_set const& attributes) const { VPackSlice const s = slice(); if (isObject()) { v8::Handle result = v8::Object::New(isolate); // only construct those attributes needed size_t left = attributes.size(); // we can only have got here if we had attributes TRI_ASSERT(left > 0); // iterate over all the object's attributes for (auto const& it : VPackObjectIterator(s)) { // check if we need to render this attribute auto it2 = attributes.find(it.key.copyString()); if (it2 == attributes.end()) { // we can skip the attribute continue; } // TODO: do we need a customTypeHandler here? result->ForceSet(TRI_V8_STD_STRING((*it2)), TRI_VPackToV8(isolate, it.value, trx->transactionContext()->getVPackOptions())); if (--left == 0) { // we have rendered all required attributes break; } } // return partial object return result; } // fallback return TRI_VPackToV8(isolate, s, trx->transactionContext()->getVPackOptions()); } //////////////////////////////////////////////////////////////////////////////// /// @brief construct a V8 value as input for the expression execution in V8 //////////////////////////////////////////////////////////////////////////////// v8::Handle AqlValue$::toV8( v8::Isolate* isolate, arangodb::AqlTransaction* trx) const { if (type() == RANGE) { Range* r = range(); size_t const n = r->size(); v8::Handle result = v8::Array::New(isolate, static_cast(n)); for (uint32_t i = 0; i < n; ++i) { // is it safe to use a double here (precision loss)? result->Set(i, v8::Number::New(isolate, static_cast(r->at(static_cast(i))))); } return result; } // all other types go here return TRI_VPackToV8(isolate, slice(), trx->transactionContext()->getVPackOptions()); } ////////////////////////////////////////////////////////////////////////////// /// @brief convert a value to VelocyPack, appending to a build ////////////////////////////////////////////////////////////////////////////// void AqlValue$::toVelocyPack(arangodb::AqlTransaction* trx, VPackBuilder& builder) const { if (type() == RANGE) { builder.openArray(); Range* r = range(); size_t const n = r->size(); for (size_t i = 0; i < n; ++i) { builder.add(VPackValue(r->at(i))); } builder.close(); } } ////////////////////////////////////////////////////////////////////////////// /// @brief convert a value to VelocyPack ////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::toVelocyPack(arangodb::AqlTransaction* trx) const { if (type() == RANGE) { VPackBuilder builder; builder.openArray(); Range* r = range(); size_t const n = r->size(); for (size_t i = 0; i < n; ++i) { builder.add(VPackValue(r->at(i))); } builder.close(); return AqlValue$(builder); } return *this; } ////////////////////////////////////////////////////////////////////////////// /// @brief clone a value ////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::clone() const { AqlValue$ temp(*this); return temp; } ////////////////////////////////////////////////////////////////////////////// /// @brief invalidates/resets a value to None ////////////////////////////////////////////////////////////////////////////// void AqlValue$::invalidate() { VPackSlice empty; // None slice memcpy(_data.internal, empty.begin(), empty.byteSize()); setType(AqlValueType::INTERNAL); } ////////////////////////////////////////////////////////////////////////////// /// @brief sets the value type ////////////////////////////////////////////////////////////////////////////// void AqlValue$::setType(AqlValueType type) { _data.internal[15] = type; } ////////////////////////////////////////////////////////////////////////////// /// @brief helper function for copy construction/assignment ////////////////////////////////////////////////////////////////////////////// void AqlValue$::copy(AqlValue$ const& other) { setType(other.type()); VPackValueLength length = other.slice().byteSize(); switch (other.type()) { case EXTERNAL: { _data.external = new VPackBuffer(length); memcpy(_data.external->data(), other._data.external->data(), length); break; } case REFERENCE: case REFERENCE_STICKY: { _data.reference = other._data.reference; break; } case INTERNAL: { memcpy(_data.internal, other._data.internal, length); break; } case RANGE: { Range* range = new Range(other.range()->_low, other.range()->_high); _data.internal[0] = 0xf4; // custom type for range _data.internal[1] = sizeof(Range*); memcpy(&_data.internal[2], &range, sizeof(Range*)); break; } } } ////////////////////////////////////////////////////////////////////////////// /// @brief helper function for move construction/assignment ////////////////////////////////////////////////////////////////////////////// void AqlValue$::move(AqlValue$& other) { setType(other.type()); switch (other.type()) { case EXTERNAL: { // steal the other's external value _data.external = other._data.external; // overwrite the other's value invalidate(other); break; } case REFERENCE: case REFERENCE_STICKY: { // reuse the other's external value _data.reference = other._data.reference; break; } case INTERNAL: { VPackValueLength length = other.slice().byteSize(); memcpy(_data.internal, other._data.internal, length); // other's value can stay in place break; } case RANGE: { // copy the range pointer over to us VPackValueLength length = other.slice().byteSize(); memcpy(_data.internal, other._data.internal, length); // overwrite the other's value invalidate(other); break; } } } ////////////////////////////////////////////////////////////////////////////// /// @brief destroy the value's internals ////////////////////////////////////////////////////////////////////////////// void AqlValue$::destroyQuick() { switch (type()) { case EXTERNAL: { if (_data.external != nullptr) { delete _data.external; } break; } case REFERENCE: case REFERENCE_STICKY: case INTERNAL: { // NOTHING TO DO break; } case RANGE: { delete range(); break; } } } ////////////////////////////////////////////////////////////////////////////// /// @brief invalidates/resets a value to None ////////////////////////////////////////////////////////////////////////////// void AqlValue$::invalidate(AqlValue$& other) { VPackSlice empty; // None slice memcpy(other._data.internal, empty.begin(), empty.byteSize()); setType(AqlValueType::INTERNAL); } ////////////////////////////////////////////////////////////////////////////// /// @brief get pointer of the included Range object ////////////////////////////////////////////////////////////////////////////// Range* AqlValue$::range() const { TRI_ASSERT(type() == RANGE); Range* range = nullptr; memcpy(&range, &_data.internal[2], sizeof(Range*)); return range; } //////////////////////////////////////////////////////////////////////////////// /// @brief create an AqlValue from a vector of AqlItemBlock*s //////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector const& src, std::vector const& variableNames) { VPackBuilder builder; builder.openArray(); for (auto const& current : src) { RegisterId const n = current->getNrRegs(); std::vector registers; for (RegisterId j = 0; j < n; ++j) { // temporaries don't have a name and won't be included if (variableNames[j][0] != '\0') { registers.emplace_back(j); } } for (size_t i = 0; i < current->size(); ++i) { builder.openObject(); // only enumerate the registers that are left for (auto const& reg : registers) { builder.add(variableNames[reg], current->getValueReference(i, reg).toVelocyPack(trx).slice()); } builder.close(); } } builder.close(); return AqlValue$(builder); } //////////////////////////////////////////////////////////////////////////////// /// @brief create an AqlValue from a vector of AqlItemBlock*s //////////////////////////////////////////////////////////////////////////////// AqlValue$ AqlValue$::CreateFromBlocks( arangodb::AqlTransaction* trx, std::vector const& src, arangodb::aql::RegisterId expressionRegister) { VPackBuilder builder; builder.openArray(); for (auto const& current : src) { for (size_t i = 0; i < current->size(); ++i) { builder.add(current->getValueReference(i, expressionRegister).toVelocyPack(trx).slice()); } } builder.close(); return AqlValue$(builder); } //////////////////////////////////////////////////////////////////////////////// /// @brief 3-way comparison for AqlValue objects //////////////////////////////////////////////////////////////////////////////// int AqlValue$::Compare(arangodb::AqlTransaction* trx, AqlValue$ const& left, AqlValue$ const& right, bool compareUtf8) { AqlValue$::AqlValueType const leftType = left.type(); AqlValue$::AqlValueType const rightType = right.type(); if (leftType != rightType) { return arangodb::basics::VelocyPackHelper::compare(left.toVelocyPack(trx).slice(), right.toVelocyPack(trx).slice(), compareUtf8); } // if we get here, types are equal switch (leftType) { case AqlValue$::AqlValueType::INTERNAL: case AqlValue$::AqlValueType::EXTERNAL: case AqlValue$::AqlValueType::REFERENCE: case AqlValue$::AqlValueType::REFERENCE_STICKY: { return arangodb::basics::VelocyPackHelper::compare(left.slice(), right.slice(), compareUtf8); } case AqlValue$::AqlValueType::RANGE: { if (left.range()->_low < right.range()->_low) { return -1; } if (left.range()->_low > right.range()->_low) { return 1; } if (left.range()->_high < right.range()->_high) { return -1; } if (left.range()->_high > right.range()->_high) { return 1; } return 0; } } return 0; }