mirror of https://gitee.com/bigwinds/arangodb
aqlvalue$
This commit is contained in:
parent
21bd087bdc
commit
e946729306
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue