diff --git a/arangod/Aql/AqlValue.cpp b/arangod/Aql/AqlValue.cpp index 41c0c1eeb7..07d51f6c61 100644 --- a/arangod/Aql/AqlValue.cpp +++ b/arangod/Aql/AqlValue.cpp @@ -28,12 +28,15 @@ #include "Utils/AqlTransaction.h" #include "Utils/ShapedJsonTransformer.h" #include "V8/v8-conv.h" +#include "V8/v8-vpack.h" #include "V8Server/v8-shape-conv.h" #include "V8Server/v8-wrapshapedjson.h" #include "VocBase/document-collection.h" #include "VocBase/VocShaper.h" #include +#include +#include #include using namespace arangodb::aql; @@ -732,74 +735,147 @@ Json AqlValue::toJson(arangodb::AqlTransaction* trx, } //////////////////////////////////////////////////////////////////////////////// -/// @brief Constructor +/// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$() { - VPackBuilder builder; - memcpy(_data.internal, builder.slice().begin(), builder.slice().byteSize()); - _data.internal[15] = AqlValueType::INTERNAL; + invalidate(*this); } //////////////////////////////////////////////////////////////////////////////// -/// @brief Constructor +/// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackBuilder const& data) { TRI_ASSERT(data.isClosed()); - VPackValueLength l = data.size(); - if (l < 16) { + VPackValueLength length = data.size(); + if (length < 16) { // Use internal - memcpy(_data.internal, data.data(), l); - _data.internal[15] = AqlValueType::INTERNAL; + memcpy(_data.internal, data.data(), length); + setType(AqlValueType::INTERNAL); } else { // Use external - _data.external = new VPackBuffer(l); - memcpy(_data.external->data(), data.data(), l); - _data.internal[15] = AqlValueType::EXTERNAL; + _data.external = new VPackBuffer(length); + memcpy(_data.external->data(), data.data(), length); + setType(AqlValueType::EXTERNAL); } } //////////////////////////////////////////////////////////////////////////////// -/// @brief Constructor +/// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackBuilder const* data) : AqlValue$(*data) {} //////////////////////////////////////////////////////////////////////////////// -/// @brief Constructor +/// @brief constructor //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(VPackSlice const& data) { - VPackValueLength l = data.byteSize(); - if (l < 16) { + VPackValueLength length = data.byteSize(); + if (length < 16) { // Use internal - memcpy(_data.internal, data.start(), l); - _data.internal[15] = AqlValueType::INTERNAL; + memcpy(_data.internal, data.begin(), length); + setType(AqlValueType::INTERNAL); } else { // Use external - _data.external = new VPackBuffer(l); - memcpy(_data.external->data(), data.start(), l); - _data.internal[15] = AqlValueType::EXTERNAL; + _data.external = new VPackBuffer(length); + memcpy(_data.external->data(), data.begin(), length); + setType(AqlValueType::EXTERNAL); } } //////////////////////////////////////////////////////////////////////////////// -/// @brief Copy Constructor. +/// @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$() { + destroy(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief copy //////////////////////////////////////////////////////////////////////////////// AqlValue$::AqlValue$(AqlValue$ const& other) { - VPackSlice s = other.slice(); - VPackValueLength length = s.byteSize(); - if (other.type()) { - // Isse external - _data.external = new VPackBuffer(length); - memcpy(_data.external->data(), other._data.external->data(), length); - _data.internal[15] = AqlValueType::EXTERNAL; - } else { - memcpy(_data.internal, other._data.internal, length); - _data.internal[15] = AqlValueType::INTERNAL; + copy(other); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief copy +//////////////////////////////////////////////////////////////////////////////// + +AqlValue$& AqlValue$::operator=(AqlValue$ const& other) { + if (this != &other) { + destroy(); + copy(other); } + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief move +//////////////////////////////////////////////////////////////////////////////// + +AqlValue$::AqlValue$(AqlValue$&& other) { + move(other); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief move +//////////////////////////////////////////////////////////////////////////////// + +AqlValue$& AqlValue$::operator=(AqlValue$&& other) { + if (this != &other) { + destroy(); + move(other); + } + return *this; } // Temporary constructor to transform an old AqlValue to a new VPackBased @@ -849,14 +925,12 @@ AqlValue$::AqlValue$(AqlValue const& other, arangodb::AqlTransaction* trx, case AqlValue::RANGE: { // TODO Has to be replaced by VPackCustom Type TRI_ASSERT(other._range != nullptr); - try { - VPackArrayBuilder b(&builder); - size_t const n = other._range->size(); - for (size_t i = 0; i < n; ++i) { - builder.add(VPackValue(other._range->at(i))); - } - } catch (...) { - } + 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*)); + _data.internal[15] = AqlValueType::RANGE; break; } case AqlValue::EMPTY: { @@ -864,60 +938,227 @@ AqlValue$::AqlValue$(AqlValue const& other, arangodb::AqlTransaction* trx, break; } default: { - TRI_ASSERT(false); - THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); - } + TRI_ASSERT(false); + THROW_ARANGO_EXCEPTION(TRI_ERROR_INTERNAL); + } } VPackValueLength length = builder.size(); if (length < 16) { // Small enough for local // copy memory from the builder into the internal data. memcpy(_data.internal, builder.data(), length); - _data.internal[15] = AqlValueType::INTERNAL; + setType(AqlValueType::INTERNAL); } else { // We need a large external buffer // TODO: Replace by SlimBuffer _data.external = new VPackBuffer(length); memcpy(_data.external->data(), builder.data(), length); - _data.internal[15] = AqlValueType::EXTERNAL; + setType(AqlValueType::EXTERNAL); } } -AqlValue$::AqlValueType AqlValue$::type() const { - return static_cast(_data.internal[15]); +////////////////////////////////////////////////////////////////////////////// +/// @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 { + return slice().isNone(); +} + +////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not the value is a number +////////////////////////////////////////////////////////////////////////////// + +bool AqlValue$::isNumber() const { + if (type() == RANGE) { + 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 (slice().isCustom()) { -#warning FIX custom - // range. TODO - //size_t rangeSize = _range->size(); - //if (rangeSize == 1) { - // return _range->at(0); - //} + 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; } - if (slice().isBoolean()) { - return slice().getBoolean() ? 1.0 : 0.0; + + VPackSlice const s = slice(); + + if (s.isBoolean()) { + return s.getBoolean() ? 1.0 : 0.0; } - if (slice().isNumber()) { - return slice().getNumber(); + if (s.isNumber()) { + return s.getNumber(); } - if (slice().isString()) { + if (s.isString()) { try { - return std::stod(slice().copyString()); + return std::stod(s.copyString()); } catch (...) { // conversion failed } return 0.0; } - if (slice().isArray()) { - if (slice().length() == 1) { - AqlValue$ tmp(slice().at(0)); + if (s.isArray()) { + if (s.length() == 1) { + AqlValue$ tmp(s.at(0)); return tmp.toDouble(); } return 0.0; @@ -931,32 +1172,36 @@ double AqlValue$::toDouble() const { ////////////////////////////////////////////////////////////////////////////// int64_t AqlValue$::toInt64() const { - if (slice().isCustom()) { -#warning FIX custom - // range. TODO - //size_t rangeSize = _range->size(); - //if (rangeSize == 1) { - // return _range->at(0); - //} - return 0; + 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; } - if (slice().isBoolean()) { - return slice().getBoolean() ? 1 : 0; + + VPackSlice const s = slice(); + + if (s.isBoolean()) { + return s.getBoolean() ? 1 : 0; } - if (slice().isNumber()) { - return slice().getNumber(); + if (s.isNumber()) { + return s.getNumber(); } - if (slice().isString()) { + if (s.isString()) { try { - return static_cast(std::stoll(slice().copyString())); + return static_cast(std::stoll(s.copyString())); } catch (...) { // conversion failed } return 0; } - if (slice().isArray()) { - if (slice().length() == 1) { - AqlValue$ tmp(slice().at(0)); + if (s.isArray()) { + if (s.length() == 1) { + AqlValue$ tmp(s.at(0)); return tmp.toInt64(); } return 0; @@ -965,36 +1210,235 @@ int64_t AqlValue$::toInt64() const { return 0; } +////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not the contained value evaluates to true +////////////////////////////////////////////////////////////////////////////// + bool AqlValue$::isTrue() const { - if (slice().isBoolean() && slice().getBoolean()) { - return true; - } - if (slice().isNumber() && slice().getNumber() != 0.0) { - return true; - } - if (slice().isString() && !slice().copyString().empty()) { - return true; - } - if (slice().isArray() || slice().isObject()) { + if (type() == RANGE) { return true; } - if (slice().isCustom()) { - // 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; } -VPackSlice AqlValue$::slice() const { - if (type()) { - // Use External - return VPackSlice(_data.external->data()); - } else { - return VPackSlice(_data.internal); +//////////////////////////////////////////////////////////////////////////////// +/// @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)); + + if (--left == 0) { + // we have rendered all required attributes + break; + } + } + + // return partial object + return result; + } + + // fallback + // TODO: do we need a customTypeHandler here? + return TRI_VPackToV8(isolate, s); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @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()); +} + +////////////////////////////////////////////////////////////////////////////// +/// @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); + 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$::destroy() { + 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; +} + + void AqlValue::toVelocyPack(arangodb::AqlTransaction* trx, TRI_document_collection_t const* document, VPackBuilder& builder) const { diff --git a/arangod/Aql/AqlValue.h b/arangod/Aql/AqlValue.h index 0dc61af117..5ccc85e1ab 100644 --- a/arangod/Aql/AqlValue.h +++ b/arangod/Aql/AqlValue.h @@ -365,68 +365,124 @@ struct AqlValue { AqlValueType _type; }; +// do not add virtual methods! struct AqlValue$ { public: + ////////////////////////////////////////////////////////////////////////////// /// @brief AqlValueType, indicates what sort of value we have ////////////////////////////////////////////////////////////////////////////// - enum AqlValueType { INTERNAL, EXTERNAL }; + enum AqlValueType : uint8_t { INTERNAL, EXTERNAL, REFERENCE, REFERENCE_STICKY, RANGE }; ////////////////////////////////////////////////////////////////////////////// - /// @brief Holds the actual data for this AqlValue it has the following - /// semantics: + /// @brief Holds the actual data for this AqlValue + /// it has the following semantics: /// /// All values with a size less than 16 will be stored directly in this /// AqlValue using the data.internal structure. - /// All values of a larger size will be store in data.external. - /// The last bit of this union will be used to identify if we have to use + /// All values of a larger size will be stored in _data.external. + /// The last byte of this union will be used to identify if we have to use /// internal or external. /// Delete of the Buffer should free every structure that is not using the - /// VPack external value type. + /// VPack external or reference value type. ////////////////////////////////////////////////////////////////////////////// private: union { char internal[16]; arangodb::velocypack::Buffer* external; + uint8_t const* reference; } _data; public: + AqlValue$(); explicit AqlValue$(arangodb::velocypack::Builder const&); explicit AqlValue$(arangodb::velocypack::Builder const*); explicit AqlValue$(arangodb::velocypack::Slice const&); - - AqlValue$(); + AqlValue$(arangodb::velocypack::Slice const&, AqlValueType); + AqlValue$(int64_t, int64_t); // range + explicit AqlValue$(bool); // boolean type AqlValue$(AqlValue const&, arangodb::AqlTransaction*, TRI_document_collection_t const*); - - ~AqlValue$() { - if (type() && _data.external != nullptr) { - delete _data.external; - } - } - + + ~AqlValue$(); + //////////////////////////////////////////////////////////////////////////////// - /// @brief Copy Constructor + /// @brief copy //////////////////////////////////////////////////////////////////////////////// AqlValue$(AqlValue$ const& other); + AqlValue$& operator=(AqlValue$ const& other); + + //////////////////////////////////////////////////////////////////////////////// + /// @brief move + //////////////////////////////////////////////////////////////////////////////// + + AqlValue$(AqlValue$&& other); + AqlValue$& operator=(AqlValue$&& other); ////////////////////////////////////////////////////////////////////////////// /// @brief Returns the type of this value. If true it uses an external pointer /// if false it uses the internal data structure ////////////////////////////////////////////////////////////////////////////// - // Read last bit of the union - AqlValueType type() const; + AqlValueType type() const { + return static_cast(_data.internal[15]); + } ////////////////////////////////////////////////////////////////////////////// - /// @brief Returns a slice to read this Value's data + /// @brief returns a slice to read this Value's data ////////////////////////////////////////////////////////////////////////////// arangodb::velocypack::Slice slice() const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief whether or not the value is empty / none + ////////////////////////////////////////////////////////////////////////////// + + bool isNone() const; + bool isEmpty() const { return isNone(); } + + ////////////////////////////////////////////////////////////////////////////// + /// @brief whether or not the value is a number + ////////////////////////////////////////////////////////////////////////////// + + bool isNumber() const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief whether or not the value is an object + ////////////////////////////////////////////////////////////////////////////// + + bool isObject() const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief whether or not the value is an array (note: this treats ranges + /// as arrays, too!) + ////////////////////////////////////////////////////////////////////////////// + + bool isArray() const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief get the (array) length (note: this treats ranges as arrays, too!) + ////////////////////////////////////////////////////////////////////////////// + + size_t length() const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief get the (array) element at position + ////////////////////////////////////////////////////////////////////////////// + + AqlValue$ at(int64_t position) const; + + ////////////////////////////////////////////////////////////////////////////// + /// @brief get the (object) element by name(s) + ////////////////////////////////////////////////////////////////////////////// + + AqlValue$ get(std::string const&) const; + + AqlValue$ get(std::vector const&) const; ////////////////////////////////////////////////////////////////////////////// /// @brief get the numeric value of an AqlValue @@ -436,10 +492,26 @@ struct AqlValue$ { int64_t toInt64() const; ////////////////////////////////////////////////////////////////////////////// - /// @brief whether or not an AqlValue evaluates to true + /// @brief whether or not an AqlValue evaluates to true/false ////////////////////////////////////////////////////////////////////////////// bool isTrue() const; + bool isFalse() const { return !isTrue(); } + + ////////////////////////////////////////////////////////////////////////////// + /// @brief construct a V8 value as input for the expression execution in V8 + ////////////////////////////////////////////////////////////////////////////// + + v8::Handle toV8(v8::Isolate* isolate, arangodb::AqlTransaction*) const; + + ////////////////////////////////////////////////////////////////////////////// + /// @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 toV8Partial(v8::Isolate* isolate, + arangodb::AqlTransaction*, + std::unordered_set const&) const; ////////////////////////////////////////////////////////////////////////////// /// @brief compare function for two values @@ -450,10 +522,66 @@ struct AqlValue$ { // TODO: implement return 0; } + + private: + + ////////////////////////////////////////////////////////////////////////////// + /// @brief sets the value type + ////////////////////////////////////////////////////////////////////////////// + + void setType(AqlValueType type); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief helper function for copy construction/assignment + ////////////////////////////////////////////////////////////////////////////// + + void copy(AqlValue$ const&); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief helper function for move construction/assignment + ////////////////////////////////////////////////////////////////////////////// + + void move(AqlValue$&); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief destroy the internal data structures + ////////////////////////////////////////////////////////////////////////////// + + void destroy(); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief invalidates/resets a value to None + ////////////////////////////////////////////////////////////////////////////// + + void invalidate(AqlValue$&); + + ////////////////////////////////////////////////////////////////////////////// + /// @brief get pointer of the included Range object + ////////////////////////////////////////////////////////////////////////////// + + Range* range() const; }; static_assert(sizeof(AqlValue$) == 16, "invalid AqlValue$ size"); +// do not add virtual methods or data members! +struct AqlValueReference final : public AqlValue$ { + public: + explicit AqlValueReference(arangodb::velocypack::Slice const& slice) + : AqlValue$(slice, AqlValueType::REFERENCE) {} +}; + +static_assert(sizeof(AqlValueReference) == 16, "invalid AqlValueReference size"); + +// do not add virtual methods or data members! +struct AqlValueDocument final : public AqlValue$ { + public: + explicit AqlValueDocument(arangodb::velocypack::Slice const& slice) + : AqlValue$(slice, AqlValueType::REFERENCE_STICKY) {} +}; + +static_assert(sizeof(AqlValueDocument) == 16, "invalid AqlValueDocument size"); + } // closes namespace arangodb::aql } // closes namespace arangodb diff --git a/arangod/Aql/AttributeAccessor.cpp b/arangod/Aql/AttributeAccessor.cpp index a58128ed60..bcf430afe9 100644 --- a/arangod/Aql/AttributeAccessor.cpp +++ b/arangod/Aql/AttributeAccessor.cpp @@ -22,14 +22,11 @@ //////////////////////////////////////////////////////////////////////////////// #include "AttributeAccessor.h" -#include "Basics/JsonHelper.h" #include "Aql/AqlItemBlock.h" #include "Aql/Variable.h" +#include "Basics/VelocyPackHelper.h" #include "Utils/AqlTransaction.h" -#include -#include - using namespace arangodb::aql; //////////////////////////////////////////////////////////////////////////////// @@ -44,36 +41,23 @@ AttributeAccessor::AttributeAccessor( TRI_ASSERT(_variable != nullptr); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroy the accessor -//////////////////////////////////////////////////////////////////////////////// - -AttributeAccessor::~AttributeAccessor() {} - //////////////////////////////////////////////////////////////////////////////// /// @brief execute the accessor //////////////////////////////////////////////////////////////////////////////// -AqlValue AttributeAccessor::get(arangodb::AqlTransaction* trx, - AqlItemBlock const* argv, size_t startPos, - std::vector const& vars, - std::vector const& regs) { +AqlValue$ AttributeAccessor::get(arangodb::AqlTransaction* trx, + AqlItemBlock const* argv, size_t startPos, + std::vector const& vars, + std::vector const& regs) { size_t i = 0; for (auto it = vars.begin(); it != vars.end(); ++it, ++i) { if ((*it)->id == _variable->id) { // get the AQL value - auto& result = argv->getValueReference(startPos, regs[i]); - - // extract the attribute - VPackSlice const slice; -#warning TODO: fill slice from AqlValue (result) - VPackSlice extracted = slice.get(_attributeParts); -#warning TODO: build result from extracted - return AqlValue(new arangodb::basics::Json(arangodb::basics::Json::Null)); + return AqlValue$(argv->getValueReference(startPos, regs[i]), trx, nullptr).get(_attributeParts); } // fall-through intentional } - return AqlValue(new arangodb::basics::Json(arangodb::basics::Json::Null)); + return AqlValueReference(arangodb::basics::VelocyPackHelper::NullValue()); } diff --git a/arangod/Aql/AttributeAccessor.h b/arangod/Aql/AttributeAccessor.h index d5a15cb9e4..22cac3199c 100644 --- a/arangod/Aql/AttributeAccessor.h +++ b/arangod/Aql/AttributeAccessor.h @@ -42,18 +42,18 @@ struct Variable; class AttributeAccessor { public: AttributeAccessor(std::vector const&, Variable const*); - - ~AttributeAccessor(); + ~AttributeAccessor() = default; ////////////////////////////////////////////////////////////////////////////// /// @brief execute the accessor ////////////////////////////////////////////////////////////////////////////// - AqlValue get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, - std::vector const&, - std::vector const&); + AqlValue$ get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, + std::vector const&, + std::vector const&); private: + ////////////////////////////////////////////////////////////////////////////// /// @brief the attribute names vector (e.g. [ "a", "b", "c" ] for a.b.c) ////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/CalculationBlock.cpp b/arangod/Aql/CalculationBlock.cpp index 391ab13cb0..efab0a7692 100644 --- a/arangod/Aql/CalculationBlock.cpp +++ b/arangod/Aql/CalculationBlock.cpp @@ -134,7 +134,7 @@ void CalculationBlock::executeExpression(AqlItemBlock* result) { } // execute the expression - AqlValue a = _expression->execute(_trx, result, i, _inVars, _inRegs); + AqlValue a = AqlValue(_expression->execute(_trx, result, i, _inVars, _inRegs)); try { TRI_IF_FAILURE("CalculationBlock::executeExpression") { diff --git a/arangod/Aql/Expression.cpp b/arangod/Aql/Expression.cpp index 50d4d06b44..bc8409addc 100644 --- a/arangod/Aql/Expression.cpp +++ b/arangod/Aql/Expression.cpp @@ -138,10 +138,10 @@ void Expression::variables(std::unordered_set& result) const { /// @brief execute the expression //////////////////////////////////////////////////////////////////////////////// -AqlValue Expression::execute(arangodb::AqlTransaction* trx, - AqlItemBlock const* argv, size_t startPos, - std::vector const& vars, - std::vector const& regs) { +AqlValue$ Expression::execute(arangodb::AqlTransaction* trx, + AqlItemBlock const* argv, size_t startPos, + std::vector const& vars, + std::vector const& regs) { if (!_built) { buildExpression(); } @@ -152,13 +152,16 @@ AqlValue Expression::execute(arangodb::AqlTransaction* trx, // and execute switch (_type) { case JSON: { + // TODO TRI_ASSERT(_data != nullptr); - return AqlValue(new Json(TRI_UNKNOWN_MEM_ZONE, _data, Json::NOFREE)); + VPackBuilder builder; + JsonHelper::toVelocyPack(_data, builder); + return AqlValue$(builder); } case SIMPLE: { - return AqlValue(executeSimpleExpression(_node, trx, argv, startPos, - vars, regs, true)); + return executeSimpleExpression(_node, trx, argv, startPos, + vars, regs, true); } case ATTRIBUTE: { @@ -268,9 +271,9 @@ void Expression::invalidate() { bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right, arangodb::AqlTransaction* trx, AstNode const* node) const { - TRI_ASSERT(right.slice().isArray()); + TRI_ASSERT(right.isArray()); - size_t const n = right.slice().length(); + size_t const n = right.length(); if (n > 3 && (node->getMember(1)->isSorted() || @@ -283,10 +286,8 @@ bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right, while (true) { // determine midpoint size_t m = l + ((r - l) / 2); - VPackSlice arrayItem = right.slice().at(m); - AqlValue$ arrayItemValue(arrayItem); - int compareResult = AqlValue$::Compare(trx, left, arrayItemValue, true); + int compareResult = AqlValue$::Compare(trx, left, AqlValue$(right.at(m).slice()), true); if (compareResult == 0) { // item found in the list @@ -306,20 +307,19 @@ bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right, return false; } } - } else { - // use linear search - for (auto const& it : VPackArrayIterator(right.slice())) { - // do not copy the list element we're looking at - int compareResult = AqlValue$::Compare(trx, left, AqlValue$(it), false); + } + + // use linear search + for (size_t i = 0; i < n; ++i) { + int compareResult = AqlValue$::Compare(trx, left, AqlValue$(right.at(i).slice()), false); - if (compareResult == 0) { - // item found in the list - return true; - } + if (compareResult == 0) { + // item found in the list + return true; } - - return false; } + + return false; } //////////////////////////////////////////////////////////////////////////////// @@ -571,14 +571,7 @@ AqlValue$ Expression::executeSimpleExpressionAttributeAccess( AqlValue$ result = executeSimpleExpression(member, trx, argv, startPos, vars, regs, false); - VPackSlice slice = result.slice(); - if (slice.isObject()) { - VPackSlice value = slice.get(name); - if (!value.isNone()) { - return AqlValue$(value); - } - } - return AqlValue$(VelocyPackHelper::NullValue()); + return result.get(name); } //////////////////////////////////////////////////////////////////////////////// @@ -608,12 +601,12 @@ AqlValue$ Expression::executeSimpleExpressionIndexedAccess( AqlValue$ result = executeSimpleExpression(member, trx, argv, startPos, vars, regs, false); - if (result.slice().isArray()) { + if (result.isArray()) { AqlValue$ indexResult = executeSimpleExpression( index, trx, argv, startPos, vars, regs, false); - if (indexResult.slice().isNumber()) { - return AqlValue$(result.slice().at(indexResult.toInt64())); + if (indexResult.isNumber()) { + return result.at(indexResult.toInt64()); } if (indexResult.slice().isString()) { @@ -622,33 +615,25 @@ AqlValue$ Expression::executeSimpleExpressionIndexedAccess( try { // stoll() might throw an exception if the string is not a number int64_t position = static_cast(std::stoll(value)); - return AqlValue$(result.slice().at(position)); + return result.at(position); } catch (...) { // no number found. } } // fall-through to returning null - } else if (result.slice().isObject()) { + } else if (result.isObject()) { AqlValue$ indexResult = executeSimpleExpression( index, trx, argv, startPos, vars, regs, false); - if (indexResult.slice().isNumber()) { - std::string const indexString = std::to_string(indexResult.slice().getNumber()); - VPackSlice value = result.slice().get(indexString); - if (!value.isNone()) { - return AqlValue$(value); - } - return AqlValue$(VelocyPackHelper::NullValue()); + if (indexResult.isNumber()) { + std::string const indexString = std::to_string(indexResult.toInt64()); + return result.get(indexString); } if (indexResult.slice().isString()) { std::string const indexString = indexResult.slice().copyString(); - VPackSlice value = result.slice().get(indexString); - if (!value.isNone()) { - return AqlValue$(value); - } - return AqlValue$(VelocyPackHelper::NullValue()); + return result.get(indexString); } // fall-through to returning null } @@ -800,16 +785,7 @@ AqlValue$ Expression::executeSimpleExpressionRange( AqlValue$ resultHigh = executeSimpleExpression( high, trx, argv, startPos, vars, regs, false); - // build a custom type for the range - VPackBuilder builder; - uint8_t* p = builder.add(VPackValuePair(2 + 2 * sizeof(int64_t), VPackValueType::Custom)); - *p++ = 0xf4; // custom type for range - *p++ = 2 * sizeof(int64_t); - memcpy(p, &low, sizeof(int64_t)); - p += sizeof(int64_t); - memcpy(p, &high, sizeof(int64_t)); - - return AqlValue$(builder); + return AqlValue$(resultLow.toInt64(), resultHigh.toInt64()); } //////////////////////////////////////////////////////////////////////////////// @@ -829,14 +805,12 @@ AqlValue$ Expression::executeSimpleExpressionFCall( auto member = node->getMemberUnchecked(0); TRI_ASSERT(member->type == NODE_TYPE_ARRAY); + VPackBuilder builder; size_t const n = member->numMembers(); + VPackFunctionParameters parameters; parameters.reserve(n); -#warning Can we get access to query here? - // std::shared_ptr builder = query->getSharedBuilder(); - VPackBuilder builder; -#warning Check if this is correct w.r.t. Memory Management for (size_t i = 0; i < n; ++i) { auto arg = member->getMemberUnchecked(i); @@ -867,7 +841,7 @@ AqlValue$ Expression::executeSimpleExpressionNot( executeSimpleExpression(node->getMember(0), trx, argv, startPos, vars, regs, false); - return AqlValue$(operand.isTrue() ? VelocyPackHelper::FalseValue() : VelocyPackHelper::TrueValue()); + return AqlValue$(operand.isFalse()); } //////////////////////////////////////////////////////////////////////////////// @@ -926,10 +900,10 @@ AqlValue$ Expression::executeSimpleExpressionComparison( if (node->type == NODE_TYPE_OPERATOR_BINARY_IN || node->type == NODE_TYPE_OPERATOR_BINARY_NIN) { // IN and NOT IN - if (!right.slice().isArray()) { + if (!right.isArray()) { // right operand must be a list, otherwise we return false // do not throw, but return "false" instead - return AqlValue$(VelocyPackHelper::FalseValue()); + return AqlValue$(false); } bool result = @@ -940,7 +914,7 @@ AqlValue$ Expression::executeSimpleExpressionComparison( result = !result; } - return AqlValue$(result ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(result); } // all other comparison operators... @@ -952,17 +926,17 @@ AqlValue$ Expression::executeSimpleExpressionComparison( int compareResult = AqlValue$::Compare(trx, left, right, compareUtf8); switch (node->type) { case NODE_TYPE_OPERATOR_BINARY_EQ: - return AqlValue$((compareResult == 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult == 0); case NODE_TYPE_OPERATOR_BINARY_NE: - return AqlValue$((compareResult != 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult != 0); case NODE_TYPE_OPERATOR_BINARY_LT: - return AqlValue$((compareResult < 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult < 0); case NODE_TYPE_OPERATOR_BINARY_LE: - return AqlValue$((compareResult <= 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult <= 0); case NODE_TYPE_OPERATOR_BINARY_GT: - return AqlValue$((compareResult > 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult > 0); case NODE_TYPE_OPERATOR_BINARY_GE: - return AqlValue$((compareResult >= 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(compareResult >= 0); default: std::string msg("unhandled type '"); msg.append(node->getTypeString()); @@ -987,23 +961,23 @@ AqlValue$ Expression::executeSimpleExpressionArrayComparison( executeSimpleExpression(node->getMember(1), trx, argv, startPos, vars, regs, false); - if (!left.slice().isArray()) { + if (!left.isArray()) { // left operand must be an array // do not throw, but return "false" instead - return AqlValue$(VelocyPackHelper::FalseValue()); + return AqlValue$(false); } if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN || node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { // IN and NOT IN - if (!right.slice().isArray()) { + if (!right.isArray()) { // right operand must be a list, otherwise we return false // do not throw, but return "false" instead - return AqlValue$(VelocyPackHelper::FalseValue()); + return AqlValue$(false); } } - size_t const n = left.slice().length(); + size_t const n = left.length(); std::pair requiredMatches = Quantifier::RequiredMatches(n, node->getMember(2)); TRI_ASSERT(requiredMatches.first <= requiredMatches.second); @@ -1015,16 +989,15 @@ AqlValue$ Expression::executeSimpleExpressionArrayComparison( bool overallResult = true; size_t matches = 0; size_t numLeft = n; - - for (auto const& it : VPackArrayIterator(left.slice())) { - AqlValue$ leftItemValue(it); + + for (size_t i = 0; i < n; ++i) { + AqlValue$ leftItemValue(left.at(i)); bool result; // IN and NOT IN if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN || node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { - result = - findInArray(leftItemValue, right, trx, node); + result = findInArray(leftItemValue, right, trx, node); if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { // revert the result in case of a NOT IN @@ -1084,7 +1057,7 @@ AqlValue$ Expression::executeSimpleExpressionArrayComparison( } } - return AqlValue$(overallResult ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); + return AqlValue$(overallResult); } //////////////////////////////////////////////////////////////////////////////// @@ -1100,14 +1073,17 @@ AqlValue$ Expression::executeSimpleExpressionTernary( executeSimpleExpression(node->getMember(0), trx, argv, startPos, vars, regs, false); + size_t position; if (condition.isTrue()) { // return true part - return executeSimpleExpression(node->getMember(1), trx, argv, - startPos, vars, regs, true); + position = 1; + } + else { + // return false part + position = 2; } - // return false part - return executeSimpleExpression(node->getMember(2), trx, argv, + return executeSimpleExpression(node->getMember(position), trx, argv, startPos, vars, regs, true); } @@ -1141,10 +1117,7 @@ AqlValue$ Expression::executeSimpleExpressionExpansion( if (offset < 0 || count <= 0) { // no items to return... can already stop here - VPackBuilder builder; - builder.openArray(); - builder.close(); - return AqlValue$(builder); + return AqlValueReference(VelocyPackHelper::ArrayValue()); } // FILTER @@ -1158,10 +1131,7 @@ AqlValue$ Expression::executeSimpleExpressionExpansion( filterNode = nullptr; } else { // filter expression is always false - VPackBuilder builder; - builder.openArray(); - builder.close(); - return AqlValue$(builder); + return AqlValueReference(VelocyPackHelper::ArrayValue()); } } @@ -1176,33 +1146,34 @@ AqlValue$ Expression::executeSimpleExpressionExpansion( value = executeSimpleExpression(node->getMember(0), trx, argv, startPos, vars, regs, false); + if (!value.isArray()) { + return AqlValueReference(VelocyPackHelper::ArrayValue()); + } + VPackBuilder builder; builder.openArray(); - - if (!value.slice().isArray()) { - builder.close(); - return AqlValue$(builder); - } // generate a new temporary for the flattened array - std::function flatten = - [&](VPackSlice const& json, int64_t level) { - if (!json.isArray()) { + std::function flatten = + [&](AqlValue$ const& v, int64_t level) { + if (!v.isArray()) { return; } - for (auto const& it : VPackArrayIterator(json)) { - bool const isArray = it.isArray(); + size_t const n = v.length(); + for (size_t i = 0; i < n; ++i) { + AqlValue$ item(v.at(i)); + bool const isArray = item.isArray(); if (!isArray || level == levels) { - builder.add(it); + builder.add(item.slice()); } else if (isArray && level < levels) { - flatten(it, level + 1); + flatten(item, level + 1); } } }; - flatten(value.slice(), 1); + flatten(value, 1); builder.close(); value = AqlValue$(builder); @@ -1210,11 +1181,8 @@ AqlValue$ Expression::executeSimpleExpressionExpansion( value = executeSimpleExpression(node->getMember(0), trx, argv, startPos, vars, regs, false); - if (!value.slice().isArray()) { - VPackBuilder builder; - builder.openArray(); - builder.close(); - return AqlValue$(builder); + if (!value.isArray()) { + return AqlValueReference(VelocyPackHelper::ArrayValue()); } } @@ -1230,8 +1198,10 @@ AqlValue$ Expression::executeSimpleExpressionExpansion( VPackBuilder builder; builder.openArray(); - for (auto const& it : VPackArrayIterator(value.slice())) { - setVariable(variable, it); + size_t const n = value.length(); + for (size_t i = 0; i < n; ++i) { + AqlValue$ item(value.at(i)); + setVariable(variable, item.slice()); bool takeItem = true; @@ -1299,19 +1269,19 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic( AqlValue$ lhs = executeSimpleExpression(node->getMember(0), trx, argv, startPos, vars, regs, true); - if (lhs.slice().isObject()) { - return AqlValue$(VelocyPackHelper::NullValue()); + if (lhs.isObject()) { + return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); } AqlValue$ rhs = executeSimpleExpression(node->getMember(1), trx, argv, startPos, vars, regs, true); - if (rhs.slice().isObject()) { - return AqlValue$(VelocyPackHelper::NullValue()); + if (rhs.isObject()) { + return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); } - double const l = lhs.slice().getNumber(); - double const r = rhs.slice().getNumber(); + double const l = lhs.toDouble(); + double const r = rhs.toDouble(); VPackBuilder builder; @@ -1328,17 +1298,17 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic( case NODE_TYPE_OPERATOR_BINARY_DIV: if (r == 0.0) { RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); - return AqlValue$(VelocyPackHelper::NullValue()); + return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); } return AqlValue$(builder); case NODE_TYPE_OPERATOR_BINARY_MOD: if (r == 0.0) { RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); - return AqlValue$(VelocyPackHelper::NullValue()); + return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); } builder.add(VPackValue(fmod(l, r))); return AqlValue$(builder); default: - return AqlValue$(VelocyPackHelper::NullValue()); + return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY); } } diff --git a/arangod/Aql/Expression.h b/arangod/Aql/Expression.h index 71410384a6..89bdfe4108 100644 --- a/arangod/Aql/Expression.h +++ b/arangod/Aql/Expression.h @@ -47,7 +47,6 @@ class StringBuffer; namespace aql { class AqlItemBlock; -struct AqlValue; struct AqlValue$; class Ast; class AttributeAccessor; @@ -169,9 +168,9 @@ class Expression { /// @brief execute the expression ////////////////////////////////////////////////////////////////////////////// - AqlValue execute(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, - std::vector const&, - std::vector const&); + AqlValue$ execute(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, + std::vector const&, + std::vector const&); ////////////////////////////////////////////////////////////////////////////// /// @brief check whether this is a JSON expression diff --git a/arangod/Aql/IndexBlock.cpp b/arangod/Aql/IndexBlock.cpp index 51819587ce..d6bcf90157 100644 --- a/arangod/Aql/IndexBlock.cpp +++ b/arangod/Aql/IndexBlock.cpp @@ -106,8 +106,8 @@ void IndexBlock::executeExpressions() { auto& toReplace = _nonConstExpressions[posInExpressions]; auto exp = toReplace->expression; TRI_document_collection_t const* myCollection = nullptr; - AqlValue a = exp->execute(_trx, cur, _pos, _inVars[posInExpressions], - _inRegs[posInExpressions]); + AqlValue a = AqlValue(exp->execute(_trx, cur, _pos, _inVars[posInExpressions], + _inRegs[posInExpressions])); auto jsonified = a.toJson(_trx, myCollection, true); a.destroy(); AstNode* evaluatedNode = ast->nodeFromJson(jsonified.json(), true); diff --git a/arangod/Aql/TraversalBlock.cpp b/arangod/Aql/TraversalBlock.cpp index 32f037d88c..e5a42ff09f 100644 --- a/arangod/Aql/TraversalBlock.cpp +++ b/arangod/Aql/TraversalBlock.cpp @@ -183,8 +183,8 @@ void TraversalBlock::executeExpressions() { if (it != nullptr && it->expression != nullptr) { // inVars and inRegs needs fixx TRI_document_collection_t const* myCollection = nullptr; - AqlValue a = it->expression->execute(_trx, cur, _pos, _inVars[i], - _inRegs[i]); + AqlValue a = AqlValue(it->expression->execute(_trx, cur, _pos, _inVars[i], + _inRegs[i])); it->compareTo.reset(new Json(a.toJson(_trx, myCollection, true))); a.destroy(); } diff --git a/arangod/Aql/V8Expression.cpp b/arangod/Aql/V8Expression.cpp index 89ce0cdc81..7fb5a69968 100644 --- a/arangod/Aql/V8Expression.cpp +++ b/arangod/Aql/V8Expression.cpp @@ -21,7 +21,7 @@ /// @author Jan Steemann //////////////////////////////////////////////////////////////////////////////// -#include "Aql/V8Expression.h" +#include "V8Expression.h" #include "Aql/AqlItemBlock.h" #include "Aql/Executor.h" #include "Aql/Query.h" @@ -30,7 +30,7 @@ #include "Basics/json-utilities.h" #include "V8/v8-conv.h" #include "V8/v8-utils.h" -#include "V8Server/v8-shape-conv.h" +#include "V8/v8-vpack.h" using namespace arangodb::aql; @@ -62,11 +62,11 @@ V8Expression::~V8Expression() { /// @brief execute the expression //////////////////////////////////////////////////////////////////////////////// -AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, - arangodb::AqlTransaction* trx, - AqlItemBlock const* argv, size_t startPos, - std::vector const& vars, - std::vector const& regs) { +AqlValue$ V8Expression::execute(v8::Isolate* isolate, Query* query, + arangodb::AqlTransaction* trx, + AqlItemBlock const* argv, size_t startPos, + std::vector const& vars, + std::vector const& regs) { size_t const n = vars.size(); TRI_ASSERT(regs.size() == n); // assert same vector length @@ -75,18 +75,17 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, v8::Handle values = v8::Object::New(isolate); for (size_t i = 0; i < n; ++i) { - auto reg = regs[i]; + RegisterId reg = regs[i]; - auto const& value(argv->getValueReference(startPos, reg)); + AqlValue$ value(argv->getValueReference(startPos, reg), trx, nullptr); - if (value.isEmpty()) { + if (value.isNone()) { continue; } - auto document = argv->getDocumentCollection(reg); - auto const& varname = vars[i]->name; + std::string const& varname = vars[i]->name; - if (hasRestrictions && value.isJson()) { + if (hasRestrictions && value.isObject()) { // check if we can get away with constructing a partial JSON object auto it = _attributeRestrictions.find(vars[i]); @@ -94,7 +93,7 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, // build a partial object values->ForceSet( TRI_V8_STD_STRING(varname), - value.toV8Partial(isolate, trx, (*it).second, document)); + value.toV8Partial(isolate, trx, (*it).second)); continue; } } @@ -103,7 +102,7 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, // build the regular object values->ForceSet(TRI_V8_STD_STRING(varname), - value.toV8(isolate, trx, document)); + value.toV8(isolate, trx)); } TRI_ASSERT(query != nullptr); @@ -128,23 +127,6 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, // won't modify their arguments is unsafe auto constantValues = v8::Local::New(isolate, _constantValues); -#ifdef ARANGODB_ENABLE_FAILURE_TESTS - // a hash function for hashing V8 object contents - auto hasher = - [](v8::Isolate* isolate, v8::Handle const obj) -> uint64_t { - std::unique_ptr json(TRI_ObjectToJson(isolate, obj)); - - if (json == nullptr) { - return 0; - } - - return TRI_FastHashJson(json.get()); - }; - - // hash the constant values that we pass into V8 - uint64_t const hash = hasher(isolate, constantValues); -#endif - v8::Handle args[] = { values, constantValues, v8::Boolean::New(isolate, _numExecutions++ == 0)}; @@ -155,17 +137,6 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, auto func = v8::Local::New(isolate, _func); result = func->Call(func, 3, args); -#ifdef ARANGODB_ENABLE_FAILURE_TESTS - // now that the V8 function call is finished, check that our - // constants actually were not modified - uint64_t cmpHash = hasher(isolate, constantValues); - - if (hash != 0 && cmpHash != 0) { - // only compare if both values are not 0 - TRI_ASSERT(hasher(isolate, constantValues) == hash); - } -#endif - v8g->_query = old; Executor::HandleV8Error(tryCatch, result); @@ -176,31 +147,21 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, } // no exception was thrown if we get here - std::unique_ptr json; + VPackBuilder builder; if (result->IsUndefined()) { // expression does not have any (defined) value. replace with null - json.reset(TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); + builder.add(VPackValue(VPackValueType::Null)); } else { // expression had a result. convert it to JSON - if (_isSimple) { - json.reset(TRI_ObjectToJsonSimple(isolate, result)); - } else { - json.reset(TRI_ObjectToJson(isolate, result)); + int res = TRI_V8ToVPack(isolate, builder, result, false); + + if (res != TRI_ERROR_NO_ERROR) { + THROW_ARANGO_EXCEPTION(res); } + // TODO: what does _isSimple do here? } - if (json.get() == nullptr) { - THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); - } - - auto j = new arangodb::basics::Json(TRI_UNKNOWN_MEM_ZONE, json.get()); - json.release(); - return AqlValue(j); + return AqlValue$(builder); } -// Local Variables: -// mode: outline-minor -// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// -// --SECTION--\\|/// @\\}" -// End diff --git a/arangod/Aql/V8Expression.h b/arangod/Aql/V8Expression.h index dc4e200a77..527400db7a 100644 --- a/arangod/Aql/V8Expression.h +++ b/arangod/Aql/V8Expression.h @@ -27,6 +27,7 @@ #include "Basics/Common.h" #include "Aql/AqlValue.h" #include "Aql/types.h" + #include namespace arangodb { @@ -37,6 +38,7 @@ class Query; struct Variable; struct V8Expression { + ////////////////////////////////////////////////////////////////////////////// /// @brief create the v8 expression ////////////////////////////////////////////////////////////////////////////// @@ -68,10 +70,10 @@ struct V8Expression { /// @brief execute the expression ////////////////////////////////////////////////////////////////////////////// - AqlValue execute(v8::Isolate* isolate, Query* query, - arangodb::AqlTransaction*, AqlItemBlock const*, size_t, - std::vector const&, - std::vector const&); + AqlValue$ execute(v8::Isolate* isolate, Query* query, + arangodb::AqlTransaction*, AqlItemBlock const*, size_t, + std::vector const&, + std::vector const&); ////////////////////////////////////////////////////////////////////////////// /// @brief the isolate used when executing and destroying the expression diff --git a/lib/Basics/VelocyPackHelper.cpp b/lib/Basics/VelocyPackHelper.cpp index 7f0665c0a6..b09020f6f5 100644 --- a/lib/Basics/VelocyPackHelper.cpp +++ b/lib/Basics/VelocyPackHelper.cpp @@ -64,6 +64,13 @@ static VPackBuilder TrueBuilder; static VPackBuilder FalseBuilder; +//////////////////////////////////////////////////////////////////////////////// +/// @brief "constant" global object for empty ARRAYs which can be shared by all +/// expressions but must never be freed +//////////////////////////////////////////////////////////////////////////////// + +static VPackBuilder ArrayBuilder; + // attribute exclude handler for skipping over system attributes struct SystemAttributeExcludeHandler : public VPackAttributeExcludeHandler { @@ -115,9 +122,15 @@ void VelocyPackHelper::initialize() { // initialize exclude handler for system attributes ExcludeHandler.reset(new SystemAttributeExcludeHandler); + // Null value NullBuilder.add(VPackValue(VPackValueType::Null)); + // True value TrueBuilder.add(VPackValue(true)); + // False value FalseBuilder.add(VPackValue(false)); + // Array value (empty) + ArrayBuilder.openArray(); + ArrayBuilder.close(); } arangodb::velocypack::Slice VelocyPackHelper::NullValue() { @@ -131,6 +144,10 @@ arangodb::velocypack::Slice VelocyPackHelper::TrueValue() { arangodb::velocypack::Slice VelocyPackHelper::FalseValue() { return FalseBuilder.slice(); } + +arangodb::velocypack::Slice VelocyPackHelper::ArrayValue() { + return ArrayBuilder.slice(); +} //////////////////////////////////////////////////////////////////////////////// /// @brief return the (global) attribute exclude handler instance diff --git a/lib/Basics/VelocyPackHelper.h b/lib/Basics/VelocyPackHelper.h index 15330908b8..2e9d0276a7 100644 --- a/lib/Basics/VelocyPackHelper.h +++ b/lib/Basics/VelocyPackHelper.h @@ -212,6 +212,7 @@ class VelocyPackHelper { static arangodb::velocypack::Slice NullValue(); static arangodb::velocypack::Slice TrueValue(); static arangodb::velocypack::Slice FalseValue(); + static arangodb::velocypack::Slice ArrayValue(); }; } }