1
0
Fork 0

aqlvalue$

This commit is contained in:
Jan Steemann 2016-03-08 18:52:34 +01:00
parent 21bd087bdc
commit e946729306
13 changed files with 852 additions and 346 deletions

View File

@ -28,12 +28,15 @@
#include "Utils/AqlTransaction.h" #include "Utils/AqlTransaction.h"
#include "Utils/ShapedJsonTransformer.h" #include "Utils/ShapedJsonTransformer.h"
#include "V8/v8-conv.h" #include "V8/v8-conv.h"
#include "V8/v8-vpack.h"
#include "V8Server/v8-shape-conv.h" #include "V8Server/v8-shape-conv.h"
#include "V8Server/v8-wrapshapedjson.h" #include "V8Server/v8-wrapshapedjson.h"
#include "VocBase/document-collection.h" #include "VocBase/document-collection.h"
#include "VocBase/VocShaper.h" #include "VocBase/VocShaper.h"
#include <velocypack/Buffer.h> #include <velocypack/Buffer.h>
#include <velocypack/Iterator.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h> #include <velocypack/velocypack-aliases.h>
using namespace arangodb::aql; using namespace arangodb::aql;
@ -732,74 +735,147 @@ Json AqlValue::toJson(arangodb::AqlTransaction* trx,
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Constructor /// @brief constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$::AqlValue$() { AqlValue$::AqlValue$() {
VPackBuilder builder; invalidate(*this);
memcpy(_data.internal, builder.slice().begin(), builder.slice().byteSize());
_data.internal[15] = AqlValueType::INTERNAL;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Constructor /// @brief constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$::AqlValue$(VPackBuilder const& data) { AqlValue$::AqlValue$(VPackBuilder const& data) {
TRI_ASSERT(data.isClosed()); TRI_ASSERT(data.isClosed());
VPackValueLength l = data.size(); VPackValueLength length = data.size();
if (l < 16) { if (length < 16) {
// Use internal // Use internal
memcpy(_data.internal, data.data(), l); memcpy(_data.internal, data.data(), length);
_data.internal[15] = AqlValueType::INTERNAL; setType(AqlValueType::INTERNAL);
} else { } else {
// Use external // Use external
_data.external = new VPackBuffer<uint8_t>(l); _data.external = new VPackBuffer<uint8_t>(length);
memcpy(_data.external->data(), data.data(), l); memcpy(_data.external->data(), data.data(), length);
_data.internal[15] = AqlValueType::EXTERNAL; setType(AqlValueType::EXTERNAL);
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Constructor /// @brief constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$::AqlValue$(VPackBuilder const* data) : AqlValue$(*data) {} AqlValue$::AqlValue$(VPackBuilder const* data) : AqlValue$(*data) {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Constructor /// @brief constructor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$::AqlValue$(VPackSlice const& data) { AqlValue$::AqlValue$(VPackSlice const& data) {
VPackValueLength l = data.byteSize(); VPackValueLength length = data.byteSize();
if (l < 16) { if (length < 16) {
// Use internal // Use internal
memcpy(_data.internal, data.start(), l); memcpy(_data.internal, data.begin(), length);
_data.internal[15] = AqlValueType::INTERNAL; setType(AqlValueType::INTERNAL);
} else { } else {
// Use external // Use external
_data.external = new VPackBuffer<uint8_t>(l); _data.external = new VPackBuffer<uint8_t>(length);
memcpy(_data.external->data(), data.start(), l); memcpy(_data.external->data(), data.begin(), length);
_data.internal[15] = AqlValueType::EXTERNAL; 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<uint8_t>(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) { AqlValue$::AqlValue$(AqlValue$ const& other) {
VPackSlice s = other.slice(); copy(other);
VPackValueLength length = s.byteSize(); }
if (other.type()) {
// Isse external ////////////////////////////////////////////////////////////////////////////////
_data.external = new VPackBuffer<uint8_t>(length); /// @brief copy
memcpy(_data.external->data(), other._data.external->data(), length); ////////////////////////////////////////////////////////////////////////////////
_data.internal[15] = AqlValueType::EXTERNAL;
} else { AqlValue$& AqlValue$::operator=(AqlValue$ const& other) {
memcpy(_data.internal, other._data.internal, length); if (this != &other) {
_data.internal[15] = AqlValueType::INTERNAL; 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 // 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: { case AqlValue::RANGE: {
// TODO Has to be replaced by VPackCustom Type // TODO Has to be replaced by VPackCustom Type
TRI_ASSERT(other._range != nullptr); TRI_ASSERT(other._range != nullptr);
try { Range* range = new Range(other._range->_low, other._range->_high);
VPackArrayBuilder b(&builder);
size_t const n = other._range->size(); _data.internal[0] = 0xf4; // custom type for range
for (size_t i = 0; i < n; ++i) { _data.internal[1] = sizeof(Range*);
builder.add(VPackValue(other._range->at(i))); memcpy(&_data.internal[2], &range, sizeof(Range*));
} _data.internal[15] = AqlValueType::RANGE;
} catch (...) {
}
break; break;
} }
case AqlValue::EMPTY: { case AqlValue::EMPTY: {
@ -873,18 +947,181 @@ AqlValue$::AqlValue$(AqlValue const& other, arangodb::AqlTransaction* trx,
// Small enough for local // Small enough for local
// copy memory from the builder into the internal data. // copy memory from the builder into the internal data.
memcpy(_data.internal, builder.data(), length); memcpy(_data.internal, builder.data(), length);
_data.internal[15] = AqlValueType::INTERNAL; setType(AqlValueType::INTERNAL);
} else { } else {
// We need a large external buffer // We need a large external buffer
// TODO: Replace by SlimBuffer // TODO: Replace by SlimBuffer
_data.external = new VPackBuffer<uint8_t>(length); _data.external = new VPackBuffer<uint8_t>(length);
memcpy(_data.external->data(), builder.data(), length); memcpy(_data.external->data(), builder.data(), length);
_data.internal[15] = AqlValueType::EXTERNAL; setType(AqlValueType::EXTERNAL);
} }
} }
AqlValue$::AqlValueType AqlValue$::type() const { //////////////////////////////////////////////////////////////////////////////
return static_cast<AqlValueType>(_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<int64_t>(n) + position;
}
if (position >= 0 && position < static_cast<int64_t>(n)) {
// only look up the value if it is within array bounds
VPackBuilder builder;
builder.add(VPackValue(r->at(static_cast<size_t>(position))));
return AqlValue$(builder);
}
// fall-through intentional
}
else {
VPackSlice const s = slice();
if (s.isArray()) {
size_t const n = static_cast<size_t>(s.length());
if (position < 0) {
// a negative position is allowed
position = static_cast<int64_t>(n) + position;
}
if (position >= 0 && position < static_cast<int64_t>(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<char const*> 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);
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -892,32 +1129,36 @@ AqlValue$::AqlValueType AqlValue$::type() const {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
double AqlValue$::toDouble() const { double AqlValue$::toDouble() const {
if (slice().isCustom()) { if (type() == RANGE) {
#warning FIX custom // special handling for ranges
// range. TODO Range* r = range();
//size_t rangeSize = _range->size(); size_t rangeSize = r->size();
//if (rangeSize == 1) {
// return _range->at(0); if (rangeSize == 1) {
//} return static_cast<double>(r->at(0));
}
return 0.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()) { if (s.isNumber()) {
return slice().getNumber<double>(); return s.getNumber<double>();
} }
if (slice().isString()) { if (s.isString()) {
try { try {
return std::stod(slice().copyString()); return std::stod(s.copyString());
} catch (...) { } catch (...) {
// conversion failed // conversion failed
} }
return 0.0; return 0.0;
} }
if (slice().isArray()) { if (s.isArray()) {
if (slice().length() == 1) { if (s.length() == 1) {
AqlValue$ tmp(slice().at(0)); AqlValue$ tmp(s.at(0));
return tmp.toDouble(); return tmp.toDouble();
} }
return 0.0; return 0.0;
@ -931,32 +1172,36 @@ double AqlValue$::toDouble() const {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
int64_t AqlValue$::toInt64() const { int64_t AqlValue$::toInt64() const {
if (slice().isCustom()) { if (type() == RANGE) {
#warning FIX custom // special handling for ranges
// range. TODO Range* r = range();
//size_t rangeSize = _range->size(); size_t rangeSize = r->size();
//if (rangeSize == 1) {
// return _range->at(0); if (rangeSize == 1) {
//} return static_cast<int64_t>(r->at(0));
return 0;
} }
if (slice().isBoolean()) { return 0.0;
return slice().getBoolean() ? 1 : 0;
} }
if (slice().isNumber()) {
return slice().getNumber<int64_t>(); VPackSlice const s = slice();
if (s.isBoolean()) {
return s.getBoolean() ? 1 : 0;
} }
if (slice().isString()) { if (s.isNumber()) {
return s.getNumber<int64_t>();
}
if (s.isString()) {
try { try {
return static_cast<int64_t>(std::stoll(slice().copyString())); return static_cast<int64_t>(std::stoll(s.copyString()));
} catch (...) { } catch (...) {
// conversion failed // conversion failed
} }
return 0; return 0;
} }
if (slice().isArray()) { if (s.isArray()) {
if (slice().length() == 1) { if (s.length() == 1) {
AqlValue$ tmp(slice().at(0)); AqlValue$ tmp(s.at(0));
return tmp.toInt64(); return tmp.toInt64();
} }
return 0; return 0;
@ -965,36 +1210,235 @@ int64_t AqlValue$::toInt64() const {
return 0; return 0;
} }
//////////////////////////////////////////////////////////////////////////////
/// @brief whether or not the contained value evaluates to true
//////////////////////////////////////////////////////////////////////////////
bool AqlValue$::isTrue() const { bool AqlValue$::isTrue() const {
if (slice().isBoolean() && slice().getBoolean()) { if (type() == RANGE) {
return true; return true;
} }
if (slice().isNumber() && slice().getNumber<double>() != 0.0) { VPackSlice const s = slice();
return true;
if (s.isBoolean()) {
return s.getBoolean();
} }
if (slice().isString() && !slice().copyString().empty()) { if (s.isNumber()) {
return true; return (s.getNumber<double>() != 0.0);
} }
if (slice().isArray() || slice().isObject()) { if (s.isString()) {
return true; VPackValueLength length;
s.getString(length);
return length > 0;
} }
if (slice().isCustom()) { if (s.isArray() || s.isObject()) {
// range
return true; return true;
} }
// all other cases, include Null and None
return false; return false;
} }
VPackSlice AqlValue$::slice() const { ////////////////////////////////////////////////////////////////////////////////
if (type()) { /// @brief construct a V8 value as input for the expression execution in V8
// Use External /// only construct those attributes that are needed in the expression
return VPackSlice(_data.external->data()); ////////////////////////////////////////////////////////////////////////////////
} else {
return VPackSlice(_data.internal); v8::Handle<v8::Value> AqlValue$::toV8Partial(
v8::Isolate* isolate, arangodb::AqlTransaction* trx,
std::unordered_set<std::string> const& attributes) const {
VPackSlice const s = slice();
if (isObject()) {
v8::Handle<v8::Object> 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<v8::Value> AqlValue$::toV8(
v8::Isolate* isolate, arangodb::AqlTransaction* trx) const {
if (type() == RANGE) {
Range* r = range();
size_t const n = r->size();
v8::Handle<v8::Array> result =
v8::Array::New(isolate, static_cast<int>(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<double>(r->at(static_cast<size_t>(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<uint8_t>(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, void AqlValue::toVelocyPack(arangodb::AqlTransaction* trx,
TRI_document_collection_t const* document, TRI_document_collection_t const* document,
VPackBuilder& builder) const { VPackBuilder& builder) const {

View File

@ -365,69 +365,125 @@ struct AqlValue {
AqlValueType _type; AqlValueType _type;
}; };
// do not add virtual methods!
struct AqlValue$ { struct AqlValue$ {
public: public:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief AqlValueType, indicates what sort of value we have /// @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 /// @brief Holds the actual data for this AqlValue
/// semantics: /// it has the following semantics:
/// ///
/// All values with a size less than 16 will be stored directly in this /// All values with a size less than 16 will be stored directly in this
/// AqlValue using the data.internal structure. /// AqlValue using the data.internal structure.
/// All values of a larger size will be store in data.external. /// All values of a larger size will be stored in _data.external.
/// The last bit of this union will be used to identify if we have to use /// The last byte of this union will be used to identify if we have to use
/// internal or external. /// internal or external.
/// Delete of the Buffer should free every structure that is not using the /// Delete of the Buffer should free every structure that is not using the
/// VPack external value type. /// VPack external or reference value type.
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
private: private:
union { union {
char internal[16]; char internal[16];
arangodb::velocypack::Buffer<uint8_t>* external; arangodb::velocypack::Buffer<uint8_t>* external;
uint8_t const* reference;
} _data; } _data;
public: public:
AqlValue$();
explicit AqlValue$(arangodb::velocypack::Builder const&); explicit AqlValue$(arangodb::velocypack::Builder const&);
explicit AqlValue$(arangodb::velocypack::Builder const*); explicit AqlValue$(arangodb::velocypack::Builder const*);
explicit AqlValue$(arangodb::velocypack::Slice const&); explicit AqlValue$(arangodb::velocypack::Slice const&);
AqlValue$(arangodb::velocypack::Slice const&, AqlValueType);
AqlValue$(); AqlValue$(int64_t, int64_t); // range
explicit AqlValue$(bool); // boolean type
AqlValue$(AqlValue const&, arangodb::AqlTransaction*, AqlValue$(AqlValue const&, arangodb::AqlTransaction*,
TRI_document_collection_t const*); TRI_document_collection_t const*);
~AqlValue$() { ~AqlValue$();
if (type() && _data.external != nullptr) {
delete _data.external;
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Copy Constructor /// @brief copy
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue$(AqlValue$ const& other); 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 /// @brief Returns the type of this value. If true it uses an external pointer
/// if false it uses the internal data structure /// if false it uses the internal data structure
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Read last bit of the union AqlValueType type() const {
AqlValueType type() const; return static_cast<AqlValueType>(_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; 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<char const*> const&) const;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief get the numeric value of an AqlValue /// @brief get the numeric value of an AqlValue
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -436,10 +492,26 @@ struct AqlValue$ {
int64_t toInt64() const; 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 isTrue() const;
bool isFalse() const { return !isTrue(); }
//////////////////////////////////////////////////////////////////////////////
/// @brief construct a V8 value as input for the expression execution in V8
//////////////////////////////////////////////////////////////////////////////
v8::Handle<v8::Value> 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<v8::Value> toV8Partial(v8::Isolate* isolate,
arangodb::AqlTransaction*,
std::unordered_set<std::string> const&) const;
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief compare function for two values /// @brief compare function for two values
@ -450,10 +522,66 @@ struct AqlValue$ {
// TODO: implement // TODO: implement
return 0; 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"); 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::aql
} // closes namespace arangodb } // closes namespace arangodb

View File

@ -22,14 +22,11 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "AttributeAccessor.h" #include "AttributeAccessor.h"
#include "Basics/JsonHelper.h"
#include "Aql/AqlItemBlock.h" #include "Aql/AqlItemBlock.h"
#include "Aql/Variable.h" #include "Aql/Variable.h"
#include "Basics/VelocyPackHelper.h"
#include "Utils/AqlTransaction.h" #include "Utils/AqlTransaction.h"
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
using namespace arangodb::aql; using namespace arangodb::aql;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -44,17 +41,11 @@ AttributeAccessor::AttributeAccessor(
TRI_ASSERT(_variable != nullptr); TRI_ASSERT(_variable != nullptr);
} }
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the accessor
////////////////////////////////////////////////////////////////////////////////
AttributeAccessor::~AttributeAccessor() {}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief execute the accessor /// @brief execute the accessor
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue AttributeAccessor::get(arangodb::AqlTransaction* trx, AqlValue$ AttributeAccessor::get(arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos, AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars, std::vector<Variable const*> const& vars,
std::vector<RegisterId> const& regs) { std::vector<RegisterId> const& regs) {
@ -62,18 +53,11 @@ AqlValue AttributeAccessor::get(arangodb::AqlTransaction* trx,
for (auto it = vars.begin(); it != vars.end(); ++it, ++i) { for (auto it = vars.begin(); it != vars.end(); ++it, ++i) {
if ((*it)->id == _variable->id) { if ((*it)->id == _variable->id) {
// get the AQL value // get the AQL value
auto& result = argv->getValueReference(startPos, regs[i]); return AqlValue$(argv->getValueReference(startPos, regs[i]), trx, nullptr).get(_attributeParts);
// 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));
} }
// fall-through intentional // fall-through intentional
} }
return AqlValue(new arangodb::basics::Json(arangodb::basics::Json::Null)); return AqlValueReference(arangodb::basics::VelocyPackHelper::NullValue());
} }

View File

@ -42,18 +42,18 @@ struct Variable;
class AttributeAccessor { class AttributeAccessor {
public: public:
AttributeAccessor(std::vector<char const*> const&, Variable const*); AttributeAccessor(std::vector<char const*> const&, Variable const*);
~AttributeAccessor() = default;
~AttributeAccessor();
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief execute the accessor /// @brief execute the accessor
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
AqlValue get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, AqlValue$ get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&, std::vector<Variable const*> const&,
std::vector<RegisterId> const&); std::vector<RegisterId> const&);
private: private:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief the attribute names vector (e.g. [ "a", "b", "c" ] for a.b.c) /// @brief the attribute names vector (e.g. [ "a", "b", "c" ] for a.b.c)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////

View File

@ -134,7 +134,7 @@ void CalculationBlock::executeExpression(AqlItemBlock* result) {
} }
// execute the expression // execute the expression
AqlValue a = _expression->execute(_trx, result, i, _inVars, _inRegs); AqlValue a = AqlValue(_expression->execute(_trx, result, i, _inVars, _inRegs));
try { try {
TRI_IF_FAILURE("CalculationBlock::executeExpression") { TRI_IF_FAILURE("CalculationBlock::executeExpression") {

View File

@ -138,7 +138,7 @@ void Expression::variables(std::unordered_set<Variable const*>& result) const {
/// @brief execute the expression /// @brief execute the expression
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue Expression::execute(arangodb::AqlTransaction* trx, AqlValue$ Expression::execute(arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos, AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars, std::vector<Variable const*> const& vars,
std::vector<RegisterId> const& regs) { std::vector<RegisterId> const& regs) {
@ -152,13 +152,16 @@ AqlValue Expression::execute(arangodb::AqlTransaction* trx,
// and execute // and execute
switch (_type) { switch (_type) {
case JSON: { case JSON: {
// TODO
TRI_ASSERT(_data != nullptr); 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: { case SIMPLE: {
return AqlValue(executeSimpleExpression(_node, trx, argv, startPos, return executeSimpleExpression(_node, trx, argv, startPos,
vars, regs, true)); vars, regs, true);
} }
case ATTRIBUTE: { case ATTRIBUTE: {
@ -268,9 +271,9 @@ void Expression::invalidate() {
bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right, bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right,
arangodb::AqlTransaction* trx, arangodb::AqlTransaction* trx,
AstNode const* node) const { 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 && if (n > 3 &&
(node->getMember(1)->isSorted() || (node->getMember(1)->isSorted() ||
@ -283,10 +286,8 @@ bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right,
while (true) { while (true) {
// determine midpoint // determine midpoint
size_t m = l + ((r - l) / 2); 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) { if (compareResult == 0) {
// item found in the list // item found in the list
@ -306,11 +307,11 @@ bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right,
return false; return false;
} }
} }
} else { }
// use linear search // use linear search
for (auto const& it : VPackArrayIterator(right.slice())) { for (size_t i = 0; i < n; ++i) {
// do not copy the list element we're looking at int compareResult = AqlValue$::Compare(trx, left, AqlValue$(right.at(i).slice()), false);
int compareResult = AqlValue$::Compare(trx, left, AqlValue$(it), false);
if (compareResult == 0) { if (compareResult == 0) {
// item found in the list // item found in the list
@ -319,7 +320,6 @@ bool Expression::findInArray(AqlValue$ const& left, AqlValue$ const& right,
} }
return false; return false;
}
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -571,14 +571,7 @@ AqlValue$ Expression::executeSimpleExpressionAttributeAccess(
AqlValue$ result = executeSimpleExpression(member, trx, argv, AqlValue$ result = executeSimpleExpression(member, trx, argv,
startPos, vars, regs, false); startPos, vars, regs, false);
VPackSlice slice = result.slice(); return result.get(name);
if (slice.isObject()) {
VPackSlice value = slice.get(name);
if (!value.isNone()) {
return AqlValue$(value);
}
}
return AqlValue$(VelocyPackHelper::NullValue());
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -608,12 +601,12 @@ AqlValue$ Expression::executeSimpleExpressionIndexedAccess(
AqlValue$ result = executeSimpleExpression(member, trx, argv, AqlValue$ result = executeSimpleExpression(member, trx, argv,
startPos, vars, regs, false); startPos, vars, regs, false);
if (result.slice().isArray()) { if (result.isArray()) {
AqlValue$ indexResult = executeSimpleExpression( AqlValue$ indexResult = executeSimpleExpression(
index, trx, argv, startPos, vars, regs, false); index, trx, argv, startPos, vars, regs, false);
if (indexResult.slice().isNumber()) { if (indexResult.isNumber()) {
return AqlValue$(result.slice().at(indexResult.toInt64())); return result.at(indexResult.toInt64());
} }
if (indexResult.slice().isString()) { if (indexResult.slice().isString()) {
@ -622,33 +615,25 @@ AqlValue$ Expression::executeSimpleExpressionIndexedAccess(
try { try {
// stoll() might throw an exception if the string is not a number // stoll() might throw an exception if the string is not a number
int64_t position = static_cast<int64_t>(std::stoll(value)); int64_t position = static_cast<int64_t>(std::stoll(value));
return AqlValue$(result.slice().at(position)); return result.at(position);
} catch (...) { } catch (...) {
// no number found. // no number found.
} }
} }
// fall-through to returning null // fall-through to returning null
} else if (result.slice().isObject()) { } else if (result.isObject()) {
AqlValue$ indexResult = executeSimpleExpression( AqlValue$ indexResult = executeSimpleExpression(
index, trx, argv, startPos, vars, regs, false); index, trx, argv, startPos, vars, regs, false);
if (indexResult.slice().isNumber()) { if (indexResult.isNumber()) {
std::string const indexString = std::to_string(indexResult.slice().getNumber<int64_t>()); std::string const indexString = std::to_string(indexResult.toInt64());
VPackSlice value = result.slice().get(indexString); return result.get(indexString);
if (!value.isNone()) {
return AqlValue$(value);
}
return AqlValue$(VelocyPackHelper::NullValue());
} }
if (indexResult.slice().isString()) { if (indexResult.slice().isString()) {
std::string const indexString = indexResult.slice().copyString(); std::string const indexString = indexResult.slice().copyString();
VPackSlice value = result.slice().get(indexString); return result.get(indexString);
if (!value.isNone()) {
return AqlValue$(value);
}
return AqlValue$(VelocyPackHelper::NullValue());
} }
// fall-through to returning null // fall-through to returning null
} }
@ -800,16 +785,7 @@ AqlValue$ Expression::executeSimpleExpressionRange(
AqlValue$ resultHigh = executeSimpleExpression( AqlValue$ resultHigh = executeSimpleExpression(
high, trx, argv, startPos, vars, regs, false); high, trx, argv, startPos, vars, regs, false);
// build a custom type for the range return AqlValue$(resultLow.toInt64(), resultHigh.toInt64());
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);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -829,14 +805,12 @@ AqlValue$ Expression::executeSimpleExpressionFCall(
auto member = node->getMemberUnchecked(0); auto member = node->getMemberUnchecked(0);
TRI_ASSERT(member->type == NODE_TYPE_ARRAY); TRI_ASSERT(member->type == NODE_TYPE_ARRAY);
VPackBuilder builder;
size_t const n = member->numMembers(); size_t const n = member->numMembers();
VPackFunctionParameters parameters; VPackFunctionParameters parameters;
parameters.reserve(n); parameters.reserve(n);
#warning Can we get access to query here?
// std::shared_ptr<VPackBuilder> builder = query->getSharedBuilder();
VPackBuilder builder;
#warning Check if this is correct w.r.t. Memory Management
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
auto arg = member->getMemberUnchecked(i); auto arg = member->getMemberUnchecked(i);
@ -867,7 +841,7 @@ AqlValue$ Expression::executeSimpleExpressionNot(
executeSimpleExpression(node->getMember(0), trx, argv, executeSimpleExpression(node->getMember(0), trx, argv,
startPos, vars, regs, false); 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 || if (node->type == NODE_TYPE_OPERATOR_BINARY_IN ||
node->type == NODE_TYPE_OPERATOR_BINARY_NIN) { node->type == NODE_TYPE_OPERATOR_BINARY_NIN) {
// IN and NOT IN // IN and NOT IN
if (!right.slice().isArray()) { if (!right.isArray()) {
// right operand must be a list, otherwise we return false // right operand must be a list, otherwise we return false
// do not throw, but return "false" instead // do not throw, but return "false" instead
return AqlValue$(VelocyPackHelper::FalseValue()); return AqlValue$(false);
} }
bool result = bool result =
@ -940,7 +914,7 @@ AqlValue$ Expression::executeSimpleExpressionComparison(
result = !result; result = !result;
} }
return AqlValue$(result ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(result);
} }
// all other comparison operators... // all other comparison operators...
@ -952,17 +926,17 @@ AqlValue$ Expression::executeSimpleExpressionComparison(
int compareResult = AqlValue$::Compare(trx, left, right, compareUtf8); int compareResult = AqlValue$::Compare(trx, left, right, compareUtf8);
switch (node->type) { switch (node->type) {
case NODE_TYPE_OPERATOR_BINARY_EQ: case NODE_TYPE_OPERATOR_BINARY_EQ:
return AqlValue$((compareResult == 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult == 0);
case NODE_TYPE_OPERATOR_BINARY_NE: case NODE_TYPE_OPERATOR_BINARY_NE:
return AqlValue$((compareResult != 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult != 0);
case NODE_TYPE_OPERATOR_BINARY_LT: case NODE_TYPE_OPERATOR_BINARY_LT:
return AqlValue$((compareResult < 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult < 0);
case NODE_TYPE_OPERATOR_BINARY_LE: case NODE_TYPE_OPERATOR_BINARY_LE:
return AqlValue$((compareResult <= 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult <= 0);
case NODE_TYPE_OPERATOR_BINARY_GT: case NODE_TYPE_OPERATOR_BINARY_GT:
return AqlValue$((compareResult > 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult > 0);
case NODE_TYPE_OPERATOR_BINARY_GE: case NODE_TYPE_OPERATOR_BINARY_GE:
return AqlValue$((compareResult >= 0) ? VelocyPackHelper::TrueValue() : VelocyPackHelper::FalseValue()); return AqlValue$(compareResult >= 0);
default: default:
std::string msg("unhandled type '"); std::string msg("unhandled type '");
msg.append(node->getTypeString()); msg.append(node->getTypeString());
@ -987,23 +961,23 @@ AqlValue$ Expression::executeSimpleExpressionArrayComparison(
executeSimpleExpression(node->getMember(1), trx, argv, executeSimpleExpression(node->getMember(1), trx, argv,
startPos, vars, regs, false); startPos, vars, regs, false);
if (!left.slice().isArray()) { if (!left.isArray()) {
// left operand must be an array // left operand must be an array
// do not throw, but return "false" instead // do not throw, but return "false" instead
return AqlValue$(VelocyPackHelper::FalseValue()); return AqlValue$(false);
} }
if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN || if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN ||
node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) {
// IN and NOT IN // IN and NOT IN
if (!right.slice().isArray()) { if (!right.isArray()) {
// right operand must be a list, otherwise we return false // right operand must be a list, otherwise we return false
// do not throw, but return "false" instead // 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<size_t, size_t> requiredMatches = Quantifier::RequiredMatches(n, node->getMember(2)); std::pair<size_t, size_t> requiredMatches = Quantifier::RequiredMatches(n, node->getMember(2));
TRI_ASSERT(requiredMatches.first <= requiredMatches.second); TRI_ASSERT(requiredMatches.first <= requiredMatches.second);
@ -1016,15 +990,14 @@ AqlValue$ Expression::executeSimpleExpressionArrayComparison(
size_t matches = 0; size_t matches = 0;
size_t numLeft = n; size_t numLeft = n;
for (auto const& it : VPackArrayIterator(left.slice())) { for (size_t i = 0; i < n; ++i) {
AqlValue$ leftItemValue(it); AqlValue$ leftItemValue(left.at(i));
bool result; bool result;
// IN and NOT IN // IN and NOT IN
if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN || if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_IN ||
node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) {
result = result = findInArray(leftItemValue, right, trx, node);
findInArray(leftItemValue, right, trx, node);
if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) { if (node->type == NODE_TYPE_OPERATOR_BINARY_ARRAY_NIN) {
// revert the result in case of a NOT IN // 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, executeSimpleExpression(node->getMember(0), trx, argv,
startPos, vars, regs, false); startPos, vars, regs, false);
size_t position;
if (condition.isTrue()) { if (condition.isTrue()) {
// return true part // return true part
return executeSimpleExpression(node->getMember(1), trx, argv, position = 1;
startPos, vars, regs, true); }
else {
// return false part
position = 2;
} }
// return false part return executeSimpleExpression(node->getMember(position), trx, argv,
return executeSimpleExpression(node->getMember(2), trx, argv,
startPos, vars, regs, true); startPos, vars, regs, true);
} }
@ -1141,10 +1117,7 @@ AqlValue$ Expression::executeSimpleExpressionExpansion(
if (offset < 0 || count <= 0) { if (offset < 0 || count <= 0) {
// no items to return... can already stop here // no items to return... can already stop here
VPackBuilder builder; return AqlValueReference(VelocyPackHelper::ArrayValue());
builder.openArray();
builder.close();
return AqlValue$(builder);
} }
// FILTER // FILTER
@ -1158,10 +1131,7 @@ AqlValue$ Expression::executeSimpleExpressionExpansion(
filterNode = nullptr; filterNode = nullptr;
} else { } else {
// filter expression is always false // filter expression is always false
VPackBuilder builder; return AqlValueReference(VelocyPackHelper::ArrayValue());
builder.openArray();
builder.close();
return AqlValue$(builder);
} }
} }
@ -1176,33 +1146,34 @@ AqlValue$ Expression::executeSimpleExpressionExpansion(
value = executeSimpleExpression(node->getMember(0), trx, value = executeSimpleExpression(node->getMember(0), trx,
argv, startPos, vars, regs, false); argv, startPos, vars, regs, false);
if (!value.isArray()) {
return AqlValueReference(VelocyPackHelper::ArrayValue());
}
VPackBuilder builder; VPackBuilder builder;
builder.openArray(); builder.openArray();
if (!value.slice().isArray()) {
builder.close();
return AqlValue$(builder);
}
// generate a new temporary for the flattened array // generate a new temporary for the flattened array
std::function<void(VPackSlice const&, int64_t)> flatten = std::function<void(AqlValue$ const&, int64_t)> flatten =
[&](VPackSlice const& json, int64_t level) { [&](AqlValue$ const& v, int64_t level) {
if (!json.isArray()) { if (!v.isArray()) {
return; return;
} }
for (auto const& it : VPackArrayIterator(json)) { size_t const n = v.length();
bool const isArray = it.isArray(); for (size_t i = 0; i < n; ++i) {
AqlValue$ item(v.at(i));
bool const isArray = item.isArray();
if (!isArray || level == levels) { if (!isArray || level == levels) {
builder.add(it); builder.add(item.slice());
} else if (isArray && level < levels) { } else if (isArray && level < levels) {
flatten(it, level + 1); flatten(item, level + 1);
} }
} }
}; };
flatten(value.slice(), 1); flatten(value, 1);
builder.close(); builder.close();
value = AqlValue$(builder); value = AqlValue$(builder);
@ -1210,11 +1181,8 @@ AqlValue$ Expression::executeSimpleExpressionExpansion(
value = executeSimpleExpression(node->getMember(0), trx, value = executeSimpleExpression(node->getMember(0), trx,
argv, startPos, vars, regs, false); argv, startPos, vars, regs, false);
if (!value.slice().isArray()) { if (!value.isArray()) {
VPackBuilder builder; return AqlValueReference(VelocyPackHelper::ArrayValue());
builder.openArray();
builder.close();
return AqlValue$(builder);
} }
} }
@ -1230,8 +1198,10 @@ AqlValue$ Expression::executeSimpleExpressionExpansion(
VPackBuilder builder; VPackBuilder builder;
builder.openArray(); builder.openArray();
for (auto const& it : VPackArrayIterator(value.slice())) { size_t const n = value.length();
setVariable(variable, it); for (size_t i = 0; i < n; ++i) {
AqlValue$ item(value.at(i));
setVariable(variable, item.slice());
bool takeItem = true; bool takeItem = true;
@ -1299,19 +1269,19 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic(
AqlValue$ lhs = executeSimpleExpression(node->getMember(0), AqlValue$ lhs = executeSimpleExpression(node->getMember(0),
trx, argv, startPos, vars, regs, true); trx, argv, startPos, vars, regs, true);
if (lhs.slice().isObject()) { if (lhs.isObject()) {
return AqlValue$(VelocyPackHelper::NullValue()); return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
} }
AqlValue$ rhs = executeSimpleExpression(node->getMember(1), AqlValue$ rhs = executeSimpleExpression(node->getMember(1),
trx, argv, startPos, vars, regs, true); trx, argv, startPos, vars, regs, true);
if (rhs.slice().isObject()) { if (rhs.isObject()) {
return AqlValue$(VelocyPackHelper::NullValue()); return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
} }
double const l = lhs.slice().getNumber<double>(); double const l = lhs.toDouble();
double const r = rhs.slice().getNumber<double>(); double const r = rhs.toDouble();
VPackBuilder builder; VPackBuilder builder;
@ -1328,17 +1298,17 @@ AqlValue$ Expression::executeSimpleExpressionArithmetic(
case NODE_TYPE_OPERATOR_BINARY_DIV: case NODE_TYPE_OPERATOR_BINARY_DIV:
if (r == 0.0) { if (r == 0.0) {
RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO);
return AqlValue$(VelocyPackHelper::NullValue()); return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
} }
return AqlValue$(builder); return AqlValue$(builder);
case NODE_TYPE_OPERATOR_BINARY_MOD: case NODE_TYPE_OPERATOR_BINARY_MOD:
if (r == 0.0) { if (r == 0.0) {
RegisterWarning(_ast, "/", TRI_ERROR_QUERY_DIVISION_BY_ZERO); 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))); builder.add(VPackValue(fmod(l, r)));
return AqlValue$(builder); return AqlValue$(builder);
default: default:
return AqlValue$(VelocyPackHelper::NullValue()); return AqlValue$(VelocyPackHelper::NullValue(), AqlValue$::AqlValueType::REFERENCE_STICKY);
} }
} }

View File

@ -47,7 +47,6 @@ class StringBuffer;
namespace aql { namespace aql {
class AqlItemBlock; class AqlItemBlock;
struct AqlValue;
struct AqlValue$; struct AqlValue$;
class Ast; class Ast;
class AttributeAccessor; class AttributeAccessor;
@ -169,7 +168,7 @@ class Expression {
/// @brief execute the expression /// @brief execute the expression
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
AqlValue execute(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t, AqlValue$ execute(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&, std::vector<Variable const*> const&,
std::vector<RegisterId> const&); std::vector<RegisterId> const&);

View File

@ -106,8 +106,8 @@ void IndexBlock::executeExpressions() {
auto& toReplace = _nonConstExpressions[posInExpressions]; auto& toReplace = _nonConstExpressions[posInExpressions];
auto exp = toReplace->expression; auto exp = toReplace->expression;
TRI_document_collection_t const* myCollection = nullptr; TRI_document_collection_t const* myCollection = nullptr;
AqlValue a = exp->execute(_trx, cur, _pos, _inVars[posInExpressions], AqlValue a = AqlValue(exp->execute(_trx, cur, _pos, _inVars[posInExpressions],
_inRegs[posInExpressions]); _inRegs[posInExpressions]));
auto jsonified = a.toJson(_trx, myCollection, true); auto jsonified = a.toJson(_trx, myCollection, true);
a.destroy(); a.destroy();
AstNode* evaluatedNode = ast->nodeFromJson(jsonified.json(), true); AstNode* evaluatedNode = ast->nodeFromJson(jsonified.json(), true);

View File

@ -183,8 +183,8 @@ void TraversalBlock::executeExpressions() {
if (it != nullptr && it->expression != nullptr) { if (it != nullptr && it->expression != nullptr) {
// inVars and inRegs needs fixx // inVars and inRegs needs fixx
TRI_document_collection_t const* myCollection = nullptr; TRI_document_collection_t const* myCollection = nullptr;
AqlValue a = it->expression->execute(_trx, cur, _pos, _inVars[i], AqlValue a = AqlValue(it->expression->execute(_trx, cur, _pos, _inVars[i],
_inRegs[i]); _inRegs[i]));
it->compareTo.reset(new Json(a.toJson(_trx, myCollection, true))); it->compareTo.reset(new Json(a.toJson(_trx, myCollection, true)));
a.destroy(); a.destroy();
} }

View File

@ -21,7 +21,7 @@
/// @author Jan Steemann /// @author Jan Steemann
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include "Aql/V8Expression.h" #include "V8Expression.h"
#include "Aql/AqlItemBlock.h" #include "Aql/AqlItemBlock.h"
#include "Aql/Executor.h" #include "Aql/Executor.h"
#include "Aql/Query.h" #include "Aql/Query.h"
@ -30,7 +30,7 @@
#include "Basics/json-utilities.h" #include "Basics/json-utilities.h"
#include "V8/v8-conv.h" #include "V8/v8-conv.h"
#include "V8/v8-utils.h" #include "V8/v8-utils.h"
#include "V8Server/v8-shape-conv.h" #include "V8/v8-vpack.h"
using namespace arangodb::aql; using namespace arangodb::aql;
@ -62,7 +62,7 @@ V8Expression::~V8Expression() {
/// @brief execute the expression /// @brief execute the expression
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query, AqlValue$ V8Expression::execute(v8::Isolate* isolate, Query* query,
arangodb::AqlTransaction* trx, arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos, AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars, std::vector<Variable const*> const& vars,
@ -75,18 +75,17 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
v8::Handle<v8::Object> values = v8::Object::New(isolate); v8::Handle<v8::Object> values = v8::Object::New(isolate);
for (size_t i = 0; i < n; ++i) { 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; continue;
} }
auto document = argv->getDocumentCollection(reg); std::string const& varname = vars[i]->name;
auto 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 // check if we can get away with constructing a partial JSON object
auto it = _attributeRestrictions.find(vars[i]); auto it = _attributeRestrictions.find(vars[i]);
@ -94,7 +93,7 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
// build a partial object // build a partial object
values->ForceSet( values->ForceSet(
TRI_V8_STD_STRING(varname), TRI_V8_STD_STRING(varname),
value.toV8Partial(isolate, trx, (*it).second, document)); value.toV8Partial(isolate, trx, (*it).second));
continue; continue;
} }
} }
@ -103,7 +102,7 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
// build the regular object // build the regular object
values->ForceSet(TRI_V8_STD_STRING(varname), values->ForceSet(TRI_V8_STD_STRING(varname),
value.toV8(isolate, trx, document)); value.toV8(isolate, trx));
} }
TRI_ASSERT(query != nullptr); TRI_ASSERT(query != nullptr);
@ -128,23 +127,6 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
// won't modify their arguments is unsafe // won't modify their arguments is unsafe
auto constantValues = v8::Local<v8::Object>::New(isolate, _constantValues); auto constantValues = v8::Local<v8::Object>::New(isolate, _constantValues);
#ifdef ARANGODB_ENABLE_FAILURE_TESTS
// a hash function for hashing V8 object contents
auto hasher =
[](v8::Isolate* isolate, v8::Handle<v8::Value> const obj) -> uint64_t {
std::unique_ptr<TRI_json_t> 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<v8::Value> args[] = { v8::Handle<v8::Value> args[] = {
values, constantValues, values, constantValues,
v8::Boolean::New(isolate, _numExecutions++ == 0)}; v8::Boolean::New(isolate, _numExecutions++ == 0)};
@ -155,17 +137,6 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
auto func = v8::Local<v8::Function>::New(isolate, _func); auto func = v8::Local<v8::Function>::New(isolate, _func);
result = func->Call(func, 3, args); 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; v8g->_query = old;
Executor::HandleV8Error(tryCatch, result); Executor::HandleV8Error(tryCatch, result);
@ -176,31 +147,21 @@ AqlValue V8Expression::execute(v8::Isolate* isolate, Query* query,
} }
// no exception was thrown if we get here // no exception was thrown if we get here
std::unique_ptr<TRI_json_t> json; VPackBuilder builder;
if (result->IsUndefined()) { if (result->IsUndefined()) {
// expression does not have any (defined) value. replace with null // expression does not have any (defined) value. replace with null
json.reset(TRI_CreateNullJson(TRI_UNKNOWN_MEM_ZONE)); builder.add(VPackValue(VPackValueType::Null));
} else { } else {
// expression had a result. convert it to JSON // expression had a result. convert it to JSON
if (_isSimple) { int res = TRI_V8ToVPack(isolate, builder, result, false);
json.reset(TRI_ObjectToJsonSimple(isolate, result));
} else { if (res != TRI_ERROR_NO_ERROR) {
json.reset(TRI_ObjectToJson(isolate, result)); THROW_ARANGO_EXCEPTION(res);
} }
// TODO: what does _isSimple do here?
} }
if (json.get() == nullptr) { return AqlValue$(builder);
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);
} }
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|//
// --SECTION--\\|/// @\\}"
// End

View File

@ -27,6 +27,7 @@
#include "Basics/Common.h" #include "Basics/Common.h"
#include "Aql/AqlValue.h" #include "Aql/AqlValue.h"
#include "Aql/types.h" #include "Aql/types.h"
#include <v8.h> #include <v8.h>
namespace arangodb { namespace arangodb {
@ -37,6 +38,7 @@ class Query;
struct Variable; struct Variable;
struct V8Expression { struct V8Expression {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/// @brief create the v8 expression /// @brief create the v8 expression
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -68,7 +70,7 @@ struct V8Expression {
/// @brief execute the expression /// @brief execute the expression
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
AqlValue execute(v8::Isolate* isolate, Query* query, AqlValue$ execute(v8::Isolate* isolate, Query* query,
arangodb::AqlTransaction*, AqlItemBlock const*, size_t, arangodb::AqlTransaction*, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&, std::vector<Variable const*> const&,
std::vector<RegisterId> const&); std::vector<RegisterId> const&);

View File

@ -64,6 +64,13 @@ static VPackBuilder TrueBuilder;
static VPackBuilder FalseBuilder; 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 // attribute exclude handler for skipping over system attributes
struct SystemAttributeExcludeHandler : public VPackAttributeExcludeHandler { struct SystemAttributeExcludeHandler : public VPackAttributeExcludeHandler {
@ -115,9 +122,15 @@ void VelocyPackHelper::initialize() {
// initialize exclude handler for system attributes // initialize exclude handler for system attributes
ExcludeHandler.reset(new SystemAttributeExcludeHandler); ExcludeHandler.reset(new SystemAttributeExcludeHandler);
// Null value
NullBuilder.add(VPackValue(VPackValueType::Null)); NullBuilder.add(VPackValue(VPackValueType::Null));
// True value
TrueBuilder.add(VPackValue(true)); TrueBuilder.add(VPackValue(true));
// False value
FalseBuilder.add(VPackValue(false)); FalseBuilder.add(VPackValue(false));
// Array value (empty)
ArrayBuilder.openArray();
ArrayBuilder.close();
} }
arangodb::velocypack::Slice VelocyPackHelper::NullValue() { arangodb::velocypack::Slice VelocyPackHelper::NullValue() {
@ -132,6 +145,10 @@ arangodb::velocypack::Slice VelocyPackHelper::FalseValue() {
return FalseBuilder.slice(); return FalseBuilder.slice();
} }
arangodb::velocypack::Slice VelocyPackHelper::ArrayValue() {
return ArrayBuilder.slice();
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief return the (global) attribute exclude handler instance /// @brief return the (global) attribute exclude handler instance
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@ -212,6 +212,7 @@ class VelocyPackHelper {
static arangodb::velocypack::Slice NullValue(); static arangodb::velocypack::Slice NullValue();
static arangodb::velocypack::Slice TrueValue(); static arangodb::velocypack::Slice TrueValue();
static arangodb::velocypack::Slice FalseValue(); static arangodb::velocypack::Slice FalseValue();
static arangodb::velocypack::Slice ArrayValue();
}; };
} }
} }