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/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 <velocypack/Buffer.h>
#include <velocypack/Iterator.h>
#include <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
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<uint8_t>(l);
memcpy(_data.external->data(), data.data(), l);
_data.internal[15] = AqlValueType::EXTERNAL;
_data.external = new VPackBuffer<uint8_t>(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<uint8_t>(l);
memcpy(_data.external->data(), data.start(), l);
_data.internal[15] = AqlValueType::EXTERNAL;
_data.external = new VPackBuffer<uint8_t>(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<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) {
VPackSlice s = other.slice();
VPackValueLength length = s.byteSize();
if (other.type()) {
// Isse external
_data.external = new VPackBuffer<uint8_t>(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<uint8_t>(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);
}
//////////////////////////////////////////////////////////////////////////////
/// @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<double>(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<double>();
if (s.isNumber()) {
return s.getNumber<double>();
}
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<int64_t>(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<int64_t>();
if (s.isNumber()) {
return s.getNumber<int64_t>();
}
if (slice().isString()) {
if (s.isString()) {
try {
return static_cast<int64_t>(std::stoll(slice().copyString()));
return static_cast<int64_t>(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<double>() != 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<double>() != 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<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,
TRI_document_collection_t const* document,
VPackBuilder& builder) const {

View File

@ -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<uint8_t>* 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<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;
//////////////////////////////////////////////////////////////////////////////
/// @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
@ -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<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
@ -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

View File

@ -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 <velocypack/Slice.h>
#include <velocypack/velocypack-aliases.h>
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<Variable const*> const& vars,
std::vector<RegisterId> const& regs) {
AqlValue$ AttributeAccessor::get(arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars,
std::vector<RegisterId> 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());
}

View File

@ -42,18 +42,18 @@ struct Variable;
class AttributeAccessor {
public:
AttributeAccessor(std::vector<char const*> const&, Variable const*);
~AttributeAccessor();
~AttributeAccessor() = default;
//////////////////////////////////////////////////////////////////////////////
/// @brief execute the accessor
//////////////////////////////////////////////////////////////////////////////
AqlValue get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&,
std::vector<RegisterId> const&);
AqlValue$ get(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&,
std::vector<RegisterId> const&);
private:
//////////////////////////////////////////////////////////////////////////////
/// @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
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") {

View File

@ -138,10 +138,10 @@ void Expression::variables(std::unordered_set<Variable const*>& result) const {
/// @brief execute the expression
////////////////////////////////////////////////////////////////////////////////
AqlValue Expression::execute(arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars,
std::vector<RegisterId> const& regs) {
AqlValue$ Expression::execute(arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars,
std::vector<RegisterId> 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<int64_t>(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<int64_t>());
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<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) {
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<size_t, size_t> 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<void(VPackSlice const&, int64_t)> flatten =
[&](VPackSlice const& json, int64_t level) {
if (!json.isArray()) {
std::function<void(AqlValue$ const&, int64_t)> 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>();
double const r = rhs.slice().getNumber<double>();
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);
}
}

View File

@ -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<Variable const*> const&,
std::vector<RegisterId> const&);
AqlValue$ execute(arangodb::AqlTransaction* trx, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&,
std::vector<RegisterId> const&);
//////////////////////////////////////////////////////////////////////////////
/// @brief check whether this is a JSON expression

View File

@ -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);

View File

@ -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();
}

View File

@ -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<Variable const*> const& vars,
std::vector<RegisterId> const& regs) {
AqlValue$ V8Expression::execute(v8::Isolate* isolate, Query* query,
arangodb::AqlTransaction* trx,
AqlItemBlock const* argv, size_t startPos,
std::vector<Variable const*> const& vars,
std::vector<RegisterId> 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<v8::Object> 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<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[] = {
values, constantValues,
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);
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<TRI_json_t> 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

View File

@ -27,6 +27,7 @@
#include "Basics/Common.h"
#include "Aql/AqlValue.h"
#include "Aql/types.h"
#include <v8.h>
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<Variable const*> const&,
std::vector<RegisterId> const&);
AqlValue$ execute(v8::Isolate* isolate, Query* query,
arangodb::AqlTransaction*, AqlItemBlock const*, size_t,
std::vector<Variable const*> const&,
std::vector<RegisterId> const&);
//////////////////////////////////////////////////////////////////////////////
/// @brief the isolate used when executing and destroying the expression

View File

@ -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

View File

@ -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();
};
}
}