1
0
Fork 0

Merge branch 'devel' of github.com:triAGENS/ArangoDB into devel

This commit is contained in:
Frank Celler 2014-12-23 23:30:19 +01:00
commit 11233fd57e
27 changed files with 507 additions and 285 deletions

View File

@ -3560,6 +3560,14 @@ void RemoveBlock::work (std::vector<AqlItemBlock*>& blocks) {
0,
nullptr,
ep->_options.waitForSync);
if (ExecutionEngine::isDBServer() &&
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
auto* node = static_cast<RemoveNode const*>(getPlanNode());
if (node->getOptions().ignoreDocumentNotFound) {
// Ignore document not found on the DBserver:
errorCode = TRI_ERROR_NO_ERROR;
}
}
}
handleResult(errorCode, ep->_options.ignoreErrors);
@ -3795,6 +3803,14 @@ void UpdateBlock::work (std::vector<AqlItemBlock*>& blocks) {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
}
}
if (ExecutionEngine::isDBServer() &&
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
auto* node = static_cast<UpdateNode const*>(getPlanNode());
if (node->getOptions().ignoreDocumentNotFound) {
// Ignore document not found on the DBserver:
errorCode = TRI_ERROR_NO_ERROR;
}
}
}
handleResult(errorCode, ep->_options.ignoreErrors, &errorMessage);
@ -3884,6 +3900,16 @@ void ReplaceBlock::work (std::vector<AqlItemBlock*>& blocks) {
// all exceptions are caught in _trx->update()
errorCode = _trx->update(trxCollection, key, 0, &mptr, json.json(), TRI_DOC_UPDATE_LAST_WRITE, 0, nullptr, ep->_options.waitForSync);
if (ExecutionEngine::isDBServer() &&
errorCode == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
auto* node = static_cast<ReplaceNode const*>(getPlanNode());
if (node->getOptions().ignoreDocumentNotFound) {
errorCode = TRI_ERROR_NO_ERROR;
}
else {
errorCode = TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED;
}
}
}
handleResult(errorCode, ep->_options.ignoreErrors);

View File

@ -425,13 +425,13 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
// this map allows to find the queries which are the parts of the big
// query. There are two cases, the first is for the remote queries on
// the DBservers, for these, the key is:
// itoa(ID of RemoteNode in original plan) + "_" + shardId
// itoa(ID of RemoteNode in original plan) + ":" + shardId
// and the value is the
// queryId on DBserver
// with a * appended, if it is a PART_MAIN query.
// The second case is a query, which lives on the coordinator but is not
// the main query. For these, we store
// itoa(ID of RemoteNode in original plan)
// itoa(ID of RemoteNode in original plan) + "/" + <name of vocbase>
// and the value is the
// queryId used in the QueryRegistry
// this is built up when we instanciate the various engines on the
@ -603,7 +603,7 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
// std::cout << "DB SERVER ANSWERED WITHOUT ERROR: " << res->answer->body() << ", REMOTENODEID: " << info.idOfRemoteNode << " SHARDID:" << res->shardID << ", QUERYID: " << queryId << "\n";
std::string theID
= triagens::basics::StringUtils::itoa(info.idOfRemoteNode)
+ "_" + res->shardID;
+ ":" + res->shardID;
if (info.part == triagens::aql::PART_MAIN) {
queryIds.emplace(theID, queryId+"*");
}
@ -736,7 +736,7 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
for (auto const& shardId : shardIds) {
std::string theId
= triagens::basics::StringUtils::itoa(remoteNode->id())
+ "_" + shardId;
+ ":" + shardId;
auto it = queryIds.find(theId);
if (it == queryIds.end()) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "could not find query id in list");
@ -952,7 +952,8 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
for (auto& q : inst.get()->queryIds) {
std::string theId = q.first;
std::string queryId = q.second;
auto pos = theId.find('_');
// std::cout << "queryIds: " << theId << " : " << queryId << std::endl;
auto pos = theId.find(':');
if (pos != std::string::npos) {
// So this is a remote one on a DBserver:
if (queryId.back() == '*') { // only the PART_MAIN one!
@ -963,6 +964,25 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
}
}
}
// Second round, this time we deal with the coordinator pieces
// and tell them the lockedShards as well, we need to copy, since
// they want to delete independently:
for (auto& q : inst.get()->queryIds) {
std::string theId = q.first;
std::string queryId = q.second;
// std::cout << "queryIds: " << theId << " : " << queryId << std::endl;
auto pos = theId.find('/');
if (pos != std::string::npos) {
// std::cout << "Setting lockedShards for query ID "
// << queryId << std::endl;
QueryId qId = triagens::basics::StringUtils::uint64(queryId);
TRI_vocbase_t* vocbase = query->vocbase();
Query* q = queryRegistry->open(vocbase, qId);
q->engine()->setLockedShards(new std::unordered_set<std::string>(*engine->_lockedShards));
queryRegistry->close(vocbase, qId);
// std::cout << "Setting lockedShards done." << std::endl;
}
}
// Now lock them all in the right order:
for (auto& p : forLocking) {
std::string const& shardId = p.first;
@ -998,7 +1018,7 @@ ExecutionEngine* ExecutionEngine::instanciateFromPlan (QueryRegistry* queryRegis
for (auto& q : inst.get()->queryIds) {
std::string theId = q.first;
std::string queryId = q.second;
auto pos = theId.find('_');
auto pos = theId.find(':');
if (pos != std::string::npos) {
// So this is a remote one on a DBserver:
std::string shardId = theId.substr(pos+1);

View File

@ -239,6 +239,14 @@ namespace triagens {
return _lockedShards;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief _lockedShards
////////////////////////////////////////////////////////////////////////////////
void setLockedShards (std::unordered_set<std::string>* lockedShards) {
_lockedShards = lockedShards;
}
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------

View File

@ -2251,6 +2251,22 @@ namespace triagens {
double estimateCost (size_t&) const override final;
////////////////////////////////////////////////////////////////////////////////
/// @brief getOptions
////////////////////////////////////////////////////////////////////////////////
ModificationOptions const& getOptions () const {
return _options;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief getOptions
////////////////////////////////////////////////////////////////////////////////
ModificationOptions& getOptions () {
return _options;
}
// -----------------------------------------------------------------------------
// --SECTION-- protected variables
// -----------------------------------------------------------------------------
@ -2553,6 +2569,8 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () const override final {
// Please do not change the order here without adjusting the
// optimizer rule distributeInCluster as well!
std::vector<Variable const*> v;
v.push_back(_inDocVariable);
@ -2669,6 +2687,8 @@ namespace triagens {
////////////////////////////////////////////////////////////////////////////////
virtual std::vector<Variable const*> getVariablesUsedHere () const override final {
// Please do not change the order here without adjusting the
// optimizer rule distributeInCluster as well!
std::vector<Variable const*> v;
v.push_back(_inDocVariable);
@ -3087,7 +3107,7 @@ namespace triagens {
: ExecutionNode(plan, id),
_vocbase(vocbase),
_collection(collection),
_varId(varId){
_varId(varId) {
}
DistributeNode (ExecutionPlan*,

View File

@ -37,6 +37,7 @@ ModificationOptions::ModificationOptions (Json const& json) {
waitForSync = JsonHelper::getBooleanValue(array.json(), "waitForSync", false);
nullMeansRemove = JsonHelper::getBooleanValue(array.json(), "nullMeansRemove", false);
mergeObjects = JsonHelper::getBooleanValue(array.json(), "mergeObjects", true);
ignoreDocumentNotFound = JsonHelper::getBooleanValue(array.json(), "ignoreDocumentNotFound", false);
}
void ModificationOptions::toJson (triagens::basics::Json& json,
@ -46,7 +47,8 @@ void ModificationOptions::toJson (triagens::basics::Json& json,
("ignoreErrors", Json(ignoreErrors))
("waitForSync", Json(waitForSync))
("nullMeansRemove", Json(nullMeansRemove))
("mergeObjects", Json(mergeObjects));
("mergeObjects", Json(mergeObjects))
("ignoreDocumentNotFound", Json(ignoreDocumentNotFound));
json ("modificationFlags", flags);
}

View File

@ -54,7 +54,8 @@ namespace triagens {
: ignoreErrors(false),
waitForSync(false),
nullMeansRemove(false),
mergeObjects(true) {
mergeObjects(true),
ignoreDocumentNotFound(false) {
}
void toJson (triagens::basics::Json& json, TRI_memory_zone_t* zone) const;
@ -67,6 +68,7 @@ namespace triagens {
bool waitForSync;
bool nullMeansRemove;
bool mergeObjects;
bool ignoreDocumentNotFound;
};

View File

@ -2499,6 +2499,12 @@ int triagens::aql::scatterInClusterRule (Optimizer* opt,
nodeType == ExecutionNode::REMOVE) {
vocbase = static_cast<ModificationNode*>(node)->vocbase();
collection = static_cast<ModificationNode*>(node)->collection();
if (nodeType == ExecutionNode::REMOVE ||
nodeType == ExecutionNode::UPDATE ||
nodeType == ExecutionNode::REPLACE) {
auto* modNode = static_cast<ModificationNode*>(node);
modNode->getOptions().ignoreDocumentNotFound = true;
}
}
else {
TRI_ASSERT(false);
@ -2553,7 +2559,7 @@ int triagens::aql::scatterInClusterRule (Optimizer* opt,
///
/// this rule inserts distribute, remote nodes so operations on sharded
/// collections actually work, this differs from scatterInCluster in that every
/// incoming row is only set to one shard and not all as in scatterInCluster
/// incoming row is only sent to one shard and not all as in scatterInCluster
///
/// it will change plans in place
////////////////////////////////////////////////////////////////////////////////
@ -2571,22 +2577,33 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt,
auto const nodeType = node->getType();
if (nodeType != ExecutionNode::INSERT &&
nodeType != ExecutionNode::REMOVE) {
nodeType != ExecutionNode::REMOVE &&
nodeType != ExecutionNode::REPLACE &&
nodeType != ExecutionNode::UPDATE) {
opt->addPlan(plan, rule->level, wasModified);
return TRI_ERROR_NO_ERROR;
}
Collection const* collection = static_cast<ModificationNode*>(node)->collection();
if (nodeType == ExecutionNode::REMOVE) {
// check if collection shard keys are only _key
std::vector<std::string> shardKeys = collection->shardKeys();
if (shardKeys.size() != 1 || shardKeys[0] != TRI_VOC_ATTRIBUTE_KEY) {
bool defaultSharding = true;
// check if collection shard keys are only _key
std::vector<std::string> shardKeys = collection->shardKeys();
if (shardKeys.size() != 1 || shardKeys[0] != TRI_VOC_ATTRIBUTE_KEY) {
defaultSharding = false;
}
if (nodeType == ExecutionNode::REMOVE ||
nodeType == ExecutionNode::UPDATE) {
if (! defaultSharding) {
// We have to use a ScatterNode.
opt->addPlan(plan, rule->level, wasModified);
return TRI_ERROR_NO_ERROR;
}
}
// In the INSERT and REPLACE cases we use a DistributeNode...
auto deps = node->getDependencies();
TRI_ASSERT(deps.size() == 1);
@ -2597,9 +2614,33 @@ int triagens::aql::distributeInClusterRule (Optimizer* opt,
TRI_vocbase_t* vocbase = static_cast<ModificationNode*>(node)->vocbase();
// insert a distribute node
TRI_ASSERT(node->getVariablesUsedHere().size() == 1);
ExecutionNode* distNode = new DistributeNode(plan, plan->nextId(),
vocbase, collection, node->getVariablesUsedHere()[0]->id);
ExecutionNode* distNode = nullptr;
if (nodeType == ExecutionNode::INSERT ||
nodeType == ExecutionNode::REMOVE) {
TRI_ASSERT(node->getVariablesUsedHere().size() == 1);
distNode = new DistributeNode(plan, plan->nextId(),
vocbase, collection, node->getVariablesUsedHere()[0]->id);
}
else if (nodeType == ExecutionNode::REPLACE) {
std::vector<Variable const*> v = node->getVariablesUsedHere();
if (defaultSharding) {
// We only look into _inKeyVariable
distNode = new DistributeNode(plan, plan->nextId(),
vocbase, collection, v[1]->id);
}
else {
// We only look into _inDocVariable
distNode = new DistributeNode(plan, plan->nextId(),
vocbase, collection, v[0]->id);
}
}
else { // if (nodeType == ExecutionNode::UPDATE)
std::vector<Variable const*> v = node->getVariablesUsedHere();
distNode = new DistributeNode(plan, plan->nextId(),
vocbase, collection, v[1]->id);
// This is the _inKeyVariable! This works, since we use a ScatterNode
// for non-default-sharding attributes.
}
plan->registerNode(distNode);
distNode->addDependency(deps[0]);

View File

@ -138,6 +138,7 @@ void QueryRegistry::insert (QueryId id,
Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
QueryId id) {
// std::cout << "Taking out query with ID " << id << std::endl;
WRITE_LOCKER(_lock);
auto m = _queries.find(vocbase->_name);
@ -161,6 +162,7 @@ Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
// If we had set _makeNolockHeaders, we need to reset it:
if (qi->_query->engine()->lockedShards() != nullptr) {
if (Transaction::_makeNolockHeaders == nullptr) {
// std::cout << "Setting _makeNolockHeaders\n";
Transaction::_makeNolockHeaders = qi->_query->engine()->lockedShards();
}
else {
@ -177,6 +179,7 @@ Query* QueryRegistry::open (TRI_vocbase_t* vocbase,
void QueryRegistry::close (TRI_vocbase_t* vocbase, QueryId id, double ttl) {
// std::cout << "Returning query with ID " << id << std::endl;
WRITE_LOCKER(_lock);
auto m = _queries.find(vocbase->_name);
@ -200,6 +203,7 @@ void QueryRegistry::close (TRI_vocbase_t* vocbase, QueryId id, double ttl) {
// If we have set _makeNolockHeaders, we need to unset it:
if (Transaction::_makeNolockHeaders != nullptr) {
if (Transaction::_makeNolockHeaders == qi->_query->engine()->lockedShards()) {
// std::cout << "Resetting _makeNolockHeaders to nullptr\n";
Transaction::_makeNolockHeaders = nullptr;
}
else {

View File

@ -589,7 +589,7 @@ void RestAqlHandler::getInfoQuery (std::string const& operation,
////////////////////////////////////////////////////////////////////////////////
triagens::rest::HttpHandler::status_t RestAqlHandler::execute () {
// std::cout << "GOT INCOMING REQUEST: " << triagens::rest::HttpRequest::translateMethod(_request->requestType()) << ", " << triagens::arango::ServerState::instance()->getId() << ": " << _request->fullUrl() << ": " << _request->body() << "\n";
// std::cout << "GOT INCOMING REQUEST: " << triagens::rest::HttpRequest::translateMethod(_request->requestType()) << ", " << triagens::arango::ServerState::instance()->getId() << ": " << _request->fullUrl() << ": " << _request->body() << "\n\n";
std::vector<std::string> const& suffix = _request->suffix();

View File

@ -196,8 +196,12 @@ ClusterCommResult* ClusterComm::asyncRequest (
op->serverID = ClusterInfo::instance()->getResponsibleServer(op->shardID);
LOG_DEBUG("Responsible server: %s", op->serverID.c_str());
if (triagens::arango::Transaction::_makeNolockHeaders != nullptr) {
// LOCKING-DEBUG
// std::cout << "Found Nolock header\n";
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(op->shardID);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// LOCKING-DEBUG
// std::cout << "Found our shard\n";
(*headerFields)["X-Arango-Nolock"] = op->shardID;
}
}
@ -240,6 +244,13 @@ ClusterCommResult* ClusterComm::asyncRequest (
op->endTime = timeout == 0.0 ? TRI_microtime()+24*60*60.0
: TRI_microtime()+timeout;
// LOCKING-DEBUG
// std::cout << "asyncRequest: sending " << triagens::rest::HttpRequest::translateMethod(reqtype) << " request to DB server '" << op->serverID << ":" << path << "\n" << body << "\n";
// for (auto& h : *headerFields) {
// std::cout << h.first << ":" << h.second << std::endl;
// }
// std::cout << std::endl;
ClusterCommResult* res = new ClusterCommResult();
*res = *static_cast<ClusterCommResult*>(op);
@ -311,8 +322,12 @@ ClusterCommResult* ClusterComm::syncRequest (
return res;
}
if (triagens::arango::Transaction::_makeNolockHeaders != nullptr) {
// LOCKING-DEBUG
// std::cout << "Found Nolock header\n";
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(res->shardID);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// LOCKING-DEBUG
// std::cout << "Found this shard: " << res->shardID << std::endl;
headersCopy["X-Arango-Nolock"] = res->shardID;
}
}
@ -355,6 +370,12 @@ ClusterCommResult* ClusterComm::syncRequest (
LOG_DEBUG("sending %s request to DB server '%s': %s",
triagens::rest::HttpRequest::translateMethod(reqtype).c_str(),
res->serverID.c_str(), body.c_str());
// LOCKING-DEBUG
// std::cout << "syncRequest: sending " << triagens::rest::HttpRequest::translateMethod(reqtype) << " request to DB server '" << res->serverID << ":" << path << "\n" << body << "\n";
// for (auto& h : headersCopy) {
// std::cout << h.first << ":" << h.second << std::endl;
// }
// std::cout << std::endl;
triagens::httpclient::SimpleHttpClient* client
= new triagens::httpclient::SimpleHttpClient(
connection->connection,

View File

@ -663,6 +663,13 @@ int RestVocbaseBaseHandler::parseDocumentId (CollectionNameResolver const* resol
void RestVocbaseBaseHandler::prepareExecute () {
bool found;
// LOCKING-DEBUG
// std::cout << "REQ coming in: " << _request->requestType() << ": " <<_request->fullUrl() << std::endl;
//std::map<std::string, std::string> h = _request->headers();
//for (auto& hh : h) {
// std::cout << hh.first << ": " << hh.second << std::endl;
//}
//std::cout << std::endl;
char const* shardId = _request->header("x-arango-nolock", found);
if (found) {
_nolockHeaderSet = new std::unordered_set<std::string>();

View File

@ -861,9 +861,13 @@ static int BeginRead (TRI_document_collection_t* document) {
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "BeginRead blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
// LOCKING-DEBUG
// std::cout << "BeginRead: " << document->_info._name << std::endl;
TRI_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
return TRI_ERROR_NO_ERROR;
@ -879,9 +883,13 @@ static int EndRead (TRI_document_collection_t* document) {
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "EndRead blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
// LOCKING-DEBUG
// std::cout << "EndRead: " << document->_info._name << std::endl;
TRI_READ_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
return TRI_ERROR_NO_ERROR;
@ -897,9 +905,13 @@ static int BeginWrite (TRI_document_collection_t* document) {
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "BeginWrite blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
// LOCKING_DEBUG
// std::cout << "BeginWrite: " << document->_info._name << std::endl;
TRI_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
return TRI_ERROR_NO_ERROR;
@ -915,9 +927,13 @@ static int EndWrite (TRI_document_collection_t* document) {
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "EndWrite blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
// LOCKING-DEBUG
// std::cout << "EndWrite: " << document->_info._name << std::endl;
TRI_WRITE_UNLOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document);
return TRI_ERROR_NO_ERROR;
@ -935,11 +951,15 @@ static int BeginReadTimed (TRI_document_collection_t* document,
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "BeginReadTimed blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
uint64_t waited = 0;
// LOCKING-DEBUG
// std::cout << "BeginReadTimed: " << document->_info._name << std::endl;
while (! TRI_TRY_READ_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document)) {
#ifdef _WIN32
usleep((unsigned long) sleepPeriod);
@ -969,11 +989,15 @@ static int BeginWriteTimed (TRI_document_collection_t* document,
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "BeginWriteTimed blocked: " << document->_info._name << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
uint64_t waited = 0;
// LOCKING-DEBUG
// std::cout << "BeginWriteTimed: " << document->_info._name << std::endl;
while (! TRI_TRY_WRITE_LOCK_DOCUMENTS_INDEXES_PRIMARY_COLLECTION(document)) {
#ifdef _WIN32
usleep((unsigned long) sleepPeriod);

View File

@ -348,6 +348,8 @@ static int LockCollection (TRI_transaction_collection_t* trxCollection,
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "LockCollection blocked: " << collName << std::endl;
return TRI_ERROR_NO_ERROR;
}
}
@ -411,6 +413,8 @@ static int UnlockCollection (TRI_transaction_collection_t* trxCollection,
auto it = triagens::arango::Transaction::_makeNolockHeaders->find(collName);
if (it != triagens::arango::Transaction::_makeNolockHeaders->end()) {
// do not lock by command
// LOCKING-DEBUG
// std::cout << "UnlockCollection blocked: " << collName << std::endl;
return TRI_ERROR_NO_ERROR;
}
}

View File

@ -145,6 +145,7 @@
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
"ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED" : { "code" : 1475, "message" : "document not found or sharding attributes changed" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },

View File

@ -145,6 +145,7 @@
"ERROR_CLUSTER_READING_PLAN_AGENCY" : { "code" : 1472, "message" : "error reading Plan in agency" },
"ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION" : { "code" : 1473, "message" : "could not truncate collection" },
"ERROR_CLUSTER_AQL_COMMUNICATION" : { "code" : 1474, "message" : "error in cluster internal communication for AQL" },
"ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED" : { "code" : 1475, "message" : "document not found or sharding attributes changed" },
"ERROR_QUERY_KILLED" : { "code" : 1500, "message" : "query killed" },
"ERROR_QUERY_PARSE" : { "code" : 1501, "message" : "%s" },
"ERROR_QUERY_EMPTY" : { "code" : 1502, "message" : "query is empty" },

View File

@ -1,3 +0,0 @@
.gitignore
node_modules
test

View File

@ -1,7 +0,0 @@
language: node_js
node_js:
- "0.10"
services:
- redis-server
after_script:
- npm test

161
js/node/node_modules/aqb/README.md generated vendored
View File

@ -2,6 +2,55 @@
The query builder allows constructing complex AQL queries with a pure JavaScript fluid API.
[![license - APACHE-2.0](https://img.shields.io/npm/l/aqb.svg)](http://opensource.org/licenses/APACHE-2.0) [![Dependencies](https://img.shields.io/david/arangodb/aqbjs.svg)](https://david-dm.org/arangodb/aqbjs)
[![NPM status](https://nodei.co/npm/aqb.png?downloads=true&stars=true)](https://npmjs.org/package/aqb)
[![Build status](https://img.shields.io/travis/arangodb/aqbjs.svg)](https://travis-ci.org/arangodb/aqbjs) [![Coverage Status](https://img.shields.io/coveralls/arangodb/aqbjs.svg)](https://coveralls.io/r/arangodb/aqbjs?branch=master) [![Codacy rating](https://img.shields.io/codacy/1d5a1be201024e0caaf0aa6bc990231e.svg)](https://www.codacy.com/public/me_4/aqbjs)
# Install
## With NPM
```sh
npm install aqb
```
## With Bower
```sh
bower install aqb
```
## ArangoDB
As of ArangoDB 1.3, a version of `aqb` comes pre-installed with ArangoDB.
```js
var qb = require('aqb');
```
If you want to use a more recent version of `aqb` in a Foxx app, you can add it to your NPM dependencies as usual.
## Browser
This CommonJS module is compatible with [browserify](http://browserify.org).
If you don't want to use browserify, you can simply use the AMD-compatible [browserify bundle](https://raw.githubusercontent.com/arangodb/aqbjs/master/dist/aqb.min.js) (~30 kB minified, ~6 kB gzipped).
If you want to use this module in non-ES5 browsers like Microsoft Internet Explorer 8 and earlier, you may need to include [es5-shim](https://www.npmjs.com/package/es5-shim) or a similar ES5 polyfill.
## From source
```sh
git clone https://github.com/arangodb/aqbjs.git
cd aqbjs
npm install
npm run dist
```
# Example
```js
// in arangosh
var db = require('org/arangodb').db;
@ -9,15 +58,15 @@ var qb = require('aqb');
console.log(db._query(qb.for('x').in('1..5').return('x')).toArray()); // [1, 2, 3, 4, 5]
```
## API
# API
### AQL Types
## AQL Types
If raw JavaScript values are passed to AQL statements, they will be wrapped in a matching AQL type automatically.
JavaScript strings wrapped in quotation marks will be wrapped in AQL strings, all other JavaScript strings will be wrapped as simple references (see below) and throw an *AQLError* if they are not well-formed.
#### Boolean
### Boolean
Wraps the given value as an AQL Boolean literal.
@ -27,7 +76,7 @@ If the value is truthy, it will be converted to the AQL Boolean *true*, otherwis
If the value is already an AQL Boolean, its own value will be wrapped instead.
#### Number
### Number
Wraps the given value as an AQL Number literal.
@ -39,7 +88,7 @@ If the value does not represent a finite number, an *AQLError* will be thrown.
If the value is already an AQL Number or AQL Integer, its own value will be wrapped instead.
#### Integer
### Integer
Wraps the given value as an AQL Integer literal.
@ -53,7 +102,7 @@ If the value is already an AQL Number or AQL Integer, its own value will be wrap
*Alias:* `qb.int_(value)`
#### String
### String
Wraps the given value as an AQL String literal.
@ -65,7 +114,7 @@ If the value is already an AQL String, its own value will be wrapped instead.
If the value is an object with a *toAQL* method, the result of calling that method will be wrapped instead.
#### List
### List
Wraps the given value as an AQL List (Array) literal.
@ -77,7 +126,7 @@ If the value is already an AQL List, its own value will be wrapped instead.
Any list elements that are not already AQL values will be converted automatically.
#### Object
### Object
Wraps the given value as an AQL Object literal.
@ -89,7 +138,7 @@ If the value is already an AQL List, its own value will be wrapped instead.
Any property values that are not already AQL values will be converted automatically.
#### Simple Reference
### Simple Reference
Wraps a given value in an AQL Simple Reference.
@ -127,9 +176,9 @@ var myUserCollection = applicationContext.collection('users');
var users = db._query(qb.for('u').in(myUserCollection).return('u')).toArray();
```
### AQL Expressions
## AQL Expressions
#### Range
### Range
Creates a range expression from the given values.
@ -137,7 +186,7 @@ Creates a range expression from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Property Access
### Property Access
Creates a property access expression from the given values.
@ -145,7 +194,7 @@ Creates a property access expression from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Raw Expression
### Raw Expression
Wraps a given value in a raw AQL expression.
@ -155,9 +204,9 @@ If the value is already an AQL Raw Expression, its value is wrapped instead.
**Warning:** Whenever possible, you should use one of the other methods or a combination thereof instead of using a raw expression. Raw expressions allow passing arbitrary strings into your AQL and thus will open you to AQL injection attacks if you are passing in untrusted user input.
### AQL Operations
## AQL Operations
#### Boolean And
### Boolean And
Creates an "and" operation from the given values.
@ -171,7 +220,7 @@ This function can take any number of arguments.
`qb.and(a, b, c, d, e, f)` -> `(a && b && c && d && e && f)`
#### Boolean Or
### Boolean Or
Creates an "or" operation from the given values.
@ -185,7 +234,7 @@ This function can take any number of arguments.
`qb.or(a, b, c, d, e, f)` -> `(a || b || c || d || e || f)`
#### Addition
### Addition
Creates an addition operation from the given values.
@ -201,7 +250,7 @@ This function can take any number of arguments.
`qb.add(a, b, c, d, e, f)` -> `(a + b + c + d + e + f)`
#### Subtraction
### Subtraction
Creates a subtraction operation from the given values.
@ -217,7 +266,7 @@ This function can take any number of arguments.
`qb.sub(a, b, c, d, e, f)` -> `(a - b - c - d - e - f)`
#### Multiplication
### Multiplication
Creates a multiplication operation from the given values.
@ -233,7 +282,7 @@ This function can take any number of arguments.
`qb.mul(a, b, c, d, e, f)` -> `(a * b * c * d * e * f)`
#### Division
### Division
Creates a division operation from the given values.
@ -247,7 +296,7 @@ This function can take any number of arguments.
`qb.div(a, b, c, d, e, f)` -> `(a / b / c / d / e / f)`
#### Modulus
### Modulus
Creates a modulus operation from the given values.
@ -261,7 +310,7 @@ This function can take any number of arguments.
`qb.mod(a, b, c, d, e, f)` -> `(a % b % c % d % e % f)`
#### Equality
### Equality
Creates an equality comparison from the given values.
@ -269,7 +318,7 @@ Creates an equality comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Inequality
### Inequality
Creates an inequality comparison from the given values.
@ -277,7 +326,7 @@ Creates an inequality comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Greater Than
### Greater Than
Creates a greater-than comparison from the given values.
@ -285,7 +334,7 @@ Creates a greater-than comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Greater Than Or Equal To
### Greater Than Or Equal To
Creates a greater-than-or-equal-to comparison from the given values.
@ -293,7 +342,7 @@ Creates a greater-than-or-equal-to comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Less Than
### Less Than
Creates a less-than comparison from the given values.
@ -301,7 +350,7 @@ Creates a less-than comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Less Than Or Equal To
### Less Than Or Equal To
Creates a less-than-or-equal-to comparison from the given values.
@ -309,7 +358,7 @@ Creates a less-than-or-equal-to comparison from the given values.
If the values are not already AQL values, they will be converted automatically.
#### Contains
### Contains
Creates an "in" comparison from the given values.
@ -319,7 +368,7 @@ If the values are not already AQL values, they will be converted automatically.
*Aliases:* `qb.in_(a, b)`
#### Negation
### Negation
Creates a negation from the given value.
@ -327,7 +376,7 @@ Creates a negation from the given value.
If the value is not already an AQL value, it will be converted automatically.
#### Negative Value
### Negative Value
Creates a negative value expression from the given value.
@ -335,7 +384,7 @@ Creates a negative value expression from the given value.
If the value is not already an AQL value, it will be converted automatically.
#### Ternary (if / else)
### Ternary (if / else)
Creates a ternary expression from the given values.
@ -345,7 +394,7 @@ If the values are not already AQL values, they will be converted automatically.
*Aliases:* `qb.if_(condition, then, otherwise)`
#### Function Call
### Function Call
Creates a function call for the given name and arguments.
@ -362,7 +411,7 @@ For built-in functions, methods with the relevant function name are already prov
* `qb.RANDOM()` -> `RANDOM()`
* `qb.FLOOR(qb.div(5, 2))` -> `FLOOR((5 / 2))`
### AQL Statements
## AQL Statements
In addition to the methods documented above, the query builder provides all methods of *PartialStatement* objects.
@ -375,59 +424,59 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
// -> FOR doc IN my_collection RETURN doc._key
```
#### FOR expression IN collection
### FOR expression IN collection
`PartialStatement::for(expression).in(collection) : Statement`
*Alias:* `for_(expression).in_(collection)`
#### LET varname = expression
### LET varname = expression
`PartialStatement::let(varname, expression) : Statement`
*Alias:* `let_(varname, expression)`
#### LET var1 = expr1, var2 = expr2, …, varn = exprn
### LET var1 = expr1, var2 = expr2, …, varn = exprn
`PartialStatement::let(definitions) : Statement`
*Alias:* `let_(definitions)`
#### FILTER expression
### FILTER expression
`PartialStatement::filter(expression) : Statement`
#### COLLECT varname = expression
### COLLECT varname = expression
`PartialStatement::collect(varname, expression) : Statement`
#### COLLECT varname1 = expression INTO varname2
### COLLECT varname1 = expression INTO varname2
`PartialStatement::collect(varname1, expression).into(varname2) : Statement`
#### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn
### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn
`PartialStatement::collect(definitions) : Statement`
#### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn INTO varname
### COLLECT var1 = expr1, var2 = expr2, …, varn = exprn INTO varname
`PartialStatement::collect(definitions).into(varname) : Statement`
#### SORT args…
### SORT args…
`PartialStatement::sort(args…) : Statement`
#### LIMIT offset, count
### LIMIT offset, count
`PartialStatement::limit([offset,] count) : Statement`
#### RETURN expression
### RETURN expression
`PartialStatement::return(expression) : Statement`
*Alias:* `return_(expression)`
#### REMOVE expression IN collection
### REMOVE expression IN collection
`PartialStatement::remove(expression).in(collection) : RemoveExpression`
@ -436,11 +485,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `remove(expression).in_(collection)`
* `remove(expression).into(collection)`
#### REMOVE … OPTIONS options
### REMOVE … OPTIONS options
`RemoveExpression::options(options) : Statement`
#### INSERT expression INTO collection
### INSERT expression INTO collection
`PartialStatement::insert(expression).into(collection) : InsertExpression`
@ -449,11 +498,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `insert(expression).in(collection)`
* `insert(expression).in_(collection)`
#### INSERT … OPTIONS options
### INSERT … OPTIONS options
`InsertExpression::options(options) : Statement`
#### UPDATE expression1 WITH expression2 IN collection
### UPDATE expression1 WITH expression2 IN collection
`PartialStatement::update(expression1).with(expression2).in(collection) : UpdateExpression`
@ -465,7 +514,7 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `update(expression1).with_(expression2).in_(collection)`
* `update(expression1).with_(expression2).into(collection)`
#### UPDATE expression IN collection
### UPDATE expression IN collection
`PartialStatement::update(expression).in(collection) : UpdateExpression`
@ -474,11 +523,11 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `update(expression).in_(collection)`
* `update(expression).into(collection)`
#### UPDATE … OPTIONS options
### UPDATE … OPTIONS options
`UpdateExpression::options(options) : Statement`
#### REPLACE expression1 WITH expression2 IN collection
### REPLACE expression1 WITH expression2 IN collection
`PartialStatement::replace(expression1).with(expression2).in(collection) : ReplaceExpression`
@ -490,7 +539,7 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `replace(expression1).with_(expression2).in_(collection)`
* `replace(expression1).with_(expression2).into(collection)`
#### REPLACE expression IN collection
### REPLACE expression IN collection
`PartialStatement::replace(expression).in(collection) : ReplaceExpression`
@ -499,6 +548,10 @@ qb.for('doc').in('my_collection').return('doc._key').toAQL()
* `replace(expression).in_(collection)`
* `replace(expression).into(collection)`
#### REPLACE … OPTIONS options
### REPLACE … OPTIONS options
`ReplaceExpression::options(options) : Statement`
# License
The Apache License, Version 2.0. For more information, see the accompanying LICENSE file.

View File

@ -1,5 +1,4 @@
/* jshint globalstrict: true, es3: true */
/* globals require: false, exports: false */
/*jshint browserify: true */
'use strict';
exports.keywords = [
'asc',

3
js/node/node_modules/aqb/errors.js generated vendored
View File

@ -1,5 +1,4 @@
/* jshint globalstrict: true, es3: true */
/* globals require: false, exports: false */
/*jshint browserify: true */
'use strict';
function AqlError(message) {
this.message = message;

59
js/node/node_modules/aqb/index.js generated vendored
View File

@ -1,26 +1,14 @@
/* jshint globalstrict: true, es3: true */
/* globals require: false, module: false, console: false */
/*jshint browserify: true */
/*globals console: false */
'use strict';
var AqlError = require('./errors').AqlError,
assumptions = require('./assumptions'),
types = require('./types'),
QB = {},
bind,
toArray,
warn;
bind = (typeof Function.prototype.bind === 'function' ? (
Function.prototype.call.bind(Function.prototype.bind)
) : (
function (fn, self) {
return function () {
return fn.apply(self, arguments);
};
}
));
toArray = bind(Function.prototype.call, Array.prototype.slice);
toArray = Function.prototype.call.bind(Array.prototype.slice);
warn = (function () {
if (typeof console !== 'undefined') {
@ -34,15 +22,14 @@ warn = (function () {
}
}());
for (var key in types._PartialStatement.prototype) {
if (types._PartialStatement.prototype.hasOwnProperty(key) && key !== 'constructor') {
QB[key] = bind(types._PartialStatement.prototype[key], null);
}
}
Object.keys(types._PartialStatement.prototype).forEach(function (key) {
if (key === 'constructor') return;
QB[key] = types._PartialStatement.prototype[key].bind(null);
});
QB.bool = function (value) {return new types.BooleanLiteral(value);};
QB.num = function (value) {return new types.NumberLiteral(value);};
QB.int_ = function (value) {return new types.IntegerLiteral(value);};
QB.int_ = QB.int = function (value) {return new types.IntegerLiteral(value);};
QB.str = function (value) {return new types.StringLiteral(value);};
QB.list = function (arr) {return new types.ListLiteral(arr);};
QB.obj = function (obj) {return new types.ObjectLiteral(obj);};
@ -58,9 +45,9 @@ QB.expr = function (value) {return new types.RawExpression(value);};
QB.and = function (/* x, y... */) {return new types.NAryOperation('&&', toArray(arguments));};
QB.or = function (/* x, y... */) {return new types.NAryOperation('||', toArray(arguments));};
QB.add = function (/* x, y... */) {return new types.NAryOperation('+', toArray(arguments));};
QB.sub = function (/* x, y... */) {return new types.NAryOperation('-', toArray(arguments));};
QB.mul = function (/* x, y... */) {return new types.NAryOperation('*', toArray(arguments));};
QB.plus = QB.add = function (/* x, y... */) {return new types.NAryOperation('+', toArray(arguments));};
QB.minus = QB.sub = function (/* x, y... */) {return new types.NAryOperation('-', toArray(arguments));};
QB.times = QB.mul = function (/* x, y... */) {return new types.NAryOperation('*', toArray(arguments));};
QB.div = function (/* x, y... */) {return new types.NAryOperation('/', toArray(arguments));};
QB.mod = function (/* x, y... */) {return new types.NAryOperation('%', toArray(arguments));};
QB.eq = function (x, y) {return new types.BinaryOperation('==', x, y);};
@ -71,16 +58,8 @@ QB.lte = function (x, y) {return new types.BinaryOperation('<=', x, y);};
QB.neq = function (x, y) {return new types.BinaryOperation('!=', x, y);};
QB.not = function (x) {return new types.UnaryOperation('!', x);};
QB.neg = function (x) {return new types.UnaryOperation('-', x);};
QB.in_ = function (x, y) {return new types.BinaryOperation('in', x, y);};
QB.if_ = function (x, y, z) {return new types.TernaryOperation('?', ':', x, y, z);};
QB.plus = QB.add;
QB.minus = QB.sub;
QB.times = QB.mul;
QB['int'] = QB.int_;
QB['in'] = QB.in_;
QB['if'] = QB.if_;
QB.in_ = QB.in = function (x, y) {return new types.BinaryOperation('in', x, y);};
QB.if_ = QB.if = function (x, y, z) {return new types.TernaryOperation('?', ':', x, y, z);};
QB.fn = function (functionName, arity) {
if (typeof arity === 'number') {
@ -120,13 +99,11 @@ function deprecateAqlFunction(fn, functionName) {
};
}
for (var key in assumptions.builtins) {
if (assumptions.builtins.hasOwnProperty(key)) {
QB[key] = QB.fn(key, assumptions.builtins[key]);
if (assumptions.deprecatedBuiltins.indexOf(key) !== -1) {
QB[key] = deprecateAqlFunction(QB[key], key);
}
Object.keys(assumptions.builtins).forEach(function (key) {
QB[key] = QB.fn(key, assumptions.builtins[key]);
if (assumptions.deprecatedBuiltins.indexOf(key) !== -1) {
QB[key] = deprecateAqlFunction(QB[key], key);
}
}
});
module.exports = QB;

File diff suppressed because one or more lines are too long

249
js/node/node_modules/aqb/types.js generated vendored
View File

@ -1,8 +1,7 @@
/* jshint globalstrict: true, es3: true */
/* globals require: false, exports: false */
/*jshint browserify: true */
'use strict';
var AqlError = require('./errors').AqlError,
keywords = require('./assumptions').keywords;
var AqlError = require('./errors').AqlError;
var keywords = require('./assumptions').keywords;
function wrapAQL(expr) {
if (
@ -15,50 +14,69 @@ function wrapAQL(expr) {
return expr.toAQL();
}
function isValidNumber(number) {
return (
number === number &&
number !== Infinity &&
number !== -Infinity
);
}
function castNumber(number) {
if (Math.floor(number) === number) {
return new IntegerLiteral(number);
}
return new NumberLiteral(number);
}
function castBoolean(bool) {
return new BooleanLiteral(bool);
}
function castString(str) {
if (str.match(NumberLiteral.re)) {
return autoCastToken(Number(str));
}
if (str.charAt(0) === '"') {
return new StringLiteral(JSON.parse(str));
}
var match = str.match(RangeExpression.re);
if (match) {
return new RangeExpression(Number(match[1]), Number(match[2]));
}
if (str.match(Identifier.re)) {
return new Identifier(str);
}
return new SimpleReference(str);
}
function castObject(obj) {
if (obj.constructor && obj.constructor.name === 'ArangoCollection') {
return new Identifier(obj.name());
}
if (Array.isArray(obj)) {
return new ListLiteral(obj);
}
return new ObjectLiteral(obj);
}
function autoCastToken(token) {
var match;
if (token === null || token === undefined) {
return new NullLiteral();
}
if (token instanceof Expression || token instanceof PartialStatement) {
return token;
}
if (typeof token === 'number' && token === token && token !== Infinity && token !== -Infinity) {
if (Math.floor(token) === token) {
return new IntegerLiteral(token);
}
return new NumberLiteral(token);
var type = typeof token;
if (Object.keys(autoCastToken).indexOf(type) === -1) {
throw new AqlError('Invalid AQL value: (' + type + ') ' + token);
}
if (typeof token === 'boolean') {
return new BooleanLiteral(token);
}
if (typeof token === 'string') {
if (token.match(/^[-+]?[0-9]+(\.[0-9]+)?$/)) {
return autoCastToken(Number(token));
}
if (token.charAt(0) === '"') {
return new StringLiteral(JSON.parse(token));
}
match = token.match(/^([0-9]+)\.\.([0-9]+)$/);
if (match) {
return new RangeExpression(Number(match[1]), Number(match[2]));
}
if (token.match(Identifier.re)) {
return new Identifier(token);
}
return new SimpleReference(token);
}
if (typeof token === 'object') {
if (token.constructor && token.constructor.name === 'ArangoCollection') {
return new Identifier(token.name());
}
if (Object.prototype.toString.call(token) === '[object Array]') {
return new ListLiteral(token);
}
return new ObjectLiteral(token);
}
throw new AqlError('Invalid AQL value: (' + (typeof token) + ') ' + token);
return autoCastToken[type](token);
}
autoCastToken.number = castNumber;
autoCastToken.boolean = castBoolean;
autoCastToken.string = castString;
autoCastToken.object = castObject;
function Expression() {}
function Operation() {}
@ -98,10 +116,11 @@ function NumberLiteral(value) {
value instanceof IntegerLiteral
)) {value = value.value;}
this.value = Number(value);
if (this.value !== this.value || this.value === Infinity) {
if (!isValidNumber(this.value)) {
throw new AqlError('Expected value to be a finite number: ' + value);
}
}
NumberLiteral.re = /^[-+]?[0-9]+(\.[0-9]+)?$/;
NumberLiteral.prototype = new Expression();
NumberLiteral.prototype.constructor = NumberLiteral;
NumberLiteral.prototype.toAQL = function () {return String(this.value);};
@ -112,7 +131,7 @@ function IntegerLiteral(value) {
value instanceof IntegerLiteral
)) {value = value.value;}
this.value = Number(value);
if (this.value !== this.value || this.value === Infinity || Math.floor(this.value) !== this.value) {
if (!isValidNumber(this.value) || Math.floor(this.value) !== this.value) {
throw new AqlError('Expected value to be a finite integer: ' + value);
}
}
@ -131,21 +150,15 @@ StringLiteral.prototype.toAQL = function () {return JSON.stringify(this.value);}
function ListLiteral(value) {
if (value && value instanceof ListLiteral) {value = value.value;}
if (!value || Object.prototype.toString.call(value) !== '[object Array]') {
if (!value || !Array.isArray(value)) {
throw new AqlError('Expected value to be an array: ' + value);
}
this.value = [];
for (var i = 0; i < value.length; i++) {
this.value[i] = autoCastToken(value[i]);
}
this.value = value.map(autoCastToken);
}
ListLiteral.prototype = new Expression();
ListLiteral.prototype.constructor = ListLiteral;
ListLiteral.prototype.toAQL = function () {
var value = [], i;
for (i = 0; i < this.value.length; i++) {
value.push(wrapAQL(this.value[i]));
}
var value = this.value.map(wrapAQL);
return '[' + value.join(', ') + ']';
};
@ -155,25 +168,22 @@ function ObjectLiteral(value) {
throw new AqlError('Expected value to be an object: ' + value);
}
this.value = {};
for (var key in value) {
if (value.hasOwnProperty(key)) {
this.value[key] = autoCastToken(value[key]);
}
}
var self = this;
Object.keys(value).forEach(function (key) {
self.value[key] = autoCastToken(value[key]);
});
}
ObjectLiteral.prototype = new Expression();
ObjectLiteral.prototype.constructor = ObjectLiteral;
ObjectLiteral.prototype.toAQL = function () {
var items = [], key;
for (key in this.value) {
if (this.value.hasOwnProperty(key)) {
if (key.match(Identifier.re) || key === String(Number(key))) {
items.push(key + ': ' + wrapAQL(this.value[key]));
} else {
items.push(JSON.stringify(key) + ': ' + wrapAQL(this.value[key]));
}
var value = this.value;
var items = Object.keys(value).map(function (key) {
if (key.match(Identifier.re) || key === String(Number(key))) {
return key + ': ' + wrapAQL(value[key]);
} else {
return JSON.stringify(key) + ': ' + wrapAQL(value[key]);
}
}
});
return '{' + items.join(', ') + '}';
};
@ -181,6 +191,7 @@ function RangeExpression(start, end) {
this.start = autoCastToken(start);
this.end = autoCastToken(end);
}
RangeExpression.re = /^([0-9]+)\.\.([0-9]+)$/;
RangeExpression.prototype = new Expression();
RangeExpression.prototype.constructor = RangeExpression;
RangeExpression.prototype.toAQL = function () {
@ -224,7 +235,7 @@ function Identifier(value) {
}
this.value = value;
}
Identifier.re = /^[_a-z][_0-9a-z]*$/i;
Identifier.re = /^[_a-z][-_0-9a-z]*$/i;
Identifier.prototype = new Expression();
Identifier.prototype.constructor = Identifier;
Identifier.prototype.toAQL = function () {
@ -249,14 +260,10 @@ SimpleReference.re = /^@{0,2}[_a-z][_0-9a-z]*(\.[_a-z][_0-9a-z]*|\[\*\])*$/i;
SimpleReference.prototype = new Expression();
SimpleReference.prototype.constructor = SimpleReference;
SimpleReference.prototype.toAQL = function () {
var value = String(this.value),
tokens = value.split('.'),
i;
for (i = 0; i < tokens.length; i++) {
if (keywords.indexOf(tokens[i]) !== -1) {
tokens[i] = '`' + tokens[i] + '`';
}
}
var value = String(this.value);
var tokens = value.split('.').map(function (token) {
return keywords.indexOf(token) === -1 ? token : '`' + token + '`';
});
return tokens.join('.');
};
@ -317,18 +324,12 @@ function NAryOperation(operator, values) {
throw new AqlError('Expected operator to be a string: ' + operator);
}
this.operator = operator;
this.values = [];
for (var i = 0; i < values.length; i++) {
this.values.push(autoCastToken(values[i]));
}
this.values = values.map(autoCastToken);
}
NAryOperation.prototype = new Operation();
NAryOperation.prototype.constructor = NAryOperation;
NAryOperation.prototype.toAQL = function () {
var values = [], i;
for (i = 0; i < this.values.length; i++) {
values.push(wrapAQL(this.values[i]));
}
var values = this.values.map(wrapAQL);
return values.join(' ' + this.operator + ' ');
};
@ -339,30 +340,22 @@ function FunctionCall(functionName, args) {
if (!functionName.match(FunctionCall.re)) {
throw new AqlError('Not a valid function name: ' + functionName);
}
if (args && Object.prototype.toString.call(args) !== '[object Array]') {
if (args && !Array.isArray(args)) {
throw new AqlError('Expected arguments to be an array: ' + args);
}
this.functionName = functionName;
this.args = [];
if (args) {
for (var i = 0; i < args.length; i++) {
this.args[i] = autoCastToken(args[i]);
}
}
this.args = args ? args.map(autoCastToken) : [];
}
FunctionCall.re = /^[_a-z][_0-9a-z]*(::[_a-z][_0-9a-z]*)*$/i;
FunctionCall.prototype = new Expression();
FunctionCall.prototype.constructor = FunctionCall;
FunctionCall.prototype.toAQL = function () {
var args = [], i;
for (i = 0; i < this.args.length; i++) {
args.push(wrapAQL(this.args[i]));
}
var args = this.args.map(wrapAQL);
return this.functionName + '(' + args.join(', ') + ')';
};
function PartialStatement() {}
PartialStatement.prototype.for_ = function (varname) {
PartialStatement.prototype.for = function (varname) {
var self = this, inFn;
inFn = function (expr) {
// assert expr is an expression
@ -371,7 +364,7 @@ PartialStatement.prototype.for_ = function (varname) {
return {'in': inFn, in_: inFn};
};
PartialStatement.prototype.filter = function (expr) {return new FilterExpression(this, expr);};
PartialStatement.prototype.let_ = function (varname, expr) {
PartialStatement.prototype.let = function (varname, expr) {
if (expr === undefined) {
return new LetExpression(this, varname);
}
@ -388,7 +381,7 @@ PartialStatement.prototype.sort = function () {
return new SortExpression(this, args);
};
PartialStatement.prototype.limit = function (x, y) {return new LimitExpression(this, x, y);};
PartialStatement.prototype.return_ = function (x) {return new ReturnExpression(this, x);};
PartialStatement.prototype.return = function (x) {return new ReturnExpression(this, x);};
PartialStatement.prototype.remove = function (expr) {
var self = this, inFn;
inFn = function (collection) {
@ -430,42 +423,39 @@ PartialStatement.prototype.replace = function (expr) {
return {'with': withFn, with_: withFn, into: inFn, 'in': inFn, in_: inFn};
};
PartialStatement.prototype['for'] = PartialStatement.prototype.for_;
PartialStatement.prototype['let'] = PartialStatement.prototype.let_;
PartialStatement.prototype['return'] = PartialStatement.prototype.return_;
PartialStatement.prototype.for_ = PartialStatement.prototype.for;
PartialStatement.prototype.let_ = PartialStatement.prototype.let;
PartialStatement.prototype.return_ = PartialStatement.prototype.return;
function Definitions(dfns) {
if (dfns instanceof Definitions) {
dfns = dfns.dfns;
}
this.dfns = [];
var self = this;
if (!dfns || typeof dfns !== 'object') {
throw new AqlError('Expected definitions to be an object');
}
if (Object.prototype.toString.call(dfns) === '[object Array]') {
for (var i = 0; i < dfns.length; i++) {
if (Object.prototype.toString.call(dfns[i]) !== '[object Array]' || dfns[i].length !== 2) {
if (Array.isArray(dfns)) {
dfns.forEach(function (dfn, i) {
if (!Array.isArray(dfn) || dfn.length !== 2) {
throw new AqlError('Expected definitions[' + i + '] to be a tuple');
}
this.dfns.push([new Identifier(dfns[i][0]), autoCastToken(dfns[i][1])]);
}
self.dfns.push([new Identifier(dfn[0]), autoCastToken(dfn[1])]);
});
} else {
for (var key in dfns) {
if (dfns.hasOwnProperty(key)) {
this.dfns.push([new Identifier(key), autoCastToken(dfns[key])]);
}
}
Object.keys(dfns).forEach(function (key) {
self.dfns.push([new Identifier(key), autoCastToken(dfns[key])]);
});
}
if (this.dfns.length === 0) {
throw new AqlError('Expected definitions not to be empty');
}
}
Definitions.prototype.toAQL = function () {
var dfns = [];
for (var i = 0; i < this.dfns.length; i++) {
dfns.push(this.dfns[i][0].toAQL() + ' = ' + wrapAQL(this.dfns[i][1]));
}
return dfns.join(', ');
return this.dfns.map(function (dfn) {
return dfn[0].toAQL() + ' = ' + wrapAQL(dfn[1]);
}).join(', ');
};
function ForExpression(prev, varname, expr) {
@ -541,7 +531,7 @@ CollectIntoExpression.prototype.toAQL = function () {
};
function SortExpression(prev, args) {
if (!args || Object.prototype.toString.call(args) !== '[object Array]') {
if (!args || !Array.isArray(args)) {
throw new AqlError('Expected sort list to be an array: ' + args);
}
if (!args.length) {
@ -549,37 +539,36 @@ function SortExpression(prev, args) {
}
this.prev = prev;
this.args = [];
var allowKeyword = false, i, value;
for (i = 0; i < args.length; i++) {
value = args[i];
if (!allowKeyword && value) {
if (value instanceof Keyword || (
typeof value === 'string' && SortExpression.keywords.indexOf(value.toUpperCase()) !== -1
var allowKeyword = false;
this.args = args.map(function (arg, i) {
if (!allowKeyword && arg) {
if (arg instanceof Keyword || (
typeof arg === 'string' && SortExpression.keywords.indexOf(arg.toUpperCase()) !== -1
)) {
throw new AqlError('Unexpected keyword ' + value.toString() + ' at offset ' + i);
throw new AqlError('Unexpected keyword ' + arg.toString() + ' at offset ' + i);
}
}
if (typeof value === 'string' && SortExpression.keywords.indexOf(value.toUpperCase()) !== -1) {
this.args[i] = new Keyword(value);
if (typeof arg === 'string' && SortExpression.keywords.indexOf(arg.toUpperCase()) !== -1) {
allowKeyword = false;
return new Keyword(arg);
} else {
this.args[i] = autoCastToken(value);
allowKeyword = true;
return autoCastToken(arg);
}
}
});
}
SortExpression.keywords = ['ASC', 'DESC'];
SortExpression.prototype = new PartialStatement();
SortExpression.prototype.constructor = SortExpression;
SortExpression.prototype.toAQL = function () {
var args = [], j = 0;
for (var i = 0; i < this.args.length; i++) {
if (this.args[i] instanceof Keyword) {
args[j] += ' ' + this.args[i].toAQL();
this.args.forEach(function (arg, i) {
if (arg instanceof Keyword) {
args[j] += ' ' + arg.toAQL();
} else {
j = args.push(wrapAQL(this.args[i])) - 1;
j = args.push(wrapAQL(arg)) - 1;
}
}
});
return (
(this.prev ? this.prev.toAQL() + ' ' : '') +
'SORT ' +

View File

@ -1,6 +1,6 @@
{
"dependencies": {
"aqb": "1.4.1",
"aqb": "1.5.1",
"buster-format": "0.5.6",
"cheerio": "0.17.0",
"coffee-script": "1.7.1",

View File

@ -181,6 +181,7 @@ ERROR_CLUSTER_ONLY_ON_COORDINATOR,1471,"this operation is only valid on a coordi
ERROR_CLUSTER_READING_PLAN_AGENCY,1472,"error reading Plan in agency","Will be raised if a coordinator or DBserver cannot read the Plan in the agency."
ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION,1473,"could not truncate collection","Will be raised if a coordinator cannot truncate all shards of a cluster collection."
ERROR_CLUSTER_AQL_COMMUNICATION,1474,"error in cluster internal communication for AQL","Will be raised if the internal communication of the cluster for AQL produces an error."
ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED,1475,"document not found or sharding attributes changed","Will be raised when a document with a given identifier or handle is unknown, or if the sharding attributes have been changed in a REPLACE operation in the cluster."
################################################################################
## ArangoDB query errors

View File

@ -141,6 +141,7 @@ void TRI_InitialiseErrorMessages () {
REG_ERROR(ERROR_CLUSTER_READING_PLAN_AGENCY, "error reading Plan in agency");
REG_ERROR(ERROR_CLUSTER_COULD_NOT_TRUNCATE_COLLECTION, "could not truncate collection");
REG_ERROR(ERROR_CLUSTER_AQL_COMMUNICATION, "error in cluster internal communication for AQL");
REG_ERROR(ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED, "document not found or sharding attributes changed");
REG_ERROR(ERROR_QUERY_KILLED, "query killed");
REG_ERROR(ERROR_QUERY_PARSE, "%s");
REG_ERROR(ERROR_QUERY_EMPTY, "query is empty");

View File

@ -344,6 +344,10 @@
/// - 1474: @LIT{error in cluster internal communication for AQL}
/// Will be raised if the internal communication of the cluster for AQL
/// produces an error.
/// - 1475: @LIT{document not found or sharding attributes changed}
/// Will be raised when a document with a given identifier or handle is
/// unknown, or if the sharding attributes have been changed in a REPLACE
/// operation in the cluster.
/// - 1500: @LIT{query killed}
/// Will be raised when a running query is killed by an explicit admin
/// command.
@ -2018,6 +2022,18 @@ void TRI_InitialiseErrorMessages ();
#define TRI_ERROR_CLUSTER_AQL_COMMUNICATION (1474)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1475: ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED
///
/// document not found or sharding attributes changed
///
/// Will be raised when a document with a given identifier or handle is
/// unknown, or if the sharding attributes have been changed in a REPLACE
/// operation in the cluster.
////////////////////////////////////////////////////////////////////////////////
#define TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND_OR_SHARDING_ATTRIBUTES_CHANGED (1475)
////////////////////////////////////////////////////////////////////////////////
/// @brief 1500: ERROR_QUERY_KILLED
///