mirror of https://gitee.com/bigwinds/arangodb
refactored cursor API, added export API
This commit is contained in:
parent
80a99ab23e
commit
e672d791b5
|
@ -5,7 +5,7 @@
|
|||
This is an introduction to ArangoDB's HTTP Interface for Queries. Results of AQL
|
||||
and simple queries are returned as cursors in order to batch the communication
|
||||
between server and client. Each call returns a number of documents in a batch
|
||||
and an indication, if the current batch has been the final batch. Depending on
|
||||
and an indication if the current batch has been the final batch. Depending on
|
||||
the query, the total number of documents in the result set might or might not be
|
||||
known in advance. In order to free server resources the client should delete the
|
||||
cursor as soon as it is no longer needed.
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
!CHAPTER HTTP Interface for Exporting Documents
|
||||
|
||||
@startDocuBlock JSF_post_api_export
|
|
@ -164,6 +164,7 @@
|
|||
* [Edges](HttpEdge/README.md)
|
||||
* [Address and ETag](HttpEdge/AddressAndEtag.md)
|
||||
* [Working with Edges](HttpEdge/WorkingWithEdges.md)
|
||||
* [Exporting data](HttpExport/README.md)
|
||||
* [AQL Query Cursors](HttpAqlQueryCursor/README.md)
|
||||
* [Query Results](HttpAqlQueryCursor/QueryResults.md)
|
||||
* [Accessing Cursors](HttpAqlQueryCursor/AccessingCursors.md)
|
||||
|
|
|
@ -44,11 +44,12 @@ import sys, os, json, string
|
|||
files = {
|
||||
"js/actions/api-aqlfunction.js" : "aqlfunction",
|
||||
"arangod/RestHandler/RestBatchHandler.cpp" : "batch",
|
||||
"js/actions/api-collection.js" : "collection",
|
||||
"js/actions/api-cursor.js" : "cursor",
|
||||
"js/actions/api-database.js" : "database",
|
||||
"js/actions/_api/collection/app.js" : "collection",
|
||||
"js/actions/_admin/database/app.js" : "database",
|
||||
"arangod/RestHandler/RestCursorHandler.cpp" : "cursor",
|
||||
"arangod/RestHandler/RestDocumentHandler.cpp" : "document",
|
||||
"arangod/RestHandler/RestEdgeHandler.cpp" : "edge",
|
||||
"arangod/RestHandler/RestExportHandler.cpp" : "export",
|
||||
"js/actions/api-edges.js" : "edges",
|
||||
"js/actions/api-endpoint.js" : "endpoint",
|
||||
"js/actions/api-explain.js" : "explain",
|
||||
|
@ -57,7 +58,7 @@ files = {
|
|||
"js/actions/api-index.js" : "index",
|
||||
"lib/HttpServer/AsyncJobManager.h" : "job",
|
||||
"lib/Admin/RestAdminLogHandler.cpp" : "log",
|
||||
"js/actions/api-query.js" : "query",
|
||||
"arangod/RestHandler/RestQueryHandler.cpp" : "query",
|
||||
"arangod/RestHandler/RestReplicationHandler.cpp" : "replication",
|
||||
"js/actions/api-simple.js" : "simple",
|
||||
"js/actions/api-structure.js" : "structure",
|
||||
|
@ -65,8 +66,9 @@ files = {
|
|||
"js/actions/api-tasks.js" : "tasks",
|
||||
"js/actions/api-transaction.js" : "transaction",
|
||||
"js/actions/api-traversal.js" : "traversal",
|
||||
"js/actions/api-user.js" : "user",
|
||||
"lib/Admin/RestVersionHandler.cpp" : "version"
|
||||
"js/actions/_api/user/app.js" : "user",
|
||||
"lib/Admin/RestVersionHandler.cpp" : "version",
|
||||
"js/actions/_admin/wal/app.js" : "wal"
|
||||
}
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
|
|
|
@ -480,7 +480,6 @@ void Query::registerErrorCustom (int code,
|
|||
errorMessage.append(": ");
|
||||
errorMessage.append(details);
|
||||
|
||||
std::cout << "REGISTER ERROR CUSTOM: " << errorMessage << "\n";
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(code, errorMessage);
|
||||
}
|
||||
|
||||
|
|
|
@ -105,6 +105,7 @@ add_executable(
|
|||
RestHandler/RestCursorHandler.cpp
|
||||
RestHandler/RestDocumentHandler.cpp
|
||||
RestHandler/RestEdgeHandler.cpp
|
||||
RestHandler/RestExportHandler.cpp
|
||||
RestHandler/RestImportHandler.cpp
|
||||
RestHandler/RestPleaseUpgradeHandler.cpp
|
||||
RestHandler/RestQueryHandler.cpp
|
||||
|
@ -116,6 +117,7 @@ add_executable(
|
|||
RestServer/VocbaseContext.cpp
|
||||
RestServer/arangod.cpp
|
||||
SkipLists/skiplistIndex.cpp
|
||||
Utils/CollectionExport.cpp
|
||||
Utils/Cursor.cpp
|
||||
Utils/CursorRepository.cpp
|
||||
Utils/DocumentHelper.cpp
|
||||
|
|
|
@ -78,6 +78,7 @@ arangod_libarangod_a_SOURCES = \
|
|||
arangod/RestHandler/RestCursorHandler.cpp \
|
||||
arangod/RestHandler/RestDocumentHandler.cpp \
|
||||
arangod/RestHandler/RestEdgeHandler.cpp \
|
||||
arangod/RestHandler/RestExportHandler.cpp \
|
||||
arangod/RestHandler/RestImportHandler.cpp \
|
||||
arangod/RestHandler/RestPleaseUpgradeHandler.cpp \
|
||||
arangod/RestHandler/RestQueryHandler.cpp \
|
||||
|
@ -89,6 +90,7 @@ arangod_libarangod_a_SOURCES = \
|
|||
arangod/RestServer/VocbaseContext.cpp \
|
||||
arangod/RestServer/arangod.cpp \
|
||||
arangod/SkipLists/skiplistIndex.cpp \
|
||||
arangod/Utils/CollectionExport.cpp \
|
||||
arangod/Utils/Cursor.cpp \
|
||||
arangod/Utils/CursorRepository.cpp \
|
||||
arangod/Utils/DocumentHelper.cpp \
|
||||
|
|
|
@ -236,67 +236,6 @@ triagens::basics::Json RestCursorHandler::buildExtra (triagens::aql::QueryResult
|
|||
return extra;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief append the contents of the cursor into the response body
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RestCursorHandler::dumpCursor (Cursor* cursor) {
|
||||
_response->body().appendText("{\"result\":[");
|
||||
|
||||
size_t const n = cursor->batchSize();
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (! cursor->hasNext()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
_response->body().appendChar(',');
|
||||
}
|
||||
|
||||
auto row = cursor->next();
|
||||
if (row == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
int res = TRI_StringifyJson(_response->body().stringBuffer(), row);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
}
|
||||
|
||||
bool hasCount = cursor->hasCount();
|
||||
size_t count = cursor->count();
|
||||
bool hasNext = cursor->hasNext();
|
||||
TRI_json_t* extra = cursor->extra();
|
||||
|
||||
_response->body().appendText("],\"hasMore\":");
|
||||
_response->body().appendText(hasNext ? "true" : "false");
|
||||
|
||||
if (hasNext) {
|
||||
// only return cursor id if there are more documents
|
||||
_response->body().appendText(",\"id\":\"");
|
||||
_response->body().appendInteger(cursor->id());
|
||||
_response->body().appendText("\"");
|
||||
}
|
||||
|
||||
if (hasCount) {
|
||||
_response->body().appendText(",\"count\":");
|
||||
_response->body().appendInteger(static_cast<uint64_t>(count));
|
||||
}
|
||||
|
||||
if (TRI_IsObjectJson(extra)) {
|
||||
_response->body().appendText(",\"extra\":");
|
||||
TRI_StringifyJson(_response->body().stringBuffer(), extra);
|
||||
}
|
||||
|
||||
_response->body().appendText(",\"error\":false,\"code\":");
|
||||
_response->body().appendInteger(static_cast<uint32_t>(_response->responseCode()));
|
||||
|
||||
_response->body().appendChar('}');
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @startDocuBlock JSF_post_api_cursor
|
||||
/// @brief create a cursor and return the first results
|
||||
|
@ -735,13 +674,18 @@ void RestCursorHandler::createCursor () {
|
|||
double ttl = triagens::basics::JsonHelper::getNumericValue<double>(options.json(), "ttl", 30);
|
||||
bool count = triagens::basics::JsonHelper::getBooleanValue(options.json(), "count", false);
|
||||
|
||||
// steal the query JSON, cursor will take owner the ownership
|
||||
// steal the query JSON, cursor will take over the ownership
|
||||
auto j = queryResult.json;
|
||||
triagens::arango::JsonCursor* cursor = cursors->createFromJson(j, batchSize, extra.steal(), ttl, count);
|
||||
queryResult.json = nullptr;
|
||||
triagens::arango::Cursor* cursor = cursors->createFromJson(j, batchSize, extra.steal(), ttl, count);
|
||||
|
||||
try {
|
||||
dumpCursor(cursor);
|
||||
_response->body().appendChar('{');
|
||||
cursor->dump(_response->body());
|
||||
_response->body().appendText(",\"error\":false,\"code\":");
|
||||
_response->body().appendInteger(static_cast<uint32_t>(_response->responseCode()));
|
||||
_response->body().appendChar('}');
|
||||
|
||||
cursors->release(cursor);
|
||||
}
|
||||
catch (...) {
|
||||
|
@ -889,7 +833,12 @@ void RestCursorHandler::modifyCursor () {
|
|||
_response = createResponse(HttpResponse::OK);
|
||||
_response->setContentType("application/json; charset=utf-8");
|
||||
|
||||
dumpCursor(cursor);
|
||||
_response->body().appendChar('{');
|
||||
cursor->dump(_response->body());
|
||||
_response->body().appendText(",\"error\":false,\"code\":");
|
||||
_response->body().appendInteger(static_cast<uint32_t>(_response->responseCode()));
|
||||
_response->body().appendChar('}');
|
||||
|
||||
cursors->release(cursor);
|
||||
}
|
||||
catch (triagens::basics::Exception const& ex) {
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace triagens {
|
|||
class Cursor;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document request handler
|
||||
/// @brief cursor request handler
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RestCursorHandler : public RestVocbaseBaseHandler {
|
||||
|
|
|
@ -0,0 +1,294 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export request handler
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014 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 Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
||||
/// @author Copyright 2010-2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "RestExportHandler.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/json.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Utils/CollectionExport.h"
|
||||
#include "Utils/Cursor.h"
|
||||
#include "Utils/CursorRepository.h"
|
||||
#include "Wal/LogfileManager.h"
|
||||
|
||||
using namespace triagens::arango;
|
||||
using namespace triagens::rest;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors and destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RestExportHandler::RestExportHandler (HttpRequest* request)
|
||||
: RestVocbaseBaseHandler(request) {
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- Handler methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HttpHandler::status_t RestExportHandler::execute () {
|
||||
// extract the sub-request type
|
||||
HttpRequest::HttpRequestType type = _request->requestType();
|
||||
|
||||
if (type == HttpRequest::HTTP_REQUEST_POST) {
|
||||
createCursor();
|
||||
return status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
generateError(HttpResponse::METHOD_NOT_ALLOWED, TRI_ERROR_HTTP_METHOD_NOT_ALLOWED);
|
||||
return status_t(HANDLER_DONE);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief build options for the query as JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json RestExportHandler::buildOptions (TRI_json_t const* json) {
|
||||
auto getAttribute = [&json] (char const* name) {
|
||||
return TRI_LookupObjectJson(json, name);
|
||||
};
|
||||
|
||||
triagens::basics::Json options(triagens::basics::Json::Object);
|
||||
|
||||
auto attribute = getAttribute("count");
|
||||
options.set("count", triagens::basics::Json(TRI_IsBooleanJson(attribute) ? attribute->_value._boolean : false));
|
||||
|
||||
attribute = getAttribute("batchSize");
|
||||
options.set("batchSize", triagens::basics::Json(TRI_IsNumberJson(attribute) ? attribute->_value._number : 1000.0));
|
||||
|
||||
if (TRI_IsNumberJson(attribute) && static_cast<size_t>(attribute->_value._number) == 0) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_TYPE_ERROR, "expecting non-zero value for <batchSize>");
|
||||
}
|
||||
|
||||
if (! options.has("ttl")) {
|
||||
attribute = getAttribute("ttl");
|
||||
options.set("ttl", triagens::basics::Json(TRI_IsNumberJson(attribute) ? attribute->_value._number : 30.0));
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @startDocuBlock JSF_post_api_export
|
||||
/// @brief export all documents from a collection, using a cursor
|
||||
///
|
||||
/// @RESTHEADER{POST /_api/export, Create export cursor}
|
||||
///
|
||||
/// @RESTBODYPARAM{options,json,optional}
|
||||
/// A JSON object with export options.
|
||||
///
|
||||
/// @RESTQUERYPARAMETERS
|
||||
///
|
||||
/// @RESTQUERYPARAM{collection,string,required}
|
||||
/// The name of the collection to export.
|
||||
///
|
||||
/// @RESTDESCRIPTION
|
||||
/// A call to this method creates a cursor containing all documents in the
|
||||
/// specified collection. In contrast to other data-producing APIs, the internal
|
||||
/// data structures produced by the export API are more lightweight, so it is
|
||||
/// the preferred way to retrieve all documents from a collection.
|
||||
///
|
||||
/// The following attributes can be used inside the JSON object:
|
||||
///
|
||||
/// - *count*: boolean flag that indicates whether the number of documents
|
||||
/// in the result set should be returned in the "count" attribute of the result (optional).
|
||||
/// Calculating the "count" attribute might in the future have a performance
|
||||
/// impact so this option is turned off by default, and "count" is only returned
|
||||
/// when requested.
|
||||
///
|
||||
/// - *batchSize*: maximum number of result documents to be transferred from
|
||||
/// the server to the client in one roundtrip (optional). If this attribute is
|
||||
/// not set, a server-controlled default value will be used.
|
||||
///
|
||||
/// - *ttl*: an optional time-to-live for the cursor (in seconds). The cursor will be
|
||||
/// removed on the server automatically after the specified amount of time. This
|
||||
/// is useful to ensure garbage collection of cursors that are not fully fetched
|
||||
/// by clients. If not set, a server-defined value will be used.
|
||||
///
|
||||
/// If the result set can be created by the server, the server will respond with
|
||||
/// *HTTP 201*. The body of the response will contain a JSON object with the
|
||||
/// result set.
|
||||
///
|
||||
/// The returned JSON object has the following properties:
|
||||
///
|
||||
/// - *error*: boolean flag to indicate that an error occurred (*false*
|
||||
/// in this case)
|
||||
///
|
||||
/// - *code*: the HTTP status code
|
||||
///
|
||||
/// - *result*: an array of result documents (might be empty if the collection was empty)
|
||||
///
|
||||
/// - *hasMore*: a boolean indicator whether there are more results
|
||||
/// available for the cursor on the server
|
||||
///
|
||||
/// - *count*: the total number of result documents available (only
|
||||
/// available if the query was executed with the *count* attribute set)
|
||||
///
|
||||
/// - *id*: id of temporary cursor created on the server (optional, see above)
|
||||
///
|
||||
/// If the JSON representation is malformed or the query specification is
|
||||
/// missing from the request, the server will respond with *HTTP 400*.
|
||||
///
|
||||
/// The body of the response will contain a JSON object with additional error
|
||||
/// details. The object has the following attributes:
|
||||
///
|
||||
/// - *error*: boolean flag to indicate that an error occurred (*true* in this case)
|
||||
///
|
||||
/// - *code*: the HTTP status code
|
||||
///
|
||||
/// - *errorNum*: the server error number
|
||||
///
|
||||
/// - *errorMessage*: a descriptive error message
|
||||
///
|
||||
/// @RESTRETURNCODES
|
||||
///
|
||||
/// @RESTRETURNCODE{201}
|
||||
/// is returned if the result set can be created by the server.
|
||||
///
|
||||
/// @RESTRETURNCODE{400}
|
||||
/// is returned if the JSON representation is malformed or the query specification is
|
||||
/// missing from the request.
|
||||
///
|
||||
/// @RESTRETURNCODE{404}
|
||||
/// The server will respond with *HTTP 404* in case a non-existing collection is
|
||||
/// accessed in the query.
|
||||
///
|
||||
/// @RESTRETURNCODE{405}
|
||||
/// The server will respond with *HTTP 405* if an unsupported HTTP method is used.
|
||||
///
|
||||
/// @endDocuBlock
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void RestExportHandler::createCursor () {
|
||||
std::vector<std::string> const& suffix = _request->suffix();
|
||||
|
||||
if (suffix.size() != 0) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_HTTP_BAD_PARAMETER,
|
||||
"expecting POST /_api/export");
|
||||
return;
|
||||
}
|
||||
|
||||
// extract the cid
|
||||
bool found;
|
||||
char const* name = _request->value("collection", found);
|
||||
|
||||
if (! found || *name == '\0') {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_ARANGO_COLLECTION_PARAMETER_MISSING,
|
||||
"'collection' is missing, expecting " + EXPORT_PATH + "?collection=<identifier>");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
std::unique_ptr<TRI_json_t> json(parseJsonBody());
|
||||
|
||||
triagens::basics::Json options;
|
||||
|
||||
if (json.get() != nullptr) {
|
||||
if (! TRI_IsObjectJson(json.get())) {
|
||||
generateError(HttpResponse::BAD, TRI_ERROR_QUERY_EMPTY);
|
||||
return;
|
||||
}
|
||||
|
||||
options = buildOptions(json.get());
|
||||
}
|
||||
else {
|
||||
// create an empty options object
|
||||
options = triagens::basics::Json(triagens::basics::Json::Object);
|
||||
}
|
||||
|
||||
bool flush = triagens::basics::JsonHelper::getBooleanValue(options.json(), "flush", true);
|
||||
|
||||
if (flush) {
|
||||
// flush the logfiles so the export can fetch all documents
|
||||
int res = triagens::wal::LogfileManager::instance()->flush(true, true, false);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
}
|
||||
|
||||
// this may throw!
|
||||
std::unique_ptr<CollectionExport> collectionExport(new CollectionExport(_vocbase, name));
|
||||
collectionExport->run();
|
||||
|
||||
{
|
||||
_response = createResponse(HttpResponse::CREATED);
|
||||
_response->setContentType("application/json; charset=utf-8");
|
||||
|
||||
size_t batchSize = triagens::basics::JsonHelper::getNumericValue<size_t>(options.json(), "batchSize", 1000);
|
||||
double ttl = triagens::basics::JsonHelper::getNumericValue<double>(options.json(), "ttl", 30);
|
||||
bool count = triagens::basics::JsonHelper::getBooleanValue(options.json(), "count", false);
|
||||
|
||||
auto cursors = static_cast<triagens::arango::CursorRepository*>(_vocbase->_cursorRepository);
|
||||
TRI_ASSERT(cursors != nullptr);
|
||||
|
||||
// create a cursor from the result
|
||||
triagens::arango::Cursor* cursor = cursors->createFromExport(collectionExport.get(), batchSize, ttl, count);
|
||||
collectionExport.release();
|
||||
|
||||
try {
|
||||
cursor->dump(_response->body());
|
||||
cursors->release(cursor);
|
||||
}
|
||||
catch (...) {
|
||||
cursors->release(cursor);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (triagens::basics::Exception const& ex) {
|
||||
generateError(HttpResponse::responseCode(ex.code()), ex.code(), ex.what());
|
||||
}
|
||||
catch (...) {
|
||||
generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_INTERNAL);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
||||
// End:
|
|
@ -0,0 +1,113 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export request handler
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014 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 Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
||||
/// @author Copyright 2010-2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_REST_HANDLER_REST_EXPORT_HANDLER_H
|
||||
#define ARANGODB_REST_HANDLER_REST_EXPORT_HANDLER_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/Mutex.h"
|
||||
#include "RestHandler/RestVocbaseBaseHandler.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class RestExportHandler
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace triagens {
|
||||
|
||||
namespace arango {
|
||||
class Cursor;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document request handler
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RestExportHandler : public RestVocbaseBaseHandler {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors and destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief constructor
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RestExportHandler (rest::HttpRequest*);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- Handler methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// {@inheritDoc}
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
status_t execute () override;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief build options for the query as JSON
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
triagens::basics::Json buildOptions (TRI_json_t const*);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create an export cursor and return the first results
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void createCursor ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
||||
// End:
|
|
@ -94,7 +94,7 @@ HttpHandler::status_t RestImportHandler::execute () {
|
|||
}
|
||||
|
||||
default:
|
||||
generateNotImplemented("ILLEGAL " + DOCUMENT_IMPORT_PATH);
|
||||
generateNotImplemented("ILLEGAL " + IMPORT_PATH);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -593,7 +593,7 @@ bool RestImportHandler::createFromJson (string const& type) {
|
|||
if (suffix.size() != 0) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_HTTP_SUPERFLUOUS_SUFFICES,
|
||||
"superfluous suffix, expecting " + DOCUMENT_IMPORT_PATH + "?collection=<identifier>");
|
||||
"superfluous suffix, expecting " + IMPORT_PATH + "?collection=<identifier>");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -608,7 +608,7 @@ bool RestImportHandler::createFromJson (string const& type) {
|
|||
if (! found || collection.empty()) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_ARANGO_COLLECTION_PARAMETER_MISSING,
|
||||
"'collection' is missing, expecting " + DOCUMENT_IMPORT_PATH + "?collection=<identifier>");
|
||||
"'collection' is missing, expecting " + IMPORT_PATH + "?collection=<identifier>");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1061,7 @@ bool RestImportHandler::createFromKeyValueList () {
|
|||
if (suffix.size() != 0) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_HTTP_SUPERFLUOUS_SUFFICES,
|
||||
"superfluous suffix, expecting " + DOCUMENT_IMPORT_PATH + "?collection=<identifier>");
|
||||
"superfluous suffix, expecting " + IMPORT_PATH + "?collection=<identifier>");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1076,7 +1076,7 @@ bool RestImportHandler::createFromKeyValueList () {
|
|||
if (! found || collection.empty()) {
|
||||
generateError(HttpResponse::BAD,
|
||||
TRI_ERROR_ARANGO_COLLECTION_PARAMETER_MISSING,
|
||||
"'collection' is missing, expecting " + DOCUMENT_IMPORT_PATH + "?collection=<identifier>");
|
||||
"'collection' is missing, expecting " + IMPORT_PATH + "?collection=<identifier>");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -56,43 +56,49 @@ using namespace triagens::arango;
|
|||
/// @brief batch path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::BATCH_PATH = "/_api/batch";
|
||||
const string RestVocbaseBaseHandler::BATCH_PATH = "/_api/batch";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief cursor path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::CURSOR_PATH = "/_api/cursor";
|
||||
const string RestVocbaseBaseHandler::CURSOR_PATH = "/_api/cursor";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::DOCUMENT_PATH = "/_api/document";
|
||||
const string RestVocbaseBaseHandler::DOCUMENT_PATH = "/_api/document";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::EDGE_PATH = "/_api/edge";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief export path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::EXPORT_PATH = "/_api/export";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief documents import path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::DOCUMENT_IMPORT_PATH = "/_api/import";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::EDGE_PATH = "/_api/edge";
|
||||
const string RestVocbaseBaseHandler::IMPORT_PATH = "/_api/import";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replication path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::REPLICATION_PATH = "/_api/replication";
|
||||
const string RestVocbaseBaseHandler::REPLICATION_PATH = "/_api/replication";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief upload path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const string RestVocbaseBaseHandler::UPLOAD_PATH = "/_api/upload";
|
||||
const string RestVocbaseBaseHandler::UPLOAD_PATH = "/_api/upload";
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief name of the queue
|
||||
|
|
|
@ -33,14 +33,12 @@
|
|||
#include "Basics/Common.h"
|
||||
|
||||
#include "Admin/RestBaseHandler.h"
|
||||
|
||||
#include "Basics/json.h"
|
||||
#include "Basics/logging.h"
|
||||
#include "Basics/json-utilities.h"
|
||||
|
||||
#include "Rest/HttpResponse.h"
|
||||
#include "Utils/transactions.h"
|
||||
#include "RestServer/VocbaseContext.h"
|
||||
#include "Utils/transactions.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- forward declarations
|
||||
|
@ -64,9 +62,10 @@ namespace triagens {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class RestVocbaseBaseHandler : public admin::RestBaseHandler {
|
||||
|
||||
private:
|
||||
RestVocbaseBaseHandler (RestVocbaseBaseHandler const&);
|
||||
RestVocbaseBaseHandler& operator= (RestVocbaseBaseHandler const&);
|
||||
RestVocbaseBaseHandler (RestVocbaseBaseHandler const&) = delete;
|
||||
RestVocbaseBaseHandler& operator= (RestVocbaseBaseHandler const&) = delete;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public constants
|
||||
|
@ -92,18 +91,24 @@ namespace triagens {
|
|||
|
||||
static const std::string DOCUMENT_PATH;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document import path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const std::string DOCUMENT_IMPORT_PATH;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief edge path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const std::string EDGE_PATH;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document export path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const std::string EXPORT_PATH;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief document import path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const std::string IMPORT_PATH;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief replication path
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "Admin/RestHandlerCreator.h"
|
||||
#include "Admin/RestShutdownHandler.h"
|
||||
#include "Aql/Query.h"
|
||||
#include "Aql/RestAqlHandler.h"
|
||||
#include "Basics/FileUtils.h"
|
||||
#include "Basics/Nonce.h"
|
||||
#include "Basics/ProgramOptions.h"
|
||||
|
@ -49,7 +50,10 @@
|
|||
#include "Basics/messages.h"
|
||||
#include "Basics/ThreadPool.h"
|
||||
#include "Basics/tri-strings.h"
|
||||
#include "Cluster/ApplicationCluster.h"
|
||||
#include "Cluster/ClusterComm.h"
|
||||
#include "Cluster/HeartbeatThread.h"
|
||||
#include "Cluster/RestShardHandler.h"
|
||||
#include "Dispatcher/ApplicationDispatcher.h"
|
||||
#include "Dispatcher/Dispatcher.h"
|
||||
#include "HttpServer/ApplicationEndpointServer.h"
|
||||
|
@ -61,6 +65,7 @@
|
|||
#include "RestHandler/RestCursorHandler.h"
|
||||
#include "RestHandler/RestDocumentHandler.h"
|
||||
#include "RestHandler/RestEdgeHandler.h"
|
||||
#include "RestHandler/RestExportHandler.h"
|
||||
#include "RestHandler/RestImportHandler.h"
|
||||
#include "RestHandler/RestPleaseUpgradeHandler.h"
|
||||
#include "RestHandler/RestQueryHandler.h"
|
||||
|
@ -77,10 +82,6 @@
|
|||
#include "VocBase/auth.h"
|
||||
#include "VocBase/server.h"
|
||||
#include "Wal/LogfileManager.h"
|
||||
#include "Cluster/ApplicationCluster.h"
|
||||
#include "Cluster/RestShardHandler.h"
|
||||
#include "Cluster/ClusterComm.h"
|
||||
#include "Aql/RestAqlHandler.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace triagens::basics;
|
||||
|
@ -130,9 +131,13 @@ void ArangoServer::defineHandlers (HttpHandlerFactory* factory) {
|
|||
// add "/edge" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::EDGE_PATH,
|
||||
RestHandlerCreator<RestEdgeHandler>::createNoData);
|
||||
|
||||
// add "/export" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::EXPORT_PATH,
|
||||
RestHandlerCreator<RestExportHandler>::createNoData);
|
||||
|
||||
// add "/import" handler
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_IMPORT_PATH,
|
||||
factory->addPrefixHandler(RestVocbaseBaseHandler::IMPORT_PATH,
|
||||
RestHandlerCreator<RestImportHandler>::createNoData);
|
||||
|
||||
// add "/replication" handler
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief collection export result container
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014 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 Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Utils/CollectionExport.h"
|
||||
#include "Basics/JsonHelper.h"
|
||||
#include "Utils/CollectionGuard.h"
|
||||
#include "Utils/CollectionReadLocker.h"
|
||||
#include "VocBase/barrier.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
using namespace triagens::arango;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class CollectionExport
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
CollectionExport::CollectionExport (TRI_vocbase_t* vocbase,
|
||||
std::string const& name)
|
||||
: _guard(nullptr),
|
||||
_document(nullptr),
|
||||
_barrier(nullptr),
|
||||
_resolver(vocbase),
|
||||
_documents(nullptr) {
|
||||
|
||||
// this may throw
|
||||
_guard = new triagens::arango::CollectionGuard(vocbase, name.c_str(), false);
|
||||
|
||||
_document = _guard->collection()->_collection;
|
||||
TRI_ASSERT(_document != nullptr);
|
||||
}
|
||||
|
||||
CollectionExport::~CollectionExport () {
|
||||
delete _documents;
|
||||
|
||||
if (_barrier != nullptr) {
|
||||
TRI_FreeBarrier(_barrier);
|
||||
}
|
||||
|
||||
delete _guard;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void CollectionExport::run () {
|
||||
// create a fake transaction for iterating over the collection
|
||||
TransactionBase trx(true);
|
||||
|
||||
_barrier = TRI_CreateBarrierElement(&_document->_barrierList);
|
||||
|
||||
if (_barrier == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
TRI_ASSERT(_documents == nullptr);
|
||||
_documents = new std::vector<void const*>();
|
||||
|
||||
// RAII read-lock
|
||||
{
|
||||
triagens::arango::CollectionReadLocker lock(_document, true);
|
||||
|
||||
size_t const n = _document->_primaryIndex._nrUsed;
|
||||
|
||||
_documents->reserve(n);
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto ptr = _document->_primaryIndex._table[i];
|
||||
|
||||
if (ptr != nullptr) {
|
||||
void const* marker = static_cast<TRI_doc_mptr_t const*>(ptr)->getDataPtr();
|
||||
|
||||
// it is only safe to use the markers from the datafiles, not the WAL
|
||||
if (! TRI_IsWalDataMarkerDatafile(marker)) {
|
||||
_documents->emplace_back(marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
||||
// End:
|
|
@ -0,0 +1,97 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief collection export result container
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2014 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 Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
||||
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_ARANGO_COLLECTION_EXPORT_H
|
||||
#define ARANGODB_ARANGO_COLLECTION_EXPORT_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Utils/CollectionNameResolver.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
|
||||
struct TRI_barrier_s;
|
||||
struct TRI_document_collection_t;
|
||||
struct TRI_vocbase_s;
|
||||
|
||||
namespace triagens {
|
||||
namespace arango {
|
||||
|
||||
class CollectionGuard;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class CollectionExport
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class CollectionExport {
|
||||
|
||||
friend class ExportCursor;
|
||||
|
||||
public:
|
||||
|
||||
CollectionExport (CollectionExport const&) = delete;
|
||||
CollectionExport& operator= (CollectionExport const&) = delete;
|
||||
|
||||
CollectionExport (TRI_vocbase_s*,
|
||||
std::string const&);
|
||||
|
||||
~CollectionExport ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
void run ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
triagens::arango::CollectionGuard* _guard;
|
||||
struct TRI_document_collection_t* _document;
|
||||
struct TRI_barrier_s* _barrier;
|
||||
triagens::arango::CollectionNameResolver _resolver;
|
||||
std::vector<void const*>* _documents;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
||||
// End:
|
|
@ -28,8 +28,12 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "Utils/Cursor.h"
|
||||
#include "Basics/json.h"
|
||||
#include "Basics/JsonHelper.h"
|
||||
#include "ShapedJson/shaped-json.h"
|
||||
#include "Utils/CollectionExport.h"
|
||||
#include "VocBase/document-collection.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "VocBase/voc-shaper.h"
|
||||
|
||||
using namespace triagens::arango;
|
||||
|
||||
|
@ -88,9 +92,15 @@ JsonCursor::JsonCursor (TRI_vocbase_t* vocbase,
|
|||
}
|
||||
|
||||
JsonCursor::~JsonCursor () {
|
||||
freeJson();
|
||||
|
||||
TRI_ReleaseVocBase(_vocbase);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check whether the cursor contains more data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -123,6 +133,59 @@ size_t JsonCursor::count () const {
|
|||
return _size;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dump the cursor contents into a string buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void JsonCursor::dump (triagens::basics::StringBuffer& buffer) {
|
||||
buffer.appendText("\"result\":[");
|
||||
|
||||
size_t const n = batchSize();
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (! hasNext()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
buffer.appendChar(',');
|
||||
}
|
||||
|
||||
auto row = next();
|
||||
if (row == nullptr) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
int res = TRI_StringifyJson(buffer.stringBuffer(), row);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.appendText("],\"hasMore\":");
|
||||
buffer.appendText(hasNext() ? "true" : "false");
|
||||
|
||||
if (hasNext()) {
|
||||
// only return cursor id if there are more documents
|
||||
buffer.appendText(",\"id\":\"");
|
||||
buffer.appendInteger(id());
|
||||
buffer.appendText("\"");
|
||||
}
|
||||
|
||||
if (hasCount()) {
|
||||
buffer.appendText(",\"count\":");
|
||||
buffer.appendInteger(static_cast<uint64_t>(count()));
|
||||
}
|
||||
|
||||
TRI_json_t const* extraJson = extra();
|
||||
|
||||
if (TRI_IsObjectJson(extraJson)) {
|
||||
buffer.appendText(",\"extra\":");
|
||||
TRI_StringifyJson(buffer.stringBuffer(), extraJson);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -140,6 +203,133 @@ void JsonCursor::freeJson () {
|
|||
_isDeleted = true;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class ExportCursor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- constructors / destructors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
ExportCursor::ExportCursor (TRI_vocbase_t* vocbase,
|
||||
CursorId id,
|
||||
triagens::arango::CollectionExport* ex,
|
||||
size_t batchSize,
|
||||
double ttl,
|
||||
bool hasCount)
|
||||
: Cursor(id, batchSize, nullptr, ttl, hasCount),
|
||||
_vocbase(vocbase),
|
||||
_ex(ex) {
|
||||
|
||||
TRI_UseVocBase(vocbase);
|
||||
}
|
||||
|
||||
ExportCursor::~ExportCursor () {
|
||||
TRI_ReleaseVocBase(_vocbase);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief check whether the cursor contains more data
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool ExportCursor::hasNext () {
|
||||
return (_position < _ex->_documents->size());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the next element (not implemented)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TRI_json_t* ExportCursor::next () {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the cursor size
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t ExportCursor::count () const {
|
||||
return _ex->_documents->size();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dump the cursor contents into a string buffer
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ExportCursor::dump (triagens::basics::StringBuffer& buffer) {
|
||||
TRI_shaper_t* shaper = _ex->_document->getShaper();
|
||||
|
||||
buffer.appendText("\"result\":[");
|
||||
|
||||
size_t const n = batchSize();
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (! hasNext()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
buffer.appendChar(',');
|
||||
}
|
||||
|
||||
auto marker = static_cast<TRI_df_marker_t const*>(_ex->_documents->at(_position++));
|
||||
|
||||
TRI_shaped_json_t shaped;
|
||||
TRI_EXTRACT_SHAPED_JSON_MARKER(shaped, marker);
|
||||
triagens::basics::Json json(shaper->_memoryZone, TRI_JsonShapedJson(shaper, &shaped));
|
||||
|
||||
// append the internal attributes
|
||||
|
||||
// _id, _key, _rev
|
||||
char const* key = TRI_EXTRACT_MARKER_KEY(marker);
|
||||
std::string id(_ex->_resolver.getCollectionName(_ex->_document->_info._cid));
|
||||
id.push_back('/');
|
||||
id.append(key);
|
||||
json(TRI_VOC_ATTRIBUTE_ID, triagens::basics::Json(id));
|
||||
json(TRI_VOC_ATTRIBUTE_REV, triagens::basics::Json(std::to_string(TRI_EXTRACT_MARKER_RID(marker))));
|
||||
json(TRI_VOC_ATTRIBUTE_KEY, triagens::basics::Json(key));
|
||||
|
||||
if (TRI_IS_EDGE_MARKER(marker)) {
|
||||
// _from
|
||||
std::string from(_ex->_resolver.getCollectionNameCluster(TRI_EXTRACT_MARKER_FROM_CID(marker)));
|
||||
from.push_back('/');
|
||||
from.append(TRI_EXTRACT_MARKER_FROM_KEY(marker));
|
||||
json(TRI_VOC_ATTRIBUTE_FROM, triagens::basics::Json(from));
|
||||
|
||||
// _to
|
||||
std::string to(_ex->_resolver.getCollectionNameCluster(TRI_EXTRACT_MARKER_TO_CID(marker)));
|
||||
to.push_back('/');
|
||||
to.append(TRI_EXTRACT_MARKER_TO_KEY(marker));
|
||||
json(TRI_VOC_ATTRIBUTE_TO, triagens::basics::Json(to));
|
||||
}
|
||||
|
||||
int res = TRI_StringifyJson(buffer.stringBuffer(), json.json());
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.appendText("],\"hasMore\":");
|
||||
buffer.appendText(hasNext() ? "true" : "false");
|
||||
|
||||
if (hasNext()) {
|
||||
// only return cursor id if there are more documents
|
||||
buffer.appendText(",\"id\":\"");
|
||||
buffer.appendInteger(id());
|
||||
buffer.appendText("\"");
|
||||
}
|
||||
|
||||
if (hasCount()) {
|
||||
buffer.appendText(",\"count\":");
|
||||
buffer.appendInteger(static_cast<uint64_t>(count()));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define ARANGODB_ARANGO_CURSOR_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/StringBuffer.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
|
||||
struct TRI_json_t;
|
||||
|
@ -39,6 +40,8 @@ struct TRI_vocbase_s;
|
|||
namespace triagens {
|
||||
namespace arango {
|
||||
|
||||
class CollectionExport;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class Cursor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -120,29 +123,23 @@ namespace triagens {
|
|||
|
||||
virtual size_t count () const = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
void freeJson ();
|
||||
virtual void dump (triagens::basics::StringBuffer&) = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// --SECTION-- protected variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
protected:
|
||||
|
||||
CursorId const _id;
|
||||
size_t const _batchSize;
|
||||
size_t _position;
|
||||
struct TRI_json_t* _extra;
|
||||
double _ttl;
|
||||
double _expires;
|
||||
bool const _hasCount;
|
||||
bool _isDeleted;
|
||||
bool _isUsed;
|
||||
CursorId const _id;
|
||||
size_t const _batchSize;
|
||||
size_t _position;
|
||||
struct TRI_json_t* _extra;
|
||||
double _ttl;
|
||||
double _expires;
|
||||
bool const _hasCount;
|
||||
bool _isDeleted;
|
||||
bool _isUsed;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -174,6 +171,8 @@ namespace triagens {
|
|||
|
||||
size_t count () const override final;
|
||||
|
||||
void dump (triagens::basics::StringBuffer&) override final;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -188,10 +187,51 @@ namespace triagens {
|
|||
|
||||
private:
|
||||
|
||||
struct TRI_vocbase_s* _vocbase;
|
||||
struct TRI_json_t* _json;
|
||||
size_t const _size;
|
||||
struct TRI_vocbase_s* _vocbase;
|
||||
struct TRI_json_t* _json;
|
||||
size_t const _size;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class ExportCursor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
class ExportCursor : public Cursor {
|
||||
public:
|
||||
|
||||
ExportCursor (struct TRI_vocbase_s*,
|
||||
CursorId,
|
||||
triagens::arango::CollectionExport*,
|
||||
size_t,
|
||||
double,
|
||||
bool);
|
||||
|
||||
~ExportCursor ();
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public functions
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
public:
|
||||
|
||||
bool hasNext () override final;
|
||||
|
||||
struct TRI_json_t* next () override final;
|
||||
|
||||
size_t count () const override final;
|
||||
|
||||
void dump (triagens::basics::StringBuffer&) override final;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- private variables
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
private:
|
||||
|
||||
struct TRI_vocbase_s* _vocbase;
|
||||
triagens::arango::CollectionExport* _ex;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "Utils/CursorRepository.h"
|
||||
#include "Basics/json.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Utils/CollectionExport.h"
|
||||
#include "VocBase/server.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
|
@ -81,15 +82,15 @@ CursorRepository::~CursorRepository () {
|
|||
/// the cursor will take ownership of both json and extra
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Cursor* CursorRepository::createFromJson (TRI_json_t* json,
|
||||
size_t batchSize,
|
||||
TRI_json_t* extra,
|
||||
double ttl,
|
||||
bool count) {
|
||||
JsonCursor* CursorRepository::createFromJson (TRI_json_t* json,
|
||||
size_t batchSize,
|
||||
TRI_json_t* extra,
|
||||
double ttl,
|
||||
bool count) {
|
||||
TRI_ASSERT(json != nullptr);
|
||||
|
||||
CursorId const id = TRI_NewTickServer();
|
||||
triagens::arango::Cursor* cursor = nullptr;
|
||||
triagens::arango::JsonCursor* cursor = nullptr;
|
||||
|
||||
try {
|
||||
cursor = new triagens::arango::JsonCursor(_vocbase, id, json, batchSize, extra, ttl, count);
|
||||
|
@ -115,6 +116,32 @@ Cursor* CursorRepository::createFromJson (TRI_json_t* json,
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief creates a cursor and stores it in the registry
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExportCursor* CursorRepository::createFromExport (triagens::arango::CollectionExport* ex,
|
||||
size_t batchSize,
|
||||
double ttl,
|
||||
bool count) {
|
||||
TRI_ASSERT(ex != nullptr);
|
||||
|
||||
CursorId const id = TRI_NewTickServer();
|
||||
triagens::arango::ExportCursor* cursor = new triagens::arango::ExportCursor(_vocbase, id, ex, batchSize, ttl, count);
|
||||
|
||||
cursor->use();
|
||||
|
||||
try {
|
||||
MUTEX_LOCKER(_lock);
|
||||
_cursors.emplace(std::make_pair(id, cursor));
|
||||
return cursor;
|
||||
}
|
||||
catch (...) {
|
||||
delete cursor;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove a cursor by id
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -227,7 +254,6 @@ bool CursorRepository::garbageCollect (bool force) {
|
|||
|
||||
{
|
||||
MUTEX_LOCKER(_lock);
|
||||
std::cout << "CLEANUP COUNT: " << _cursors.size() << "\n";
|
||||
|
||||
for (auto it = _cursors.begin(); it != _cursors.end(); /* no hoisting */) {
|
||||
auto cursor = (*it).second;
|
||||
|
|
|
@ -41,6 +41,8 @@ struct TRI_vocbase_s;
|
|||
namespace triagens {
|
||||
namespace arango {
|
||||
|
||||
class CollectionExport;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- class CursorRepository
|
||||
// -----------------------------------------------------------------------------
|
||||
|
@ -78,11 +80,20 @@ namespace triagens {
|
|||
/// the cursor will take ownership of both json and extra
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Cursor* createFromJson (struct TRI_json_t*,
|
||||
size_t,
|
||||
struct TRI_json_t*,
|
||||
double,
|
||||
bool);
|
||||
JsonCursor* createFromJson (struct TRI_json_t*,
|
||||
size_t,
|
||||
struct TRI_json_t*,
|
||||
double,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief creates a cursor and stores it in the registry
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ExportCursor* createFromExport (triagens::arango::CollectionExport*,
|
||||
size_t,
|
||||
double,
|
||||
bool);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief remove a cursor by id
|
||||
|
|
|
@ -1471,6 +1471,10 @@ void TRI_DestroyInitialVocBase (TRI_vocbase_t* vocbase) {
|
|||
TRI_DestroyVectorPointer(&vocbase->_deadCollections);
|
||||
|
||||
TRI_DestroySpin(&vocbase->_usage._lock);
|
||||
|
||||
if (vocbase->_cursorRepository != nullptr) {
|
||||
delete static_cast<triagens::arango::CursorRepository*>(vocbase->_cursorRepository);
|
||||
}
|
||||
|
||||
if (vocbase->_queries != nullptr) {
|
||||
delete static_cast<triagens::aql::QueryList*>(vocbase->_queries);
|
||||
|
|
|
@ -249,20 +249,20 @@ function ahuacatlModifySuite () {
|
|||
assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR d IN @@cn REMOVE d IN @@cn OPTIONS 'foo'", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test usage of OLD
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidUsageOfNew : function () {
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "REMOVE 'abc' IN @@cn LET removed = NEW RETURN removed", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test usage of NEW
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidUsageOfNew : function () {
|
||||
assertQueryError(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, "REMOVE 'abc' IN @@cn LET removed = NEW RETURN removed", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test usage of OLD
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidUsageOfOld : function () {
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "INSERT { } IN @@cn LET inserted = OLD RETURN inserted", { "@cn": cn1 });
|
||||
assertQueryError(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, "INSERT { } IN @@cn LET inserted = OLD RETURN inserted", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -270,7 +270,7 @@ function ahuacatlModifySuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidVariableNames1 : function () {
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "REMOVE 'abc' IN @@cn LET removed1 = OLD RETURN removed2", { "@cn": cn1 });
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "REMOVE 'abc' IN @@cn LET removed1 = OLD RETURN @@cn", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -278,7 +278,23 @@ function ahuacatlModifySuite () {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidVariableNames2 : function () {
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "UPDATE 'abc' WITH { } IN @@cn LET updated = NEW RETURN foo", { "@cn": cn1 });
|
||||
assertQueryError(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, "REMOVE 'abc' IN @@cn LET removed1 = OLD RETURN removed2", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test variable names
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidVariableNames3 : function () {
|
||||
assertQueryError(errors.ERROR_QUERY_ACCESS_AFTER_MODIFICATION.code, "UPDATE 'abc' WITH { } IN @@cn LET updated = NEW RETURN @@cn", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test variable names
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testInvalidVariableNames4 : function () {
|
||||
assertQueryError(errors.ERROR_ARANGO_COLLECTION_NOT_FOUND.code, "UPDATE 'abc' WITH { } IN @@cn LET updated = NEW RETURN foo", { "@cn": cn1 });
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in New Issue