diff --git a/Documentation/Books/Users/AqlExamples/Join.mdpp b/Documentation/Books/Users/AqlExamples/Join.mdpp index e0d38b098f..3c8ca228f8 100644 --- a/Documentation/Books/Users/AqlExamples/Join.mdpp +++ b/Documentation/Books/Users/AqlExamples/Join.mdpp @@ -64,7 +64,7 @@ FOR u IN users !SUBSECTION Horizontal lists -Note that in the above result, an user might be returned multiple times. This is the +Note that in the above result, a user might be returned multiple times. This is the SQL way of returning data. If this is not desired, the friends' ids of each user can be returned in a horizontal list. This will return each user at most once. diff --git a/Documentation/Books/Users/localtheme/assets/style.css b/Documentation/Books/Users/localtheme/assets/style.css index d60b911c02..3b91ab54df 100755 --- a/Documentation/Books/Users/localtheme/assets/style.css +++ b/Documentation/Books/Users/localtheme/assets/style.css @@ -1,221 +1,221 @@ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary { - display: block +display: block } audio,canvas,video { - display: inline-block +display: inline-block } audio:not([controls]) { - display: none; - height: 0 +display: none; +height: 0 } [hidden] { - display: none +display: none } html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100% +font-family: sans-serif; +-webkit-text-size-adjust: 100%; +-ms-text-size-adjust: 100% } body { - margin: 0 +margin: 0 } a:focus { - outline: thin dotted +outline: thin dotted } a:active,a:hover { - outline: 0 +outline: 0 } h1 { - font-size: 2em; - margin: .67em 0 +font-size: 2em; +margin: .67em 0 } abbr[title] { - border-bottom: 1px dotted +border-bottom: 1px dotted } b,strong { - font-weight: bold +font-weight: bold } dfn { - font-style: italic +font-style: italic } hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0 +-moz-box-sizing: content-box; +box-sizing: content-box; +height: 0 } mark { - background: #ff0; - color: #000 +background: #ff0; +color: #000 } code,kbd,pre,samp { - font-family: monospace,serif; - font-size: 1em +font-family: monospace,serif; +font-size: 1em } pre { - white-space: pre-wrap +white-space: pre-wrap } q { - quotes: "\201C" "\201D" "\2018" "\2019" +quotes: "\201C" "\201D" "\2018" "\2019" } small { - font-size: 80% +font-size: 80% } sub,sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline +font-size: 75%; +line-height: 0; +position: relative; +vertical-align: baseline } sup { - top: -0.5em +top: -0.5em } sub { - bottom: -0.25em +bottom: -0.25em } img { - border: 0 +border: 0 } svg:not(:root) { - overflow: hidden +overflow: hidden } figure { - margin: 0 +margin: 0 } fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: .35em .625em .75em +border: 1px solid #c0c0c0; +margin: 0 2px; +padding: .35em .625em .75em } legend { - border: 0; - padding: 0 +border: 0; +padding: 0 } button,input,select,textarea { - font-family: inherit; - font-size: 100%; - margin: 0 +font-family: inherit; +font-size: 100%; +margin: 0 } button,input { - line-height: normal +line-height: normal } button,select { - text-transform: none +text-transform: none } button,html input[type="button"],input[type="reset"],input[type="submit"] { - -webkit-appearance: button; - cursor: pointer +-webkit-appearance: button; +cursor: pointer } button[disabled],html input[disabled] { - cursor: default +cursor: default } input[type="checkbox"],input[type="radio"] { - box-sizing: border-box; - padding: 0 +box-sizing: border-box; +padding: 0 } input[type="search"] { - -webkit-appearance: textfield; - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - box-sizing: content-box +-webkit-appearance: textfield; +-moz-box-sizing: content-box; +-webkit-box-sizing: content-box; +box-sizing: content-box } input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none +-webkit-appearance: none } button::-moz-focus-inner,input::-moz-focus-inner { - border: 0; - padding: 0 +border: 0; +padding: 0 } textarea { - overflow: auto; - vertical-align: top +overflow: auto; +vertical-align: top } table { - border-collapse: collapse; - border-spacing: 0 +border-collapse: collapse; +border-spacing: 0 } @font-face { - font-family: 'FontAwesome'; - src: url('.//fonts/fontawesome/fontawesome-webfont.eot?v=4.1.0'); - src: url('.//fonts/fontawesome/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('.//fonts/fontawesome/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('.//fonts/fontawesome/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('.//fonts/fontawesome/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); - font-weight: normal; - font-style: normal +font-family: 'FontAwesome'; +src: url('.//fonts/fontawesome/fontawesome-webfont.eot?v=4.1.0'); +src: url('.//fonts/fontawesome/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('.//fonts/fontawesome/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('.//fonts/fontawesome/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('.//fonts/fontawesome/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); +font-weight: normal; +font-style: normal } .fa { - display: inline-block; - font-family: FontAwesome; - font-style: normal; - font-weight: normal; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale +display: inline-block; +font-family: FontAwesome; +font-style: normal; +font-weight: normal; +line-height: 1; +-webkit-font-smoothing: antialiased; +-moz-osx-font-smoothing: grayscale } .fa-lg { - font-size: 1.3333333333333333em; - line-height: .75em; - vertical-align: -15% +font-size: 1.3333333333333333em; +line-height: .75em; +vertical-align: -15% } .fa-2x { - font-size: 2em +font-size: 2em } .fa-3x { - font-size: 3em +font-size: 3em } .fa-4x { - font-size: 4em +font-size: 4em } .fa-5x { - font-size: 5em +font-size: 5em } .fa-fw { - width: 1.2857142857142858em; - text-align: center +width: 1.2857142857142858em; +text-align: center } .fa-ul { - padding-left: 0; - margin-left: 2.142857142857143em; - list-style-type: none +padding-left: 0; +margin-left: 2.142857142857143em; +list-style-type: none } .fa-ul>li { - position: relative +position: relative } .fa-li { - position: absolute; - left: -2.142857142857143em; - width: 2.142857142857143em; - top: .14285714285714285em; - text-align: center +position: absolute; +left: -2.142857142857143em; +width: 2.142857142857143em; +top: .14285714285714285em; +text-align: center } .fa-li.fa-lg { - left: -1.8571428571428572em +left: -1.8571428571428572em } .fa-border { - padding: .2em .25em .15em; - border: solid .08em #eee; - border-radius: .1em +padding: .2em .25em .15em; +border: solid .08em #eee; +border-radius: .1em } .pull-right { - float: right +float: right } .pull-left { - float: left +float: left } .fa.pull-left { - margin-right: .3em +margin-right: .3em } .fa.pull-right { - margin-left: .3em +margin-left: .3em } .fa-spin { - -webkit-animation: spin 2s infinite linear; - -moz-animation: spin 2s infinite linear; - -o-animation: spin 2s infinite linear; - animation: spin 2s infinite linear +-webkit-animation: spin 2s infinite linear; +-moz-animation: spin 2s infinite linear; +-o-animation: spin 2s infinite linear; +animation: spin 2s infinite linear } @-moz-keyframes spin {0% { - -moz-transform: rotate(0deg) +-moz-transform: rotate(0deg) } 100% { - -moz-transform: rotate(359deg) +-moz-transform: rotate(359deg) } } @@ -1634,6 +1634,9 @@ color: inherit .hidden { display: none } +.lets-see-who-copies-this: { +font-family: 'proven!'; +} @font-face { font-family: 'Merriweather'; font-style: normal; @@ -2904,10 +2907,10 @@ font-family: "Open Sans","Clear Sans","Helvetica Neue",Helvetica,Arial,sans-seri font-size: 14px } div.example_show_button { - border: medium solid lightgray; - text-align: center; - position: relative; - top: -10px; +border: medium solid lightgray; +text-align: center; +position: relative; +top: -10px; } .book .book-body .page-wrapper .page-inner section.normal .deprecated{ @@ -2919,26 +2922,26 @@ background-color: rgba(240,240,0,0.4); } #clear-search { - display: inline; - position: absolute; - top: 12px; - right: 9px; - font-size: 18pt; - line-height: 18pt; - color: #364149; // needs to be updated on theme change +display: inline; +position: absolute; +top: 12px; +right: 9px; +font-size: 18pt; +line-height: 18pt; +color: #364149; // needs to be updated on theme change } #clear-search:hover { - color: #008cff; // needs to be updated on theme change - text-decoration: none; +color: #008cff; // needs to be updated on theme change +text-decoration: none; } .book .book-summary .book-search input{ - padding-right: 20px !important; +padding-right: 20px !important; } .book .book-header { - background-color: #FFFFFF !important; - width: 84%; - position:fixed !important; -} \ No newline at end of file +background-color: #FFFFFF !important; +width: 84%; +position:fixed !important; +} diff --git a/arangod/Aql/Collection.cpp b/arangod/Aql/Collection.cpp index 5aa9dc0328..77e8f3b410 100644 --- a/arangod/Aql/Collection.cpp +++ b/arangod/Aql/Collection.cpp @@ -111,6 +111,23 @@ std::vector Collection::shardIds () const { return ids; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the shard keys of a collection +//////////////////////////////////////////////////////////////////////////////// + +std::vector Collection::shardKeys () const { + auto clusterInfo = triagens::arango::ClusterInfo::instance(); + auto collectionInfo = clusterInfo->getCollection(std::string(vocbase->_name), name); + if (collectionInfo.get() == nullptr) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "collection not found"); + } + + std::vector keys; + for (auto const& x : collectionInfo.get()->shardKeys()) { + keys.emplace_back(x); + } + return keys; +} //////////////////////////////////////////////////////////////////////////////// /// @brief returns the indexes of the collection //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Collection.h b/arangod/Aql/Collection.h index bd932b0f00..de34f007a2 100644 --- a/arangod/Aql/Collection.h +++ b/arangod/Aql/Collection.h @@ -145,6 +145,12 @@ namespace triagens { std::vector shardIds () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the shard keys of a collection +//////////////////////////////////////////////////////////////////////////////// + + std::vector shardKeys () const; + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the indexes of the collection //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionBlock.cpp b/arangod/Aql/ExecutionBlock.cpp index 892cd1d9e8..82462888b9 100644 --- a/arangod/Aql/ExecutionBlock.cpp +++ b/arangod/Aql/ExecutionBlock.cpp @@ -3717,6 +3717,28 @@ size_t BlockWithClients::getClientId (std::string const& shardId) { return ((*it).second); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief preInitCursor: check if we should really init the cursor, and reset +/// _doneForClient +//////////////////////////////////////////////////////////////////////////////// + +bool BlockWithClients::preInitCursor () { + + if (!_initOrShutdown) { + return false; + } + + _doneForClient.clear(); + _doneForClient.reserve(_nrClients); + + for (size_t i = 0; i < _nrClients; i++) { + _doneForClient.push_back(false); + } + + _initOrShutdown = false; + return true; +} + // ----------------------------------------------------------------------------- // --SECTION-- class ScatterBlock // ----------------------------------------------------------------------------- @@ -3727,25 +3749,20 @@ size_t BlockWithClients::getClientId (std::string const& shardId) { int ScatterBlock::initializeCursor (AqlItemBlock* items, size_t pos) { - if (!_initOrShutdown) { + if (!preInitCursor()) { return TRI_ERROR_NO_ERROR; } - - int res = ExecutionBlock::initializeCursor(items, pos); + int res = ExecutionBlock::initializeCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; } _posForClient.clear(); - _doneForClient.clear(); - _doneForClient.reserve(_nrClients); - + for (size_t i = 0; i < _nrClients; i++) { _posForClient.push_back(std::make_pair(0, 0)); - _doneForClient.push_back(false); } - _initOrShutdown = false; return TRI_ERROR_NO_ERROR; } @@ -3896,27 +3913,18 @@ DistributeBlock::DistributeBlock (ExecutionEngine* engine, int DistributeBlock::initializeCursor (AqlItemBlock* items, size_t pos) { - if (!_initOrShutdown) { + if (!preInitCursor()) { return TRI_ERROR_NO_ERROR; } - - int res = ExecutionBlock::initializeCursor(items, pos); + int res = ExecutionBlock::initializeCursor(items, pos); if (res != TRI_ERROR_NO_ERROR) { return res; } - for (auto x: _distBuffer) { - x.clear(); - } _distBuffer.clear(); _distBuffer.reserve(_nrClients); - for (auto x: _doneForClient) { - x = false; - } - - _initOrShutdown = false; return TRI_ERROR_NO_ERROR; } @@ -4114,11 +4122,18 @@ bool DistributeBlock::getBlockForClient (size_t atLeast, //////////////////////////////////////////////////////////////////////////////// size_t DistributeBlock::sendToClient (AqlValue val) { - - TRI_ASSERT(val._type == AqlValue::JSON); - //TODO should work for SHAPED too (maybe this requires converting it to TRI_json_t) - TRI_json_t const* json = val._json->json(); + TRI_json_t const* json; + if (val._type == AqlValue::JSON) { + json = val._json->json(); + } + else if (val._type == AqlValue::SHAPED) { + json = val.toJson(_trx, _collection->documentCollection()).json(); + } + else { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_FAILED, + "DistributeBlock: can only send JSON or SHAPED"); + } std::string shardId; bool usesDefaultShardingAttributes; diff --git a/arangod/Aql/ExecutionBlock.h b/arangod/Aql/ExecutionBlock.h index 12dc2f973b..1a64de131d 100644 --- a/arangod/Aql/ExecutionBlock.h +++ b/arangod/Aql/ExecutionBlock.h @@ -1613,6 +1613,13 @@ namespace triagens { // --SECTION-- BlockWithClients protected methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief preInitCursor: check if we should really init the cursor, and reset +/// _doneForClient +//////////////////////////////////////////////////////////////////////////////// + + bool preInitCursor (); + //////////////////////////////////////////////////////////////////////////////// /// @brief getOrSkipSomeForShard //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/ExecutionNode.cpp b/arangod/Aql/ExecutionNode.cpp index a5cbcbc399..88673ba3e5 100644 --- a/arangod/Aql/ExecutionNode.cpp +++ b/arangod/Aql/ExecutionNode.cpp @@ -2448,7 +2448,6 @@ void DistributeNode::toJsonHelper (triagens::basics::Json& nodes, ("collection", triagens::basics::Json(_collection->getName())) ("varId", triagens::basics::Json(static_cast(_varId))); - // And add it: nodes(json); } diff --git a/arangod/Aql/Executor.cpp b/arangod/Aql/Executor.cpp index 9d31e76313..286501ae08 100644 --- a/arangod/Aql/Executor.cpp +++ b/arangod/Aql/Executor.cpp @@ -421,6 +421,16 @@ void Executor::generateCodeString (char const* value) { _buffer->appendChar('"'); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief generates code for a string value +//////////////////////////////////////////////////////////////////////////////// + +void Executor::generateCodeString (std::string const& value) { + _buffer->appendChar('"'); + _buffer->appendJsonEncoded(value.c_str()); + _buffer->appendChar('"'); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief generate JavaScript code for a list //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Executor.h b/arangod/Aql/Executor.h index e1b5bfa790..ae87d0b712 100644 --- a/arangod/Aql/Executor.h +++ b/arangod/Aql/Executor.h @@ -27,8 +27,8 @@ /// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -#ifndef ARANGODB_AQL_V8_EXECUTOR_H -#define ARANGODB_AQL_V8_EXECUTOR_H 1 +#ifndef ARANGODB_AQL_EXECUTOR_H +#define ARANGODB_AQL_EXECUTOR_H 1 #include "Basics/Common.h" #include "Aql/Function.h" @@ -121,6 +121,12 @@ namespace triagens { void generateCodeString (char const*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief generates code for a string value +//////////////////////////////////////////////////////////////////////////////// + + void generateCodeString (std::string const&); + //////////////////////////////////////////////////////////////////////////////// /// @brief generate JavaScript code for a list //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/OptimizerRules.cpp b/arangod/Aql/OptimizerRules.cpp index 93280898c2..51a871769b 100644 --- a/arangod/Aql/OptimizerRules.cpp +++ b/arangod/Aql/OptimizerRules.cpp @@ -1688,7 +1688,6 @@ int triagens::aql::scatterInCluster (Optimizer* opt, // re-link with the remote node node->addDependency(remoteNode); - // insert another remote node remoteNode = new RemoteNode(plan, plan->nextId(), vocbase, collection, "", "", ""); @@ -1742,21 +1741,30 @@ int triagens::aql::distributeInCluster (Optimizer* opt, auto const nodeType = node->getType(); if (nodeType != ExecutionNode::INSERT && - nodeType != ExecutionNode::UPDATE && - nodeType != ExecutionNode::REPLACE && nodeType != ExecutionNode::REMOVE) { opt->addPlan(plan, rule->level, wasModified); return TRI_ERROR_NO_ERROR; } + + Collection const* collection = static_cast(node)->collection(); + + if (nodeType == ExecutionNode::REMOVE) { + // check if collection shard keys are only _key + std::vector shardKeys = collection->shardKeys(); + if (shardKeys.size() != 1 || shardKeys[0] != "_key") { + opt->addPlan(plan, rule->level, wasModified); + return TRI_ERROR_NO_ERROR; + } + } + auto deps = node->getDependencies(); TRI_ASSERT(deps.size() == 1); // unlink the node plan->unlinkNode(node, true); - // extract database and collection from plan node + // extract database from plan node TRI_vocbase_t* vocbase = static_cast(node)->vocbase(); - Collection const* collection = static_cast(node)->collection(); // insert a distribute node TRI_ASSERT(node->getVariablesUsedHere().size() == 1); @@ -1782,6 +1790,7 @@ int triagens::aql::distributeInCluster (Optimizer* opt, opt->addPlan(plan, rule->level, wasModified); return TRI_ERROR_NO_ERROR; } + //////////////////////////////////////////////////////////////////////////////// /// @brief move filters up into the cluster distribution part of the plan /// this rule modifies the plan in place diff --git a/arangod/Aql/Query.cpp b/arangod/Aql/Query.cpp index 8ecaac1e58..14bea4d43a 100644 --- a/arangod/Aql/Query.cpp +++ b/arangod/Aql/Query.cpp @@ -37,9 +37,11 @@ #include "Basics/JsonHelper.h" #include "Basics/json.h" #include "Basics/tri-strings.h" +#include "Cluster/ServerState.h" #include "Utils/AqlTransaction.h" #include "Utils/Exception.h" #include "Utils/V8TransactionContext.h" +#include "V8Server/ApplicationV8.h" #include "VocBase/vocbase.h" using namespace triagens::aql; @@ -115,14 +117,18 @@ TRI_json_t* Profile::toJson (TRI_memory_zone_t*) { /// @brief creates a query //////////////////////////////////////////////////////////////////////////////// -Query::Query (TRI_vocbase_t* vocbase, +Query::Query (triagens::arango::ApplicationV8* applicationV8, + bool contextOwnedByExterior, + TRI_vocbase_t* vocbase, char const* queryString, size_t queryLength, TRI_json_t* bindParameters, TRI_json_t* options, QueryPart part) - : _vocbase(vocbase), + : _applicationV8(applicationV8), + _vocbase(vocbase), _executor(nullptr), + _context(nullptr), _queryString(queryString), _queryLength(queryLength), _queryJson(), @@ -137,7 +143,9 @@ Query::Query (TRI_vocbase_t* vocbase, _parser(nullptr), _trx(nullptr), _engine(nullptr), - _part(part) { + _part(part), + _clusterStatus(-1), + _contextOwnedByExterior(contextOwnedByExterior) { TRI_ASSERT(_vocbase != nullptr); @@ -156,12 +164,16 @@ Query::Query (TRI_vocbase_t* vocbase, /// @brief creates a query from Json //////////////////////////////////////////////////////////////////////////////// -Query::Query (TRI_vocbase_t* vocbase, +Query::Query (triagens::arango::ApplicationV8* applicationV8, + bool contextOwnedByExterior, + TRI_vocbase_t* vocbase, triagens::basics::Json queryStruct, TRI_json_t* options, QueryPart part) - : _vocbase(vocbase), + : _applicationV8(applicationV8), + _vocbase(vocbase), _executor(nullptr), + _context(nullptr), _queryString(nullptr), _queryLength(0), _queryJson(queryStruct), @@ -176,7 +188,9 @@ Query::Query (TRI_vocbase_t* vocbase, _parser(nullptr), _trx(nullptr), _engine(nullptr), - _part(part) { + _part(part), + _clusterStatus(-1), + _contextOwnedByExterior(contextOwnedByExterior) { TRI_ASSERT(_vocbase != nullptr); @@ -213,6 +227,12 @@ Query::~Query () { _executor = nullptr; } + if (_context != nullptr) { + TRI_ASSERT(! _contextOwnedByExterior); + _applicationV8->exitContext(_context); + _context = nullptr; + } + if (_ast != nullptr) { delete _ast; _ast = nullptr; @@ -242,7 +262,9 @@ Query* Query::clone (QueryPart part) { std::unique_ptr clone; try { - clone.reset(new Query(_vocbase, + clone.reset(new Query(_applicationV8, + false, + _vocbase, _queryString, _queryLength, nullptr, @@ -759,6 +781,55 @@ char* Query::registerString (std::string const& p, // --SECTION-- private methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not we are running in a cluster +//////////////////////////////////////////////////////////////////////////////// + +bool Query::isRunningInCluster () { + if (_clusterStatus == -1) { + // not yet determined + _clusterStatus = 0; + if (triagens::arango::ServerState::instance()->isRunningInCluster()) { + _clusterStatus = 1; + } + } + TRI_ASSERT(_clusterStatus == 0 || _clusterStatus == 1); + return (_clusterStatus == 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief enter a V8 context +//////////////////////////////////////////////////////////////////////////////// + +void Query::enterContext () { + if (! _contextOwnedByExterior) { + if (_context == nullptr) { + _context = _applicationV8->enterContext("STANDARD", _vocbase, false, false); + if (_context == nullptr) { + THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "cannot enter V8 context"); + } + } + + TRI_ASSERT(_context != nullptr); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief return a V8 context +//////////////////////////////////////////////////////////////////////////////// + +void Query::exitContext () { + if (! _contextOwnedByExterior) { + if (_context != nullptr) { + if (isRunningInCluster()) { + _applicationV8->exitContext(_context); + _context = nullptr; + } + } + TRI_ASSERT(_context == nullptr); + } +} + //////////////////////////////////////////////////////////////////////////////// /// @brief fetch a boolean value from the options //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/Aql/Query.h b/arangod/Aql/Query.h index de9f4faf1f..9fa479a7df 100644 --- a/arangod/Aql/Query.h +++ b/arangod/Aql/Query.h @@ -38,6 +38,7 @@ #include "Aql/types.h" #include "Utils/AqlTransaction.h" #include "Utils/V8TransactionContext.h" +#include "V8Server/ApplicationV8.h" struct TRI_json_t; struct TRI_vocbase_s; @@ -127,14 +128,18 @@ namespace triagens { public: - Query (struct TRI_vocbase_s*, + Query (triagens::arango::ApplicationV8*, + bool, + struct TRI_vocbase_s*, char const*, size_t, struct TRI_json_t*, struct TRI_json_t*, QueryPart); - Query (struct TRI_vocbase_s*, + Query (triagens::arango::ApplicationV8*, + bool, + struct TRI_vocbase_s*, triagens::basics::Json queryStruct, struct TRI_json_t*, QueryPart); @@ -362,10 +367,28 @@ namespace triagens { void setPlan (ExecutionPlan *plan); +//////////////////////////////////////////////////////////////////////////////// +/// @brief enter a V8 context +//////////////////////////////////////////////////////////////////////////////// + + void enterContext (); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief exits a V8 context +//////////////////////////////////////////////////////////////////////////////// + + void exitContext (); + // ----------------------------------------------------------------------------- // --SECTION-- private methods // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not we are running in a cluster +//////////////////////////////////////////////////////////////////////////////// + + bool isRunningInCluster (); + //////////////////////////////////////////////////////////////////////////////// /// @brief fetch a boolean value from the options //////////////////////////////////////////////////////////////////////////////// @@ -416,96 +439,108 @@ namespace triagens { private: +//////////////////////////////////////////////////////////////////////////////// +/// @brief application v8 used in the query, we need this for V8 context access +//////////////////////////////////////////////////////////////////////////////// + + triagens::arango::ApplicationV8* _applicationV8; + //////////////////////////////////////////////////////////////////////////////// /// @brief all nodes created in the AST - will be used for freeing them later //////////////////////////////////////////////////////////////////////////////// - std::vector _nodes; + std::vector _nodes; //////////////////////////////////////////////////////////////////////////////// /// @brief pointer to vocbase the query runs in //////////////////////////////////////////////////////////////////////////////// - struct TRI_vocbase_s* _vocbase; + struct TRI_vocbase_s* _vocbase; //////////////////////////////////////////////////////////////////////////////// /// @brief V8 code executor //////////////////////////////////////////////////////////////////////////////// - Executor* _executor; + Executor* _executor; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief the currently used V8 context +//////////////////////////////////////////////////////////////////////////////// + + triagens::arango::ApplicationV8::V8Context* _context; //////////////////////////////////////////////////////////////////////////////// /// @brief the actual query string //////////////////////////////////////////////////////////////////////////////// - char const* _queryString; + char const* _queryString; //////////////////////////////////////////////////////////////////////////////// /// @brief length of the query string in bytes //////////////////////////////////////////////////////////////////////////////// - size_t const _queryLength; + size_t const _queryLength; //////////////////////////////////////////////////////////////////////////////// /// @brief query in a JSON structure //////////////////////////////////////////////////////////////////////////////// - triagens::basics::Json const _queryJson; + triagens::basics::Json const _queryJson; //////////////////////////////////////////////////////////////////////////////// /// @brief bind parameters for the query //////////////////////////////////////////////////////////////////////////////// - BindParameters _bindParameters; + BindParameters _bindParameters; //////////////////////////////////////////////////////////////////////////////// /// @brief query options //////////////////////////////////////////////////////////////////////////////// - TRI_json_t* _options; + TRI_json_t* _options; //////////////////////////////////////////////////////////////////////////////// /// @brief collections used in the query //////////////////////////////////////////////////////////////////////////////// - Collections _collections; + Collections _collections; //////////////////////////////////////////////////////////////////////////////// /// @brief all strings created in the query - used for easy memory deallocation //////////////////////////////////////////////////////////////////////////////// - std::vector _strings; + std::vector _strings; //////////////////////////////////////////////////////////////////////////////// /// @brief _ast, we need an ast to manage the memory for AstNodes, even /// if we do not have a parser, because AstNodes occur in plans and engines //////////////////////////////////////////////////////////////////////////////// - Ast* _ast; + Ast* _ast; //////////////////////////////////////////////////////////////////////////////// /// @brief query execution profile //////////////////////////////////////////////////////////////////////////////// - Profile* _profile; + Profile* _profile; //////////////////////////////////////////////////////////////////////////////// /// @brief current state the query is in (used for profiling and error messages) //////////////////////////////////////////////////////////////////////////////// - ExecutionState _state; + ExecutionState _state; //////////////////////////////////////////////////////////////////////////////// /// @brief the ExecutionPlan object, if the query is prepared //////////////////////////////////////////////////////////////////////////////// - ExecutionPlan* _plan; + ExecutionPlan* _plan; //////////////////////////////////////////////////////////////////////////////// /// @brief the Parser object, if the query is prepared //////////////////////////////////////////////////////////////////////////////// - Parser* _parser; + Parser* _parser; //////////////////////////////////////////////////////////////////////////////// /// @brief the transaction object, in a distributed query every part of @@ -519,13 +554,25 @@ namespace triagens { /// @brief the ExecutionEngine object, if the query is prepared //////////////////////////////////////////////////////////////////////////////// - ExecutionEngine* _engine; + ExecutionEngine* _engine; //////////////////////////////////////////////////////////////////////////////// /// @brief the query part //////////////////////////////////////////////////////////////////////////////// - QueryPart const _part; + QueryPart const _part; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief internal variable we use to determine whether we are in a cluster +//////////////////////////////////////////////////////////////////////////////// + + short int _clusterStatus; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief whether or not someone else has acquired a V8 context for us +//////////////////////////////////////////////////////////////////////////////// + + bool const _contextOwnedByExterior; }; } diff --git a/arangod/Aql/RestAqlHandler.cpp b/arangod/Aql/RestAqlHandler.cpp index 7775a94426..9e711a3fbb 100644 --- a/arangod/Aql/RestAqlHandler.cpp +++ b/arangod/Aql/RestAqlHandler.cpp @@ -127,7 +127,7 @@ void RestAqlHandler::createQueryFromJson () { std::string const part = JsonHelper::getStringValue(queryJson.json(), "part", ""); - auto query = new Query(_vocbase, plan, options.steal(), (part == "main" ? PART_MAIN : PART_DEPENDENT)); + auto query = new Query(_applicationV8, false, _vocbase, plan, options.steal(), (part == "main" ? PART_MAIN : PART_DEPENDENT)); QueryResult res = query->prepare(_queryRegistry); if (res.code != TRI_ERROR_NO_ERROR) { generateError(HttpResponse::BAD, TRI_ERROR_QUERY_BAD_JSON_PLAN, @@ -188,7 +188,7 @@ void RestAqlHandler::parseQuery () { return; } - auto query = new Query(_vocbase, queryString.c_str(), queryString.size(), + auto query = new Query(_applicationV8, false, _vocbase, queryString.c_str(), queryString.size(), nullptr, nullptr, PART_MAIN); QueryResult res = query->parse(); if (res.code != TRI_ERROR_NO_ERROR) { @@ -244,7 +244,7 @@ void RestAqlHandler::explainQuery () { Json options; options = queryJson.get("options").copy(); // cannot throw - auto query = new Query(_vocbase, queryString.c_str(), queryString.size(), + auto query = new Query(_applicationV8, false, _vocbase, queryString.c_str(), queryString.size(), parameters.steal(), options.steal(), PART_MAIN); QueryResult res = query->explain(); if (res.code != TRI_ERROR_NO_ERROR) { @@ -304,7 +304,7 @@ void RestAqlHandler::createQueryFromString () { Json options; options = queryJson.get("options").copy(); // cannot throw - auto query = new Query(_vocbase, queryString.c_str(), queryString.size(), + auto query = new Query(_applicationV8, false, _vocbase, queryString.c_str(), queryString.size(), parameters.steal(), options.steal(), (part == "main" ? PART_MAIN : PART_DEPENDENT)); QueryResult res = query->prepare(_queryRegistry); if (res.code != TRI_ERROR_NO_ERROR) { @@ -586,84 +586,73 @@ 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"; - auto context = _applicationV8->enterContext("STANDARD", _vocbase, - false, false); - - if (nullptr == context) { - generateError(HttpResponse::SERVER_ERROR, TRI_ERROR_INTERNAL, - "cannot enter V8 context"); - } - else { - std::vector const& suffix = _request->suffix(); + std::vector const& suffix = _request->suffix(); - // extract the sub-request type - HttpRequest::HttpRequestType type = _request->requestType(); + // extract the sub-request type + HttpRequest::HttpRequestType type = _request->requestType(); - // execute one of the CRUD methods - switch (type) { - case HttpRequest::HTTP_REQUEST_POST: { - if (suffix.size() != 1) { - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - } - else if (suffix[0] == "instanciate") { - createQueryFromJson(); - } - else if (suffix[0] == "parse") { - parseQuery(); - } - else if (suffix[0] == "explain") { - explainQuery(); - } - else if (suffix[0] == "query") { - createQueryFromString(); - } - else { - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - } - break; + // execute one of the CRUD methods + switch (type) { + case HttpRequest::HTTP_REQUEST_POST: { + if (suffix.size() != 1) { + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); } - case HttpRequest::HTTP_REQUEST_DELETE: { - if (suffix.size() != 1) { - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - } - else { - deleteQuery(suffix[0]); - } - break; + else if (suffix[0] == "instanciate") { + createQueryFromJson(); } - case HttpRequest::HTTP_REQUEST_PUT: { - if (suffix.size() != 2) { - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - } - else { - useQuery(suffix[0], suffix[1]); - } - break; + else if (suffix[0] == "parse") { + parseQuery(); } - case HttpRequest::HTTP_REQUEST_GET: { - if (suffix.size() != 2) { - generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); - } - else { - getInfoQuery(suffix[0], suffix[1]); - } - break; + else if (suffix[0] == "explain") { + explainQuery(); } - case HttpRequest::HTTP_REQUEST_HEAD: - case HttpRequest::HTTP_REQUEST_PATCH: - case HttpRequest::HTTP_REQUEST_OPTIONS: - case HttpRequest::HTTP_REQUEST_ILLEGAL: { - generateError(HttpResponse::METHOD_NOT_ALLOWED, - TRI_ERROR_NOT_IMPLEMENTED, - "illegal method for /_api/aql"); - break; + else if (suffix[0] == "query") { + createQueryFromString(); } + else { + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + } + break; + } + case HttpRequest::HTTP_REQUEST_DELETE: { + if (suffix.size() != 1) { + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + } + else { + deleteQuery(suffix[0]); + } + break; + } + case HttpRequest::HTTP_REQUEST_PUT: { + if (suffix.size() != 2) { + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + } + else { + useQuery(suffix[0], suffix[1]); + } + break; + } + case HttpRequest::HTTP_REQUEST_GET: { + if (suffix.size() != 2) { + generateError(HttpResponse::NOT_FOUND, TRI_ERROR_HTTP_NOT_FOUND); + } + else { + getInfoQuery(suffix[0], suffix[1]); + } + break; + } + case HttpRequest::HTTP_REQUEST_HEAD: + case HttpRequest::HTTP_REQUEST_PATCH: + case HttpRequest::HTTP_REQUEST_OPTIONS: + case HttpRequest::HTTP_REQUEST_ILLEGAL: { + generateError(HttpResponse::METHOD_NOT_ALLOWED, + TRI_ERROR_NOT_IMPLEMENTED, + "illegal method for /_api/aql"); + break; } - } - - _applicationV8->exitContext(context); + //std::cout << "REQUEST HANDLING DONE: " << triagens::arango::ServerState::instance()->getId() << ": " << _request->fullUrl() << ": " << _response->responseCode() << ", CONTENT-LENGTH: " << _response->contentLength() << "\n"; return status_t(HANDLER_DONE); diff --git a/arangod/Cluster/ServerState.cpp b/arangod/Cluster/ServerState.cpp index 1625c50d0f..38299fc108 100644 --- a/arangod/Cluster/ServerState.cpp +++ b/arangod/Cluster/ServerState.cpp @@ -255,7 +255,6 @@ bool ServerState::isRunningInCluster () { _role == ServerState::ROLE_COORDINATOR); } - //////////////////////////////////////////////////////////////////////////////// /// @brief get the server id //////////////////////////////////////////////////////////////////////////////// diff --git a/arangod/V8Server/ApplicationV8.cpp b/arangod/V8Server/ApplicationV8.cpp index 70ece19d95..f8965e8725 100644 --- a/arangod/V8Server/ApplicationV8.cpp +++ b/arangod/V8Server/ApplicationV8.cpp @@ -1224,7 +1224,7 @@ bool ApplicationV8::prepareV8Instance (const string& name, size_t i, bool useAct context->_context->Enter(); - TRI_InitV8VocBridge(context->_context, _queryRegistry, _server, _vocbase, &_startupLoader, i); + TRI_InitV8VocBridge(this, context->_context, _queryRegistry, _server, _vocbase, &_startupLoader, i); TRI_InitV8Queries(context->_context); TRI_InitV8UserStructures(context->_context); diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index 68d6c7f19a..9775713a2d 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -1343,8 +1343,7 @@ void TRI_InitV8Actions (v8::Handle context, GlobalV8Dealer = applicationV8; // check the isolate - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - /* TRI_v8_global_t* v8g = */ TRI_CreateV8Globals(isolate); + v8::Isolate::GetCurrent(); // ............................................................................. // create the global functions diff --git a/arangod/V8Server/v8-dispatcher.cpp b/arangod/V8Server/v8-dispatcher.cpp index e1a959b7fb..d6490e70fb 100644 --- a/arangod/V8Server/v8-dispatcher.cpp +++ b/arangod/V8Server/v8-dispatcher.cpp @@ -462,8 +462,7 @@ void TRI_InitV8Dispatcher (v8::Handle context, GlobalV8Dealer = applicationV8; // check the isolate - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - /* TRI_v8_global_t* v8g = */ TRI_CreateV8Globals(isolate); + v8::Isolate::GetCurrent(); // ............................................................................. // create the global functions diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index cc08a24b08..83572e666b 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -821,7 +821,8 @@ static v8::Handle JS_ParseAql (v8::Arguments const& argv) { string const&& queryString = TRI_ObjectToString(argv[0]); - triagens::aql::Query query(vocbase, queryString.c_str(), queryString.size(), nullptr, nullptr, triagens::aql::PART_MAIN); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + triagens::aql::Query query(v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), nullptr, nullptr, triagens::aql::PART_MAIN); auto parseResult = query.parse(); @@ -906,7 +907,8 @@ static v8::Handle JS_ExplainAql (v8::Arguments const& argv) { } // bind parameters will be freed by the query later - triagens::aql::Query query(vocbase, queryString.c_str(), queryString.size(), parameters, options, triagens::aql::PART_MAIN); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + triagens::aql::Query query(v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), parameters, options, triagens::aql::PART_MAIN); auto queryResult = query.explain(); @@ -998,8 +1000,8 @@ static v8::Handle JS_ExecuteAqlJson (v8::Arguments const& argv) { } TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + triagens::aql::Query query(v8g->_applicationV8, true, vocbase, Json(TRI_UNKNOWN_MEM_ZONE, queryjson), options, triagens::aql::PART_MAIN); - triagens::aql::Query query(vocbase, Json(TRI_UNKNOWN_MEM_ZONE, queryjson), options, triagens::aql::PART_MAIN); auto queryResult = query.execute(static_cast(v8g->_queryRegistry)); if (queryResult.code != TRI_ERROR_NO_ERROR) { @@ -1133,10 +1135,10 @@ static v8::Handle JS_ExecuteAql (v8::Arguments const& argv) { options = TRI_ObjectToJson(argv[2]); } - TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); - // bind parameters will be freed by the query later - triagens::aql::Query query(vocbase, queryString.c_str(), queryString.size(), parameters, options, triagens::aql::PART_MAIN); + TRI_v8_global_t* v8g = static_cast(v8::Isolate::GetCurrent()->GetData()); + triagens::aql::Query query(v8g->_applicationV8, true, vocbase, queryString.c_str(), queryString.size(), parameters, options, triagens::aql::PART_MAIN); + auto queryResult = query.execute(static_cast(v8g->_queryRegistry)); if (queryResult.code != TRI_ERROR_NO_ERROR) { @@ -2489,7 +2491,8 @@ void TRI_V8ReloadRouting (v8::Handle context) { /// @brief creates a TRI_vocbase_t global context //////////////////////////////////////////////////////////////////////////////// -void TRI_InitV8VocBridge (v8::Handle context, +void TRI_InitV8VocBridge (triagens::arango::ApplicationV8* applicationV8, + v8::Handle context, triagens::aql::QueryRegistry* queryRegistry, TRI_server_t* server, TRI_vocbase_t* vocbase, @@ -2502,7 +2505,6 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_v8_global_t* v8g = TRI_CreateV8Globals(isolate); TRI_ASSERT(v8g->_transactionContext == nullptr); - v8g->_transactionContext = new V8TransactionContext(true); static_cast(v8g->_transactionContext)->makeGlobal(); @@ -2517,6 +2519,9 @@ void TRI_InitV8VocBridge (v8::Handle context, // register the startup loader v8g->_loader = loader; + + // register the context dealer + v8g->_applicationV8 = applicationV8; v8::Handle ArangoNS; v8::Handle rt; diff --git a/arangod/V8Server/v8-vocbase.h b/arangod/V8Server/v8-vocbase.h index 5c323928d5..3167aaa21a 100644 --- a/arangod/V8Server/v8-vocbase.h +++ b/arangod/V8Server/v8-vocbase.h @@ -45,6 +45,7 @@ namespace triagens { } namespace arango { + class ApplicationV8; class CollectionNameResolver; class JSLoader; } @@ -95,7 +96,8 @@ void TRI_V8ReloadRouting (v8::Handle); /// @brief creates a TRI_vocbase_t global context //////////////////////////////////////////////////////////////////////////////// -void TRI_InitV8VocBridge (v8::Handle, +void TRI_InitV8VocBridge (triagens::arango::ApplicationV8*, + v8::Handle, triagens::aql::QueryRegistry*, struct TRI_server_s*, struct TRI_vocbase_s*, diff --git a/js/apps/system/aardvark/frontend/js/collections/arangoDocuments.js b/js/apps/system/aardvark/frontend/js/collections/arangoDocuments.js index 540c431920..e8de4c76ce 100644 --- a/js/apps/system/aardvark/frontend/js/collections/arangoDocuments.js +++ b/js/apps/system/aardvark/frontend/js/collections/arangoDocuments.js @@ -159,7 +159,7 @@ "count": this.getPageSize() }; - query = "FOR x in @@collection"; + query = "FOR x in @@collection let att = slice(ATTRIBUTES(x), 0, 10)"; query += this.setFiltersForQuery(bindVars); // Sort result, only useful for a small number of docs if (this.getTotal() < this.MAX_SORT) { @@ -173,14 +173,14 @@ } if (bindVars.count !== 'all') { - query += " LIMIT @offset, @count RETURN x"; + query += " LIMIT @offset, @count RETURN keep(x, att)"; } else { tmp = { "@collection": this.collectionID }; bindVars = tmp; - query += " RETURN x"; + query += " RETURN keep(x, att)"; } queryObj = { diff --git a/js/apps/system/aardvark/frontend/js/config/dygraphConfig.js b/js/apps/system/aardvark/frontend/js/config/dygraphConfig.js index b4652828cf..36e21f3264 100644 --- a/js/apps/system/aardvark/frontend/js/config/dygraphConfig.js +++ b/js/apps/system/aardvark/frontend/js/config/dygraphConfig.js @@ -106,7 +106,20 @@ visibility: [true, false], labels: ["datetime", "Major Page", "Minor Page"], div: "pageFaultsChart", - labelsKMG2: false + labelsKMG2: false, + axes: { + y: { + valueFormatter: function (y) { + return parseFloat(y.toPrecision(3)); + }, + axisLabelFormatter: function (y) { + if (y === 0) { + return 0; + } + return parseFloat(y.toPrecision(3)); + } + } + } }, systemUserTime: { diff --git a/js/apps/system/aardvark/frontend/js/views/collectionsView.js b/js/apps/system/aardvark/frontend/js/views/collectionsView.js index 78c3584ddc..c5f576b87f 100644 --- a/js/apps/system/aardvark/frontend/js/views/collectionsView.js +++ b/js/apps/system/aardvark/frontend/js/views/collectionsView.js @@ -46,9 +46,15 @@ $('#collectionsToggle').addClass('activated'); } - var length = searchOptions.searchPhrase.length; + var length; + + try { + length = searchOptions.searchPhrase.length; + } + catch (ignore) { + } $('#searchInput').val(searchOptions.searchPhrase); - $('#searchInput').focus(); + $('#searchInput').focus(); $('#searchInput')[0].setSelectionRange(length, length); arangoHelper.fixTooltips(".icon_arangodb, .arangoicon", "left"); diff --git a/js/server/tests/aql-optimizer-rule-distribute-in-cluster.js b/js/server/tests/aql-optimizer-rule-distribute-in-cluster.js index 77267d3244..5aeb9db4ad 100644 --- a/js/server/tests/aql-optimizer-rule-distribute-in-cluster.js +++ b/js/server/tests/aql-optimizer-rule-distribute-in-cluster.js @@ -80,7 +80,7 @@ function optimizerRuleTestSuite () { }, //////////////////////////////////////////////////////////////////////////////// - /// @brief test that rule does not fire when it is not enabled + /// @brief test that the rule fires when it is enabled //////////////////////////////////////////////////////////////////////////////// testThisRuleEnabled : function () { @@ -89,10 +89,71 @@ function optimizerRuleTestSuite () { [ "FOR d IN " + cn1 + " REMOVE d._key in " + cn1, 1], [ "FOR d IN " + cn1 + " INSERT d in " + cn2, 2], [ "FOR d IN " + cn1 + " INSERT d._key in " + cn2, 3], - [ "FOR d IN " + cn1 + " REPLACE d in " + cn1, 4], - [ "FOR d IN " + cn1 + " REPLACE d._key in " + cn1, 5], - [ "FOR d IN " + cn1 + " UPDATE d in " + cn1 , 6], - [ "FOR d IN " + cn1 + " UPDATE d._key in " + cn1 , 7] + ]; + + var expectedRules = [ + [ + "distribute-in-cluster", + "scatter-in-cluster", + ], + [ + "distribute-in-cluster", + "scatter-in-cluster", + "distribute-filtercalc-to-cluster" + ] + ]; + + var expectedNodes = [ + [ + "SingletonNode", + "ScatterNode", + "RemoteNode", + "EnumerateCollectionNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ], + [ + "SingletonNode", + "ScatterNode", + "RemoteNode", + "EnumerateCollectionNode", + "CalculationNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ] + ]; + + var finalNodes = [ + "RemoveNode", "RemoveNode", + "InsertNode", "InsertNode" + ]; + + queries.forEach(function(query) { + // can't turn this rule off so should always get the same answer + var i = query[1] % 2; + var result = AQL_EXPLAIN(query[0], { }, thisRuleEnabled); + assertEqual(expectedRules[i], result.plan.rules, query); + expectedNodes[i].push(finalNodes[query[1]]); + assertEqual(expectedNodes[i], explain(result), query); + expectedNodes[i].pop(); + + }); + }, + + //////////////////////////////////////////////////////////////////////////////// + /// @brief test that rule fires when it is disabled (i.e. it can't be disabled) + //////////////////////////////////////////////////////////////////////////////// + + testThisRuleDisabled : function () { + var queries = [ + [ "FOR d IN " + cn1 + " REMOVE d in " + cn1, 0], + [ "FOR d IN " + cn1 + " REMOVE d._key in " + cn1, 1], + [ "FOR d IN " + cn1 + " INSERT d in " + cn2, 2], + [ "FOR d IN " + cn1 + " INSERT d._key in " + cn2, 3], ]; var expectedRules = [ @@ -131,12 +192,74 @@ function optimizerRuleTestSuite () { var finalNodes = [ "RemoveNode", "RemoveNode", - "InsertNode", "InsertNode", - "ReplaceNode", "ReplaceNode", - "UpdateNode", "UpdateNode" + "InsertNode", "InsertNode" ]; queries.forEach(function(query) { + // can't turn this rule off so should always get the same answer + var i = query[1] % 2; + var result = AQL_EXPLAIN(query[0], { }, rulesAll); + assertEqual(expectedRules[i], result.plan.rules, query); + expectedNodes[i].push(finalNodes[query[1]]); + result = AQL_EXPLAIN(query[0], { }, thisRuleDisabled); + assertEqual(expectedNodes[i], explain(result), query); + expectedNodes[i].pop(); + }); + }, + + //////////////////////////////////////////////////////////////////////////////// + /// @brief test that rule does not fire when it is not enabled + //////////////////////////////////////////////////////////////////////////////// + + testRulesAll : function () { + var queries = [ + [ "FOR d IN " + cn1 + " REMOVE d in " + cn1, 0], + [ "FOR d IN " + cn1 + " REMOVE d._key in " + cn1, 1], + [ "FOR d IN " + cn1 + " INSERT d in " + cn2, 2], + [ "FOR d IN " + cn1 + " INSERT d._key in " + cn2, 3], + ]; + + var expectedRules = [ + [ + "distribute-in-cluster", + "scatter-in-cluster", + "remove-unnecessary-remote-scatter" + ], + [ + "distribute-in-cluster", + "scatter-in-cluster", + "distribute-filtercalc-to-cluster", + "remove-unnecessary-remote-scatter" + ] + ]; + + var expectedNodes = [ + [ + "SingletonNode", + "EnumerateCollectionNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ], + [ + "SingletonNode", + "EnumerateCollectionNode", + "CalculationNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ] + ]; + + var finalNodes = [ + "RemoveNode", "RemoveNode", + "InsertNode", "InsertNode" + ]; + + queries.forEach(function(query) { + // can't turn this rule off so should always get the same answer var i = query[1] % 2; var result = AQL_EXPLAIN(query[0], { }, rulesAll); assertEqual(expectedRules[i], result.plan.rules, query); @@ -146,6 +269,71 @@ function optimizerRuleTestSuite () { }); }, + //////////////////////////////////////////////////////////////////////////////// + /// @brief test that rule does not fire when it is not enabled + //////////////////////////////////////////////////////////////////////////////// + + testRulesNone : function () { + var queries = [ + [ "FOR d IN " + cn1 + " REMOVE d in " + cn1, 0], + [ "FOR d IN " + cn1 + " REMOVE d._key in " + cn1, 1], + [ "FOR d IN " + cn1 + " INSERT d in " + cn2, 2], + [ "FOR d IN " + cn1 + " INSERT d._key in " + cn2, 3], + ]; + + var expectedRules = [ + [ + "distribute-in-cluster", + "scatter-in-cluster", + ], + [ + "distribute-in-cluster", + "scatter-in-cluster", + "distribute-filtercalc-to-cluster", + ] + ]; + + var expectedNodes = [ + [ + "SingletonNode", + "ScatterNode", + "RemoteNode", + "EnumerateCollectionNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ], + [ + "SingletonNode", + "ScatterNode", + "RemoteNode", + "EnumerateCollectionNode", + "CalculationNode", + "RemoteNode", + "GatherNode", + "DistributeNode", + "RemoteNode" + ] + ]; + + var finalNodes = [ + "RemoveNode", "RemoveNode", + "InsertNode", "InsertNode" + ]; + + queries.forEach(function(query) { + // can't turn this rule off so should always get the same answer + var i = query[1] % 2; + var result = AQL_EXPLAIN(query[0], { }, rulesNone); + assertEqual(expectedRules[i], result.plan.rules, query); + expectedNodes[i].push(finalNodes[query[1]]); + assertEqual(expectedNodes[i], explain(result), query); + expectedNodes[i].pop(); + + }); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test that rule has no effect //////////////////////////////////////////////////////////////////////////////// @@ -153,6 +341,10 @@ function optimizerRuleTestSuite () { testRuleNoEffect : function () { var queries = [ "FOR d IN " + cn1 + " RETURN d", + "FOR d IN " + cn1 + " REPLACE d in " + cn1, + "FOR d IN " + cn1 + " REPLACE d._key in " + cn1, + "FOR d IN " + cn1 + " UPDATE d in " + cn1, + "FOR d IN " + cn1 + " UPDATE d._key in " + cn1 , "FOR i IN 1..10 RETURN i" ]; queries.forEach(function(query) { diff --git a/lib/V8/v8-buffer.cpp b/lib/V8/v8-buffer.cpp index 274d7fd927..d7c36f88f2 100644 --- a/lib/V8/v8-buffer.cpp +++ b/lib/V8/v8-buffer.cpp @@ -1625,7 +1625,7 @@ void TRI_InitV8Buffer (v8::Handle context) { // check the isolate v8::Isolate* isolate = v8::Isolate::GetCurrent(); - TRI_v8_global_t* v8g = TRI_CreateV8Globals(isolate); + TRI_v8_global_t* v8g = TRI_GetV8Globals(isolate); // ............................................................................. // generate the general SlowBuffer template diff --git a/lib/V8/v8-globals.cpp b/lib/V8/v8-globals.cpp index 8c44f6269c..e9193a6f57 100644 --- a/lib/V8/v8-globals.cpp +++ b/lib/V8/v8-globals.cpp @@ -134,6 +134,7 @@ TRI_v8_global_s::TRI_v8_global_s (v8::Isolate* isolate) _vocbase(nullptr), _allowUseDatabase(true), _hasDeadObjects(false), + _applicationV8(nullptr), _loader(nullptr), _canceled(false) { v8::HandleScope scope; @@ -228,11 +229,24 @@ TRI_v8_global_s::~TRI_v8_global_s () { TRI_v8_global_t* TRI_CreateV8Globals (v8::Isolate* isolate) { TRI_v8_global_t* v8g = static_cast(isolate->GetData()); + TRI_ASSERT(v8g == nullptr); + v8g = new TRI_v8_global_t(isolate); + isolate->SetData(v8g); + + return v8g; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns a global context +//////////////////////////////////////////////////////////////////////////////// + +TRI_v8_global_t* TRI_GetV8Globals (v8::Isolate* isolate) { + TRI_v8_global_t* v8g = static_cast(isolate->GetData()); if (v8g == nullptr) { - v8g = new TRI_v8_global_t(isolate); - isolate->SetData(v8g); + v8g = TRI_CreateV8Globals(isolate); } + TRI_ASSERT(v8g != nullptr); return v8g; } diff --git a/lib/V8/v8-globals.h b/lib/V8/v8-globals.h index 1ef7497e83..3ea6036629 100644 --- a/lib/V8/v8-globals.h +++ b/lib/V8/v8-globals.h @@ -42,6 +42,7 @@ struct TRI_vocbase_s; namespace triagens { namespace arango { + class ApplicationV8; class JSLoader; } } @@ -809,6 +810,12 @@ typedef struct TRI_v8_global_s { // --SECTION-- GENERAL // ----------------------------------------------------------------------------- +//////////////////////////////////////////////////////////////////////////////// +/// @brief pointer to the context dealer (ApplicationV8*) +//////////////////////////////////////////////////////////////////////////////// + + triagens::arango::ApplicationV8* _applicationV8; + //////////////////////////////////////////////////////////////////////////////// /// @brief pointer to the startup loader (JSLoader*) //////////////////////////////////////////////////////////////////////////////// @@ -833,6 +840,12 @@ TRI_v8_global_t; TRI_v8_global_t* TRI_CreateV8Globals(v8::Isolate*); +//////////////////////////////////////////////////////////////////////////////// +/// @brief gets the global context +//////////////////////////////////////////////////////////////////////////////// + +TRI_v8_global_t* TRI_GetV8Globals(v8::Isolate*); + //////////////////////////////////////////////////////////////////////////////// /// @brief adds a method to the prototype of an object //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index 28f3e57a1a..48fcfd5382 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -3792,7 +3792,7 @@ void TRI_InitV8Utils (v8::Handle context, // check the isolate v8::Isolate* isolate = v8::Isolate::GetCurrent(); - TRI_v8_global_t* v8g = TRI_CreateV8Globals(isolate); + TRI_v8_global_t* v8g = TRI_GetV8Globals(isolate); v8::Handle ft; v8::Handle rt;