mirror of https://gitee.com/bigwinds/arangodb
Replace rocksdb export cursor with query (#5657)
This commit is contained in:
parent
83bbd403b2
commit
8c48fdf4ab
|
@ -681,31 +681,38 @@ describe ArangoDB do
|
|||
end
|
||||
|
||||
it "calls wrong cursor API" do
|
||||
cmd = api + "?collection=#{@cn}"
|
||||
body = "{ \"count\" : true, \"batchSize\" : 100, \"flush\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-limit-return", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
doc.parsed_response['id'].should be_kind_of(String)
|
||||
doc.parsed_response['id'].should match(@reId)
|
||||
doc.parsed_response['hasMore'].should eq(true)
|
||||
doc.parsed_response['count'].should eq(2000)
|
||||
doc.parsed_response['result'].length.should eq(100)
|
||||
|
||||
id = doc.parsed_response['id']
|
||||
cmd = "/_api/engine"
|
||||
doc = ArangoDB.log_get("#{prefix}-limit-return", cmd)
|
||||
doc.code.should eq(200)
|
||||
if doc.parsed_response['name'] != 'rocksdb'
|
||||
|
||||
cmd = api + "?collection=#{@cn}"
|
||||
body = "{ \"count\" : true, \"batchSize\" : 100, \"flush\" : true }"
|
||||
doc = ArangoDB.log_post("#{prefix}-limit-return", cmd, :body => body)
|
||||
|
||||
doc.code.should eq(201)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(false)
|
||||
doc.parsed_response['code'].should eq(201)
|
||||
doc.parsed_response['id'].should be_kind_of(String)
|
||||
doc.parsed_response['id'].should match(@reId)
|
||||
doc.parsed_response['hasMore'].should eq(true)
|
||||
doc.parsed_response['count'].should eq(2000)
|
||||
doc.parsed_response['result'].length.should eq(100)
|
||||
|
||||
# intentionally wrong
|
||||
cmd = "/_api/cursor/#{id}"
|
||||
doc = ArangoDB.log_put("#{prefix}-return-cont", cmd)
|
||||
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1600)
|
||||
id = doc.parsed_response['id']
|
||||
|
||||
# intentionally wrong
|
||||
cmd = "/_api/cursor/#{id}"
|
||||
doc = ArangoDB.log_put("#{prefix}-return-cont", cmd)
|
||||
|
||||
doc.code.should eq(404)
|
||||
doc.headers['content-type'].should eq("application/json; charset=utf-8")
|
||||
doc.parsed_response['error'].should eq(true)
|
||||
doc.parsed_response['code'].should eq(404)
|
||||
doc.parsed_response['errorNum'].should eq(1600)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -121,6 +121,7 @@ class Query {
|
|||
return _profile.get();
|
||||
}
|
||||
|
||||
velocypack::Slice optionsSlice() const { return _options->slice(); }
|
||||
TEST_VIRTUAL QueryOptions const& queryOptions() const { return _queryOptions; }
|
||||
|
||||
void increaseMemoryUsage(size_t value) { _resourceMonitor.increaseMemoryUsage(value); }
|
||||
|
@ -348,10 +349,10 @@ class Query {
|
|||
/// @brief bind parameters for the query
|
||||
BindParameters _bindParameters;
|
||||
|
||||
/// @brief query options
|
||||
/// @brief raw query options
|
||||
std::shared_ptr<arangodb::velocypack::Builder> _options;
|
||||
|
||||
/// @brief query options
|
||||
/// @brief parsed query options
|
||||
QueryOptions _queryOptions;
|
||||
|
||||
/// @brief collections used in the query
|
||||
|
|
|
@ -152,7 +152,7 @@ QueryStreamCursor::QueryStreamCursor(
|
|||
)
|
||||
: Cursor(id, batchSize, ttl, /*hasCount*/ false),
|
||||
_guard(vocbase),
|
||||
_queryString(query) {
|
||||
_exportCount(-1) {
|
||||
TRI_ASSERT(QueryRegistryFeature::QUERY_REGISTRY != nullptr);
|
||||
auto prevLockHeaders = CollectionLockState::_noLockHeaders;
|
||||
TRI_DEFER(CollectionLockState::_noLockHeaders = prevLockHeaders);
|
||||
|
@ -160,14 +160,30 @@ QueryStreamCursor::QueryStreamCursor(
|
|||
_query = std::make_unique<Query>(
|
||||
false,
|
||||
_guard.database(),
|
||||
aql::QueryString(_queryString.c_str(), _queryString.length()),
|
||||
aql::QueryString(query),
|
||||
std::move(bindVars),
|
||||
std::move(opts),
|
||||
arangodb::aql::PART_MAIN
|
||||
);
|
||||
_query->prepare(QueryRegistryFeature::QUERY_REGISTRY, aql::Query::DontCache);
|
||||
TRI_ASSERT(_query->state() == aql::QueryExecutionState::ValueType::EXECUTION);
|
||||
|
||||
|
||||
// we replaced the rocksdb export cursor with a stream AQL query
|
||||
// for this case we need to support printing the collection "count"
|
||||
if (_query->optionsSlice().hasKey("exportCollection")) {
|
||||
std::string cname = _query->optionsSlice().get("exportCollection").copyString();
|
||||
TRI_ASSERT(_query->trx()->status() == transaction::Status::RUNNING);
|
||||
OperationResult opRes = _query->trx()->count(cname, true);
|
||||
if (opRes.fail()) {
|
||||
THROW_ARANGO_EXCEPTION(opRes.result);
|
||||
}
|
||||
_exportCount = opRes.slice().getInt();
|
||||
VPackSlice limit = _query->bindParameters()->slice().get("limit");
|
||||
if (limit.isInteger()) {
|
||||
_exportCount = std::min(limit.getInt(), _exportCount);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have set _noLockHeaders, we need to unset it:
|
||||
if (CollectionLockState::_noLockHeaders != nullptr &&
|
||||
CollectionLockState::_noLockHeaders == _query->engine()->lockedShards()) {
|
||||
|
@ -195,7 +211,7 @@ Result QueryStreamCursor::dump(VPackBuilder& builder) {
|
|||
TRI_DEFER(CollectionLockState::_noLockHeaders = prevLockHeaders);
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::QUERIES) << "executing query " << _id << ": '"
|
||||
<< _queryString.substr(1024) << "'";
|
||||
<< _query->queryString().extract(1024) << "'";
|
||||
|
||||
VPackOptions const* oldOptions = builder.options;
|
||||
TRI_DEFER(builder.options = oldOptions);
|
||||
|
@ -241,7 +257,9 @@ Result QueryStreamCursor::dump(VPackBuilder& builder) {
|
|||
if (hasMore) {
|
||||
builder.add("id", VPackValue(std::to_string(id())));
|
||||
}
|
||||
|
||||
if (_exportCount >= 0) { // this is coming from /_api/export
|
||||
builder.add("count", VPackValue(_exportCount));
|
||||
}
|
||||
builder.add("cached", VPackValue(false));
|
||||
} catch (...) {
|
||||
delete value;
|
||||
|
@ -250,7 +268,7 @@ Result QueryStreamCursor::dump(VPackBuilder& builder) {
|
|||
|
||||
if (!hasMore) {
|
||||
QueryResult result;
|
||||
_query->finalize(result);
|
||||
_query->finalize(result); // will commit transaction
|
||||
if (result.extra && result.extra->slice().isObject()) {
|
||||
builder.add("extra", result.extra->slice());
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ class QueryStreamCursor final : public arangodb::Cursor {
|
|||
|
||||
private:
|
||||
DatabaseGuard _guard;
|
||||
std::string _queryString;
|
||||
int64_t _exportCount; // used by RocksDBRestExportHandler
|
||||
std::unique_ptr<aql::Query> _query;
|
||||
};
|
||||
|
||||
|
|
|
@ -45,7 +45,6 @@ set(ROCKSDB_SOURCES
|
|||
RocksDBEngine/RocksDBComparator.cpp
|
||||
RocksDBEngine/RocksDBEdgeIndex.cpp
|
||||
RocksDBEngine/RocksDBEngine.cpp
|
||||
RocksDBEngine/RocksDBExportCursor.cpp
|
||||
RocksDBEngine/RocksDBFulltextIndex.cpp
|
||||
RocksDBEngine/RocksDBGeoIndex.cpp
|
||||
RocksDBEngine/RocksDBHashIndex.cpp
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Jan Steemann
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RocksDBEngine/RocksDBExportCursor.h"
|
||||
#include "Basics/WriteLocker.h"
|
||||
#include "Indexes/IndexIterator.h"
|
||||
#include "Logger/Logger.h"
|
||||
#include "RocksDBEngine/RocksDBCollection.h"
|
||||
#include "StorageEngine/EngineSelectorFeature.h"
|
||||
#include "StorageEngine/PhysicalCollection.h"
|
||||
#include "StorageEngine/StorageEngine.h"
|
||||
#include "Transaction/Hints.h"
|
||||
#include "Transaction/StandaloneContext.h"
|
||||
#include "Utils/SingleCollectionTransaction.h"
|
||||
#include "VocBase/LogicalCollection.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/Dumper.h>
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/Options.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
|
||||
using namespace arangodb;
|
||||
|
||||
RocksDBExportCursor::RocksDBExportCursor(
|
||||
TRI_vocbase_t& vocbase,
|
||||
std::string const& name,
|
||||
CollectionExport::Restrictions const& restrictions,
|
||||
CursorId id,
|
||||
size_t limit,
|
||||
size_t batchSize,
|
||||
double ttl,
|
||||
bool hasCount
|
||||
): Cursor(id, batchSize, ttl, hasCount),
|
||||
_guard(vocbase),
|
||||
_resolver(vocbase),
|
||||
_restrictions(restrictions),
|
||||
_name(name),
|
||||
_trx(new SingleCollectionTransaction(
|
||||
transaction::StandaloneContext::Create(vocbase),
|
||||
_name,
|
||||
AccessMode::Type::READ
|
||||
)),
|
||||
_position(0) {
|
||||
Result res = _trx->begin();
|
||||
|
||||
if (!res.ok()) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
|
||||
LogicalCollection* collection = _trx->documentCollection();
|
||||
TRI_ASSERT(collection != nullptr);
|
||||
|
||||
auto rocksColl = static_cast<RocksDBCollection*>(collection->getPhysical());
|
||||
_iter = rocksColl->getAllIterator(_trx.get());
|
||||
|
||||
_size = collection->numberDocuments(_trx.get());
|
||||
if (limit > 0 && limit < _size) {
|
||||
_size = limit;
|
||||
}
|
||||
}
|
||||
|
||||
RocksDBExportCursor::~RocksDBExportCursor() {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check whether the cursor contains more data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool RocksDBExportCursor::hasNext() {
|
||||
if (_iter.get() == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (_position < _size);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the next element (not implemented)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VPackSlice RocksDBExportCursor::next() {
|
||||
// should not be called directly
|
||||
return VPackSlice();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the cursor size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t RocksDBExportCursor::count() const { return _size; }
|
||||
|
||||
Result RocksDBExportCursor::dump(VPackBuilder& builder) {
|
||||
auto ctx = transaction::StandaloneContext::Create(_guard.database());
|
||||
VPackOptions const* oldOptions = builder.options;
|
||||
|
||||
builder.options = ctx->getVPackOptions();
|
||||
|
||||
TRI_ASSERT(_iter.get() != nullptr);
|
||||
|
||||
auto const restrictionType = _restrictions.type;
|
||||
|
||||
try {
|
||||
builder.add("result", VPackValue(VPackValueType::Array));
|
||||
|
||||
size_t const n = batchSize();
|
||||
|
||||
auto cb = [&, this](LocalDocumentId const& token, VPackSlice slice) {
|
||||
if (_position == _size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
builder.openObject();
|
||||
|
||||
// Copy over shaped values
|
||||
for (auto const& entry : VPackObjectIterator(slice)) {
|
||||
std::string key(entry.key.copyString());
|
||||
|
||||
if (!CollectionExport::IncludeAttribute(restrictionType,
|
||||
_restrictions.fields, key)) {
|
||||
// Ignore everything that should be excluded or not included
|
||||
continue;
|
||||
}
|
||||
// If we get here we need this entry in the final result
|
||||
if (entry.value.isCustom()) {
|
||||
builder.add(key,
|
||||
VPackValue(builder.options->customTypeHandler->toString(
|
||||
entry.value, builder.options, slice)));
|
||||
} else {
|
||||
builder.add(key, entry.value);
|
||||
}
|
||||
}
|
||||
builder.close();
|
||||
_position++;
|
||||
return true;
|
||||
};
|
||||
|
||||
_iter->nextDocument(cb, n);
|
||||
|
||||
builder.close(); // close Array
|
||||
|
||||
// builder.add("hasMore", VPackValue(hasNext() ? "true" : "false"));
|
||||
// //should not be string
|
||||
builder.add("hasMore", VPackValue(hasNext()));
|
||||
|
||||
if (hasNext()) {
|
||||
builder.add("id", VPackValue(std::to_string(id())));
|
||||
}
|
||||
|
||||
if (hasCount()) {
|
||||
builder.add("count", VPackValue(static_cast<uint64_t>(count())));
|
||||
}
|
||||
|
||||
if (!hasNext()) {
|
||||
// mark the cursor as deleted
|
||||
_iter.reset();
|
||||
this->deleted();
|
||||
}
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
return Result(ex.code(), ex.what());
|
||||
} catch (std::exception const& ex) {
|
||||
return Result(TRI_ERROR_INTERNAL, ex.what());
|
||||
} catch (...) {
|
||||
return Result(TRI_ERROR_INTERNAL, "internal error during RocksDBExportCursor::dump");
|
||||
}
|
||||
builder.options = oldOptions;
|
||||
return Result();
|
||||
}
|
||||
|
||||
std::shared_ptr<transaction::Context> RocksDBExportCursor::context() const {
|
||||
return _trx->transactionContext(); // likely not used
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Jan Steemann
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_ROCKSDB_ROCKSDB_EXPORT_CURSOR_H
|
||||
#define ARANGOD_ROCKSDB_ROCKSDB_EXPORT_CURSOR_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Utils/CollectionExport.h"
|
||||
#include "Utils/CollectionGuard.h"
|
||||
#include "Utils/CollectionNameResolver.h"
|
||||
#include "Utils/Cursor.h"
|
||||
#include "Utils/DatabaseGuard.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
|
||||
namespace arangodb {
|
||||
|
||||
class IndexIterator;
|
||||
class SingleCollectionTransaction;
|
||||
|
||||
class RocksDBExportCursor final : public Cursor {
|
||||
public:
|
||||
RocksDBExportCursor(
|
||||
TRI_vocbase_t& vocbase,
|
||||
std::string const& name,
|
||||
CollectionExport::Restrictions const& restrictions,
|
||||
CursorId id,
|
||||
size_t limit,
|
||||
size_t batchSize,
|
||||
double ttl,
|
||||
bool hasCount
|
||||
);
|
||||
|
||||
~RocksDBExportCursor();
|
||||
|
||||
CursorType type() const override final { return CURSOR_EXPORT; }
|
||||
|
||||
bool hasNext();
|
||||
|
||||
arangodb::velocypack::Slice next();
|
||||
|
||||
size_t count() const override final;
|
||||
|
||||
Result dump(velocypack::Builder&) override final;
|
||||
|
||||
std::shared_ptr<transaction::Context> context() const override final;
|
||||
|
||||
private:
|
||||
DatabaseGuard _guard;
|
||||
arangodb::CollectionNameResolver _resolver;
|
||||
CollectionExport::Restrictions _restrictions;
|
||||
std::string const _name;
|
||||
std::unique_ptr<SingleCollectionTransaction> _trx;
|
||||
std::unique_ptr<IndexIterator> _iter;
|
||||
size_t _position;
|
||||
size_t _size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -25,12 +25,14 @@
|
|||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "Cluster/ServerState.h"
|
||||
#include "RocksDBEngine/RocksDBCommon.h"
|
||||
#include "RocksDBEngine/RocksDBEngine.h"
|
||||
#include "RocksDBEngine/RocksDBExportCursor.h"
|
||||
#include "StorageEngine/EngineSelectorFeature.h"
|
||||
#include "Transaction/StandaloneContext.h"
|
||||
#include "Utils/Cursor.h"
|
||||
#include "Utils/CursorRepository.h"
|
||||
#include "Utils/SingleCollectionTransaction.h"
|
||||
#include "VocBase/ticks.h"
|
||||
|
||||
#include <velocypack/Builder.h>
|
||||
|
@ -43,8 +45,9 @@ using namespace arangodb;
|
|||
using namespace arangodb::rest;
|
||||
|
||||
RocksDBRestExportHandler::RocksDBRestExportHandler(GeneralRequest* request,
|
||||
GeneralResponse* response)
|
||||
: RestVocbaseBaseHandler(request, response), _restrictions() {}
|
||||
GeneralResponse* response,
|
||||
aql::QueryRegistry* queryRegistry)
|
||||
: RestCursorHandler(request, response, queryRegistry), _restrictions() {}
|
||||
|
||||
RestStatus RocksDBRestExportHandler::execute() {
|
||||
if (ServerState::instance()->isCoordinator()) {
|
||||
|
@ -63,13 +66,11 @@ RestStatus RocksDBRestExportHandler::execute() {
|
|||
}
|
||||
|
||||
if (type == rest::RequestType::PUT) {
|
||||
modifyCursor();
|
||||
return RestStatus::DONE;
|
||||
return RestCursorHandler::execute();
|
||||
}
|
||||
|
||||
if (type == rest::RequestType::DELETE_REQ) {
|
||||
deleteCursor();
|
||||
return RestStatus::DONE;
|
||||
return RestCursorHandler::execute();
|
||||
}
|
||||
|
||||
generateError(rest::ResponseCode::METHOD_NOT_ALLOWED,
|
||||
|
@ -81,17 +82,15 @@ RestStatus RocksDBRestExportHandler::execute() {
|
|||
/// @brief build options for the query as JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VPackBuilder RocksDBRestExportHandler::buildOptions(VPackSlice const& slice) {
|
||||
VPackBuilder RocksDBRestExportHandler::buildQueryOptions(std::string const& cname,
|
||||
VPackSlice const& slice) {
|
||||
if (!slice.isObject()) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER);
|
||||
}
|
||||
|
||||
VPackBuilder options;
|
||||
options.openObject();
|
||||
|
||||
VPackSlice count = slice.get("count");
|
||||
if (count.isBool()) {
|
||||
options.add("count", count);
|
||||
} else {
|
||||
options.add("count", VPackValue(false));
|
||||
}
|
||||
|
||||
VPackSlice batchSize = slice.get("batchSize");
|
||||
if (batchSize.isNumber()) {
|
||||
if ((batchSize.isInteger() && batchSize.getUInt() == 0) ||
|
||||
|
@ -103,73 +102,95 @@ VPackBuilder RocksDBRestExportHandler::buildOptions(VPackSlice const& slice) {
|
|||
} else {
|
||||
options.add("batchSize", VPackValue(1000));
|
||||
}
|
||||
|
||||
VPackSlice limit = slice.get("limit");
|
||||
if (limit.isNumber()) {
|
||||
options.add("limit", limit);
|
||||
}
|
||||
|
||||
VPackSlice flush = slice.get("flush");
|
||||
if (flush.isBool()) {
|
||||
options.add("flush", flush);
|
||||
} else {
|
||||
options.add("flush", VPackValue(false));
|
||||
}
|
||||
|
||||
|
||||
VPackSlice ttl = slice.get("ttl");
|
||||
if (ttl.isNumber()) {
|
||||
options.add("ttl", ttl);
|
||||
} else {
|
||||
options.add("ttl", VPackValue(30));
|
||||
}
|
||||
|
||||
VPackSlice flushWait = slice.get("flushWait");
|
||||
if (flushWait.isNumber()) {
|
||||
options.add("flushWait", flushWait);
|
||||
} else {
|
||||
options.add("flushWait", VPackValue(10));
|
||||
|
||||
int64_t limit = INT64_MAX;
|
||||
VPackSlice limitSlice = slice.get("limit");
|
||||
if (limitSlice.isNumber()) {
|
||||
limit = limitSlice.getInt();
|
||||
}
|
||||
options.close();
|
||||
|
||||
|
||||
options.add("options", VPackValue(VPackValueType::Object));
|
||||
options.add("stream", VPackValue(true)); // important!!
|
||||
VPackSlice count = slice.get("count");
|
||||
if (count.isBool() && count.getBool()) {
|
||||
// QueryStreamCursor will add `exportCount` as `count`
|
||||
options.add("exportCollection", VPackValue(cname));
|
||||
}
|
||||
options.close(); // options
|
||||
|
||||
std::string query = "FOR doc IN @@collection ";
|
||||
|
||||
options.add("bindVars", VPackValue(VPackValueType::Object));
|
||||
options.add("@collection", VPackValue(cname));
|
||||
if (limit != INT64_MAX) {
|
||||
query.append("LIMIT @limit ");
|
||||
options.add("limit", limitSlice);
|
||||
}
|
||||
|
||||
// handle "restrict" parameter
|
||||
VPackSlice restrct = slice.get("restrict");
|
||||
if (!restrct.isObject()) {
|
||||
if (!restrct.isNone()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TYPE_ERROR,
|
||||
"expecting object for 'restrict'");
|
||||
}
|
||||
} else {
|
||||
if (restrct.isObject()) {
|
||||
// "restrict"."type"
|
||||
VPackSlice type = restrct.get("type");
|
||||
if (!type.isString()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
|
||||
"expecting string for 'restrict.type'");
|
||||
}
|
||||
std::string typeString = type.copyString();
|
||||
|
||||
if (typeString == "include") {
|
||||
_restrictions.type = CollectionExport::Restrictions::RESTRICTION_INCLUDE;
|
||||
} else if (typeString == "exclude") {
|
||||
_restrictions.type = CollectionExport::Restrictions::RESTRICTION_EXCLUDE;
|
||||
} else {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
TRI_ERROR_BAD_PARAMETER,
|
||||
"expecting either 'include' or 'exclude' for 'restrict.type'");
|
||||
}
|
||||
|
||||
// "restrict"."fields"
|
||||
|
||||
VPackSlice fields = restrct.get("fields");
|
||||
if (!fields.isArray()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
|
||||
"expecting array for 'restrict.fields'");
|
||||
}
|
||||
for (auto const& name : VPackArrayIterator(fields)) {
|
||||
if (name.isString()) {
|
||||
_restrictions.fields.emplace(name.copyString());
|
||||
|
||||
if (type.isEqualString("include")) {
|
||||
if (fields.length() == 0) {
|
||||
query.append("RETURN {}");
|
||||
} else {
|
||||
query.append("RETURN KEEP(doc");
|
||||
}
|
||||
} else if (type.isEqualString("exclude")) {
|
||||
if (fields.length() == 0) {
|
||||
query.append("RETURN doc");
|
||||
} else {
|
||||
query.append("RETURN UNSET(doc");
|
||||
}
|
||||
} else {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
|
||||
"expecting either 'include' or 'exclude' for 'restrict.type'");
|
||||
}
|
||||
|
||||
if (fields.length() > 0) {
|
||||
// "restrict"."fields"
|
||||
int i = 0;
|
||||
for (auto const& name : VPackArrayIterator(fields)) {
|
||||
if (name.isString()) {
|
||||
std::string varName = std::string("var").append(std::to_string(i++));
|
||||
query.append(", @").append(varName);
|
||||
options.add(varName, VPackValue(name.copyString()));
|
||||
}
|
||||
}
|
||||
query += ")";
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!restrct.isNone()) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TYPE_ERROR, "expecting object for 'restrict'");
|
||||
}
|
||||
query.append("RETURN doc");
|
||||
}
|
||||
|
||||
options.close(); // bindVars
|
||||
options.add("query", VPackValue(query));
|
||||
options.close();
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
@ -205,147 +226,6 @@ void RocksDBRestExportHandler::createCursor() {
|
|||
return;
|
||||
}
|
||||
|
||||
VPackBuilder optionsBuilder;
|
||||
if (!body.isNone()) {
|
||||
if (!body.isObject()) {
|
||||
generateError(rest::ResponseCode::BAD, TRI_ERROR_QUERY_EMPTY);
|
||||
return;
|
||||
}
|
||||
optionsBuilder = buildOptions(body);
|
||||
} else {
|
||||
// create an empty options object
|
||||
optionsBuilder.openObject();
|
||||
optionsBuilder.close();
|
||||
}
|
||||
|
||||
VPackSlice options = optionsBuilder.slice();
|
||||
size_t limit = arangodb::basics::VelocyPackHelper::getNumericValue<size_t>(
|
||||
options, "limit", 0);
|
||||
|
||||
size_t batchSize =
|
||||
arangodb::basics::VelocyPackHelper::getNumericValue<size_t>(
|
||||
options, "batchSize", 1000);
|
||||
double ttl = arangodb::basics::VelocyPackHelper::getNumericValue<double>(
|
||||
options, "ttl", 30);
|
||||
bool count = arangodb::basics::VelocyPackHelper::getBooleanValue(
|
||||
options, "count", false);
|
||||
|
||||
auto cursors = _vocbase.cursorRepository();
|
||||
TRI_ASSERT(cursors != nullptr);
|
||||
|
||||
Cursor* c = nullptr;
|
||||
|
||||
{
|
||||
auto cursor = std::make_unique<RocksDBExportCursor>(
|
||||
_vocbase,
|
||||
name,
|
||||
_restrictions,
|
||||
TRI_NewTickServer(),
|
||||
limit,
|
||||
batchSize,
|
||||
ttl,
|
||||
count
|
||||
);
|
||||
|
||||
cursor->use();
|
||||
c = cursors->addCursor(std::move(cursor));
|
||||
}
|
||||
TRI_ASSERT(c != nullptr);
|
||||
TRI_DEFER(cursors->release(c));
|
||||
|
||||
resetResponse(rest::ResponseCode::CREATED);
|
||||
|
||||
VPackBuffer<uint8_t> buffer;
|
||||
VPackBuilder builder(buffer);
|
||||
builder.openObject();
|
||||
builder.add(StaticStrings::Error, VPackValue(false));
|
||||
builder.add(StaticStrings::Code, VPackValue(static_cast<int>(ResponseCode::CREATED)));
|
||||
Result r = c->dump(builder);
|
||||
if (r.fail()) {
|
||||
generateError(r);
|
||||
return;
|
||||
}
|
||||
builder.close();
|
||||
|
||||
_response->setContentType(rest::ContentType::JSON);
|
||||
generateResult(rest::ResponseCode::CREATED, std::move(buffer));
|
||||
}
|
||||
|
||||
void RocksDBRestExportHandler::modifyCursor() {
|
||||
std::vector<std::string> const& suffixes = _request->suffixes();
|
||||
|
||||
if (suffixes.size() != 1) {
|
||||
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
|
||||
"expecting PUT /_api/export/<cursor-id>");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string const& id = suffixes[0];
|
||||
|
||||
auto cursors = _vocbase.cursorRepository();
|
||||
TRI_ASSERT(cursors != nullptr);
|
||||
|
||||
auto cursorId = static_cast<arangodb::CursorId>(
|
||||
arangodb::basics::StringUtils::uint64(id));
|
||||
bool busy;
|
||||
auto cursor = cursors->find(cursorId, Cursor::CURSOR_EXPORT, busy);
|
||||
if (cursor == nullptr) {
|
||||
if (busy) {
|
||||
generateError(GeneralResponse::responseCode(TRI_ERROR_CURSOR_BUSY),
|
||||
TRI_ERROR_CURSOR_BUSY);
|
||||
} else {
|
||||
generateError(GeneralResponse::responseCode(TRI_ERROR_CURSOR_NOT_FOUND),
|
||||
TRI_ERROR_CURSOR_NOT_FOUND);
|
||||
}
|
||||
return;
|
||||
}
|
||||
TRI_DEFER(cursors->release(cursor));
|
||||
|
||||
VPackBuffer<uint8_t> buffer;
|
||||
VPackBuilder builder(buffer);
|
||||
builder.openObject();
|
||||
builder.add(StaticStrings::Error, VPackValue(false));
|
||||
builder.add(StaticStrings::Code, VPackValue(static_cast<int>(ResponseCode::OK)));
|
||||
Result r = cursor->dump(builder);
|
||||
if (r.fail()) {
|
||||
generateError(r);
|
||||
return;
|
||||
}
|
||||
builder.close();
|
||||
|
||||
_response->setContentType(rest::ContentType::JSON);
|
||||
generateResult(rest::ResponseCode::OK, std::move(buffer));
|
||||
}
|
||||
|
||||
void RocksDBRestExportHandler::deleteCursor() {
|
||||
std::vector<std::string> const& suffixes = _request->suffixes();
|
||||
|
||||
if (suffixes.size() != 1) {
|
||||
generateError(rest::ResponseCode::BAD, TRI_ERROR_HTTP_BAD_PARAMETER,
|
||||
"expecting DELETE /_api/export/<cursor-id>");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string const& id = suffixes[0];
|
||||
|
||||
CursorRepository* cursors = _vocbase.cursorRepository();
|
||||
TRI_ASSERT(cursors != nullptr);
|
||||
|
||||
auto cursorId = static_cast<arangodb::CursorId>(
|
||||
arangodb::basics::StringUtils::uint64(id));
|
||||
bool found = cursors->remove(cursorId, Cursor::CURSOR_EXPORT);
|
||||
|
||||
if (!found) {
|
||||
generateError(rest::ResponseCode::NOT_FOUND, TRI_ERROR_CURSOR_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
VPackBuilder result;
|
||||
result.openObject();
|
||||
result.add("id", VPackValue(id));
|
||||
result.add(StaticStrings::Error, VPackValue(false));
|
||||
result.add(StaticStrings::Code, VPackValue(static_cast<int>(ResponseCode::ACCEPTED)));
|
||||
result.close();
|
||||
|
||||
generateResult(rest::ResponseCode::ACCEPTED, result.slice());
|
||||
VPackBuilder queryBody = buildQueryOptions(name, body);
|
||||
RestCursorHandler::processQuery(queryBody.slice());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2014-2018 ArangoDB GmbH, Cologne, Germany
|
||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -19,31 +19,34 @@
|
|||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Jan Steemann
|
||||
/// @author Simon Grätzer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGOD_MMFILES_MMFILES_REST_EXPORT_HANDLER_H
|
||||
#define ARANGOD_MMFILES_MMFILES_REST_EXPORT_HANDLER_H 1
|
||||
#ifndef ARANGOD_ROCKSDB_ROCKSDB_REST_EXPORT_HANDLER_H
|
||||
#define ARANGOD_ROCKSDB_ROCKSDB_REST_EXPORT_HANDLER_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/Mutex.h"
|
||||
#include "RestHandler/RestVocbaseBaseHandler.h"
|
||||
#include "RestHandler/RestCursorHandler.h"
|
||||
#include "Utils/CollectionExport.h"
|
||||
|
||||
namespace arangodb {
|
||||
class RocksDBRestExportHandler : public RestVocbaseBaseHandler {
|
||||
namespace aql {
|
||||
class QueryRegistry;
|
||||
}
|
||||
|
||||
class RocksDBRestExportHandler : public RestCursorHandler {
|
||||
public:
|
||||
RocksDBRestExportHandler(GeneralRequest*, GeneralResponse*);
|
||||
RocksDBRestExportHandler(GeneralRequest*, GeneralResponse*, aql::QueryRegistry*);
|
||||
|
||||
public:
|
||||
RestStatus execute() override;
|
||||
char const* name() const override final { return "RocksDBRestExportHandler"; }
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief build options for the query as JSON
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
VPackBuilder buildOptions(VPackSlice const&);
|
||||
VPackBuilder buildQueryOptions(std::string const& cname, VPackSlice const&);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an export cursor and return the first results
|
||||
|
@ -51,18 +54,6 @@ class RocksDBRestExportHandler : public RestVocbaseBaseHandler {
|
|||
|
||||
void createCursor();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the next results from an existing cursor
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void modifyCursor();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dispose an existing cursor
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void deleteCursor();
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief restrictions for export
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RocksDBRestHandlers.h"
|
||||
|
||||
#include "Aql/QueryRegistry.h"
|
||||
#include "GeneralServer/RestHandlerFactory.h"
|
||||
#include "RestHandler/RestHandlerCreator.h"
|
||||
#include "RestServer/QueryRegistryFeature.h"
|
||||
#include "RocksDBEngine/RocksDBRestExportHandler.h"
|
||||
#include "RocksDBEngine/RocksDBRestReplicationHandler.h"
|
||||
#include "RocksDBEngine/RocksDBRestWalHandler.h"
|
||||
|
@ -32,9 +35,12 @@ using namespace arangodb;
|
|||
|
||||
void RocksDBRestHandlers::registerResources(
|
||||
rest::RestHandlerFactory* handlerFactory) {
|
||||
|
||||
auto queryRegistry = QueryRegistryFeature::QUERY_REGISTRY;
|
||||
handlerFactory->addPrefixHandler(
|
||||
"/_api/export",
|
||||
RestHandlerCreator<RocksDBRestExportHandler>::createNoData);
|
||||
RestHandlerCreator<RocksDBRestExportHandler>::createData<aql::QueryRegistry*>,
|
||||
queryRegistry);
|
||||
|
||||
handlerFactory->addPrefixHandler(
|
||||
"/_api/replication",
|
||||
|
|
Loading…
Reference in New Issue