mirror of https://gitee.com/bigwinds/arangodb
630 lines
21 KiB
C++
630 lines
21 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. Frank Celler
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "V8Server/v8-voccursor.h"
|
|
#include "Aql/QueryCursor.h"
|
|
#include "Aql/QueryResult.h"
|
|
#include "Basics/StringUtils.h"
|
|
#include "Basics/VelocyPackHelper.h"
|
|
#include "Basics/conversions.h"
|
|
#include "Basics/ScopeGuard.h"
|
|
#include "Transaction/Context.h"
|
|
#include "Transaction/V8Context.h"
|
|
#include "Utils/CollectionNameResolver.h"
|
|
#include "Utils/Cursor.h"
|
|
#include "Utils/CursorRepository.h"
|
|
#include "V8/v8-conv.h"
|
|
#include "V8/v8-globals.h"
|
|
#include "V8/v8-utils.h"
|
|
#include "V8/v8-vpack.h"
|
|
#include "v8-vocbaseprivate.h"
|
|
|
|
#include <velocypack/Iterator.h>
|
|
#include <velocypack/velocypack-aliases.h>
|
|
|
|
#include <stdio.h>
|
|
#include "Logger/Logger.h"
|
|
|
|
using namespace arangodb;
|
|
using namespace arangodb::basics;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief generates a general cursor from an array
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void JS_CreateCursor(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
auto& vocbase = GetContextVocBase(isolate);
|
|
|
|
if (args.Length() < 1) {
|
|
TRI_V8_THROW_EXCEPTION_USAGE("CREATE_CURSOR(<data>, <batchSize>, <ttl>)");
|
|
}
|
|
|
|
if (!args[0]->IsArray()) {
|
|
TRI_V8_THROW_TYPE_ERROR("<data> must be an array");
|
|
}
|
|
|
|
// extract objects
|
|
v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(args[0]);
|
|
auto builder = std::make_shared<VPackBuilder>();
|
|
int res = TRI_V8ToVPack(isolate, *builder, array, false);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_THROW_TYPE_ERROR("cannot convert <array> to JSON");
|
|
}
|
|
|
|
// maximum number of results to return at once
|
|
uint32_t batchSize = 1000;
|
|
|
|
if (args.Length() >= 2) {
|
|
int64_t maxValue = TRI_ObjectToInt64(isolate, args[1]);
|
|
if (maxValue > 0 && maxValue < (int64_t)UINT32_MAX) {
|
|
batchSize = static_cast<uint32_t>(maxValue);
|
|
}
|
|
}
|
|
|
|
double ttl = 0.0;
|
|
if (args.Length() >= 3) {
|
|
ttl = TRI_ObjectToDouble(isolate, args[2]);
|
|
}
|
|
|
|
if (ttl <= 0.0) {
|
|
ttl = 30.0; // default ttl
|
|
}
|
|
|
|
auto* cursors = vocbase.cursorRepository(); // create a cursor
|
|
arangodb::aql::QueryResult result(TRI_ERROR_NO_ERROR);
|
|
|
|
result.data = builder;
|
|
result.cached = false;
|
|
result.context = transaction::V8Context::Create(vocbase, false);
|
|
|
|
TRI_ASSERT(builder.get() != nullptr);
|
|
|
|
try {
|
|
arangodb::Cursor* cursor =
|
|
cursors->createFromQueryResult(std::move(result),
|
|
static_cast<size_t>(batchSize), ttl, true);
|
|
|
|
TRI_ASSERT(cursor != nullptr);
|
|
TRI_DEFER(cursors->release(cursor));
|
|
|
|
// need to fetch id before release() as release() might delete the cursor
|
|
CursorId id = cursor->id();
|
|
|
|
auto result = TRI_V8UInt64String<TRI_voc_tick_t>(isolate, id);
|
|
TRI_V8_RETURN(result);
|
|
} catch (...) {
|
|
TRI_V8_THROW_EXCEPTION_MEMORY();
|
|
}
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief generates a JSON object from the specified cursor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void JS_JsonCursor(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
auto& vocbase = GetContextVocBase(isolate);
|
|
|
|
if (args.Length() != 1) {
|
|
TRI_V8_THROW_EXCEPTION_USAGE("JSON_CURSOR(<id>)");
|
|
}
|
|
|
|
std::string const id = TRI_ObjectToString(isolate, args[0]);
|
|
auto cursorId =
|
|
static_cast<arangodb::CursorId>(arangodb::basics::StringUtils::uint64(id));
|
|
|
|
// find the cursor
|
|
auto cursors = vocbase.cursorRepository();
|
|
TRI_ASSERT(cursors != nullptr);
|
|
|
|
bool busy;
|
|
Cursor* cursor = cursors->find(cursorId, Cursor::CURSOR_VPACK, busy);
|
|
TRI_DEFER(cursors->release(cursor));
|
|
|
|
if (cursor == nullptr) {
|
|
if (busy) {
|
|
TRI_V8_THROW_EXCEPTION(TRI_ERROR_CURSOR_BUSY);
|
|
}
|
|
|
|
TRI_V8_THROW_EXCEPTION(TRI_ERROR_CURSOR_NOT_FOUND);
|
|
}
|
|
|
|
VPackOptions* opts = cursor->context()->getVPackOptions();
|
|
VPackBuilder builder(opts);
|
|
builder.openObject(true); // conversion uses sequential iterator, no indexing
|
|
Result r = cursor->dumpSync(builder);
|
|
if (r.fail()) {
|
|
TRI_V8_THROW_EXCEPTION_MEMORY(); // for compatibility
|
|
}
|
|
builder.close();
|
|
|
|
v8::Handle<v8::Value> result = TRI_VPackToV8(isolate, builder.slice());
|
|
TRI_V8_RETURN(result);
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
// .............................................................................
|
|
// cursor v8 class
|
|
// .............................................................................
|
|
|
|
struct V8Cursor final {
|
|
static constexpr uint16_t CID = 4956;
|
|
|
|
V8Cursor(v8::Isolate* isolate, v8::Handle<v8::Object> holder,
|
|
TRI_vocbase_t& vocbase, CursorId cursorId)
|
|
: _isolate(isolate),
|
|
_cursorId(cursorId),
|
|
_resolver(vocbase),
|
|
_cte(transaction::Context::createCustomTypeHandler(vocbase, _resolver)) {
|
|
// sanity checks
|
|
TRI_ASSERT(_handle.IsEmpty());
|
|
TRI_ASSERT(holder->InternalFieldCount() > 0);
|
|
|
|
// create a new persistent handle
|
|
holder->SetAlignedPointerInInternalField(0, this);
|
|
_handle.Reset(_isolate, holder);
|
|
_handle.SetWrapperClassId(CID);
|
|
|
|
// and make it weak, so that we can garbage collect
|
|
_handle.SetWeak(this, weakCallback, v8::WeakCallbackType::kParameter);
|
|
_options.customTypeHandler = _cte.get();
|
|
}
|
|
|
|
~V8Cursor() {
|
|
if (!_handle.IsEmpty()) {
|
|
TRI_ASSERT(_handle.IsNearDeath());
|
|
|
|
_handle.ClearWeak();
|
|
|
|
_handle.Reset();
|
|
}
|
|
if (_isolate) {
|
|
TRI_GET_GLOBALS2(_isolate);
|
|
TRI_vocbase_t* vocbase = v8g->_vocbase;
|
|
if (vocbase) {
|
|
CursorRepository* cursors = vocbase->cursorRepository();
|
|
cursors->remove(_cursorId, Cursor::CURSOR_VPACK);
|
|
}
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unwraps a structure
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static V8Cursor* unwrap(v8::Local<v8::Object> handle) {
|
|
TRI_ASSERT(handle->InternalFieldCount() > 0);
|
|
return static_cast<V8Cursor*>(handle->GetAlignedPointerFromInternalField(0));
|
|
}
|
|
|
|
/// @brief return false on error
|
|
bool maybeFetchBatch(v8::Isolate* isolate) {
|
|
if (_dataIterator == nullptr && _hasMore) { // fetch more data
|
|
TRI_GET_GLOBALS();
|
|
CursorRepository* cursors = v8g->_vocbase->cursorRepository();
|
|
bool busy;
|
|
Cursor* cc = cursors->find(_cursorId, Cursor::CURSOR_VPACK, busy);
|
|
if (busy || cc == nullptr) {
|
|
TRI_V8_SET_ERROR(TRI_errno_string(TRI_ERROR_CURSOR_BUSY));
|
|
return false; // someone else is using it
|
|
}
|
|
TRI_DEFER(cc->release());
|
|
|
|
Result r = fetchData(cc);
|
|
if (r.fail()) {
|
|
TRI_CreateErrorObject(isolate, r);
|
|
return false;
|
|
}
|
|
}
|
|
return true; // still got some data
|
|
}
|
|
|
|
/// @brief fetch the next batch
|
|
Result fetchData(Cursor* cursor) {
|
|
TRI_ASSERT(cursor != nullptr && cursor->isUsed());
|
|
|
|
TRI_ASSERT(_hasMore);
|
|
TRI_ASSERT(_dataIterator == nullptr);
|
|
_dataSlice = VPackSlice::noneSlice();
|
|
_extraSlice = VPackSlice::noneSlice();
|
|
|
|
_tmpResult.clear();
|
|
_tmpResult.openObject();
|
|
Result r = cursor->dumpSync(_tmpResult);
|
|
if (r.fail()) {
|
|
return r;
|
|
}
|
|
_tmpResult.close();
|
|
|
|
TRI_ASSERT(_tmpResult.slice().isObject());
|
|
// TODO as an optimization
|
|
for (auto pair : VPackObjectIterator(_tmpResult.slice(), true)) {
|
|
if (pair.key.isEqualString("result")) {
|
|
_dataSlice = pair.value;
|
|
TRI_ASSERT(_dataSlice.isArray());
|
|
if (!_dataSlice.isEmptyArray()) {
|
|
_dataIterator = std::make_unique<VPackArrayIterator>(_dataSlice);
|
|
}
|
|
} else if (pair.key.isEqualString("hasMore")) {
|
|
_hasMore = pair.value.getBool();
|
|
} else if (pair.key.isEqualString("extra")) {
|
|
_extraSlice = pair.value;
|
|
}
|
|
}
|
|
// cursor should delete itself
|
|
TRI_ASSERT(_hasMore || cursor->isDeleted());
|
|
return Result{};
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief constructs a new streaming cursor from arguments
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void New(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
TRI_GET_GLOBALS();
|
|
|
|
if (!args.IsConstructCall()) { // if not call as a constructor call it
|
|
TRI_V8_THROW_EXCEPTION_USAGE("only instance-able by constructor");
|
|
}
|
|
|
|
if (args.Length() < 1 || args.Length() > 3) {
|
|
TRI_V8_THROW_EXCEPTION_USAGE(
|
|
"ArangoQueryStreamCursor(<queryString>, <bindVars>, <options>)");
|
|
}
|
|
|
|
// get the query string
|
|
if (!args[0]->IsString()) {
|
|
TRI_V8_THROW_TYPE_ERROR("expecting string for <queryString>");
|
|
}
|
|
std::string const queryString(TRI_ObjectToString(isolate, args[0]));
|
|
|
|
// bind parameters
|
|
std::shared_ptr<VPackBuilder> bindVars;
|
|
|
|
if (args.Length() > 1) {
|
|
if (!args[1]->IsUndefined() && !args[1]->IsNull() && !args[1]->IsObject()) {
|
|
TRI_V8_THROW_TYPE_ERROR("expecting object for <bindVars>");
|
|
}
|
|
if (args[1]->IsObject()) {
|
|
bindVars.reset(new VPackBuilder);
|
|
int res = TRI_V8ToVPack(isolate, *(bindVars.get()), args[1], false);
|
|
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_THROW_EXCEPTION(res);
|
|
}
|
|
}
|
|
}
|
|
|
|
// options
|
|
auto options = std::make_shared<VPackBuilder>();
|
|
if (args.Length() > 2) {
|
|
// we have options! yikes!
|
|
if (!args[2]->IsObject()) {
|
|
TRI_V8_THROW_TYPE_ERROR("expecting object for <options>");
|
|
}
|
|
|
|
int res = TRI_V8ToVPack(isolate, *options, args[2], false);
|
|
if (res != TRI_ERROR_NO_ERROR) {
|
|
TRI_V8_THROW_EXCEPTION(res);
|
|
}
|
|
} else {
|
|
VPackObjectBuilder guard(options.get());
|
|
}
|
|
size_t batchSize =
|
|
VelocyPackHelper::getNumericValue<size_t>(options->slice(), "batchSize", 1000);
|
|
|
|
const bool contextOwnedByExterior = transaction::V8Context::isEmbedded();
|
|
TRI_vocbase_t* vocbase = v8g->_vocbase;
|
|
TRI_ASSERT(vocbase != nullptr);
|
|
auto* cursors = vocbase->cursorRepository(); // create a cursor
|
|
double ttl = std::numeric_limits<double>::max();
|
|
// specify ID 0 so it uses the external V8 context
|
|
auto cc = cursors->createQueryStream(queryString, std::move(bindVars),
|
|
std::move(options), batchSize, ttl,
|
|
contextOwnedByExterior,
|
|
/*trxCtx*/ nullptr);
|
|
TRI_DEFER(cc->release());
|
|
// args.Holder() is supposedly better than args.This()
|
|
auto self = std::make_unique<V8Cursor>(isolate, args.Holder(), *vocbase, cc->id());
|
|
Result r = self->fetchData(cc);
|
|
self.release(); // args.Holder() owns the pointer
|
|
if (r.fail()) {
|
|
TRI_V8_THROW_EXCEPTION(r);
|
|
} else {
|
|
TRI_V8_RETURN(args.This());
|
|
}
|
|
// do not delete self, its owned by V8 now
|
|
|
|
TRI_V8_RETURN(args.This());
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.toArray = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void toArray(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self == nullptr) {
|
|
TRI_V8_RETURN_UNDEFINED();
|
|
}
|
|
|
|
v8::Handle<v8::Array> resArray = v8::Array::New(isolate);
|
|
|
|
// iterate over result and return it
|
|
uint32_t j = 0;
|
|
while (self->maybeFetchBatch(isolate)) {
|
|
if (!self->_dataIterator) {
|
|
break;
|
|
}
|
|
|
|
if (V8PlatformFeature::isOutOfMemory(isolate)) {
|
|
TRI_V8_SET_EXCEPTION_MEMORY();
|
|
break;
|
|
}
|
|
|
|
while (self->_dataIterator->valid()) {
|
|
VPackSlice s = self->_dataIterator->value();
|
|
resArray->Set(j++, TRI_VPackToV8(isolate, s, &self->_options));
|
|
++(*self->_dataIterator);
|
|
}
|
|
// reset so that the next one can fetch again
|
|
self->_dataIterator.reset();
|
|
}
|
|
TRI_V8_RETURN(resArray);
|
|
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.getExtra = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void getExtra(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self == nullptr) {
|
|
TRI_V8_RETURN(v8::Undefined(isolate));
|
|
}
|
|
|
|
// we always need to fetch
|
|
if (!self->maybeFetchBatch(isolate)) { // sets exception
|
|
return;
|
|
}
|
|
|
|
if (self->_extraSlice.isObject()) {
|
|
TRI_V8_RETURN(TRI_VPackToV8(isolate, self->_extraSlice));
|
|
}
|
|
|
|
TRI_V8_THROW_EXCEPTION_MESSAGE(
|
|
TRI_ERROR_BAD_PARAMETER,
|
|
"getExtra() is only valid after all data has been fetched");
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.hasNext = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void hasNext(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self == nullptr) {
|
|
TRI_V8_RETURN_UNDEFINED();
|
|
}
|
|
|
|
// we always need to fetch
|
|
if (!self->maybeFetchBatch(isolate)) { // sets exception
|
|
return;
|
|
}
|
|
|
|
if (self->_dataIterator != nullptr) {
|
|
TRI_V8_RETURN_TRUE();
|
|
} else {
|
|
TRI_V8_RETURN_FALSE();
|
|
}
|
|
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.next = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void next(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self == nullptr) {
|
|
TRI_V8_RETURN_UNDEFINED();
|
|
}
|
|
|
|
// we always need to fetch
|
|
if (!self->maybeFetchBatch(isolate)) { // sets exception
|
|
return;
|
|
}
|
|
|
|
if (self->_dataIterator) { // got a current batch
|
|
TRI_ASSERT(self->_dataIterator->valid());
|
|
|
|
VPackSlice s = self->_dataIterator->value();
|
|
v8::Local<v8::Value> val = TRI_VPackToV8(isolate, s, &self->_options);
|
|
|
|
++(*self->_dataIterator);
|
|
// reset so that the next one can fetch again
|
|
if (!self->_dataIterator->valid()) {
|
|
self->_dataIterator.reset();
|
|
}
|
|
TRI_V8_RETURN(val);
|
|
}
|
|
|
|
TRI_V8_RETURN_UNDEFINED();
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.count = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void count(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
v8::HandleScope scope(isolate);
|
|
TRI_V8_RETURN_UNDEFINED(); // always undefined
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief explicitly discard cursor, mostly relevant for testing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void dispose(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self != nullptr) {
|
|
TRI_GET_GLOBALS();
|
|
CursorRepository* cursors = v8g->_vocbase->cursorRepository();
|
|
cursors->remove(self->_cursorId, Cursor::CURSOR_VPACK);
|
|
self->_hasMore = false;
|
|
self->_dataSlice = VPackSlice::noneSlice();
|
|
self->_extraSlice = VPackSlice::noneSlice();
|
|
self->_dataIterator.reset();
|
|
self->_tmpResult.clear();
|
|
}
|
|
TRI_V8_TRY_CATCH_END
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ArangoQueryStreamCursor.prototype.id = ...
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void id(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
|
v8::Isolate* isolate = args.GetIsolate();
|
|
v8::HandleScope scope(isolate);
|
|
V8Cursor* self = V8Cursor::unwrap(args.Holder());
|
|
if (self == nullptr) {
|
|
TRI_V8_RETURN(v8::Undefined(isolate));
|
|
}
|
|
TRI_V8_RETURN(TRI_V8UInt64String<TRI_voc_tick_t>(isolate, self->_cursorId));
|
|
}
|
|
|
|
private:
|
|
/// called when GC deletes the value
|
|
static void weakCallback(const v8::WeakCallbackInfo<V8Cursor>& data) {
|
|
auto obj = data.GetParameter();
|
|
delete obj;
|
|
}
|
|
|
|
private:
|
|
/// @brief persistent handle for V8 object
|
|
v8::Persistent<v8::Object> _handle;
|
|
/// @brief isolate
|
|
v8::Isolate* _isolate;
|
|
/// @brief temporary result buffer
|
|
VPackBuilder _tmpResult;
|
|
/// @brief id of cursor
|
|
CursorId _cursorId;
|
|
|
|
/// cache has more variable
|
|
bool _hasMore = true;
|
|
VPackSlice _dataSlice = VPackSlice::noneSlice();
|
|
/// cached extras which might be attached to the stream
|
|
VPackSlice _extraSlice = VPackSlice::noneSlice();
|
|
/// @brief pointer to the current result
|
|
std::unique_ptr<VPackArrayIterator> _dataIterator;
|
|
|
|
CollectionNameResolver _resolver;
|
|
std::shared_ptr<VPackCustomTypeHandler> _cte;
|
|
VPackOptions _options = VPackOptions::Defaults;
|
|
};
|
|
|
|
// .............................................................................
|
|
// generate the general cursor template
|
|
// .............................................................................
|
|
|
|
void TRI_InitV8cursor(v8::Handle<v8::Context> context, TRI_v8_global_t* v8g) {
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
|
|
// cursor functions. not intended to be used by end users
|
|
// these cursor functions are the APIs implemented in js/actions/api-simple.js
|
|
TRI_AddGlobalFunctionVocbase(isolate,
|
|
TRI_V8_ASCII_STRING(isolate, "CREATE_CURSOR"),
|
|
JS_CreateCursor, true);
|
|
TRI_AddGlobalFunctionVocbase(isolate,
|
|
TRI_V8_ASCII_STRING(isolate, "JSON_CURSOR"),
|
|
JS_JsonCursor, true);
|
|
|
|
// streaming query cursor class, intended to be used via
|
|
// ArangoStatement.execute
|
|
v8::Handle<v8::ObjectTemplate> rt;
|
|
v8::Handle<v8::FunctionTemplate> ft;
|
|
|
|
ft = v8::FunctionTemplate::New(isolate, V8Cursor::New);
|
|
ft->SetClassName(TRI_V8_ASCII_STRING(isolate, "ArangoQueryStreamCursor"));
|
|
|
|
rt = ft->InstanceTemplate();
|
|
rt->SetInternalFieldCount(1);
|
|
|
|
ft->PrototypeTemplate()->Set(TRI_V8_ASCII_STRING(isolate,
|
|
"isArangoResultSet"),
|
|
v8::True(isolate));
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "toArray"),
|
|
V8Cursor::toArray);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "getExtra"),
|
|
V8Cursor::getExtra);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "hasNext"),
|
|
V8Cursor::hasNext);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "next"), V8Cursor::next);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "count"), V8Cursor::count);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "dispose"),
|
|
V8Cursor::dispose);
|
|
TRI_V8_AddProtoMethod(isolate, ft, TRI_V8_ASCII_STRING(isolate, "id"), V8Cursor::id);
|
|
|
|
v8g->StreamQueryCursorTempl.Reset(isolate, ft);
|
|
v8::MaybeLocal<v8::Function> ctor = ft->GetFunction(context);
|
|
if (ctor.IsEmpty()) {
|
|
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
|
|
"error creating v8 stream cursor");
|
|
}
|
|
// ft->SetClassName(TRI_V8_ASCII_STRING(isolate,
|
|
// "ArangoStreamQueryCursorCtor"));
|
|
TRI_AddGlobalFunctionVocbase(isolate,
|
|
TRI_V8_ASCII_STRING(isolate,
|
|
"ArangoQueryStreamCursor"),
|
|
ctor.ToLocalChecked(), true);
|
|
}
|