diff --git a/BasicsC/json.c b/BasicsC/json.c index 592784afb0..f40cfe8ce7 100644 --- a/BasicsC/json.c +++ b/BasicsC/json.c @@ -100,7 +100,7 @@ static void StringifyJson (TRI_string_buffer_t* buffer, TRI_json_t const* object } StringifyJson(buffer, TRI_AtVector(&object->_value._objects, i), true); - TRI_AppendStringStringBuffer(buffer, ":"); + TRI_AppendCharStringBuffer(buffer, ':'); StringifyJson(buffer, TRI_AtVector(&object->_value._objects, i + 1), true); } diff --git a/Demos/Scripts/demo.js b/Demos/Scripts/demo.js new file mode 100644 index 0000000000..3bd53a6a1c --- /dev/null +++ b/Demos/Scripts/demo.js @@ -0,0 +1,19 @@ +#! avocsh + +var console = require("console"); +var name = "demo"; + +var collection = db[name]; + +collection.drop(name); + +collection.save({ hallo : "world" }); + +collection.save({ world : "hallo" }); + +collection.save({ name : "Hugo", + firstName : "Egon", + address : { + street : "Neue Strasse", + city : "Hier" }, + hobbies : [ "swimming", "programming" ]}); diff --git a/Demos/Scripts/five.js b/Demos/Scripts/five.js new file mode 100644 index 0000000000..0468aad68d --- /dev/null +++ b/Demos/Scripts/five.js @@ -0,0 +1,16 @@ +#! avocsh + +var console = require("console"); +var name = "five"; + +var collection = db[name]; + +collection.drop(name); +collection.ensureUniqueConstraint("a"); +collection.ensureUniqueConstraint("b"); + +collection.save({ a : 1, b : "One" }); +collection.save({ a : 2, b : "Two" }); +collection.save({ a : 3, b : "Three" }); +collection.save({ a : 4, b : "Four" }); +collection.save({ a : 5, b : "Five" }); diff --git a/Demos/Scripts/geo.js b/Demos/Scripts/geo.js new file mode 100644 index 0000000000..55fb9f34f0 --- /dev/null +++ b/Demos/Scripts/geo.js @@ -0,0 +1,22 @@ +#! avocsh + +var console = require("console"); +var name = "geo"; + +var collection = db[name]; + +collection.drop(name); + +db.geo.ensureGeoIndex("home"); +db.geo.ensureGeoIndex("work.l", "work.b"); + +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geo.save({ + name : "Name/" + i + "/" + j, + home : [ i, j ], + work : { b : i, l : j } + }); + } +} + diff --git a/Doxygen/Examples.AvocadoDB/key-value_delete-key-bad b/Doxygen/Examples.AvocadoDB/key-value_delete-key-bad new file mode 100644 index 0000000000..dd76583c16 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_delete-key-bad @@ -0,0 +1,4 @@ + HTTP/1.1 404 Bad Request + ... + + {"error":true,"code":404,"errorNum":41404,"errorMessage":"Key value pair not found"} diff --git a/Doxygen/Examples.AvocadoDB/key-value_delete-key-ok b/Doxygen/Examples.AvocadoDB/key-value_delete-key-ok new file mode 100644 index 0000000000..897d562daa --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_delete-key-ok @@ -0,0 +1,4 @@ + HTTP/1.1 200 OK + ... + + {"removed":true,"error":false,"code":202} diff --git a/Doxygen/Examples.AvocadoDB/key-value_delete-key-request b/Doxygen/Examples.AvocadoDB/key-value_delete-key-request new file mode 100644 index 0000000000..2b56fdf3ec --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_delete-key-request @@ -0,0 +1,2 @@ + DELETE /_api/key/example_collection/example_key1 HTTP/1.1 + Host: localhost:9000 diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-key-bad b/Doxygen/Examples.AvocadoDB/key-value_get-key-bad new file mode 100644 index 0000000000..e714b22472 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-key-bad @@ -0,0 +1,4 @@ + HTTP/1.1 404 Bad Request + ... + + {"error":true,"code":404,"errorNum":41404,"errorMessage":"Key value pair not found"} diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-key-ok b/Doxygen/Examples.AvocadoDB/key-value_get-key-ok new file mode 100644 index 0000000000..aeeb6cbc58 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-key-ok @@ -0,0 +1,7 @@ + HTTP/1.1 200 OK + x-voc-created: 2010-09-15T09:35:01Z + x-voc-expires: 2011-09-29T08:00:00Z + x-voc-extended: {"option1":35,"option2":"x"} + ... + + 13 diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-key-request b/Doxygen/Examples.AvocadoDB/key-value_get-key-request new file mode 100644 index 0000000000..9872f8b706 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-key-request @@ -0,0 +1,2 @@ + GET /_api/key/example_collection/example_key1 HTTP/1.1 + Host: localhost:9000 diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-keys-bad b/Doxygen/Examples.AvocadoDB/key-value_get-keys-bad new file mode 100644 index 0000000000..6ee71f2ebd --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-keys-bad @@ -0,0 +1,4 @@ + HTTP/1.1 404 Bad Request + ... + + {"error":true,"code":404,"errorNum":41404,"errorMessage":"Collection not found"} diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-keys-ok b/Doxygen/Examples.AvocadoDB/key-value_get-keys-ok new file mode 100644 index 0000000000..45f2e798f2 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-keys-ok @@ -0,0 +1,4 @@ + HTTP/1.1 200 OK + ... + + ["example_keyboard","example_keystone"] diff --git a/Doxygen/Examples.AvocadoDB/key-value_get-keys-request b/Doxygen/Examples.AvocadoDB/key-value_get-keys-request new file mode 100644 index 0000000000..af36ab66aa --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_get-keys-request @@ -0,0 +1,2 @@ + GET /_api/keys/example_collection/example_key HTTP/1.1 + Host: localhost:9000 diff --git a/Doxygen/Examples.AvocadoDB/key-value_post-key-bad b/Doxygen/Examples.AvocadoDB/key-value_post-key-bad new file mode 100644 index 0000000000..d12a74f399 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_post-key-bad @@ -0,0 +1,7 @@ + HTTP/1.1 404 Bad Request + content-type: application/json + connection: Keep-Alive + server: triagens GmbH High-Performance HTTP Server + content-length: 75 + + {"error":true,"code":404,"errorNum":41304,"errorMessage":"Use PUT to change value"} diff --git a/Doxygen/Examples.AvocadoDB/key-value_post-key-ok b/Doxygen/Examples.AvocadoDB/key-value_post-key-ok new file mode 100644 index 0000000000..7c8e52725e --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_post-key-ok @@ -0,0 +1,7 @@ + HTTP/1.1 201 Created + connection: Keep-Alive + content-type: application/json + server: triagens GmbH High-Performance HTTP Server + content-length: 62 + + {"saved":true,"_id":"155630:9500735","error":false,"code":201} diff --git a/Doxygen/Examples.AvocadoDB/key-value_post-key-request b/Doxygen/Examples.AvocadoDB/key-value_post-key-request new file mode 100644 index 0000000000..ed51457e60 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_post-key-request @@ -0,0 +1,7 @@ + POST /_api/key/example_collection/example_key1 HTTP/1.1 + Host: localhost:9000 + x-voc-expires: 2011-09-29T08:00:00Z + x-voc-extended: {"option1":35,"option2":"x"} + Content-Length: 2 + + 12 \ No newline at end of file diff --git a/Doxygen/Examples.AvocadoDB/key-value_put-key-bad b/Doxygen/Examples.AvocadoDB/key-value_put-key-bad new file mode 100644 index 0000000000..e714b22472 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_put-key-bad @@ -0,0 +1,4 @@ + HTTP/1.1 404 Bad Request + ... + + {"error":true,"code":404,"errorNum":41404,"errorMessage":"Key value pair not found"} diff --git a/Doxygen/Examples.AvocadoDB/key-value_put-key-ok b/Doxygen/Examples.AvocadoDB/key-value_put-key-ok new file mode 100644 index 0000000000..e1888156d9 --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_put-key-ok @@ -0,0 +1,7 @@ + HTTP/1.1 202 Accepted + connection: Keep-Alive + content-type: application/json + server: triagens GmbH High-Performance HTTP Server + content-length: 41 + + {"changed":true,"error":false,"code":202} \ No newline at end of file diff --git a/Doxygen/Examples.AvocadoDB/key-value_put-key-request b/Doxygen/Examples.AvocadoDB/key-value_put-key-request new file mode 100644 index 0000000000..8870a8f83f --- /dev/null +++ b/Doxygen/Examples.AvocadoDB/key-value_put-key-request @@ -0,0 +1,7 @@ + PUT /_api/key/example_collection/example_key1 HTTP/1.1 + Host: localhost:9000 + x-voc-expires: 2011-09-29T08:00:00Z + x-voc-extended: {"option1":35,"option2":"x"} + Content-Length: 2 + + 13 diff --git a/Doxygen/Examples.Durham/demo1 b/Doxygen/Examples.Durham/demo1 new file mode 100644 index 0000000000..bf2e5b2af3 --- /dev/null +++ b/Doxygen/Examples.Durham/demo1 @@ -0,0 +1,29 @@ +avocsh> db.demo.all(); +[ + { + _id : "161039/4323870", + _rev : 4323870, + name : "Hugo", + firstName : "Egon", + address : { + city : "Hier", + street : "Neue Strasse" + }, + hobbies : [ + "swimming", + "programming" + ] + }, + + { + _id : "161039/4192798", + _rev : 4192798, + hallo : "world" + }, + + { + _id : "161039/4258334", + _rev : 4258334, + world : "hallo" + } +] diff --git a/Doxygen/Examples.Durham/five1 b/Doxygen/Examples.Durham/five1 new file mode 100644 index 0000000000..458e7ed751 --- /dev/null +++ b/Doxygen/Examples.Durham/five1 @@ -0,0 +1,37 @@ +avocsh> db.five.all(); +[ + { + _id : "138663/2631399", + _rev : 2631399, + a : 2, + b : "Two" + }, + + { + _id : "138663/2828007", + _rev : 2828007, + a : 5, + b : "Five" + }, + + { + _id : "138663/2696935", + _rev : 2696935, + a : 3, + b : "Three" + }, + + { + _id : "138663/2565863", + _rev : 2565863, + a : 1, + b : "One" + }, + + { + _id : "138663/2762471", + _rev : 2762471, + a : 4, + b : "Four" + } +] diff --git a/Doxygen/Examples.Durham/geojs1 b/Doxygen/Examples.Durham/geojs1 new file mode 100644 index 0000000000..be3139003e --- /dev/null +++ b/Doxygen/Examples.Durham/geojs1 @@ -0,0 +1,44 @@ +[ + { + _id : "154092/301894631", + _rev : 301894631, + work : { + b : -70, + l : -30 + }, + name : "Name/-70/-30", + home : [ + -70, + -30 + ] + }, + + { + _id : "154092/305433575", + _rev : 305433575, + work : { + b : -60, + l : 140 + }, + name : "Name/-60/140", + home : [ + -60, + 140 + ] + }, + + { + _id : "154092/314215399", + _rev : 314215399, + work : { + b : -20, + l : 0 + }, + name : "Name/-20/0", + home : [ + -20, + 0 + ] + }, + ... +] \ No newline at end of file diff --git a/Doxygen/avocado.doxy.in b/Doxygen/avocado.doxy.in index 63d524102c..361839f436 100644 --- a/Doxygen/avocado.doxy.in +++ b/Doxygen/avocado.doxy.in @@ -206,7 +206,7 @@ ALIASES += CMDOPT{1}="\1" ALIASES += CA{1}="\1" ALIASES += CO{1}="\1" ALIASES += REST{1}="\1" -ALIASES += GE{1}="\1" +ALIASES += GE{1}="\1" ALIASES += EXAMPLES="Examples
" # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C @@ -689,7 +689,8 @@ EXCLUDE_SYMBOLS = EXAMPLE_PATH = \ @srcdir@/Doxygen/Examples.AvocadoDB \ @srcdir@/Doxygen/Examples.Durham \ - @srcdir@/Doxygen/Examples.Fyn + @srcdir@/Doxygen/Examples.Fyn \ + @srcdir@/Demos/Scripts # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp diff --git a/HashIndex/hashindex.c b/HashIndex/hashindex.c index 84fd08eb01..3b515f055f 100755 --- a/HashIndex/hashindex.c +++ b/HashIndex/hashindex.c @@ -124,9 +124,6 @@ static bool isEqualJsonJson (const TRI_json_t* left, const TRI_json_t* right) { static bool isEqualShapedJsonShapedJson (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right) { - - int result; - if (left == NULL && right == NULL) { return true; } @@ -478,7 +475,6 @@ static bool isMultiEqualElementElement (struct TRI_multi_array_s* multiArray, void* leftElement, void* rightElement) { HashIndexElement* hLeftElement = (HashIndexElement*)(leftElement); HashIndexElement* hRightElement = (HashIndexElement*)(rightElement); - int result; if (leftElement == NULL || rightElement == NULL) { return false; @@ -495,7 +491,6 @@ static bool isMultiEqualKeyElement (struct TRI_multi_array_s* multiArray, void* leftElement, void* rightElement) { HashIndexElement* hLeftElement = (HashIndexElement*)(leftElement); HashIndexElement* hRightElement = (HashIndexElement*)(rightElement); - int result; size_t j; if (leftElement == NULL || rightElement == NULL) { diff --git a/Logger/Logger.cpp b/Logger/Logger.cpp index 6fa7dbf225..5a864f6d29 100644 --- a/Logger/Logger.cpp +++ b/Logger/Logger.cpp @@ -440,8 +440,6 @@ static void OutputMachine (string const& text, LoggerData::Info const& info) { } } - line.appendChar('\0'); - TRI_RawLog(info._level, info._severity, line.c_str(), line.length() - 1); line.free(); diff --git a/Makefile.files b/Makefile.files index 5784de380f..780e31405f 100644 --- a/Makefile.files +++ b/Makefile.files @@ -139,7 +139,7 @@ avocado_SOURCES = \ QL/parser.c \ QL/tokens.c \ RestHandler/RestActionHandler.cpp \ - RestHandler/RestCollectionHandler.cpp \ + RestHandler/RestDocumentHandler.cpp \ RestHandler/RestVocbaseBaseHandler.cpp \ RestServer/ActionDispatcherThread.cpp \ RestServer/AvocadoHttpServer.cpp \ @@ -294,6 +294,7 @@ WIKI = \ Doxygen/xml/CommandLineScheduler.md \ Doxygen/xml/Compiling.md \ Doxygen/xml/DefineAction.md \ + Doxygen/xml/ExamplesSetup.md \ Doxygen/xml/Glossary.md \ Doxygen/xml/Graphs.md \ Doxygen/xml/HttpInterface.md \ diff --git a/Makefile.in b/Makefile.in index 7a2fe21527..c3bef29154 100644 --- a/Makefile.in +++ b/Makefile.in @@ -241,7 +241,7 @@ am_avocado_OBJECTS = Admin/ApplicationAdminServer.$(OBJEXT) \ QL/formatter.$(OBJEXT) QL/optimize.$(OBJEXT) \ QL/parser.$(OBJEXT) QL/tokens.$(OBJEXT) \ RestHandler/RestActionHandler.$(OBJEXT) \ - RestHandler/RestCollectionHandler.$(OBJEXT) \ + RestHandler/RestDocumentHandler.$(OBJEXT) \ RestHandler/RestVocbaseBaseHandler.$(OBJEXT) \ RestServer/ActionDispatcherThread.$(OBJEXT) \ RestServer/AvocadoHttpServer.$(OBJEXT) \ @@ -687,7 +687,7 @@ avocado_SOURCES = \ QL/parser.c \ QL/tokens.c \ RestHandler/RestActionHandler.cpp \ - RestHandler/RestCollectionHandler.cpp \ + RestHandler/RestDocumentHandler.cpp \ RestHandler/RestVocbaseBaseHandler.cpp \ RestServer/ActionDispatcherThread.cpp \ RestServer/AvocadoHttpServer.cpp \ @@ -823,6 +823,7 @@ WIKI = \ Doxygen/xml/CommandLineScheduler.md \ Doxygen/xml/Compiling.md \ Doxygen/xml/DefineAction.md \ + Doxygen/xml/ExamplesSetup.md \ Doxygen/xml/Glossary.md \ Doxygen/xml/Graphs.md \ Doxygen/xml/HttpInterface.md \ @@ -1426,7 +1427,7 @@ RestHandler/$(DEPDIR)/$(am__dirstamp): @: > RestHandler/$(DEPDIR)/$(am__dirstamp) RestHandler/RestActionHandler.$(OBJEXT): RestHandler/$(am__dirstamp) \ RestHandler/$(DEPDIR)/$(am__dirstamp) -RestHandler/RestCollectionHandler.$(OBJEXT): \ +RestHandler/RestDocumentHandler.$(OBJEXT): \ RestHandler/$(am__dirstamp) \ RestHandler/$(DEPDIR)/$(am__dirstamp) RestHandler/RestVocbaseBaseHandler.$(OBJEXT): \ @@ -1713,7 +1714,7 @@ mostlyclean-compile: -rm -f Rest/SslInterface.$(OBJEXT) -rm -f Rest/Url.$(OBJEXT) -rm -f RestHandler/RestActionHandler.$(OBJEXT) - -rm -f RestHandler/RestCollectionHandler.$(OBJEXT) + -rm -f RestHandler/RestDocumentHandler.$(OBJEXT) -rm -f RestHandler/RestVocbaseBaseHandler.$(OBJEXT) -rm -f RestServer/ActionDispatcherThread.$(OBJEXT) -rm -f RestServer/AvocadoHttpServer.$(OBJEXT) @@ -1910,7 +1911,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@Rest/$(DEPDIR)/SslInterface.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@Rest/$(DEPDIR)/Url.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@RestHandler/$(DEPDIR)/RestActionHandler.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@RestHandler/$(DEPDIR)/RestCollectionHandler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@RestHandler/$(DEPDIR)/RestDocumentHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@RestHandler/$(DEPDIR)/RestVocbaseBaseHandler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@RestServer/$(DEPDIR)/ActionDispatcherThread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@RestServer/$(DEPDIR)/AvocadoHttpServer.Po@am__quote@ diff --git a/RestHandler/RestCollectionHandler.cpp b/RestHandler/RestDocumentHandler.cpp similarity index 79% rename from RestHandler/RestCollectionHandler.cpp rename to RestHandler/RestDocumentHandler.cpp index d755c2b57a..f07f9152a5 100644 --- a/RestHandler/RestCollectionHandler.cpp +++ b/RestHandler/RestDocumentHandler.cpp @@ -25,7 +25,7 @@ /// @author Copyright 2010-2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -#include "RestCollectionHandler.h" +#include "RestDocumentHandler.h" #include "Basics/StringUtils.h" #include "BasicsC/string-buffer.h" @@ -51,7 +51,7 @@ using namespace triagens::avocado; /// @brief constructor //////////////////////////////////////////////////////////////////////////////// -RestCollectionHandler::RestCollectionHandler (HttpRequest* request, TRI_vocbase_t* vocbase) +RestDocumentHandler::RestDocumentHandler (HttpRequest* request, TRI_vocbase_t* vocbase) : RestVocbaseBaseHandler(request, vocbase) { } @@ -72,7 +72,7 @@ RestCollectionHandler::RestCollectionHandler (HttpRequest* request, TRI_vocbase_ /// {@inheritDoc} //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::isDirect () { +bool RestDocumentHandler::isDirect () { return false; } @@ -80,7 +80,7 @@ bool RestCollectionHandler::isDirect () { /// {@inheritDoc} //////////////////////////////////////////////////////////////////////////////// -string const& RestCollectionHandler::queue () { +string const& RestDocumentHandler::queue () { static string const client = "CLIENT"; return client; @@ -90,7 +90,7 @@ string const& RestCollectionHandler::queue () { /// {@inheritDoc} //////////////////////////////////////////////////////////////////////////////// -HttpHandler::status_e RestCollectionHandler::execute () { +HttpHandler::status_e RestDocumentHandler::execute () { // extract the sub-request type HttpRequest::HttpRequestType type = request->requestType(); @@ -160,7 +160,7 @@ HttpHandler::status_e RestCollectionHandler::execute () { //////////////////////////////////////////////////////////////////////////////// /// @brief creates a document /// -/// @REST{POST /collection/@FA{collection-identifier}} +/// @REST{POST /document?collection=@FA{collection-identifier}} /// /// Creates a new document in the collection identified by the /// @FA{collection-identifier}. A JSON representation of the document must be @@ -168,21 +168,27 @@ HttpHandler::status_e RestCollectionHandler::execute () { /// /// If the document was created successfully, then a @LIT{HTTP 201} is returned /// and the "Location" header contains the path to the newly created -/// document. The "ETag" header field contains the revision of the newly created -/// document. +/// document. The "ETag" header field contains the revision of the document. +/// +/// The body of the response contains a JSON object with the same information. +/// The attribute @LIT{_id} contains the document handle of the newly created +/// document, the attribute @LIT{_rev} contains the document revision. /// /// If the collection parameter @LIT{waitForSync} is @LIT{false}, then a /// @LIT{HTTP 202} is returned in order to indicate that the document has been /// accepted but not yet stored. /// /// If the @FA{collection-identifier} is unknown, then a @LIT{HTTP 404} is -/// returned. +/// returned and the body of the response contains an error document. /// /// If the body does not contain a valid JSON representation of an document, -/// then a @LIT{HTTP 400} is returned. +/// then a @LIT{HTTP 400} is returned and the body of the response contains +/// an error document. /// -/// Instead of a @FA{collection-identifier}, a collection name can be -/// given. +/// @REST{POST /document?collection=@FA{collection-identifier}&createCollection=@FA{create}} +/// +/// Instead of a @FA{collection-identifier}, a collection name can be used. If +/// @FA{create} is true, then the collection is created if it does not exists. /// /// @EXAMPLES /// @@ -203,7 +209,7 @@ HttpHandler::status_e RestCollectionHandler::execute () { /// @verbinclude rest6 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::createDocument () { +bool RestDocumentHandler::createDocument () { vector const& suffix = request->suffix(); if (suffix.size() != 1) { @@ -277,16 +283,16 @@ bool RestCollectionHandler::createDocument () { /// Either readSingleDocument or readAllDocuments. //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::readDocument () { +bool RestDocumentHandler::readDocument () { switch (request->suffix().size()) { - case 1: + case 0: return readAllDocuments(); - case 2: + case 1: return readSingleDocument(true); default: - generateError(HttpResponse::BAD, "superfluous identifier"); + generateError(HttpResponse::BAD, "expecting URI /document/"); return false; } } @@ -294,26 +300,18 @@ bool RestCollectionHandler::readDocument () { //////////////////////////////////////////////////////////////////////////////// /// @brief reads a single document /// -/// @REST{GET /collection/@FA{collection-identifier}/@FA{document-identifier}} +/// @REST{GET /document/@FA{document-handle}} /// -/// Returns the document identified by @FA{document-identifier} from the -/// collection identified by @FA{collection-identifier}. +/// Returns the document identified by @FA{document-handle}. The returned +/// document contains two special attributes: @LIT{_id} containing the document +/// handle and @LIT{_rev} containing the revision. /// /// If the document exists, then a @LIT{HTTP 200} is returned and the JSON /// representation of the document is the body of the response. /// -/// If the collection identifier points to a non-existing collection, then a +/// If the @FA{document-handle} points to a non-existing document, then a /// @LIT{HTTP 404} is returned and the body contains an error document. /// -/// If the document identifier points to a non-existing document, then a -/// @LIT{HTTP 404} is returned and the body contains an error document. -/// -/// Instead of a @FA{document-identifier}, a document reference can be given. A -/// @LIT{HTTP 400} is returned, if there is a mismatch between the -/// @FA{collection-identifier} and the @FA{document-reference}. -/// -/// Instead of a @FA{collection-identifier}, a collection name can be given. -/// /// @EXAMPLES /// /// Use a collection and document identfier: @@ -333,19 +331,21 @@ bool RestCollectionHandler::readDocument () { /// @verbinclude rest17 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::readSingleDocument (bool generateBody) { +bool RestDocumentHandler::readSingleDocument (bool generateBody) { vector const& suffix = request->suffix(); - // find and load collection given by name oder identifier - bool ok = findCollection(suffix[0]) && loadCollection(); + // split the document reference + string cid; + string did; + + bool ok = splitDocumentReference(suffix[0], cid, did); if (! ok) { return false; } - // split the document reference - string did; - ok = splitDocumentReference(suffix[1], did); + // find and load collection given by name oder identifier + bool ok = findCollection(cid) && loadCollection(); if (! ok) { return false; @@ -366,7 +366,7 @@ bool RestCollectionHandler::readSingleDocument (bool generateBody) { // ............................................................................. if (document == 0) { - generateDocumentNotFound(suffix[0], did); + generateDocumentNotFound(suffix[0]); return false; } @@ -377,10 +377,10 @@ bool RestCollectionHandler::readSingleDocument (bool generateBody) { //////////////////////////////////////////////////////////////////////////////// /// @brief reads all documents /// -/// @REST{GET /collection/@FA{collection-identifier}} +/// @REST{GET /document?collection=@FA{collection-identifier}} /// -/// Returns the URI for all documents from the collection identified by -/// @FA{collection-identifier}. +/// Returns a list of all URI for all documents from the collection identified +/// by @FA{collection-identifier}. /// /// Instead of a @FA{collection-identifier}, a collection name can be given. /// @@ -389,7 +389,7 @@ bool RestCollectionHandler::readSingleDocument (bool generateBody) { /// @verbinclude rest20 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::readAllDocuments () { +bool RestDocumentHandler::readAllDocuments () { vector const& suffix = request->suffix(); // find and load collection given by name oder identifier @@ -470,48 +470,64 @@ bool RestCollectionHandler::readAllDocuments () { //////////////////////////////////////////////////////////////////////////////// /// @brief reads a single document head /// -/// @REST{HEAD /collection/@FA{collection-identifier}/@FA{document-identifier}} +/// @REST{HEAD /document/@FA{document-handle}} /// -/// Like @FN{GET}, but does not return the body. +/// Like @FN{GET}, but only returns the header fields and not the body. You +/// can use this call to get the current revision of a document or check if +/// the document was deleted. /// /// @EXAMPLES /// /// @verbinclude rest19 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::checkDocument () { - return readSingleDocument(false); +bool RestDocumentHandler::checkDocument () { + if (suffix.size() != 1) { + generateError(HttpResponse::BAD, "expecting URI /document/"); + return false; + } + + return readDocument(false); } //////////////////////////////////////////////////////////////////////////////// /// @brief updates a document /// -/// @REST{PUT /collection/@FA{collection-identifier}/@FA{document-identifier}} +/// @REST{PUT /document/@FA{document-handle}} /// -/// Updates the document identified by @FA{document-identifier} in the -/// collection identified by @FA{collection-identifier}. If the document exists -/// and could be updated, then a @LIT{HTTP 201} is returned and the "ETag" -/// header field contains the new revision of the document. +/// Updates the document identified by @FA{document-handle}. If the document exists +/// and can be updated, then a @LIT{HTTP 201} is returned and the "ETag" header +/// field contains the new revision of the document. /// -/// If the document does not exists, then a @LIT{HTTP 404} is returned. +/// Note the updated document passed in the body of the request normally also +/// contains the @FA{document-handle} in the attribute @LIT{_id} and the +/// revision in @LIT{_rev}. These attributes, however, are ignored. Only the URI +/// and the "ETag" header are relevant in order to avoid confusion when using +/// proxies. /// -/// If an etag is supplied in the "ETag" field, then the AvocadoDB checks that -/// the revision of the document is equal to the etag. If there is a mismatch, -/// then a @LIT{HTTP 409} conflict is returned and no update is performed. +/// The body of the response contains a JSON object with the information about +/// the handle and the revision. The attribute @LIT{_id} contains the known +/// @FA{document-handle} of the updated document, the attribute @LIT{_rev} +/// contains the new document revision. /// -/// Instead of a @FA{document-identifier}, a document reference can be given. +/// If the document does not exists, then a @LIT{HTTP 404} is returned and the +/// body of the response contains an error document. /// -/// Instead of a @FA{collection-identifier}, a collection name can be given. +/// If an etag is supplied in the "If-Match" header field, then the AvocadoDB +/// checks that the revision of the document is equal to the etag. If there is a +/// mismatch, then a @LIT{HTTP 409} conflict is returned and no update is +/// performed. /// -/// @REST{PUT /collection/@FA{collection-identifier}/@FA{document-identifier}?policy=@FA{policy}} +/// @REST{PUT /document/@FA{document-handle}?policy=@FA{policy}} /// /// As before, if @FA{policy} is @LIT{error}. If @FA{policy} is @LIT{last}, /// then the last write will win. /// -/// @REST{PUT /collection/@FA{collection-identifier}/@FA{document-identifier}?_rev=@FA{etag}} +/// @REST{PUT /document/@FA{collection-identifier}/@FA{document-identifier}?rev=@FA{etag}} /// -/// You can also supply the etag using the parameter "_rev" instead of an "ETag" -/// header. +/// You can also supply the etag using the parameter @LIT{rev} instead of an "ETag" +/// header. You must never supply both the "ETag" header and the @LIT{rev} +/// parameter. /// /// @EXAMPLES /// @@ -536,7 +552,7 @@ bool RestCollectionHandler::checkDocument () { /// @verbinclude rest11 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::updateDocument () { +bool RestDocumentHandler::updateDocument () { vector const& suffix = request->suffix(); // find and load collection given by name oder identifier @@ -615,31 +631,35 @@ bool RestCollectionHandler::updateDocument () { //////////////////////////////////////////////////////////////////////////////// /// @brief deletes a document /// -/// @REST{DELETE /collection/@FA{collection-identifier}/@FA{document-identifier}} +/// @REST{DELETE /document/@FA{document-handle}} /// -/// Deletes the document identified by @FA{document-identifier} from the -/// collection identified by @FA{collection-identifier}. If the document exists -/// and could be deleted, then a @LIT{HTTP 204} is returned. +/// Deletes the document identified by @FA{document-handle} from the collection +/// identified by @FA{collection-identifier}. If the document exists and could +/// be deleted, then a @LIT{HTTP 204} is returned. /// -/// If the document does not exists, then a @LIT{HTTP 404} is returned. +/// The body of the response contains a JSON object with the information about +/// the handle and the revision. The attribute @LIT{_id} contains the known +/// @FA{document-handle} of the updated document, the attribute @LIT{_rev} +/// contains the known document revision. /// -/// If an etag is supplied in the "ETag" field, then the AvocadoDB checks that -/// the revision of the document is equal to the etag. If there is a mismatch, -/// then a @LIT{HTTP 409} conflict is returned and no delete is performed. +/// If the document does not exists, then a @LIT{HTTP 404} is returned and the +/// body of the response contains an error document. /// -/// Instead of a @FA{document-identifier}, a document reference can be given. +/// If an etag is supplied in the "If-Match" field, then the AvocadoDB checks +/// that the revision of the document is equal to the tag. If there is a +/// mismatch, then a @LIT{HTTP 409} conflict is returned and no delete is +/// performed. /// -/// Instead of a @FA{collection-identifier}, a collection name can be given. -/// -/// @REST{DELETE /collection/@FA{collection-identifier}/@FA{document-identifier}?policy=@FA{policy}} +/// @REST{DELETE /document/@FA{document-handle}?policy=@FA{policy}} /// /// As before, if @FA{policy} is @LIT{error}. If @FA{policy} is @LIT{last}, then /// the last write will win. /// -/// @REST{DELETE /collection/@FA{collection-identifier}/@FA{document-identifier}? _rev=@FA{etag}} +/// @REST{DELETE /document/@FA{document-handle}?rev=@FA{etag}} /// -/// You can also supply the etag using the parameter "_rev" instead of an "ETag" -/// header. +/// You can also supply the etag using the parameter @LIT{rev} instead of an "ETag" +/// header. You must never supply both the "ETag" header and the @LIT{rev} +/// parameter. /// /// @EXAMPLES /// @@ -656,7 +676,7 @@ bool RestCollectionHandler::updateDocument () { /// @verbinclude rest12 //////////////////////////////////////////////////////////////////////////////// -bool RestCollectionHandler::deleteDocument () { +bool RestDocumentHandler::deleteDocument () { vector suffix = request->suffix(); // find and load collection given by name oder identifier diff --git a/RestHandler/RestCollectionHandler.h b/RestHandler/RestDocumentHandler.h similarity index 96% rename from RestHandler/RestCollectionHandler.h rename to RestHandler/RestDocumentHandler.h index 63f0290557..6699406711 100644 --- a/RestHandler/RestCollectionHandler.h +++ b/RestHandler/RestDocumentHandler.h @@ -31,7 +31,7 @@ #include "RestHandler/RestVocbaseBaseHandler.h" // ----------------------------------------------------------------------------- -// --SECTION-- RestCollectionHandler +// --SECTION-- RestDocumentHandler // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -46,7 +46,7 @@ namespace triagens { /// @brief collection request handler //////////////////////////////////////////////////////////////////////////////// - class RestCollectionHandler : public RestVocbaseBaseHandler { + class RestDocumentHandler : public RestVocbaseBaseHandler { //////////////////////////////////////////////////////////////////////////////// /// @} @@ -67,7 +67,7 @@ namespace triagens { /// @brief constructor //////////////////////////////////////////////////////////////////////////////// - RestCollectionHandler (rest::HttpRequest* request, struct TRI_vocbase_s* vocbase); + RestDocumentHandler (rest::HttpRequest* request, struct TRI_vocbase_s* vocbase); //////////////////////////////////////////////////////////////////////////////// /// @} diff --git a/RestHandler/RestVocbaseBaseHandler.cpp b/RestHandler/RestVocbaseBaseHandler.cpp index 167fa233da..7de63f3e04 100644 --- a/RestHandler/RestVocbaseBaseHandler.cpp +++ b/RestHandler/RestVocbaseBaseHandler.cpp @@ -73,7 +73,7 @@ LoggerData::Extra const RestVocbaseBaseHandler::RES_FAIL; /// @brief document path //////////////////////////////////////////////////////////////////////////////// -string RestVocbaseBaseHandler::DOCUMENT_PATH = "/collection"; +string RestVocbaseBaseHandler::DOCUMENT_PATH = "/document"; //////////////////////////////////////////////////////////////////////////////// /// @brief collection path @@ -220,17 +220,20 @@ void RestVocbaseBaseHandler::generateDocument (TRI_doc_mptr_t const* document, TRI_string_buffer_t buffer; if (generateDocument) { - string id = StringUtils::itoa(_documentCollection->base._cid) + ":" + StringUtils::itoa(document->_did); + string id = StringUtils::itoa(_documentCollection->base._cid) + TRI_DOCUMENT_HANDLE_SEPARATOR_STR + StringUtils::itoa(document->_did); TRI_json_t augmented; TRI_InitArrayJson(&augmented); TRI_json_t* _id = TRI_CreateStringCopyJson(id.c_str()); - TRI_json_t* _rev = TRI_CreateNumberJson(document->_rid); + if (_id) { TRI_Insert2ArrayJson(&augmented, "_id", _id); } + + TRI_json_t* _rev = TRI_CreateNumberJson(document->_rid); + if (_rev) { TRI_Insert2ArrayJson(&augmented, "_rev", _rev); } @@ -266,7 +269,7 @@ void RestVocbaseBaseHandler::generateDocument (TRI_doc_mptr_t const* document, //////////////////////////////////////////////////////////////////////////////// bool RestVocbaseBaseHandler::splitDocumentReference (string const& name, string& did) { - vector doc = StringUtils::split(name, ":"); + vector doc = StringUtils::split(name, TRI_DOCUMENT_HANDLE_SEPARATOR_STR); switch (doc.size()) { case 1: diff --git a/RestServer/AvocadoServer.cpp b/RestServer/AvocadoServer.cpp index 8ea1b6c014..8f1b1bd4af 100644 --- a/RestServer/AvocadoServer.cpp +++ b/RestServer/AvocadoServer.cpp @@ -47,7 +47,7 @@ #include "Logger/Logger.h" #include "Rest/Initialise.h" #include "RestHandler/RestActionHandler.h" -#include "RestHandler/RestCollectionHandler.h" +#include "RestHandler/RestDocumentHandler.h" #include "RestServer/ActionDispatcherThread.h" #include "RestServer/AvocadoHttpServer.h" #include "V8/JSLoader.h" @@ -548,7 +548,7 @@ int AvocadoServer::startupServer () { _applicationAdminServer->addBasicHandlers(factory); - factory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createData, _vocbase); + factory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createData, _vocbase); factory->addPrefixHandler("/", RestHandlerCreator::createData, _vocbase); _httpServer = _applicationHttpServer->buildServer(new AvocadoHttpServer(scheduler, dispatcher), factory, ports); @@ -567,7 +567,7 @@ int AvocadoServer::startupServer () { _applicationAdminServer->addBasicHandlers(adminFactory); _applicationAdminServer->addHandlers(adminFactory, "/admin"); - adminFactory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createData, _vocbase); + adminFactory->addPrefixHandler(RestVocbaseBaseHandler::DOCUMENT_PATH, RestHandlerCreator::createData, _vocbase); adminFactory->addPrefixHandler("/", RestHandlerCreator::createData, _vocbase); _adminHttpServer = _applicationHttpServer->buildServer(adminFactory, adminPorts); diff --git a/RestServer/examples-setup.dox b/RestServer/examples-setup.dox new file mode 100644 index 0000000000..1ca7ff1dbe --- /dev/null +++ b/RestServer/examples-setup.dox @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief over the wire protocol +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @page ExamplesSetup Preparing the Examples +/// +/// All examples in the manuals assume that you have created the following +/// collections: +/// +/// - demo +/// - five +/// - geo +/// +/// Some of the examples may destroy the collection. In that case you can +/// use one of the following script to restore the collection. +/// +/// @LIT{examples.js}: A JavaScript script usable within the AvocadoDB shell to +/// regenerate the above collections. +/// +/// @LIT{examples.sh}: A shell script using curl to setup the collections. The +/// script works without any additional client, but will not be able to detect +/// any errors. +/// +/// @section ExamplesSetupDemo Collection "Demo" +//////////////////////////////////////////////// +/// +/// @verbinclude demo1 +/// +/// was generated by +/// +/// @verbinclude demo.js +/// +/// @section ExamplesSetupFive Collection "five" +//////////////////////////////////////////////// +/// +/// The collection "five" has a unique constraint on the attribute @LIT{a}, +/// and a second unique constraint on the attribute @LIT{b}. +/// +/// @verbinclude five1 +/// +/// was generated by +/// +/// @verbinclude five.js +/// +/// @section ExamplesSetupGeo Collection "geo" +////////////////////////////////////////////// +/// +/// The collection "geo" has two geo fields, one @LIT{home} being a list with +/// two elements, one @LIT{work} being an hash array with latitude @LIT{b} and +/// longitude @LIT{l}. +/// +/// @verbinclude geojs1 +/// +/// was generated by +/// +/// @verbinclude geo.js +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: c++ +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @\\}\\)" +// End: diff --git a/RestServer/glossary.dox b/RestServer/glossary.dox index e6ee417678..3e1deb3993 100644 --- a/RestServer/glossary.dox +++ b/RestServer/glossary.dox @@ -29,8 +29,13 @@ /// @page Glossary /// /// @copydoc GlossaryCollectionIdentifier +/// +/// @copydoc GlossaryDocument +/// /// @copydoc GlossaryDocumentHandle -/// @cooydoc GlossaryDocumentIdentifier +/// +/// @copydoc GlossaryDocumentIdentifier +/// /// @copydoc GlossaryDocumentRevision /// /// @page GlossaryCollectionIdentifier @@ -38,6 +43,11 @@ /// @GE{Collection Identifier}: A collection identifier identifies a collection /// in a database. It is an integer and is unique within the database. /// +/// @page GlossaryDocument +/// +/// @GE{Document}: Documents in AvocadoDB are JSON objects. These objects can be +/// nested (to any depth) and may contains lists. +/// /// @page GlossaryDocumentHandle /// /// @GE{Document Handle}: A document handle uniquely identifies a document in @@ -47,7 +57,7 @@ /// @page GlossaryDocumentIdentifier /// /// @GE{Document Identifier}: A document identifier identifies a document in a -/// database. It is an integer and is unique within the collection of the +/// given collection. It is an integer and is unique within the collection of the /// document. /// /// @page GlossaryDocumentRevision diff --git a/RestServer/key-value.dox b/RestServer/key-value.dox new file mode 100644 index 0000000000..9c55397dd8 --- /dev/null +++ b/RestServer/key-value.dox @@ -0,0 +1,208 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief statements and cursors +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2010-2012 triagens GmbH, Cologne, Germany +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// http://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Achim Brandt +/// @author Copyright 2012, triagens GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @page Key-Value REST Interface for storing key-value pairs +/// +/// @section Key-ValueCreate Create a new key-value pair in the data store. +/// +/// Creates a new key-value pair with the given key, the data passed in content +/// and optional attributes passed in the header. +/// If a key-value pair with the same key exists in the data store, the request +/// returns an error. +/// +/// @anchor Key-ValuePost +/// @REST{POST /_api/key/@FA{collection-name}/@FA{key}} +/// +/// Optional Attributes: +/// - @LIT{x-voc-expires} Expiry-date for this key-value pair. +/// - @LIT{x-voc-extended} A JSON-object of one or more attributes to be attached +/// to the key-value pair. +/// - TODO @LIT{binary data} +/// +/// If the document was created successfully, then a @LIT{HTTP 201} is returned. +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is +/// returned. +/// +/// If the @FA{key} is used, then a @LIT{HTTP 404} is returned. +/// +/// @EXAMPLES +/// +/// Create a key-value pair +/// +/// @verbinclude key-value_post-key-request +/// +/// Response (Success) +/// +/// @verbinclude key-value_post-key-ok +/// +/// Response (Failure) +/// +/// @verbinclude key-value_post-key-bad +/// +/// +/// +/// @section Key-ValueChange Creates or changes the value for a given key. +/// +/// Sets the value associated with key to the new data passed in content. +/// +/// @anchor Key-ValuePut +/// @REST{PUT /_api/key/@FA{collection-name}/@FA{key}} +/// +/// Optional parameter: +/// - @LIT{?create=1} When set to 1 database will create keys if they do not +/// exist. When set to 0 (default) it will return an error if a key does not +/// exist. +/// +/// If the document was updated successfully, then a @LIT{HTTP 202} is returned. +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is +/// returned. +/// +/// If the @FA{key} is not found and @LIT{?create=0}, then a @LIT{HTTP 404} is +/// returned. +/// +/// @EXAMPLES +/// +/// Update a key-value pair +/// +/// @verbinclude key-value_put-key-request +/// +/// Response (Success) +/// +/// @verbinclude key-value_put-key-ok +/// +/// Response (Failure) +/// +/// @verbinclude key-value_put-key-bad +/// +/// +/// +/// @section Key-ValueReturn Returns the value associated with a given key. +/// +/// Returns the value associated with the key in content. The Attributes of +/// the key-value pair are returned in the header. +/// +/// @anchor Key-ValueGet +/// @REST{GET /_api/key/@FA{collection-name}/@FA{key}} +/// +/// If the document was found, then a @LIT{HTTP 200} is returned. +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is +/// returned. +/// +/// If the @FA{key} is not found, then a @LIT{HTTP 404} is returned. +/// +/// @EXAMPLES +/// +/// Get a key-value pair +/// +/// @verbinclude key-value_get-key-request +/// +/// Response (Success) with attributes +/// +/// @verbinclude key-value_get-key-ok +/// +/// Response (Failure) +/// +/// @verbinclude key-value_get-key-bad +/// +/// +/// +/// @section Key-ValueRemove This function deletes a key-value pair from the data store. +/// +/// Deletes the key-value pair with key from the store. +/// Deletions cannot be undone! +/// +/// @anchor Key-ValueDelete +/// @REST{DELETE /_api/key/@FA{collection-name}/@FA{key}} +/// +/// This function does not support any header parameters. +/// +/// If the document was found and deleted, then a @LIT{HTTP 200} is returned. +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is +/// returned. +/// +/// If the @FA{key} is not found, then a @LIT{HTTP 404} is returned. +/// +/// @EXAMPLES +/// +/// Delete a key-value pair +/// +/// @verbinclude key-value_delete-key-request +/// +/// Response (Success) +/// +/// @verbinclude key-value_delete-key-ok +/// +/// Response (Failure) +/// +/// @verbinclude key-value_delete-key-bad +/// +/// +/// +/// @section Key-ValueLookup This function returns all keys matching a given prefix. +/// +/// Retrieves all values that begin with key and returns them as a JSON-array in +/// content. +/// +/// @anchor Key-ValueSearch +/// @REST{GET /_api/keys/@FA{collection-name}/@FA{prefix}} +/// +/// @FA{prefix} (=beginning of word) to search for. Note that any /-characters +/// contained in key are considered part of the key and have no special meaning. +/// +/// TODO Optional parameter: +/// - @LIT{?filter=Filter Expression} Return only those keys that match the given +/// filter expression (have certain attributes). Please note that the filter +/// expression has to be URL-encoded +/// +/// If the @FA{collection-name} is unknown, then a @LIT{HTTP 404} is +/// returned. +/// +/// @EXAMPLES +/// +/// Find keys by prefix +/// +/// @verbinclude key-value_get-keys-request +/// +/// Response (Success) +/// +/// @verbinclude key-value_get-keys-ok +/// +/// Response (Failure) +/// +/// @verbinclude key-value_get-keys-bad +/// +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/RestServer/otwp.dox b/RestServer/otwp.dox index 7fd9eccf95..9ca77e9953 100644 --- a/RestServer/otwp.dox +++ b/RestServer/otwp.dox @@ -33,12 +33,12 @@ ///
    ///
  1. Documents ///
      -///
    1. @ref RestCollectionCreate "POST /collection/@FA{collection-identifier}"
    2. -///
    3. @ref RestCollectionRead "GET /collection/@FA{collection-identifier}/@FA{document-identifier}"
    4. -///
    5. @ref RestCollectionReadAll "GET /collection/@FA{collection-identifier}"
    6. -///
    7. @ref RestCollectionUpdate "PUT /collection/@FA{collection-identifier}/@FA{document-identifier}"
    8. -///
    9. @ref RestCollectionDelete "DELETE /collection/@FA{collection-identifier}/@FA{document-identifier}"
    10. -///
    11. @ref RestCollectionHead "HEAD /collection/@FA{collection-identifier}/@FA{document-identifier}"
    12. +///
    13. @ref RestDocumentRead "GET /document/@FA{document-handle}"
    14. +///
    15. @ref RestDocumentCreate "POST /document/collection=@FA{collection-identifier}"
    16. +///
    17. @ref RestDocumentUpdate "PUT /document/@FA{document-handle}"
    18. +///
    19. @ref RestDocumentDelete "DELETE /document/@FA{document-handle}"
    20. +///
    21. @ref RestDocumentHead "HEAD /document/@FA{document-handle}"
    22. +///
    23. @ref RestDocumentReadAll "GET /document/collection=@FA{collection-identifier}"
    24. ///
    ///
  2. ///
  3. @ref OTWPSimpleQueries @@ -57,6 +57,15 @@ ///
  4. @ref RestQueryPost "POST /_api/query"
  5. ///
/// +///
  • @ref Key-Value +///
      +///
    1. @ref Key-ValuePost "POST /_api/key/@FA{collection-name}/@FA{key}"
    2. +///
    3. @ref Key-ValuePut "PUT /_api/key/@FA{collection-name}/@FA{key}"
    4. +///
    5. @ref Key-ValueGet "GET /_api/key/@FA{collection-name}/@FA{key}"
    6. +///
    7. @ref Key-ValueDelete "DELETE /_api/key/@FA{collection-name}/@FA{key}"
    8. +///
    9. @ref Key-ValueSearch "GET /_api/keys/@FA{collection-name}/@FA{prefix}"
    10. +///
    +///
  • /// /// ///
  • @ref OTWPDatabase diff --git a/RestServer/rest-document.dox b/RestServer/rest-document.dox index dcfa7fce6c..48d02ff4ce 100644 --- a/RestServer/rest-document.dox +++ b/RestServer/rest-document.dox @@ -29,25 +29,44 @@ /// @page RestDocumentTOC /// ///
      -///
    1. @ref RestCollectionCreate "POST /collection/@FA{collection-identifier}"
    2. -///
    3. @ref RestCollectionRead "GET /collection/@FA{collection-identifier}/@FA{document-identifier}"
    4. -///
    5. @ref RestCollectionReadAll "GET /collection/@FA{collection-identifier}"
    6. -///
    7. @ref RestCollectionUpdate "PUT /collection/@FA{collection-identifier}/@FA{document-identifier}"
    8. -///
    9. @ref RestCollectionDelete "DELETE /collection/@FA{collection-identifier}/@FA{document-identifier}"
    10. -///
    11. @ref RestCollectionHead "HEAD /collection/@FA{collection-identifier}/@FA{document-identifier}"
    12. +///
    13. @ref RestDocumentIntro
    14. +///
    15. @ref RestDocumentResource
    16. +///
    17. @ref RestDocumentHttp +///
        +///
      1. @ref RestDocumentRead "GET /document/@FA{document-handle}"
      2. +///
      3. @ref RestDocumentCreate "POST /document/collection=@FA{collection-identifier}"
      4. +///
      5. @ref RestDocumentUpdate "PUT /document/@FA{document-handle}"
      6. +///
      7. @ref RestDocumentDelete "DELETE /document/@FA{document-handle}"
      8. +///
      9. @ref RestDocumentHead "HEAD /document/@FA{document-handle}"
      10. +///
      11. @ref RestDocumentReadAll "GET /document/collection=@FA{collection-identifier}"
      12. +///
      +///
    18. ///
    //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @page RestDocument REST Interface for Documents /// -/// Documents in AvocadoDB are JSON objects. The objects can be nested to any -/// depth and can contains lists, for example: +/// This is an introduction to AvocadoDB's REST interface to documents. +/// +///
    +/// @copydoc RestDocumentTOC +///
    +/// +/// @section RestDocumentIntro Documents, Identifiers, Handles +////////////////////////////////////////////////////////////// +/// +/// @copydoc GlossaryDocument +/// +/// For example: /// /// @verbinclude document1 /// /// All documents contain two special fields, the document handle in @LIT{_id} -/// and the document revision in @LIT{_rev}. +/// and the etag aka document revision in @LIT{_rev}. +/// +/// All examples in the document assume that you have created some example +/// collections, see @ref ExamplesSetup. /// /// @copydoc GlossaryDocumentHandle /// @@ -59,36 +78,56 @@ /// /// The basic operations (create, read, update, delete) for documents are mapped /// to the standard HTTP methods (POST, GET, PUT, DELETE). An identifier for the -/// document revision is returned in the "ETag" field. If you modify a document, -/// you can use the "ETag" field to detect conflicts. The revision of a document -/// can be checking using the HTTP method HEAD. +/// document revision is returned in the "ETag" header field. If you modify a +/// document, you can use the "If-Match" field to detect conflicts. The revision +/// of a document can be checking using the HTTP method HEAD. /// -///
    -/// @copydoc RestDocumentTOC +/// @section RestDocumentResource Address and ETag of an Document +///////////////////////////////////////////////////////////////// +/// +/// All documents in AvocadoDB have a document handle. This handle uniquely +/// defines a document and is managed by AvocadoDB. Assume that the document +/// handle, which is stored in the @LIT{_id} field of the document, is +/// @LIT{7254820/362549736}, then the HTTP URI of that document is: +/// +/// @LIT{http://localhost:8529/document/7254820/362549736} +/// +/// Each document also has a document revision or etag with is returned +/// in the "ETag" header field when requesting a document. +/// +/// If you obtain a document using @LIT{GET} and you want to check if a +/// newer revision is available, then you can use the "If-None-Match" +/// header. If the document is unchanged, a @LIT{HTTP 412} is returned. +/// +/// If you want to update or delete a document, then you can use the +/// "If-Match" header. If the document has changed, then the +/// operation is aborted and a @LIT{HTTP 409} is returned. +/// +/// @section RestDocumentHttp Working with Documents using REST +/////////////////////////////////////////////////////////////// +/// +/// @anchor RestDocumentRead +/// @copydetails triagens::avocado::RestDocumentHandler::readSingleDocument ///
    /// -/// @anchor RestCollectionCreate -/// @copydetails triagens::avocado::RestCollectionHandler::createDocument +/// @anchor RestDocumentCreate +/// @copydetails triagens::avocado::RestDocumentHandler::createDocument ///
    /// -/// @anchor RestCollectionRead -/// @copydetails triagens::avocado::RestCollectionHandler::readSingleDocument +/// @anchor RestDocumentUpdate +/// @copydetails triagens::avocado::RestDocumentHandler::updateDocument ///
    /// -/// @anchor RestCollectionReadAll -/// @copydetails triagens::avocado::RestCollectionHandler::readAllDocuments +/// @anchor RestDocumentDelete +/// @copydetails triagens::avocado::RestDocumentHandler::deleteDocument ///
    /// -/// @anchor RestCollectionUpdate -/// @copydetails triagens::avocado::RestCollectionHandler::updateDocument +/// @anchor RestDocumentHead +/// @copydetails triagens::avocado::RestDocumentHandler::checkDocument ///
    /// -/// @anchor RestCollectionDelete -/// @copydetails triagens::avocado::RestCollectionHandler::deleteDocument -///
    -/// -/// @anchor RestCollectionHead -/// @copydetails triagens::avocado::RestCollectionHandler::checkDocument +/// @anchor RestDocumentReadAll +/// @copydetails triagens::avocado::RestDocumentHandler::readAllDocuments //////////////////////////////////////////////////////////////////////////////// // Local Variables: diff --git a/SimpleHttpClient/SimpleHttpConnection.cpp b/SimpleHttpClient/SimpleHttpConnection.cpp index e456594136..13ad6f3a4c 100644 --- a/SimpleHttpClient/SimpleHttpConnection.cpp +++ b/SimpleHttpClient/SimpleHttpConnection.cpp @@ -194,10 +194,10 @@ namespace triagens { return contentLength; } + toRead -= _readBufferSize; + stream.write(_readBuffer, _readBufferSize); _readBufferSize = 0; - - toRead -= _readBufferSize; } size_t len = 0; @@ -216,14 +216,11 @@ namespace triagens { len += len_read; runtime = now() - start; } - - //buffer[len] = '\0'; - stream.write(buffer, len); free(buffer); - return len; + return (contentLength - toRead + len); } //////////////////////////////////////////////////////////////////////////////// diff --git a/SkipLists/skiplist.c b/SkipLists/skiplist.c index 3eb981088b..c2ec8834fd 100755 --- a/SkipLists/skiplist.c +++ b/SkipLists/skiplist.c @@ -714,8 +714,6 @@ bool TRI_InsertKeySkipList (TRI_skiplist_t* skiplist, void* key, void* element, //////////////////////////////////////////////////////////////////////////////// void* TRI_LeftLookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - - int32_t level; int32_t currentLevel; TRI_skiplist_node_t* currentNode; TRI_skiplist_node_t* nextNode; @@ -839,7 +837,7 @@ void* TRI_LeftLookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - END: +// END: assert(false); // there is no way we can be here return NULL; @@ -862,8 +860,6 @@ void* TRI_LookupByElementSkipList (TRI_skiplist_t* skiplist, void* element) { //////////////////////////////////////////////////////////////////////////////// void* TRI_LookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - - int32_t level; int32_t currentLevel; TRI_skiplist_node_t* currentNode; TRI_skiplist_node_t* nextNode; @@ -979,7 +975,7 @@ void* TRI_LookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - END: +// END: assert(false); // there is no way we can be here return NULL; @@ -1184,8 +1180,6 @@ bool TRI_RemoveKeySkipList (TRI_skiplist_t* skiplist, void* key, void* old) { //////////////////////////////////////////////////////////////////////////////// void* TRI_RightLookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - - int32_t level; int32_t currentLevel; TRI_skiplist_node_t* currentNode; TRI_skiplist_node_t* prevNode; @@ -1312,7 +1306,7 @@ void* TRI_RightLookupByKeySkipList (TRI_skiplist_t* skiplist, void* key) { - END: +// END: assert(false); // there is no way we can be here return NULL; @@ -1526,8 +1520,6 @@ void* TRI_EndNodeSkipListMulti(TRI_skiplist_multi_t* skiplist) { //////////////////////////////////////////////////////////////////////////////// void* TRI_LeftLookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key) { - - int32_t level; int32_t currentLevel; TRI_skiplist_node_t* currentNode; TRI_skiplist_node_t* nextNode; @@ -1647,7 +1639,7 @@ void* TRI_LeftLookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key - END: +// END: assert(false); // there is no way we can be here return NULL; @@ -2115,8 +2107,6 @@ bool TRI_RemoveKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key, void* //////////////////////////////////////////////////////////////////////////////// void* TRI_RightLookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* key) { - - int32_t level; int32_t currentLevel; TRI_skiplist_node_t* currentNode; TRI_skiplist_node_t* prevNode; @@ -2234,7 +2224,7 @@ void* TRI_RightLookupByKeySkipListMulti(TRI_skiplist_multi_t* skiplist, void* ke - END: +// END: assert(false); // there is no way we can be here return NULL; diff --git a/V8/v8-conv.cpp b/V8/v8-conv.cpp index 3a7fa21d14..1f4cc37e46 100644 --- a/V8/v8-conv.cpp +++ b/V8/v8-conv.cpp @@ -33,6 +33,7 @@ #include "BasicsC/string-buffer.h" #include "BasicsC/strings.h" #include "ShapedJson/shaped-json.h" +#include "VocBase/vocbase.h" #include "V8/v8-json.h" #include "V8/v8-utils.h" @@ -1171,7 +1172,7 @@ v8::Handle TRI_ObjectReference (TRI_voc_cid_t cid, TRI_voc_did_t did) TRI_InitStringBuffer(&buffer); TRI_AppendUInt64StringBuffer(&buffer, cid); - TRI_AppendStringStringBuffer(&buffer, ":"); + TRI_AppendCharStringBuffer(&buffer, TRI_DOCUMENT_HANDLE_SEPARATOR_CHR); TRI_AppendUInt64StringBuffer(&buffer, did); v8::Handle ref = v8::String::New(buffer._buffer); @@ -1198,7 +1199,7 @@ bool TRI_IdentifiersObjectReference (v8::Handle value, TRI_voc_cid_t& string v = TRI_ObjectToString(value); - vector doc = StringUtils::split(v, ":"); + vector doc = StringUtils::split(v, TRI_DOCUMENT_HANDLE_SEPARATOR_STR); switch (doc.size()) { case 1: @@ -1380,6 +1381,40 @@ char TRI_ObjectToCharacter (v8::Handle value, bool& error) { return (*sep)[0]; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to an int64_t +//////////////////////////////////////////////////////////////////////////////// + +int64_t TRI_ObjectToInt64 (v8::Handle value) { + if (value->IsNumber()) { + return (int64_t) value->ToNumber()->Value(); + } + + if (value->IsNumberObject()) { + v8::Handle no = v8::Handle::Cast(value); + return (int64_t) no->NumberValue(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a uint64_t +//////////////////////////////////////////////////////////////////////////////// + +uint64_t TRI_ObjectToUInt64 (v8::Handle value) { + if (value->IsNumber()) { + return (uint64_t) value->ToNumber()->Value(); + } + + if (value->IsNumberObject()) { + v8::Handle no = v8::Handle::Cast(value); + return (uint64_t) no->NumberValue(); + } + + return 0; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief converts an V8 object to a double //////////////////////////////////////////////////////////////////////////////// diff --git a/V8/v8-conv.h b/V8/v8-conv.h index 03cda939d2..4951b2c1d9 100644 --- a/V8/v8-conv.h +++ b/V8/v8-conv.h @@ -83,6 +83,18 @@ std::string TRI_ObjectToString (v8::Handle); char TRI_ObjectToCharacter (v8::Handle, bool& error); +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to an int64_t +//////////////////////////////////////////////////////////////////////////////// + +int64_t TRI_ObjectToInt64 (v8::Handle); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief converts an V8 object to a uint64_t +//////////////////////////////////////////////////////////////////////////////// + +uint64_t TRI_ObjectToUInt64 (v8::Handle); + //////////////////////////////////////////////////////////////////////////////// /// @brief converts an V8 object to a double //////////////////////////////////////////////////////////////////////////////// diff --git a/V8/v8-globals.h b/V8/v8-globals.h index 18dd81b419..9b582fe196 100644 --- a/V8/v8-globals.h +++ b/V8/v8-globals.h @@ -63,7 +63,6 @@ typedef struct TRI_v8_global_s { TRI_v8_global_s () : JSFluentQueries(), - JSQueryTemplates(), JSQueryCursors(), JSQueries(), // DEPRECATED JSCursors(), // DEPRECATED @@ -75,7 +74,6 @@ typedef struct TRI_v8_global_s { EdgesTempl(), EdgesColTempl(), FluentQueryTempl(), - QueryTemplateTempl(), QueryCursorTempl(), QueryErrorTempl(), QueryTempl(), // DEPRECATED @@ -105,12 +103,6 @@ typedef struct TRI_v8_global_s { std::map< void*, v8::Persistent > JSFluentQueries; -//////////////////////////////////////////////////////////////////////////////// -/// @brief template mapping for weak pointers -//////////////////////////////////////////////////////////////////////////////// - - std::map< void*, v8::Persistent > JSQueryTemplates; - //////////////////////////////////////////////////////////////////////////////// /// @brief cursor mapping for weak pointers //////////////////////////////////////////////////////////////////////////////// @@ -202,12 +194,6 @@ typedef struct TRI_v8_global_s { v8::Persistent FluentQueryTempl; -//////////////////////////////////////////////////////////////////////////////// -/// @brief query template template -//////////////////////////////////////////////////////////////////////////////// - - v8::Persistent QueryTemplateTempl; - //////////////////////////////////////////////////////////////////////////////// /// @brief cursor template //////////////////////////////////////////////////////////////////////////////// @@ -443,10 +429,6 @@ typedef struct TRI_v8_global_s { // --SECTION-- REGULAR EXPRESSIONS // ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- -// --SECTION-- private variables -// ----------------------------------------------------------------------------- - //////////////////////////////////////////////////////////////////////////////// /// @addtogroup V8Globals /// @{ diff --git a/V8/v8-vocbase.cpp b/V8/v8-vocbase.cpp index cd35a9a5c4..9ef2bd321e 100644 --- a/V8/v8-vocbase.cpp +++ b/V8/v8-vocbase.cpp @@ -232,6 +232,21 @@ static T* UnwrapClass (v8::Handle obj, int32_t type) { return static_cast(v8::Handle::Cast(obj->GetInternalField(SLOT_CLASS))->Value()); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief Get the vocbase pointer from the current V8 context +//////////////////////////////////////////////////////////////////////////////// + +static TRI_vocbase_t* GetContextVocBase () { + v8::Handle currentContext = v8::Context::GetCurrent(); + v8::Handle db = currentContext->Global()->Get(v8::String::New("db"))->ToObject(); + TRI_vocbase_t* vocbase = UnwrapClass(db, WRP_VOCBASE_TYPE); + if (!vocbase) { + return 0; + } + + return vocbase; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief checks if argument is a document identifier //////////////////////////////////////////////////////////////////////////////// @@ -610,67 +625,6 @@ static v8::Handle WrapQuery (TRI_query_t* query) { return queryObject; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief weak reference callback for query instances -//////////////////////////////////////////////////////////////////////////////// -/* -static void WeakQueryInstanceCallback (v8::Persistent object, void* parameter) { - TRI_query_instance_t* instance; - TRI_v8_global_t* v8g; - - v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); - instance = (TRI_query_instance_t*) parameter; - - LOG_TRACE("weak-callback for query instance called"); - - TRI_query_template_t* template_ = (TRI_query_template_t*) instance->_template; - assert(template_); - - TRI_DecreaseRefCountQueryTemplate(template_); - - // find the persistent handle - v8::Persistent persistent = v8g->JSQueryInstances[instance]; - v8g->JSQueryInstances.erase(instance); - - // dispose and clear the persistent handle - persistent.Dispose(); - persistent.Clear(); - - TRI_FreeQueryInstance(instance); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief stores a query instance in a javascript object -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle WrapQueryInstance (TRI_query_instance_t* instance) { - TRI_v8_global_t* v8g; - v8::HandleScope scope; - - v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); - - v8::Handle queryInstance = v8g->QueryInstanceTempl->NewInstance(); - map< void*, v8::Persistent >::iterator i = v8g->JSQueryInstances.find(instance); - - if (i == v8g->JSQueryInstances.end()) { - v8::Persistent persistent = v8::Persistent::New(v8::External::New(instance)); - - queryInstance->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_QUERY_INSTANCE_TYPE)); - queryInstance->SetInternalField(SLOT_CLASS, persistent); - - v8g->JSQueryInstances[instance] = persistent; - - LOG_TRACE("creating new query instance"); - persistent.MakeWeak(instance, WeakQueryInstanceCallback); - } - else { - queryInstance->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_QUERY_INSTANCE_TYPE)); - queryInstance->SetInternalField(SLOT_CLASS, i->second); - } - - return scope.Close(queryInstance); -} -*/ //////////////////////////////////////////////////////////////////////////////// /// @brief Create a query error in a javascript object //////////////////////////////////////////////////////////////////////////////// @@ -744,154 +698,32 @@ static TRI_rc_cursor_t* ExecuteQuery (v8::Handle queryObject, return cursor; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query instance or uses existing result set -//////////////////////////////////////////////////////////////////////////////// - -/* -static TRI_query_cursor_t* ExecuteQueryInstance (v8::Handle queryObject, - bool doCount, - uint32_t max, - v8::Handle* err) { - v8::TryCatch tryCatch; - - TRI_query_instance_t* instance = UnwrapClass(queryObject, WRP_QUERY_INSTANCE_TYPE); - - if (!instance) { - *err = v8::String::New("corrupted query instance"); - return 0; - } - - LOG_TRACE("executing query"); - - TRI_query_cursor_t* cursor = TRI_ExecuteQueryInstance(instance, doCount, max); - if (!cursor) { - if (tryCatch.HasCaught()) { - *err = tryCatch.Exception(); - } - else { - *err = CreateQueryErrorObject(&instance->_error); - } - - return NULL; - } - - return cursor; -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/// @brief weak reference callback for templates -//////////////////////////////////////////////////////////////////////////////// -/* -static void WeakQueryTemplateCallback (v8::Persistent object, void* parameter) { - LOG_TRACE("weak-callback for query template called"); - - TRI_query_template_t* template_ = (TRI_query_template_t*) parameter; - - // mutex lock - TRI_LockQueryTemplate(template_); - - try { - // find the persistent handle - TRI_v8_global_t* v8g; - v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); - v8::Persistent persistent = v8g->JSQueryTemplates[template_]; - v8g->JSQueryTemplates.erase(template_); - - // dispose and clear the persistent handle - persistent.Dispose(); - persistent.Clear(); - } - catch (...) { - } - - assert(template_->_shadow); - if (!TRI_ReleaseShadowData(template_->_vocbase->_templates, template_->_shadow)) { - // unlock mutex only if template was not disposed - TRI_UnlockQueryTemplate(template_); - } -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/// @brief stores a template in a javascript object -//////////////////////////////////////////////////////////////////////////////// -/* -static v8::Handle WrapQueryTemplate (TRI_query_template_t* template_) { - TRI_v8_global_t* v8g; - v8::HandleScope scope; - - v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); - - v8::Handle templateObject = v8g->QueryTemplateTempl->NewInstance(); - map< void*, v8::Persistent >::iterator i = v8g->JSQueryTemplates.find(template_); - - if (i == v8g->JSQueryTemplates.end()) { - v8::Persistent persistent = v8::Persistent::New(v8::External::New(template_)); - - templateObject->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_QUERY_TEMPLATE_TYPE)); - templateObject->SetInternalField(SLOT_CLASS, persistent); - - v8g->JSQueryTemplates[template_] = persistent; - - persistent.MakeWeak(template_, WeakQueryTemplateCallback); - } - else { - templateObject->SetInternalField(SLOT_CLASS_TYPE, v8::Integer::New(WRP_QUERY_TEMPLATE_TYPE)); - templateObject->SetInternalField(SLOT_CLASS, i->second); - } - - return scope.Close(templateObject); -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/// @brief extracts a template from a javascript object -//////////////////////////////////////////////////////////////////////////////// - -static TRI_query_template_t* UnwrapQueryTemplate (v8::Handle templateObject) { - return UnwrapClass(templateObject, WRP_QUERY_TEMPLATE_TYPE); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief weak reference callback for cursors //////////////////////////////////////////////////////////////////////////////// static void WeakQueryCursorCallback (v8::Persistent object, void* parameter) { + v8::HandleScope scope; + LOG_TRACE("weak-callback for query cursor called"); + + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return; + } TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) parameter; + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); - // mutex lock - TRI_LockQueryCursor(cursor); + // find the persistent handle + TRI_v8_global_t* v8g; + v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); + v8::Persistent persistent = v8g->JSQueryCursors[cursor]; + v8g->JSQueryCursors.erase(cursor); - try { - // find the persistent handle - TRI_v8_global_t* v8g; - v8g = (TRI_v8_global_t*) v8::Isolate::GetCurrent()->GetData(); - v8::Persistent persistent = v8g->JSQueryCursors[cursor]; - v8g->JSQueryCursors.erase(cursor); - - // dispose and clear the persistent handle - persistent.Dispose(); - persistent.Clear(); - } - catch (...) { - } - - bool mustFree = false; - if (cursor->_shadow) { - TRI_DecreaseRefcountShadowData(cursor->_vocbase->_cursors, cursor->_shadow); - } else { - mustFree = true; - } - // unlock mutex - TRI_UnlockQueryCursor(cursor); - - if (mustFree) { - cursor->free(cursor); - } + // dispose and clear the persistent handle + persistent.Dispose(); + persistent.Clear(); } //////////////////////////////////////////////////////////////////////////////// @@ -1318,7 +1150,6 @@ static v8::Handle JS_InEdgesQuery (v8::Arguments const& argv) { return EdgesQuery(TRI_EDGE_IN, argv); } - //////////////////////////////////////////////////////////////////////////////// /// @brief finds points near a given coordinate //////////////////////////////////////////////////////////////////////////////// @@ -1497,53 +1328,10 @@ static v8::Handle JS_WithinQuery (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief get a (persistent) template by its id +/// @addtogroup VocBase +/// @{ //////////////////////////////////////////////////////////////////////////////// -/* -static v8::Handle JS_Template (v8::Arguments const& argv) { - v8::HandleScope scope; - - if (argv.Length() != 2) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_TEMPLATE(, )"))); - } - - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); - - if (!vocbase) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); - } - - // get the id - v8::Handle idArg = argv[1]->ToNumber(); - if (!idArg->IsNumber()) { - return scope.Close(v8::ThrowException(v8::String::New("expecting number for "))); - } - - double id = TRI_ObjectToDouble(idArg); - - TRI_shadow_t* shadow = TRI_FindShadowData(vocbase->_templates, (TRI_shadow_id) id); - if (!shadow) { - return scope.Close(v8::ThrowException(v8::String::New("no template found for id"))); - } - - TRI_query_template_t* template_ = (TRI_query_template_t*) shadow->_data; - if (!template_) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed template"))); - } - - TRI_LockQueryTemplate(template_); - if (template_->_deleted) { - TRI_UnlockQueryTemplate(template_); - return scope.Close(v8::ThrowException(v8::String::New("template has already been deleted"))); - } - TRI_UnlockQueryTemplate(template_); - - return scope.Close(WrapQueryTemplate(template_)); -} -*/ - //////////////////////////////////////////////////////////////////////////////// /// @brief get a (persistent) cursor by its id //////////////////////////////////////////////////////////////////////////////// @@ -1551,42 +1339,29 @@ static v8::Handle JS_Template (v8::Arguments const& argv) { static v8::Handle JS_Cursor (v8::Arguments const& argv) { v8::HandleScope scope; - if (argv.Length() != 2) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_CURSOR(, )"))); + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_CURSOR()"))); } - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); - + TRI_vocbase_t* vocbase = GetContextVocBase(); if (!vocbase) { return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } // get the id - v8::Handle idArg = argv[1]->ToNumber(); - if (!idArg->IsNumber()) { - return scope.Close(v8::ThrowException(v8::String::New("expecting number for "))); + v8::Handle idArg = argv[0]->ToString(); + if (!idArg->IsString()) { + return scope.Close(v8::ThrowException(v8::String::New("expecting string for "))); } + string idString = TRI_ObjectToString(idArg); + uint64_t id = TRI_UInt64String(idString.c_str()); - double id = TRI_ObjectToDouble(idArg); - - TRI_shadow_t* shadow = TRI_FindShadowData(vocbase->_cursors, (TRI_shadow_id) id); - if (!shadow) { - return scope.Close(v8::ThrowException(v8::String::New("no cursor found for id"))); - } - - TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) shadow->_data; + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageIdShadowData(vocbase->_cursors, id); if (!cursor) { return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("cursor has already been deleted"))); - } - TRI_UnlockQueryCursor(cursor); - return scope.Close(WrapQueryCursor(cursor)); } @@ -1608,8 +1383,6 @@ static v8::Handle JS_Cursor (v8::Arguments const& argv) { //////////////////////////////////////////////////////////////////////////////// static TRI_json_t* ConvertHelper(v8::Handle parameter) { - - if (parameter->IsBoolean()) { v8::Handle booleanParameter = parameter->ToBoolean(); return TRI_CreateBooleanJson(booleanParameter->Value()); @@ -1664,181 +1437,22 @@ static TRI_json_t* ConvertHelper(v8::Handle parameter) { return NULL; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief constructs a new query template from a string -//////////////////////////////////////////////////////////////////////////////// -/* -static v8::Handle JS_CreateTemplateAql (v8::Arguments const& argv) { - v8::HandleScope scope; - - if (argv.Length() < 3) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_CREATE_TEMPLATE(, , )"))); - } - - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); - if (!vocbase) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); - } - - // get the query string - v8::Handle queryArg = argv[1]; - if (!queryArg->IsString()) { - return scope.Close(v8::ThrowException(v8::String::New("expecting string for "))); - } - string queryString = TRI_ObjectToString(queryArg); - - // get the persistent/non-persistent flag - v8::Handle persistentArg = argv[2]; - if (!persistentArg->IsBoolean()) { - return scope.Close(v8::ThrowException(v8::String::New("expecting bool for "))); - } - - bool persistent = TRI_ObjectToBoolean(persistentArg); - TRI_query_template_type_e persistMode; - if (persistent) { - persistMode = QUERY_TEMPLATE_PERSISTENT; - } - else { - persistMode = QUERY_TEMPLATE_TRANSIENT; - } - - // create a template object - TRI_query_template_t* template_ = TRI_CreateQueryTemplate(queryString.c_str(), - vocbase, - persistMode); - - if (template_) { - bool ok = TRI_ParseQueryTemplate(template_); - if (ok) { - return scope.Close(WrapQueryTemplate(template_)); - } - - v8::Handle errorObject = CreateQueryErrorObject(&template_->_error); - TRI_FreeQueryTemplate(template_); - return scope.Close(errorObject); - } - - return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/// @brief execute a stored statement -//////////////////////////////////////////////////////////////////////////////// -/* -static v8::Handle JS_StoredStatementAql (v8::Arguments const& argv) { - v8::HandleScope scope; - - if (argv.Length() < 2 || argv.Length() > 5) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_STORED_STATEMENT(, , , , )"))); - } - - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); - if (!vocbase) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); - } - - // get the statement id - v8::Handle idArg = argv[1]; - TRI_voc_cid_t cid; - TRI_voc_did_t did; - - if (! IsDocumentId(idArg, cid, did)) { - return scope.Close(v8::ThrowException(v8::String::New(" must be a document identifier"))); - } - - LOG_INFO("looking up shadow"); - TRI_shadow_document_t* shadow = TRI_FindShadowDocument(vocbase->_statements, vocbase, cid, did); - LOG_INFO("looked up shadow %p", shadow); - - //TRI_StoreShadowData((TRI_shadow_store_t*) vocbase->_templates, - // (const void* const) template_); - - // return number of total records in cursor? - bool doCount = false; - if (argv.Length() > 0) { - doCount = TRI_ObjectToBoolean(argv[3]); - } - - // maximum number of results to return at once - uint32_t max = 1000; - if (argv.Length() > 1) { - double maxValue = TRI_ObjectToDouble(argv[4]); - if (maxValue >= 1.0) { - max = (uint32_t) maxValue; - } - } - - TRI_query_template_t* template_ = NULL; - TRI_query_instance_t* instance = NULL; - TRI_query_cursor_t* cursor = NULL; - TRI_json_t* parameters = NULL; -// template_ = TRI_CreateQueryTemplate(queryString.c_str(), -// vocbase, -// QUERY_TEMPLATE_TRANSIENT); - if (!template_) { - return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); - } - - bool ok = TRI_ParseQueryTemplate(template_); - if (!ok) { - v8::Handle errorObject = CreateQueryErrorObject(&template_->_error); - TRI_FreeQueryTemplate(template_); - return scope.Close(errorObject); - } - - if (argv.Length() > 2) { - parameters = ConvertHelper(argv[2]); - } - - instance = TRI_CreateQueryInstance(template_, parameters); - if (!instance) { - if (parameters) { - TRI_FreeJson(parameters); - } - return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); - } - - if (!instance->_doAbort) { - cursor = TRI_ExecuteQueryInstance(instance, doCount, max); - } - - if (parameters) { - TRI_FreeJson(parameters); - } - - if (!cursor) { - v8::Handle errorObject = CreateQueryErrorObject(&instance->_error); - - TRI_FreeQueryInstance(instance); - return scope.Close(errorObject); - } - - TRI_FreeQueryInstance(instance); - - return scope.Close(WrapQueryCursor(cursor)); -} -*/ - //////////////////////////////////////////////////////////////////////////////// /// @brief parses a query and returns the parse result //////////////////////////////////////////////////////////////////////////////// static v8::Handle JS_ParseAql (v8::Arguments const& argv) { v8::HandleScope scope; - - if (argv.Length() < 2 || argv.Length() > 5) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_PARSE(, )"))); + + if (argv.Length() != 1) { + return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_PARSE()"))); } - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); + TRI_vocbase_t* vocbase = GetContextVocBase(); if (!vocbase) { return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - + // get the query string v8::Handle queryArg = argv[1]; if (!queryArg->IsString()) { @@ -1848,10 +1462,7 @@ static v8::Handle JS_ParseAql (v8::Arguments const& argv) { TRI_query_template_t* template_ = NULL; - template_ = TRI_CreateQueryTemplate(queryString.c_str(), - vocbase, - QUERY_TEMPLATE_TRANSIENT); - + template_ = TRI_CreateQueryTemplate(queryString.c_str(), vocbase); if (!template_) { return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); } @@ -1893,18 +1504,17 @@ static v8::Handle JS_ParseAql (v8::Arguments const& argv) { static v8::Handle JS_StatementAql (v8::Arguments const& argv) { v8::HandleScope scope; - if (argv.Length() < 2 || argv.Length() > 5) { - return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_STATEMENT(, , , , )"))); + if (argv.Length() < 1 || argv.Length() > 4) { + return scope.Close(v8::ThrowException(v8::String::New("usage: AQL_STATEMENT(, , , )"))); } - - v8::Handle dbArg = argv[0]->ToObject(); - TRI_vocbase_t* vocbase = UnwrapClass(dbArg, WRP_VOCBASE_TYPE); + + TRI_vocbase_t* vocbase = GetContextVocBase(); if (!vocbase) { return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } // get the query string - v8::Handle queryArg = argv[1]; + v8::Handle queryArg = argv[0]; if (!queryArg->IsString()) { return scope.Close(v8::ThrowException(v8::String::New("expecting string for "))); } @@ -1913,13 +1523,13 @@ static v8::Handle JS_StatementAql (v8::Arguments const& argv) { // return number of total records in cursor? bool doCount = false; if (argv.Length() > 0) { - doCount = TRI_ObjectToBoolean(argv[3]); + doCount = TRI_ObjectToBoolean(argv[2]); } // maximum number of results to return at once uint32_t max = 1000; if (argv.Length() > 1) { - double maxValue = TRI_ObjectToDouble(argv[4]); + double maxValue = TRI_ObjectToDouble(argv[3]); if (maxValue >= 1.0) { max = (uint32_t) maxValue; } @@ -1930,10 +1540,7 @@ static v8::Handle JS_StatementAql (v8::Arguments const& argv) { TRI_query_cursor_t* cursor = NULL; TRI_json_t* parameters = NULL; - template_ = TRI_CreateQueryTemplate(queryString.c_str(), - vocbase, - QUERY_TEMPLATE_TRANSIENT); - + template_ = TRI_CreateQueryTemplate(queryString.c_str(), vocbase); if (!template_) { return scope.Close(v8::ThrowException(v8::String::New("out of memory"))); } @@ -1945,8 +1552,8 @@ static v8::Handle JS_StatementAql (v8::Arguments const& argv) { return scope.Close(errorObject); } - if (argv.Length() > 2) { - parameters = ConvertHelper(argv[2]); + if (argv.Length() > 1) { + parameters = ConvertHelper(argv[1]); } // TODO: properly free parameters @@ -1978,6 +1585,8 @@ static v8::Handle JS_StatementAql (v8::Arguments const& argv) { TRI_FreeQueryInstance(instance); TRI_FreeQueryTemplate(template_); + TRI_StoreShadowData(vocbase->_cursors, (const void* const) cursor); + return scope.Close(WrapQueryCursor(cursor)); } @@ -2910,110 +2519,6 @@ static v8::Handle JS_ExecuteAql (v8::Arguments const& argv) { return scope.Close(WrapCursor(cursor)); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroy a template -//////////////////////////////////////////////////////////////////////////////// - -static v8::Handle JS_DisposeQueryTemplate (v8::Arguments const& argv) { - v8::HandleScope scope; - v8::TryCatch tryCatch; - - if (argv.Length() != 0) { - return scope.Close(v8::ThrowException(v8::String::New("usage: dispose()"))); - } - - v8::Handle self = argv.Holder(); - TRI_query_template_t* template_ = UnwrapQueryTemplate(self); - if (!template_) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted template"))); - } - - // set the deleted flag so the gc can catch this instance - TRI_LockQueryTemplate(template_); - if (template_->_deleted) { - TRI_UnlockQueryTemplate(template_); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already disposed template"))); - } - template_->_deleted = true; - TRI_UnlockQueryTemplate(template_); - - return scope.Close(v8::BooleanObject::New(true)); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the id of the template -//////////////////////////////////////////////////////////////////////////////// - -/* -static v8::Handle JS_IdQueryTemplate (v8::Arguments const& argv) { - v8::HandleScope scope; - v8::TryCatch tryCatch; - - if (argv.Length() != 0) { - return scope.Close(v8::ThrowException(v8::String::New("usage: id()"))); - } - - v8::Handle self = argv.Holder(); - TRI_query_template_t* template_ = UnwrapQueryTemplate(self); - if (!template_) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted template"))); - } - - TRI_LockQueryTemplate(template_); - if (template_->_deleted) { - TRI_UnlockQueryTemplate(template_); - return scope.Close(v8::ThrowException(v8::String::New("corrupted template"))); - } - - if (template_->_type == QUERY_TEMPLATE_PERSISTENT) { - TRI_shadow_id id = template_->_shadow->_id; - TRI_UnlockQueryTemplate(template_); - return scope.Close(v8::Number::New(id)); - } - else { - TRI_UnlockQueryTemplate(template_); - return scope.Close(v8::ThrowException(v8::String::New("template is non-persistent"))); - } -} -*/ - -//////////////////////////////////////////////////////////////////////////////// -/// @brief executes a query, returns a cursor -//////////////////////////////////////////////////////////////////////////////// -/* -static v8::Handle JS_ExecuteQueryInstance (v8::Arguments const& argv) { - v8::HandleScope scope; - - // return number of total records in cursor? - bool doCount = false; - if (argv.Length() > 0) { - doCount = TRI_ObjectToBoolean(argv[3]); - } - - // maximum number of results to return at once - uint32_t max = 1000; - if (argv.Length() > 1) { - double maxValue = TRI_ObjectToDouble(argv[1]); - if (maxValue >= 1.0) { - max = (uint32_t) maxValue; - } - } - - v8::Handle err; - TRI_query_cursor_t* cursor = ExecuteQueryInstance(argv.Holder(), doCount, max, &err); - - if (!cursor) { - return v8::ThrowException(err); - } - - // we must do this to increase the refcount of the shadow - TRI_shadow_t* shadow = TRI_FindShadowData(cursor->_vocbase->_cursors, cursor->_shadow->_id); - assert(shadow); - - return scope.Close(WrapQueryCursor(cursor)); -} -*/ - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -3033,28 +2538,21 @@ static v8::Handle JS_ExecuteQueryInstance (v8::Arguments const& argv) static v8::Handle JS_DisposeQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; - v8::TryCatch tryCatch; if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: dispose()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted cursor"))); + + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - // set the deleted flag so the gc can catch this instance - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already disposed cursor"))); + if (TRI_DeleteDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder()))) { + return scope.Close(v8::True()); } - cursor->_deleted = true; - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::True()); + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already disposed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3063,34 +2561,22 @@ static v8::Handle JS_DisposeQueryCursor (v8::Arguments const& argv) { static v8::Handle JS_IdQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; - v8::TryCatch tryCatch; if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: id()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted cursor"))); + + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted cursor"))); - } - - if (cursor->_shadow) { - TRI_shadow_id id = cursor->_shadow->_id; - TRI_UnlockQueryCursor(cursor); - + + TRI_shadow_id id = TRI_GetIdDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + if (id) { return scope.Close(v8::Number::New(id)); } - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("unknown cursor"))); + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already disposed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3099,28 +2585,27 @@ static v8::Handle JS_IdQueryCursor (v8::Arguments const& argv) { static v8::Handle JS_CountQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; - v8::TryCatch tryCatch; if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: count()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - TRI_select_size_t length = cursor->_length; - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::Number::New(length)); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + TRI_select_size_t length = 0; + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + + if (cursor) { + length = cursor->_length; + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); + return scope.Close(v8::Number::New(length)); + } + + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3129,64 +2614,58 @@ static v8::Handle JS_CountQueryCursor (v8::Arguments const& argv) { static v8::Handle JS_NextQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; - v8::TryCatch tryCatch; if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: next()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - v8::Handle value; - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - bool ok = true; - TRI_js_exec_context_t context = NULL; - // exceptions must be caught in the following part because we hold an exclusive - // lock that might otherwise not be freed - try { - TRI_rc_result_t* next = cursor->next(cursor); - if (!next) { - value = v8::Undefined(); - } - else { - context = TRI_CreateExecutionContext(cursor->_functionCode); - if (context) { - TRI_DefineSelectExecutionContext(context, next); - ok = TRI_ExecuteExecutionContext(context, (void*) &value); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + bool result = false; + TRI_js_exec_context_t context = NULL; + v8::Handle value; + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + + if (cursor) { + TRI_LockQueryCursor(cursor); + + // exceptions must be caught in the following part because we hold an exclusive + // lock that might otherwise not be freed + try { + TRI_rc_result_t* next = cursor->next(cursor); + if (!next) { + value = v8::Undefined(); + } + else { + context = TRI_CreateExecutionContext(cursor->_functionCode); + if (context) { + TRI_DefineSelectExecutionContext(context, next); + if (TRI_ExecuteExecutionContext(context, (void*) &value)) { + result = true; + } + } } } - } - catch (...) { - } - - if (context) { - TRI_FreeExecutionContext(context); - } - // always free lock - TRI_UnlockQueryCursor(cursor); - - if (!ok) { - if (tryCatch.HasCaught()) { - return scope.Close(v8::ThrowException(tryCatch.Exception())); + catch (...) { } - else { - return scope.Close(v8::ThrowException(v8::String::New("cannot convert to JavaScript"))); + + TRI_UnlockQueryCursor(cursor); + + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); + if (context) { + TRI_FreeExecutionContext(context); + } + + if (result) { + return scope.Close(value); } } - return scope.Close(value); + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3199,27 +2678,18 @@ static v8::Handle JS_PersistQueryCursor (v8::Arguments const& argv) { if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: persist()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - if (!cursor->_shadow) { - cursor->_shadow = TRI_StoreShadowData((TRI_shadow_store_t*) cursor->_vocbase->_cursors, - (const void* const) cursor); + bool result = TRI_PersistDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + if (result) { + return scope.Close(v8::True()); } - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::True()); + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3231,101 +2701,93 @@ static v8::Handle JS_PersistQueryCursor (v8::Arguments const& argv) { static v8::Handle JS_GetRowsQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; - v8::TryCatch tryCatch; if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: getRows()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - v8::Handle result = v8::Array::New(); - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); } - + + bool result = false; TRI_js_exec_context_t context = NULL; - bool ok = true; - - // exceptions must be caught in the following part because we hold an exclusive - // lock that might otherwise not be freed - try { - uint32_t max = cursor->getMax(cursor); - context = TRI_CreateExecutionContext(cursor->_functionCode); + v8::Handle rows = v8::Array::New(); + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); - if (context) { - for (uint32_t i = 0; i < max; i++) { - TRI_rc_result_t* next = cursor->next(cursor); - if (!next) { - break; - } + if (cursor) { + TRI_LockQueryCursor(cursor); + + // exceptions must be caught in the following part because we hold an exclusive + // lock that might otherwise not be freed + try { + uint32_t max = cursor->getBatchSize(cursor); + context = TRI_CreateExecutionContext(cursor->_functionCode); + + if (context) { + for (uint32_t i = 0; i < max; ++i) { + TRI_rc_result_t* next = cursor->next(cursor); + if (!next) { + break; + } - TRI_DefineSelectExecutionContext(context, next); - v8::Handle value; - ok = TRI_ExecuteExecutionContext(context, (void*) &value); - if (ok) { - result->Set(i, value); + TRI_DefineSelectExecutionContext(context, next); + v8::Handle value; + if (TRI_ExecuteExecutionContext(context, (void*) &value)) { + rows->Set(i, value); + } } + result = true; } } - } - catch (...) { - } - - if (context) { - TRI_FreeExecutionContext(context); - } - // always free lock - TRI_UnlockQueryCursor(cursor); + catch (...) { + } + + TRI_UnlockQueryCursor(cursor); + + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); - if (!ok) { - if (tryCatch.HasCaught()) { - return scope.Close(v8::ThrowException(tryCatch.Exception())); + if (context) { + TRI_FreeExecutionContext(context); } - else { - return scope.Close(v8::ThrowException(v8::String::New("cannot convert to JavaScript"))); + + if (result) { + return scope.Close(rows); } } - return scope.Close(result); + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// /// @brief return max number of results per transfer for cursor //////////////////////////////////////////////////////////////////////////////// -static v8::Handle JS_GetMaxQueryCursor (v8::Arguments const& argv) { +static v8::Handle JS_GetBatchSizeQueryCursor (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() != 0) { - return scope.Close(v8::ThrowException(v8::String::New("usage: getMax()"))); + return scope.Close(v8::ThrowException(v8::String::New("usage: getBatchSize()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - uint32_t max = cursor->getMax(cursor); - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::Number::New(max)); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + + if (cursor) { + uint32_t max = cursor->getBatchSize(cursor); + + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); + return scope.Close(v8::Number::New(max)); + } + + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3338,24 +2800,23 @@ static v8::Handle JS_HasCountQueryCursor (v8::Arguments const& argv) if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: hasCount()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - bool hasCount = cursor->hasCount(cursor); - TRI_UnlockQueryCursor(cursor); - return scope.Close(hasCount ? v8::True() : v8::False()); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + + if (cursor) { + bool hasCount = cursor->hasCount(cursor); + + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); + return scope.Close(hasCount ? v8::True() : v8::False()); + } + + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3368,24 +2829,25 @@ static v8::Handle JS_HasNextQueryCursor (v8::Arguments const& argv) { if (argv.Length() != 0) { return scope.Close(v8::ThrowException(v8::String::New("usage: hasNext()"))); } - - v8::Handle self = argv.Holder(); - TRI_query_cursor_t* cursor = UnwrapQueryCursor(self); - - if (!cursor) { - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - TRI_LockQueryCursor(cursor); - if (cursor->_deleted) { - TRI_UnlockQueryCursor(cursor); - return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); - } - - bool hasNext = cursor->hasNext(cursor); - TRI_UnlockQueryCursor(cursor); - return scope.Close(hasNext ? v8::True() : v8::False()); + TRI_vocbase_t* vocbase = GetContextVocBase(); + if (!vocbase) { + return scope.Close(v8::ThrowException(v8::String::New("corrupted vocbase"))); + } + + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) + TRI_BeginUsageDataShadowData(vocbase->_cursors, UnwrapQueryCursor(argv.Holder())); + + if (cursor) { + TRI_LockQueryCursor(cursor); + bool hasNext = cursor->hasNext(cursor); + TRI_UnlockQueryCursor(cursor); + + TRI_EndUsageDataShadowData(vocbase->_cursors, cursor); + return scope.Close(hasNext ? v8::True() : v8::False()); + } + + return scope.Close(v8::ThrowException(v8::String::New("corrupted or already freed cursor"))); } //////////////////////////////////////////////////////////////////////////////// @@ -3488,7 +2950,7 @@ static v8::Handle JS_NextRefCursor (v8::Arguments const& argv) { // always use the primary collection TRI_voc_cid_t cid = cursor->_context->_primary->base._cid; TRI_voc_did_t did = next->_primary->_did; - string ref = StringUtils::itoa(cid) + ":" + StringUtils::itoa(did); + string ref = StringUtils::itoa(cid) + TRI_DOCUMENT_HANDLE_SEPARATOR_STR + StringUtils::itoa(did); return scope.Close(v8::String::New(ref.c_str())); } @@ -4732,7 +4194,7 @@ static v8::Handle JS_ReplaceVocbaseCol (v8::Arguments const& argv) { char* cidStr = TRI_StringUInt64(collection->base._cid); char* didStr = TRI_StringUInt64(did); - string name = cidStr + string(":") + didStr; + string name = cidStr + string(TRI_DOCUMENT_HANDLE_SEPARATOR_STR) + didStr; TRI_FreeString(didStr); TRI_FreeString(cidStr); @@ -4823,7 +4285,7 @@ static v8::Handle JS_SaveVocbaseCol (v8::Arguments const& argv) { char* cidStr = TRI_StringUInt64(collection->base._cid); char* didStr = TRI_StringUInt64(did); - string name = cidStr + string(":") + didStr; + string name = cidStr + string(TRI_DOCUMENT_HANDLE_SEPARATOR_STR) + didStr; TRI_FreeString(didStr); TRI_FreeString(cidStr); @@ -4984,7 +4446,7 @@ static v8::Handle JS_SaveEdgesCol (v8::Arguments const& argv) { char* cidStr = TRI_StringUInt64(collection->base._cid); char* didStr = TRI_StringUInt64(did); - string name = cidStr + string(":") + didStr; + string name = cidStr + string(TRI_DOCUMENT_HANDLE_SEPARATOR_STR) + didStr; TRI_FreeString(didStr); TRI_FreeString(cidStr); @@ -5670,7 +5132,9 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas } // create the regular expressions - if (regcomp(&v8g->DocumentIdRegex, "([0-9][0-9]*):([0-9][0-9]*)", REG_ICASE | REG_EXTENDED) != 0) { + string expr = "([0-9][0-9]*)" + string(TRI_DOCUMENT_HANDLE_SEPARATOR_STR) + "([0-9][0-9]*)"; + + if (regcomp(&v8g->DocumentIdRegex, expr.c_str(), REG_ICASE | REG_EXTENDED) != 0) { LOG_FATAL("cannot compile regular expression"); exit(EXIT_FAILURE); } @@ -5707,8 +5171,8 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas v8::Handle EnsureUniqueConstraintFuncName = v8::Persistent::New(v8::String::New("ensureUniqueConstraint")); v8::Handle ExecuteFuncName = v8::Persistent::New(v8::String::New("execute")); v8::Handle FiguresFuncName = v8::Persistent::New(v8::String::New("figures")); + v8::Handle GetBatchSizeFuncName = v8::Persistent::New(v8::String::New("getBatchSize")); v8::Handle GetIndexesFuncName = v8::Persistent::New(v8::String::New("getIndexes")); - v8::Handle GetMaxFuncName = v8::Persistent::New(v8::String::New("getMax")); v8::Handle GetRowsFuncName = v8::Persistent::New(v8::String::New("getRows")); v8::Handle HasCountFuncName = v8::Persistent::New(v8::String::New("hasCount")); v8::Handle HasNextFuncName = v8::Persistent::New(v8::String::New("hasNext")); @@ -5936,43 +5400,6 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas ft->GetFunction()); /* DEPRECATED END */ - // ............................................................................. - // generate the query template template - // ............................................................................. - - ft = v8::FunctionTemplate::New(); - ft->SetClassName(v8::String::New("AvocadoQueryTemplate")); - - rt = ft->InstanceTemplate(); - rt->SetInternalFieldCount(2); - - rt->Set(DisposeFuncName, v8::FunctionTemplate::New(JS_DisposeQueryTemplate)); -// rt->Set(IdFuncName, v8::FunctionTemplate::New(JS_IdQueryTemplate)); - - v8g->QueryTemplateTempl = v8::Persistent::New(rt); - - // must come after SetInternalFieldCount - context->Global()->Set(v8::String::New("AvocadoQueryTemplate"), - ft->GetFunction()); - - // ............................................................................. - // generate the query instance template - // ............................................................................. - -// ft = v8::FunctionTemplate::New(); -// ft->SetClassName(v8::String::New("AvocadoQueryInstance")); - -// rt = ft->InstanceTemplate(); -// rt->SetInternalFieldCount(2); - -// rt->Set(ExecuteFuncName, v8::FunctionTemplate::New(JS_ExecuteQueryInstance)); - -// v8g->QueryInstanceTempl = v8::Persistent::New(rt); - - // must come after SetInternalFieldCount -// context->Global()->Set(v8::String::New("AvocadoQueryInstance"), -// ft->GetFunction()); - // ............................................................................. // generate the query error template // ............................................................................. @@ -6001,7 +5428,7 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas rt->Set(CountFuncName, v8::FunctionTemplate::New(JS_CountQueryCursor)); rt->Set(DisposeFuncName, v8::FunctionTemplate::New(JS_DisposeQueryCursor)); - rt->Set(GetMaxFuncName, v8::FunctionTemplate::New(JS_GetMaxQueryCursor)); + rt->Set(GetBatchSizeFuncName, v8::FunctionTemplate::New(JS_GetBatchSizeQueryCursor)); rt->Set(GetRowsFuncName, v8::FunctionTemplate::New(JS_GetRowsQueryCursor)); rt->Set(HasCountFuncName, v8::FunctionTemplate::New(JS_HasCountQueryCursor)); rt->Set(HasNextFuncName, v8::FunctionTemplate::New(JS_HasNextQueryCursor)); @@ -6037,9 +5464,6 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas context->Global()->Set(v8::String::New("AvocadoCursor"), ft->GetFunction()); - - - // ............................................................................. // generate the skip list operator template // ............................................................................. @@ -6124,9 +5548,6 @@ void TRI_InitV8VocBridge (v8::Handle context, TRI_vocbase_t* vocbas v8::ReadOnly); /* DEPRECATED END */ -// context->Global()->Set(v8::String::New("AQL_STORED_STATEMENT"), -// v8::FunctionTemplate::New(JS_StoredStatementAql)->GetFunction(), -// v8::ReadOnly); context->Global()->Set(v8::String::New("AQL_PARSE"), v8::FunctionTemplate::New(JS_ParseAql)->GetFunction(), v8::ReadOnly); diff --git a/VocBase/compactor.c b/VocBase/compactor.c index 1b013bbb98..c0fdb4141f 100644 --- a/VocBase/compactor.c +++ b/VocBase/compactor.c @@ -471,10 +471,7 @@ static void CleanupShadows (TRI_vocbase_t* const vocbase) { LOG_TRACE("cleaning shadows"); // clean unused cursors - TRI_CleanupShadowData(vocbase->_cursors, SHADOW_CURSOR_MAX_AGE); - - // clean unused statements -// TRI_CleanupShadowDocuments(vocbase->_statements, SHADOW_STATEMENT_MAX_AGE); + TRI_CleanupShadowData(vocbase->_cursors, SHADOW_CURSOR_MAX_AGE, false); } //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/query-base.c b/VocBase/query-base.c index 103f15255d..1763585778 100644 --- a/VocBase/query-base.c +++ b/VocBase/query-base.c @@ -365,122 +365,7 @@ TRI_bind_parameter_t* TRI_CreateBindParameter (const char* name, // ----------------------------------------------------------------------------- // --SECTION-- query template // ----------------------------------------------------------------------------- -/* -//////////////////////////////////////////////////////////////////////////////// -/// @brief decrease the refcount of a template -//////////////////////////////////////////////////////////////////////////////// -void TRI_DecreaseRefCountQueryTemplate (TRI_query_template_t* const template_) { - assert(template_); -// assert(template_->_shadow); - - TRI_LockQueryTemplate(template_); -// TRI_DecreaseRefCountShadowData(template_->_vocbase->_templates, -// template_->_shadow->_id); - - TRI_UnlockQueryTemplate(template_); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief increase the refcount of a template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_IncreaseRefCountQueryTemplate (TRI_query_template_t* const template_) { - assert(template_); -// assert(template_->_shadow); - - TRI_LockQueryTemplate(template_); -// TRI_IncreaseRefCountShadowData(template_->_vocbase->_templates, -// template_->_shadow->_id); - - TRI_UnlockQueryTemplate(template_); -} -*/ -//////////////////////////////////////////////////////////////////////////////// -/// @brief exclusively lock a query template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_LockQueryTemplate (TRI_query_template_t* const template_) { - assert(template_); - - TRI_LockMutex(&template_->_lock); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief unlock a query template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_UnlockQueryTemplate (TRI_query_template_t* const template_) { - assert(template_); - - TRI_UnlockMutex(&template_->_lock); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a template based on its shadow -//////////////////////////////////////////////////////////////////////////////// -/* -void TRI_FreeShadowQueryTemplate (TRI_shadow_document_store_t* store, - TRI_shadow_document_t* shadow) { - TRI_query_template_t* template_ = (TRI_query_template_t*) shadow->_base->_data; - - if (!template_) { - return; - } - - TRI_FreeQueryTemplate(template_); -} -*/ -/* -// TODO: move to own file -void* TRI_CreateShadowQueryTemplate (TRI_shadow_document_store_t* store, TRI_doc_collection_t* collection, TRI_doc_mptr_t const* document) { - TRI_json_t* json; - TRI_json_t* query; - char* queryString; - - LOG_DEBUG("creating shadow for %lu", (unsigned long) document->_did); - - json = TRI_JsonShapedJson(collection->_shaper, &document->_document); - if (!json) { - return NULL; - } - - query = TRI_LookupArrayJson(json, "query"); - if (!query) { -// TODO: fix this -// TRI_FreeJson(json); - return NULL; - } - - if (query->_type != TRI_JSON_STRING || !query->_value._string.data) { -// TODO: fix this -// TRI_FreeJson(json); - return NULL; - } - - queryString = query->_value._string.data; - assert(queryString); - - - -// TODO: fix this -// TRI_FreeJson(json); - - // TODO: fix - return NULL; -} -bool TRI_VerifyShadowQueryTemplate (TRI_shadow_document_store_t* store, TRI_doc_collection_t* collection, TRI_doc_mptr_t const* document, void* shadow) { - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief create shadow data store for templates -//////////////////////////////////////////////////////////////////////////////// - -TRI_shadow_document_store_t* TRI_CreateShadowsQueryTemplate (void) { - return TRI_CreateShadowDocumentStore(&TRI_CreateShadowQueryTemplate, &TRI_VerifyShadowQueryTemplate, &TRI_FreeShadowQueryTemplate); -} -*/ //////////////////////////////////////////////////////////////////////////////// /// @brief Initialize the structs contained in a query template and perform /// some basic optimizations and type detections @@ -528,8 +413,7 @@ bool TRI_AddBindParameterQueryTemplate (TRI_query_template_t* const template_, //////////////////////////////////////////////////////////////////////////////// TRI_query_template_t* TRI_CreateQueryTemplate (const char* queryString, - const TRI_vocbase_t* const vocbase, - const TRI_query_template_type_e type) { + const TRI_vocbase_t* const vocbase) { TRI_query_template_t* template_; assert(queryString); @@ -553,9 +437,6 @@ TRI_query_template_t* TRI_CreateQueryTemplate (const char* queryString, return NULL; } - template_->_type = type;; - template_->_shadow = NULL; - template_->_vocbase = (TRI_vocbase_t*) vocbase; TRI_InitQueryError(&template_->_error); diff --git a/VocBase/query-base.h b/VocBase/query-base.h index aa44faac9e..bd09f0fbdd 100644 --- a/VocBase/query-base.h +++ b/VocBase/query-base.h @@ -36,7 +36,6 @@ #include #include "VocBase/vocbase.h" -#include "VocBase/shadow-data.h" #include "VocBase/query-node.h" #include "VocBase/query-error.h" @@ -120,24 +119,6 @@ TRI_bind_parameter_t* TRI_CreateBindParameter (const char*, const TRI_json_t*); // --SECTION-- query template // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @brief query template types -/// -/// There are two types of templates: -/// - QUERY_TEMPLATE_TRANSIENT: these templates only exist for one query and are -/// disposed automatically when the query execution is finished -/// - QUERY_TEMPLATE_PERSISTENT: these templates are created on the server and -/// can be shared for multiple executions. they are disposed automatically -/// if they are not referenced anymore and have not been used for a certain -/// period of time (garbage collection) -//////////////////////////////////////////////////////////////////////////////// - -typedef enum { - QUERY_TEMPLATE_TRANSIENT, - QUERY_TEMPLATE_PERSISTENT -} -TRI_query_template_type_e; - //////////////////////////////////////////////////////////////////////////////// /// @brief lexer state /// @@ -173,8 +154,6 @@ TRI_query_parser_t; //////////////////////////////////////////////////////////////////////////////// typedef struct TRI_query_template_s { - TRI_query_template_type_e _type; - TRI_shadow_document_t* _shadow; TRI_vocbase_t* _vocbase; char* _queryString; QL_ast_query_t* _query; @@ -192,47 +171,6 @@ typedef struct TRI_query_template_s { } TRI_query_template_t; -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a template based on its shadow -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeShadowQueryTemplate (TRI_shadow_document_store_t*, - TRI_shadow_document_t*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief decrease the refcount of a template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_DecreaseRefCountQueryTemplate (TRI_query_template_t* const); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief increase the refcount of a template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_IncreaseRefCountQueryTemplate (TRI_query_template_t* const); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief exclusively lock a query template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_LockQueryTemplate (TRI_query_template_t* const); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief unlock a query template -//////////////////////////////////////////////////////////////////////////////// - -void TRI_UnlockQueryTemplate (TRI_query_template_t* const); - -// TODO: move to own file -void* TRI_CreateShadowQueryTemplate (TRI_shadow_document_store_t*, TRI_doc_collection_t*, TRI_doc_mptr_t const*); -bool TRI_VerifyShadowQueryTemplate (TRI_shadow_document_store_t*, TRI_doc_collection_t*, TRI_doc_mptr_t const*, void*); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief create shadow data store for templates -//////////////////////////////////////////////////////////////////////////////// - -TRI_shadow_document_store_t* TRI_CreateShadowsQueryTemplate (void); - //////////////////////////////////////////////////////////////////////////////// /// @brief Initialize the structs contained in a query template and perform /// some basic optimizations and type detections @@ -252,8 +190,7 @@ bool TRI_AddBindParameterQueryTemplate (TRI_query_template_t* const, //////////////////////////////////////////////////////////////////////////////// TRI_query_template_t* TRI_CreateQueryTemplate (const char*, - const TRI_vocbase_t* const, - const TRI_query_template_type_e); + const TRI_vocbase_t* const); //////////////////////////////////////////////////////////////////////////////// /// @brief Free a query template diff --git a/VocBase/query-cursor.c b/VocBase/query-cursor.c index b11bc2e5c4..e3c504c760 100644 --- a/VocBase/query-cursor.c +++ b/VocBase/query-cursor.c @@ -27,7 +27,6 @@ #include "VocBase/query-cursor.h" #include "VocBase/query-context.h" -#include "VocBase/shadow-data.h" //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase @@ -85,7 +84,7 @@ static bool HasCountQueryCursor (const TRI_query_cursor_t* const cursor) { /// @brief returns the maximum number of results per transfer //////////////////////////////////////////////////////////////////////////////// -static uint32_t GetMaxQueryCursor (const TRI_query_cursor_t* const cursor) { +static uint32_t GetBatchSizeQueryCursor (const TRI_query_cursor_t* const cursor) { return cursor->_batchSize; } @@ -93,7 +92,7 @@ static uint32_t GetMaxQueryCursor (const TRI_query_cursor_t* const cursor) { /// @brief frees a cursor //////////////////////////////////////////////////////////////////////////////// -static void FreeQueryCursor (TRI_query_cursor_t* cursor) { +void TRI_FreeQueryCursor (TRI_query_cursor_t* cursor) { assert(cursor->_functionCode); TRI_Free(cursor->_functionCode); @@ -136,7 +135,6 @@ TRI_query_cursor_t* TRI_CreateQueryCursor (TRI_query_instance_t* const instance, return NULL; } - cursor->_shadow = NULL; cursor->_hasCount = doCount; cursor->_batchSize = batchSize; cursor->_deleted = false; @@ -148,8 +146,8 @@ TRI_query_cursor_t* TRI_CreateQueryCursor (TRI_query_instance_t* const instance, cursor->next = NextQueryCursor; cursor->hasNext = HasNextQueryCursor; cursor->hasCount = HasCountQueryCursor; - cursor->getMax = GetMaxQueryCursor; - cursor->free = FreeQueryCursor; + cursor->getBatchSize = GetBatchSizeQueryCursor; + cursor->free = TRI_FreeQueryCursor; TRI_InitMutex(&cursor->_lock); TRI_InitVectorPointer(&cursor->_containers); @@ -157,27 +155,11 @@ TRI_query_cursor_t* TRI_CreateQueryCursor (TRI_query_instance_t* const instance, return cursor; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a cursor based on its shadow -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeShadowQueryCursor (TRI_shadow_store_t* store, TRI_shadow_t* shadow) { - TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) shadow->_data; - - if (!cursor) { - return; - } - - FreeQueryCursor(cursor); -} - //////////////////////////////////////////////////////////////////////////////// /// @brief exclusively lock a query cursor //////////////////////////////////////////////////////////////////////////////// void TRI_LockQueryCursor (TRI_query_cursor_t* const cursor) { - assert(cursor); - TRI_LockMutex(&cursor->_lock); } @@ -191,6 +173,16 @@ void TRI_UnlockQueryCursor (TRI_query_cursor_t* const cursor) { TRI_UnlockMutex(&cursor->_lock); } +//////////////////////////////////////////////////////////////////////////////// +/// @brief Free a cursor based on its shadow data pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeShadowQueryCursor (void* data) { + TRI_query_cursor_t* cursor = (TRI_query_cursor_t*) data; + + TRI_FreeQueryCursor(cursor); +} + //////////////////////////////////////////////////////////////////////////////// /// @brief create shadow data store for cursors //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/query-cursor.h b/VocBase/query-cursor.h index 0c26429198..2da20a985e 100644 --- a/VocBase/query-cursor.h +++ b/VocBase/query-cursor.h @@ -48,7 +48,6 @@ extern "C" { typedef struct TRI_query_cursor_s { TRI_vocbase_t* _vocbase; - TRI_shadow_t* _shadow; char* _functionCode; bool _hasCount; uint32_t _batchSize; @@ -64,11 +63,17 @@ typedef struct TRI_query_cursor_s { TRI_rc_result_t* (*next)(struct TRI_query_cursor_s* const); bool (*hasNext)(const struct TRI_query_cursor_s* const); bool (*hasCount)(const struct TRI_query_cursor_s* const); - uint32_t (*getMax)(const struct TRI_query_cursor_s* const); + uint32_t (*getBatchSize)(const struct TRI_query_cursor_s* const); } TRI_query_cursor_t; +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a cursor +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeQueryCursor (TRI_query_cursor_t*); + //////////////////////////////////////////////////////////////////////////////// /// @brief create a cursor //////////////////////////////////////////////////////////////////////////////// @@ -78,12 +83,6 @@ TRI_query_cursor_t* TRI_CreateQueryCursor (TRI_query_instance_t* const, const bool, const uint32_t); -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a cursor based on its shadow -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeShadowQueryCursor (TRI_shadow_store_t*, TRI_shadow_t*); - //////////////////////////////////////////////////////////////////////////////// /// @brief exclusively lock a query cursor //////////////////////////////////////////////////////////////////////////////// @@ -96,6 +95,12 @@ void TRI_LockQueryCursor (TRI_query_cursor_t* const); void TRI_UnlockQueryCursor (TRI_query_cursor_t* const); +//////////////////////////////////////////////////////////////////////////////// +/// @brief Free a cursor based on its data pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeShadowQueryCursor (void*); + //////////////////////////////////////////////////////////////////////////////// /// @brief create shadow data store for cursors //////////////////////////////////////////////////////////////////////////////// diff --git a/VocBase/shadow-data.c b/VocBase/shadow-data.c index c925a63a8c..dbad841a65 100644 --- a/VocBase/shadow-data.c +++ b/VocBase/shadow-data.c @@ -54,7 +54,7 @@ static inline void UpdateTimestampShadow (TRI_shadow_t* const shadow) { /// @brief init a shadow data structure //////////////////////////////////////////////////////////////////////////////// -static TRI_shadow_t* CreateShadow (const void* const element) { +static TRI_shadow_t* CreateShadow (const void* const data) { TRI_shadow_t* shadow = (TRI_shadow_t*) TRI_Allocate(sizeof(TRI_shadow_t)); if (!shadow) { @@ -62,33 +62,81 @@ static TRI_shadow_t* CreateShadow (const void* const element) { } shadow->_rc = 1; - shadow->_data = (void*) element; + shadow->_data = (void*) data; shadow->_id = TRI_NewTickVocBase(); + shadow->_deleted = false; + shadow->_type = SHADOW_TRANSIENT; UpdateTimestampShadow(shadow); + + LOG_TRACE("created shadow %p with data ptr %p and id %lu", + shadow, + data, + (unsigned long) shadow->_id); return shadow; } //////////////////////////////////////////////////////////////////////////////// -/// @brief hashes an element +/// @brief hashes an element in the ids index //////////////////////////////////////////////////////////////////////////////// -static uint64_t HashShadowElement (TRI_associative_pointer_t* array, void const* e) { +static uint64_t HashKeyId (TRI_associative_pointer_t* array, void const* k) { + TRI_shadow_id key = *((TRI_shadow_id*) k); + + return (uint64_t) key; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief hashes an element in the ids index +//////////////////////////////////////////////////////////////////////////////// + +static uint64_t HashElementId (TRI_associative_pointer_t* array, void const* e) { TRI_shadow_t const* element = e; - return element->_id; + return (uint64_t) element->_id; } //////////////////////////////////////////////////////////////////////////////// /// @brief tests if two elements are equal //////////////////////////////////////////////////////////////////////////////// -static bool EqualShadowElement (TRI_associative_pointer_t* array, void const* l, void const* r) { - TRI_shadow_t const* left = l; - TRI_shadow_t const* right = r; +static bool EqualKeyId (TRI_associative_pointer_t* array, void const* k, void const* e) { + TRI_shadow_t const* element = e; + TRI_shadow_id key = *((TRI_shadow_id*) k); - return left->_id == right->_id; + return (key == element->_id); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief hashes an element in the pointers index +//////////////////////////////////////////////////////////////////////////////// + +static uint64_t HashKeyData (TRI_associative_pointer_t* array, void const* k) { + uint64_t key = 0; + + key = (uint64_t) k; + return key; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief hashes an element in the pointers index +//////////////////////////////////////////////////////////////////////////////// + +static uint64_t HashElementData (TRI_associative_pointer_t* array, void const* e) { + TRI_shadow_t const* element = e; + + return (uint64_t) element->_data; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief tests if two elements are equal +//////////////////////////////////////////////////////////////////////////////// + +static bool EqualKeyData (TRI_associative_pointer_t* array, void const* k, void const* e) { + TRI_shadow_t const* element = e; + + return ((uint64_t) k == (uint64_t) element->_data); } //////////////////////////////////////////////////////////////////////////////// @@ -108,16 +156,22 @@ static bool EqualShadowElement (TRI_associative_pointer_t* array, void const* l, /// @brief creates a shadow data storage //////////////////////////////////////////////////////////////////////////////// -TRI_shadow_store_t* TRI_CreateShadowStore (void (*destroy) (TRI_shadow_store_t*, TRI_shadow_t*)) { +TRI_shadow_store_t* TRI_CreateShadowStore (void (*destroy) (void*)) { TRI_shadow_store_t* store = (TRI_shadow_store_t*) TRI_Allocate(sizeof(TRI_shadow_store_t)); if (store) { - TRI_InitAssociativePointer(&store->_index, - NULL, - HashShadowElement, - NULL, - EqualShadowElement); + TRI_InitAssociativePointer(&store->_ids, + HashKeyId, + HashElementId, + EqualKeyId, + NULL); + + TRI_InitAssociativePointer(&store->_pointers, + HashKeyData, + HashElementData, + EqualKeyData, + NULL); store->destroyShadow = destroy; @@ -129,50 +183,22 @@ TRI_shadow_store_t* TRI_CreateShadowStore (void (*destroy) (TRI_shadow_store_t*, //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a shadow data storage +/// +/// Note: all remaining shadows will be destroyed //////////////////////////////////////////////////////////////////////////////// void TRI_FreeShadowStore (TRI_shadow_store_t* const store) { assert(store); + // force deletion of all remaining shadows + TRI_CleanupShadowData(store, 0, true); + TRI_DestroyMutex(&store->_lock); - TRI_DestroyAssociativePointer(&store->_index); + TRI_DestroyAssociativePointer(&store->_ids); + TRI_DestroyAssociativePointer(&store->_pointers); TRI_Free(store); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief Update the refcount of a shadow data element (increase or decrease) -//////////////////////////////////////////////////////////////////////////////// - -static bool UpdateRefCountShadowData (TRI_shadow_store_t* const store, - const TRI_shadow_id id, - const bool increase) { - TRI_shadow_t* shadow; - TRI_shadow_t search; - union { TRI_shadow_t* s; TRI_shadow_t const* c; } cnv; - - assert(store); - - TRI_LockMutex(&store->_lock); - - search._id = id; - cnv.c = (TRI_shadow_t*) TRI_LookupByElementAssociativePointer(&store->_index, - &search); - shadow = cnv.s; - - if (shadow) { - if (increase) { - ++shadow->_rc; - } - else { - --shadow->_rc; - } - } - - TRI_UnlockMutex(&store->_lock); - - return (shadow != NULL); -} - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -187,10 +213,304 @@ static bool UpdateRefCountShadowData (TRI_shadow_store_t* const store, //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief enumerate all shadows and remove them if expired +/// @brief look up a shadow in the index using its data pointer and return +/// its id +//////////////////////////////////////////////////////////////////////////////// + +TRI_shadow_id TRI_GetIdDataShadowData (TRI_shadow_store_t* const store, + const void* const data) { + TRI_shadow_t* shadow; + TRI_shadow_id id = 0; + + assert(store); + + if (data) { + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); + + if (shadow && !shadow->_deleted) { + id = shadow->_id; + UpdateTimestampShadow(shadow); + } + + TRI_UnlockMutex(&store->_lock); + } + + return id; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its data pointer +/// +/// If the shadow is found, this will return the data pointer, NULL otherwise. +/// When the shadow is found, its refcount will also be increased by one +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_BeginUsageDataShadowData (TRI_shadow_store_t* const store, + const void* const data) { + TRI_shadow_t* shadow; + + assert(store); + + if (!data) { + return NULL; + } + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + ++shadow->_rc; + UpdateTimestampShadow(shadow); + TRI_UnlockMutex(&store->_lock); + return shadow->_data; + } + + TRI_UnlockMutex(&store->_lock); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its id +/// +/// If the shadow is found, this will return the data pointer, NULL otherwise. +/// When the shadow is found, its refcount will also be increased by one +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_BeginUsageIdShadowData (TRI_shadow_store_t* const store, + const TRI_shadow_id id) { + TRI_shadow_t* shadow; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, (void const*) &id); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("increasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + ++shadow->_rc; + UpdateTimestampShadow(shadow); + TRI_UnlockMutex(&store->_lock); + return shadow->_data; + } + + TRI_UnlockMutex(&store->_lock); + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its data pointer +/// +/// If the shadow is found, its refcount will be decreased by one. +/// If the refcount is 0 and the shadow is of type SHADOW_TRANSIENT, the shadow +/// object will be destroyed. +//////////////////////////////////////////////////////////////////////////////// + +void TRI_EndUsageDataShadowData (TRI_shadow_store_t* const store, + const void* const data) { + TRI_shadow_t* shadow; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { + LOG_TRACE("deleting shadow %p", shadow); + + TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); + TRI_RemoveKeyAssociativePointer(&store->_pointers, data); + store->destroyShadow(shadow->_data); + TRI_Free(shadow); + } + } + + TRI_UnlockMutex(&store->_lock); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its id +/// +/// If the shadow is found, its refcount will be decreased by one. +/// If the refcount is 0 and the shadow is of type SHADOW_TRANSIENT, the shadow +/// object will be destroyed. +//////////////////////////////////////////////////////////////////////////////// + +void TRI_EndUsageIdShadowData (TRI_shadow_store_t* const store, + const TRI_shadow_id id) { + TRI_shadow_t* shadow; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("decreasing refcount for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + if (--shadow->_rc <= 0 && shadow->_type == SHADOW_TRANSIENT) { + LOG_TRACE("deleting shadow %p", shadow); + + TRI_RemoveKeyAssociativePointer(&store->_ids, &id); + TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); + store->destroyShadow(shadow->_data); + TRI_Free(shadow); + } + } + + TRI_UnlockMutex(&store->_lock); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the persistence flag for a shadow using its data pointer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_PersistDataShadowData (TRI_shadow_store_t* const store, + const void* const data) { + TRI_shadow_t* shadow; + bool result = false; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("persisting shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_type = SHADOW_PERSISTENT; + UpdateTimestampShadow(shadow); + result = true; + } + + TRI_UnlockMutex(&store->_lock); + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the persistence flag for a shadow using its id +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_PersistIdShadowData (TRI_shadow_store_t* const store, + const TRI_shadow_id id) { + TRI_shadow_t* shadow; + bool result = false; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("persisting shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_type = SHADOW_PERSISTENT; + UpdateTimestampShadow(shadow); + result = true; + } + + TRI_UnlockMutex(&store->_lock); + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the deleted flag for a shadow using its data pointer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteDataShadowData (TRI_shadow_store_t* const store, + const void* const data) { + TRI_shadow_t* shadow; + bool found = false; + + assert(store); + + if (data) { + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_pointers, data); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("setting deleted flag for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_deleted = true; + found = true; + } + + TRI_UnlockMutex(&store->_lock); + } + + return found; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the deleted flag for a shadow using its id +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteIdShadowData (TRI_shadow_store_t* const store, + const TRI_shadow_id id) { + TRI_shadow_t* shadow; + bool found = false; + + assert(store); + + TRI_LockMutex(&store->_lock); + shadow = (TRI_shadow_t*) TRI_LookupByKeyAssociativePointer(&store->_ids, &id); + + if (shadow && !shadow->_deleted) { + LOG_TRACE("setting deleted flag for shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + + shadow->_deleted = true; + found = true; + } + + TRI_UnlockMutex(&store->_lock); + return found; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief enumerate all shadows and remove them if +/// - their refcount is 0 and they are transient +/// - their refcount is 0 and they are expired +/// - the force flag is set +/// +/// The max age must be specified in seconds. The max age is ignored if the +/// force flag is set. In this case all remaining shadows will be deleted //////////////////////////////////////////////////////////////////////////////// -void TRI_CleanupShadowData (TRI_shadow_store_t* const store, const double maxAge) { +void TRI_CleanupShadowData (TRI_shadow_store_t* const store, + const double maxAge, + const bool force) { double compareStamp = TRI_microtime() - maxAge; // age must be specified in secs size_t deleteCount = 0; @@ -199,28 +519,34 @@ void TRI_CleanupShadowData (TRI_shadow_store_t* const store, const double maxAge // loop until there's nothing to delete or // we have deleted SHADOW_MAX_DELETE elements - while (deleteCount++ < SHADOW_MAX_DELETE) { + while (deleteCount++ < SHADOW_MAX_DELETE || force) { bool deleted = false; size_t i; - for (i = 0; i < store->_index._nrAlloc; i++) { + for (i = 0; i < store->_ids._nrAlloc; i++) { // enum all shadows - TRI_shadow_t* shadow = (TRI_shadow_t*) store->_index._table[i]; + TRI_shadow_t* shadow = (TRI_shadow_t*) store->_ids._table[i]; if (!shadow) { continue; } // check if shadow is unused and expired - if (shadow->_rc <= 1 && shadow->_timestamp < compareStamp) { - LOG_DEBUG("cleaning expired shadow %p", shadow); - TRI_RemoveElementAssociativePointer(&store->_index, shadow); - store->destroyShadow(store, shadow); - TRI_Free(shadow); + if (shadow->_rc < 1 || force) { + if (shadow->_type == SHADOW_TRANSIENT || + shadow->_timestamp < compareStamp || + force) { + LOG_TRACE("cleaning expired shadow %p", shadow); - deleted = true; - // the remove might reposition elements in the container. - // therefore break here and start iteration anew - break; + TRI_RemoveKeyAssociativePointer(&store->_ids, &shadow->_id); + TRI_RemoveKeyAssociativePointer(&store->_pointers, shadow->_data); + store->destroyShadow(shadow->_data); + TRI_Free(shadow); + + deleted = true; + // the remove might reposition elements in the container. + // therefore break here and start iteration anew + break; + } } } @@ -234,70 +560,33 @@ void TRI_CleanupShadowData (TRI_shadow_store_t* const store, const double maxAge TRI_UnlockMutex(&store->_lock); } -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a shadow by id and decreases its refcount if it exists -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_DecreaseRefCountShadowData (TRI_shadow_store_t* const store, - const TRI_shadow_id id) { - return UpdateRefCountShadowData(store, id, false); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a shadow by id and increases its refcount if it exists -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_IncreaseRefCountShadowData (TRI_shadow_store_t* const store, - const TRI_shadow_id id) { - return UpdateRefCountShadowData(store, id, true); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a shadow by id -//////////////////////////////////////////////////////////////////////////////// - -TRI_shadow_t* TRI_FindShadowData (TRI_shadow_store_t* const store, - const TRI_shadow_id id) { - TRI_shadow_t* shadow; - TRI_shadow_t search; - union { TRI_shadow_t* s; TRI_shadow_t const* c; } cnv; - - LOG_INFO("trying to find shadow %lu", (unsigned long) id); - assert(store); - - TRI_LockMutex(&store->_lock); - - search._id = id; - cnv.c = (TRI_shadow_t*) TRI_LookupByElementAssociativePointer(&store->_index, - &search); - shadow = cnv.s; - - if (shadow) { - ++shadow->_rc; - UpdateTimestampShadow(shadow); - } - - TRI_UnlockMutex(&store->_lock); - - // might be NULL if shadow not found - return shadow; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief store a new shadow in the store //////////////////////////////////////////////////////////////////////////////// TRI_shadow_t* TRI_StoreShadowData (TRI_shadow_store_t* const store, - const void* const element) { + const void* const data) { TRI_shadow_t* shadow; assert(store); - shadow = CreateShadow(element); - LOG_INFO("inserting shadow %lu", (unsigned long) shadow->_id); + shadow = CreateShadow(data); if (shadow) { + LOG_TRACE("storing shadow %p with data ptr %p and id %lu", + shadow, + shadow->_data, + (unsigned long) shadow->_id); + TRI_LockMutex(&store->_lock); - TRI_InsertElementAssociativePointer(&store->_index, shadow, true); + if (TRI_InsertKeyAssociativePointer(&store->_ids, &shadow->_id, shadow, false)) { + // duplicate entry + LOG_INFO("storing shadow failed"); + TRI_UnlockMutex(&store->_lock); + TRI_Free(shadow); + return NULL; + } + TRI_InsertKeyAssociativePointer(&store->_pointers, data, shadow, false); + TRI_UnlockMutex(&store->_lock); } @@ -305,61 +594,15 @@ TRI_shadow_t* TRI_StoreShadowData (TRI_shadow_store_t* const store, return shadow; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief decrease the refcount of a shadow without deleting it -//////////////////////////////////////////////////////////////////////////////// - -int64_t TRI_DecreaseRefcountShadowData (TRI_shadow_store_t* const store, - TRI_shadow_t* const shadow) { - int64_t result; - - assert(shadow); - - TRI_LockMutex(&store->_lock); - - // release the element - result = --shadow->_rc; - - TRI_UnlockMutex(&store->_lock); - - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief releases shadow data -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_ReleaseShadowData (TRI_shadow_store_t* const store, TRI_shadow_t* shadow) { - bool result; - - assert(shadow); - - TRI_LockMutex(&store->_lock); - - // release the element - --shadow->_rc; - - // need to destroy the element - if (shadow->_rc < 1) { - LOG_INFO("releasing shadow %lu", (unsigned long) shadow->_id); - TRI_RemoveElementAssociativePointer(&store->_index, shadow); - store->destroyShadow(store, shadow); - TRI_Free(shadow); - result = true; // object was destroyed - } - else { - result = false; // object was not destroyed - } - - TRI_UnlockMutex(&store->_lock); - - return result; -} - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// +/* +// ----------------------------------------------------------------------------- +// --SECTION-- UNUSED AND UNTESTED CODE FOLLOWS +// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- // --SECTION-- SHADOW DOCUMENTS // ----------------------------------------------------------------------------- @@ -675,10 +918,10 @@ void TRI_CleanupShadowDocuments (TRI_shadow_document_store_t* const store, const // release lock TRI_UnlockMutex(&store->_base->_lock); } - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// +*/ // Local Variables: // mode: outline-minor diff --git a/VocBase/shadow-data.h b/VocBase/shadow-data.h index 3846eb9831..d028a6beff 100644 --- a/VocBase/shadow-data.h +++ b/VocBase/shadow-data.h @@ -71,6 +71,21 @@ extern "C" { /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief typedef for shadow types +/// +/// Shadows are first created with the SHADOW_TRANSIENT type. This means that +/// the shadow will exist only temporarily and will be destroyed when the +/// refcount gets back to 0. Shadows of type SHADOW_PERSISTENT will remain in +/// the shadow store even with a refcount of 0 until their ttl is over. +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + SHADOW_TRANSIENT = 1, + SHADOW_PERSISTENT = 2 +} +TRI_shadow_type_e; + //////////////////////////////////////////////////////////////////////////////// /// @brief typedef for shadow ids //////////////////////////////////////////////////////////////////////////////// @@ -82,10 +97,12 @@ typedef TRI_voc_tick_t TRI_shadow_id; //////////////////////////////////////////////////////////////////////////////// typedef struct TRI_shadow_s { - TRI_shadow_id _id; - int64_t _rc; // refcount - double _timestamp; // creation timestamp - void* _data; + TRI_shadow_id _id; + int64_t _rc; // refcount + double _timestamp; // creation timestamp + void* _data; // pointer to data + bool _deleted; // deleted flag + TRI_shadow_type_e _type; // transient or persistent } TRI_shadow_t; @@ -95,9 +112,10 @@ TRI_shadow_t; typedef struct TRI_shadow_store_s { TRI_mutex_t _lock; - TRI_associative_pointer_t _index; + TRI_associative_pointer_t _ids; // ids + TRI_associative_pointer_t _pointers; // data pointers - void (*destroyShadow) (struct TRI_shadow_store_s*, TRI_shadow_t*); + void (*destroyShadow) (void*); } TRI_shadow_store_t; @@ -118,10 +136,12 @@ TRI_shadow_store_t; /// @brief creates a shadow data storage //////////////////////////////////////////////////////////////////////////////// -TRI_shadow_store_t* TRI_CreateShadowStore (void (*destroy) (TRI_shadow_store_t*, TRI_shadow_t*)); +TRI_shadow_store_t* TRI_CreateShadowStore (void (*destroy) (void*)); //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a shadow data storage +/// +/// Note: all remaining shadows will be destroyed //////////////////////////////////////////////////////////////////////////////// void TRI_FreeShadowStore (TRI_shadow_store_t* const store); @@ -140,54 +160,103 @@ void TRI_FreeShadowStore (TRI_shadow_store_t* const store); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief enumerate all shadows and remove them if expired +/// @brief look up a shadow in the index using its data pointer and return +/// its id +//////////////////////////////////////////////////////////////////////////////// + +TRI_shadow_id TRI_GetIdDataShadowData (TRI_shadow_store_t* const, + const void* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its data pointer +/// +/// If the shadow is found, this will return the data pointer, NULL otherwise. +/// When the shadow is found, its refcount will also be increased by one +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_BeginUsageDataShadowData (TRI_shadow_store_t* const, const void* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its id +/// +/// If the shadow is found, this will return the data pointer, NULL otherwise. +/// When the shadow is found, its refcount will also be increased by one +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_BeginUsageIdShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its data pointer +/// +/// If the shadow is found, its refcount will be decreased by one. +/// If the refcount is 0 and the shadow is of type SHADOW_TRANSIENT, the shadow +/// object will be destroyed. +//////////////////////////////////////////////////////////////////////////////// + +void TRI_EndUsageDataShadowData (TRI_shadow_store_t* const, const void* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief look up a shadow in the index using its id +/// +/// If the shadow is found, its refcount will be decreased by one. +/// If the refcount is 0 and the shadow is of type SHADOW_TRANSIENT, the shadow +/// object will be destroyed. +//////////////////////////////////////////////////////////////////////////////// + +void TRI_EndUsageIdShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the persistence flag for a shadow using its data pointer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_PersistDataShadowData (TRI_shadow_store_t* const, const void* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the persistence flag for a shadow using its id +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_PersistIdShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the deleted flag for a shadow using its data pointer +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteDataShadowData (TRI_shadow_store_t* const, const void* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief set the deleted flag for a shadow using its id +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_DeleteIdShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief enumerate all shadows and remove them if +/// - their refcount is 0 and they are transient +/// - their refcount is 0 and they are expired +/// - the force flag is set /// -/// The max age must be specified in seconds +/// The max age must be specified in seconds. The max age is ignored if the +/// force flag is set. In this case all remaining shadows will be deleted //////////////////////////////////////////////////////////////////////////////// -void TRI_CleanupShadowData (TRI_shadow_store_t* const, const double); +void TRI_CleanupShadowData (TRI_shadow_store_t* const, const double, const bool); //////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a shadow by id and decreases its refcount if it exists -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_DecreaseRefCountShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up a shadow by id and increases its refcount if it exists -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_IncreaseRefCountShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief looks up shadow data -//////////////////////////////////////////////////////////////////////////////// - -TRI_shadow_t* TRI_FindShadowData (TRI_shadow_store_t* const, const TRI_shadow_id); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief stores shadow data +/// @brief store a new shadow in the store //////////////////////////////////////////////////////////////////////////////// TRI_shadow_t* TRI_StoreShadowData (TRI_shadow_store_t* const, const void* const); -//////////////////////////////////////////////////////////////////////////////// -/// @brief decrease the refcount of a shadow without deleting it -//////////////////////////////////////////////////////////////////////////////// - -int64_t TRI_DecreaseRefcountShadowData (TRI_shadow_store_t* const, TRI_shadow_t* const); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief releases shadow data -//////////////////////////////////////////////////////////////////////////////// - -bool TRI_ReleaseShadowData (TRI_shadow_store_t* const, TRI_shadow_t*); - //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// +/* +// ----------------------------------------------------------------------------- +// --SECTION-- UNUSED AND UNTESTED CODE FOLLOWS +// ----------------------------------------------------------------------------- + // ----------------------------------------------------------------------------- // --SECTION-- SHADOW DOCUMENTS // ----------------------------------------------------------------------------- @@ -282,7 +351,7 @@ void TRI_CleanupShadowDocuments (TRI_shadow_document_store_t* const, const doubl //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// - +*/ #ifdef __cplusplus } #endif diff --git a/VocBase/simple-collection.c b/VocBase/simple-collection.c index 7a676582bd..6c8c2e8daa 100644 --- a/VocBase/simple-collection.c +++ b/VocBase/simple-collection.c @@ -1244,7 +1244,6 @@ static bool OpenIndexIterator (char const* filename, void* data) { lat = TRI_LookupArrayJson(json, "latitude"); lon = TRI_LookupArrayJson(json, "longitude"); gjs = TRI_LookupArrayJson(json, "geoJson"); - iid = 0; geoJson = false; if (gjs != NULL && gjs->_type == TRI_JSON_BOOLEAN) { diff --git a/VocBase/vocbase.c b/VocBase/vocbase.c index 1980e6561c..c3165dc031 100644 --- a/VocBase/vocbase.c +++ b/VocBase/vocbase.c @@ -450,14 +450,6 @@ TRI_vocbase_t* TRI_OpenVocBase (char const* path) { return NULL; } - // set up shadow data stores for queries -/* vocbase->_statements = TRI_CreateShadowsQueryTemplate(); - if (!vocbase->_statements) { - TRI_Free(vocbase); - LOG_ERROR("out of memory when opening vocbase"); - return NULL; - } -*/ vocbase->_cursors = TRI_CreateShadowsQueryCursor(); if (!vocbase->_cursors) { TRI_FreeShadowStore(vocbase->_cursors); @@ -470,7 +462,6 @@ TRI_vocbase_t* TRI_OpenVocBase (char const* path) { vocbase->_path = TRI_DuplicateString(path); if (!vocbase->_path) { -// TRI_FreeShadowDocumentStore(vocbase->_statements); TRI_FreeShadowStore(vocbase->_cursors); TRI_Free(vocbase); LOG_ERROR("out of memory when opening vocbase"); @@ -528,12 +519,7 @@ void TRI_CloseVocBase (TRI_vocbase_t* vocbase) { // cursors TRI_FreeShadowStore(vocbase->_cursors); } -/* - if (vocbase->_statements) { - // statements - TRI_FreeShadowDocumentStore(vocbase->_statements); - } -*/ + TRI_DestroyLockFile(vocbase->_lockFile); TRI_FreeString(vocbase->_lockFile); } diff --git a/VocBase/vocbase.h b/VocBase/vocbase.h index 3277e3a069..2b339d8df9 100644 --- a/VocBase/vocbase.h +++ b/VocBase/vocbase.h @@ -72,6 +72,18 @@ extern size_t PageSize; #define DEFAULT_MAXIMAL_SIZE (1024 * 1024 * 128) +//////////////////////////////////////////////////////////////////////////////// +/// @brief document handle separator as character +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_DOCUMENT_HANDLE_SEPARATOR_CHR '/' + +//////////////////////////////////////////////////////////////////////////////// +/// @brief document handle separator as string +//////////////////////////////////////////////////////////////////////////////// + +#define TRI_DOCUMENT_HANDLE_SEPARATOR_STR "/" + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/actions/system/aql-cursor.js b/js/actions/system/aql-cursor.js index c5f529a589..29ca99c706 100644 --- a/js/actions/system/aql-cursor.js +++ b/js/actions/system/aql-cursor.js @@ -89,7 +89,6 @@ function postCursor(req, res) { } try { - var cursor; var json = JSON.parse(req.requestBody); if (!json || !(json instanceof Object)) { @@ -97,18 +96,9 @@ function postCursor(req, res) { return; } - if (json._id != undefined) { - /* - cursor = AQL_STORED_STATEMENT(db, - json._id, - json.bindVars, - (json.count != undefined ? json.count : false), - (json.batchSize != undefined ? json.batchSize : 1000)); - */ - } - else if (json.query != undefined) { - cursor = AQL_STATEMENT(db, - json.query, + var cursor; + if (json.query != undefined) { + cursor = AQL_STATEMENT(json.query, json.bindVars, (json.count != undefined ? json.count : false), (json.batchSize != undefined ? json.batchSize : 1000)); @@ -145,7 +135,7 @@ function putCursor(req, res) { try { var cursorId = decodeURIComponent(req.suffix[0]); - var cursor = AQL_CURSOR(db, cursorId); + var cursor = AQL_CURSOR(cursorId); if (!(cursor instanceof AvocadoQueryCursor)) { throw "cursor not found"; } @@ -170,7 +160,7 @@ function deleteCursor(req, res) { try { var cursorId = decodeURIComponent(req.suffix[0]); - var cursor = AQL_CURSOR(db, cursorId); + var cursor = AQL_CURSOR(cursorId); if (!(cursor instanceof AvocadoQueryCursor)) { throw "cursor not found"; } diff --git a/js/actions/system/aql-query.js b/js/actions/system/aql-query.js index 8767f42966..402e8f6726 100644 --- a/js/actions/system/aql-query.js +++ b/js/actions/system/aql-query.js @@ -54,7 +54,7 @@ function postQuery(req, res) { return; } - var result = AQL_PARSE(db, json.query); + var result = AQL_PARSE(json.query); if (result instanceof AvocadoQueryError) { actions.actionResultError (req, res, 404, result.code, result.message); return; diff --git a/js/actions/system/key-value.js b/js/actions/system/key-value.js index 4d0915bed8..8698252cdd 100644 --- a/js/actions/system/key-value.js +++ b/js/actions/system/key-value.js @@ -42,11 +42,32 @@ var simple = require("simple-query"); // --SECTION-- private functions // ----------------------------------------------------------------------------- +function formatTimeStamp (timestamp) { + var d = new Date(timestamp * 1000); + + var year = d.getUTCFullYear(); + var month = d.getUTCMonth() + 1; + var date = d.getUTCDate(); + + if (month < 10) month = "0" + month; + if (date < 10) hour = "0" + date; + + var hour = d.getUTCHours(); + var minutes = d.getUTCMinutes(); + var seconds = d.getUTCSeconds(); + + if (hour < 10) hour = "0" + hour; + if (minutes < 10) minutes = "0" + minutes; + if (seconds < 10) seconds = "0" + seconds; + + return year + "-" + month + "-" + date + "T" + hour + ":" + minutes + ":" + seconds + "Z"; +} + function buildDocumentFromReq(req) { // Example requests: // Header: - // POST /_api/key/example_collection/example_key1 + // POST /_api/key/example_collection/example_key1 HTTP/1.1 // Host: localhost:9000 // x-voc-expires: 2011-09-29T08:00:00Z // x-voc-extended: {"option1":35,"option2":"x"} @@ -67,8 +88,9 @@ function buildDocumentFromReq(req) { } if (req.headers["x-voc-expires"] != undefined) { - // TODO check value - doc["x-voc-expires"] = req.headers["x-voc-expires"]; + var d = new Date(req.headers["x-voc-expires"]); + // store time stamp as double + doc["x-voc-expires"] = d.getTime() / 1000; } if (req.headers["x-voc-extended"] != undefined) { @@ -78,6 +100,7 @@ function buildDocumentFromReq(req) { } } + // store time stamp as double doc["x-voc-created"] = internal.time(); return doc; @@ -97,9 +120,14 @@ function postKeyValue(req, res) { try { var collection = req.suffix[0]; + if (db._collection(collection) == null) { + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + return; + } + var doc = buildDocumentFromReq(req); - var s = db[collection].select({ "key" : doc.key }); + var s = db[collection].byExample({"key" : doc.key}); s.execute(); if (s._countTotal != 0) { @@ -115,7 +143,7 @@ function postKeyValue(req, res) { } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created."); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not created. " + e.message); } } @@ -125,36 +153,59 @@ function postKeyValue(req, res) { function putKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); return; } try { var collection = req.suffix[0]; + if (db._collection(collection) == null) { + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + return; + } + var doc = buildDocumentFromReq(req); - var s = db[collection].select({ "key" : doc.key }); + var s = db[collection].byExample({"key" : doc.key}); s.execute(); if (s._countTotal < 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + if (req.parameters["create"] == 1) { + var id = db[collection].save(doc); + var result = { + "saved" : true, + "_id" : id + } + actions.actionResultOK(req, res, 201, result); + return; + } + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); } else if (s._countTotal > 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. Wrong key?"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); } else { + // get _id var id = s._execution._documents[0]._id; + + // save x-voc-created + var created = s._execution._documents[0]["x-voc-created"]; + if (created != undefined) { + doc["x-voc-created"] = created; + } + + // replace the document if (db[collection].replace(id, doc)) { - actions.actionResultOK(req, res, 202, { "changed" : true }); + actions.actionResultOK(req, res, 202, {"changed" : true}); } else { - actions.actionResultError(req, res, 404, actions.keyValueNotFound, "Value not changed"); + actions.actionResultError(req, res, 404, actions.keyValueNotModified, "Value not changed"); } } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); } } @@ -164,40 +215,45 @@ function putKeyValue(req, res) { function deleteKeyValue(req, res) { if (req.suffix.length < 2) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); return; } try { var collection = req.suffix[0]; + if (db._collection(collection) == null) { + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Collection not found."); + return; + } + var key = req.suffix[1]; for (var i = 2; i < req.suffix.length; ++i) { key += "/" + req.suffix[i]; } - var s = db[collection].select({ "key" : key }); + var s = db[collection].byExample({"key" : key}); s.execute(); if (s._countTotal < 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found"); } else if (s._countTotal > 1) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. Wrong key?"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. Wrong key?"); } else { var id = s._execution._documents[0]._id; if (db[collection].delete(id)) { - actions.actionResultOK(req, res, 202, { "removed" : true }); + actions.actionResultOK(req, res, 202, {"removed" : true}); } else { - actions.actionResultError(req, res, 404, actions.keyValueNotFound, "Value not removed"); + actions.actionResultError(req, res, 404, actions.keyValueNotModified, "Value not removed"); } } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotModified, "Key value pair not found. " + e.message); } } @@ -214,13 +270,18 @@ function getKeyValue(req, res) { try { var collection = req.suffix[0]; + if (db._collection(collection) == null) { + actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); + return; + } + var key = req.suffix[1]; for (var i = 2; i < req.suffix.length; ++i) { key += "/" + req.suffix[i]; } - var s = db[collection].select({ "key" : key }); + var s = db[collection].byExample({"key" : key}); s.execute(); if (s._countTotal < 1) { @@ -233,20 +294,23 @@ function getKeyValue(req, res) { var headers = {}; if (s._execution._documents[0]["x-voc-expires"] != undefined) { - headers["x-voc-expires"] = s._execution._documents[0]["x-voc-expires"]; + // format timestamp + headers["x-voc-expires"] = formatTimeStamp(s._execution._documents[0]["x-voc-expires"]); } if (s._execution._documents[0]["x-voc-extended"] != undefined) { + // serialize header value headers["x-voc-extended"] = JSON.stringify(s._execution._documents[0]["x-voc-extended"]); } if (s._execution._documents[0]["x-voc-created"] != undefined) { - headers["x-voc-created"] = s._execution._documents[0]["x-voc-created"]; + // format timestamp + headers["x-voc-created"] = formatTimeStamp(s._execution._documents[0]["x-voc-created"]); } actions.actionResultOK(req, res, 200, s._execution._documents[0].value, headers); } } catch (e) { - actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found"); + actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pair not found. " + e.message); } } @@ -255,7 +319,7 @@ function getKeyValue(req, res) { // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// -/// @brief cursor actions gateway +/// @brief key value pair actions gateway //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ @@ -264,19 +328,19 @@ actions.defineHttp({ callback : function (req, res) { switch (req.requestType) { - case ("POST") : + case ("POST") : postKeyValue(req, res); break; - case ("GET") : + case ("GET") : getKeyValue(req, res); break; - case ("PUT") : + case ("PUT") : putKeyValue(req, res); break; - case ("DELETE") : + case ("DELETE") : deleteKeyValue(req, res); break; @@ -286,6 +350,94 @@ actions.defineHttp({ } }); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup AvocadoAPI +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- private functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief key value pair search +//////////////////////////////////////////////////////////////////////////////// + +function searchKeyValue(req, res) { + if (req.suffix.length < 2) { + actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found."); + return; + } + + try { + var collection = req.suffix[0]; + + if (db._collection(collection) == null) { + actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Collection not found."); + return; + } + + var prefix = req.suffix[1]; + + for (var i = 2; i < req.suffix.length; ++i) { + prefix += "/" + req.suffix[i]; + } + + // + // TODO: build a query which selects the keys + // + + var query = "select f from " + collection + " f "; + var bindVars = {}; + var cursor = AQL_STATEMENT(db, + query, + bindVars, + false, + 1000); + result = []; + while (cursor.hasNext() ) { + var doc = cursor.next(); + if (doc["key"] != undefined && doc["key"].indexOf(prefix) === 0) { + result.push(doc["key"]); + } + } + + actions.actionResult (req, res, 200, result); + } + catch (e) { + actions.actionResultError (req, res, 404, actions.keyValueNotFound, "Key value pairs not found. " + e.message); + } +} + +// ----------------------------------------------------------------------------- +// --SECTION-- initialiser +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief key value pair actions gateway +//////////////////////////////////////////////////////////////////////////////// + +actions.defineHttp({ + url : "_api/keys", + context : "api", + + callback : function (req, res) { + switch (req.requestType) { + case ("GET") : + searchKeyValue(req, res); + break; + + default: + actions.actionResultUnsupported(req, res); + } + } +}); + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/js/server/js-server.h b/js/server/js-server.h index 222c2e676a..6ecdaf7cda 100644 --- a/js/server/js-server.h +++ b/js/server/js-server.h @@ -26,27 +26,6 @@ static string JS_server_server = "/// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @page JSModuleAvocadoTOC\n" - "///\n" - "///
      \n" - "///
    1. @ref JSModuleAvocadoDefineHttpSystemAction \"avocado.defineHttpSystemAction\"
    2. \n" - "///
    \n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @page JSModuleAvocado Module \"avocado\"\n" - "///\n" - "/// The following functions are used avocadoly.\n" - "///\n" - "///
    \n" - "/// @copydoc JSModuleAvocadoTOC\n" - "///
    \n" - "///\n" - "/// @anchor JSModuleAvocadoDefineHttpSystemAction\n" - "/// @copydetails JS_DefineSystemAction\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" "// -----------------------------------------------------------------------------\n" "// --SECTION-- Module \"internal\"\n" "// -----------------------------------------------------------------------------\n" @@ -79,31 +58,6 @@ static string JS_server_server = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "// -----------------------------------------------------------------------------\n" - "// --SECTION-- Module \"avocado\"\n" - "// -----------------------------------------------------------------------------\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @addtogroup V8ModuleAvocado\n" - "/// @{\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @brief avocado module\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "ModuleCache[\"/avocado\"] = new Module(\"/avocado\");\n" - "\n" - "if (typeof defineSystemAction == \"function\") {\n" - " ModuleCache[\"/avocado\"].exports.defineHttpSystemAction = defineSystemAction;\n" - "}\n" - "\n" - "avocado = ModuleCache[\"/avocado\"].exports;\n" - "\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "/// @}\n" - "////////////////////////////////////////////////////////////////////////////////\n" - "\n" - "// -----------------------------------------------------------------------------\n" "// --SECTION-- Module \"simple-query\"\n" "// -----------------------------------------------------------------------------\n" "\n" @@ -124,7 +78,7 @@ static string JS_server_server = "////////////////////////////////////////////////////////////////////////////////\n" "\n" "// -----------------------------------------------------------------------------\n" - "// --SECTION-- PRINT\n" + "// --SECTION-- ShapedJson\n" "// -----------------------------------------------------------------------------\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" @@ -149,6 +103,75 @@ static string JS_server_server = "/// @}\n" "////////////////////////////////////////////////////////////////////////////////\n" "\n" + "// -----------------------------------------------------------------------------\n" + "// --SECTION-- AvocadoDatabase\n" + "// -----------------------------------------------------------------------------\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @addtogroup V8Shell\n" + "/// @{\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief drops a collection\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "AvocadoDatabase.prototype._drop = function(name) {\n" + " var collection = name;\n" + "\n" + " if (typeof name === \"string\") {\n" + " collection = db[name];\n" + " }\n" + "\n" + " // new born collection\n" + " if (collection.status() == 1) {\n" + " return;\n" + " }\n" + "\n" + " // drop all indexes\n" + " var idx = collection.getIndexes();\n" + "\n" + " for (var i = 0; i < idx.length; ++i) {\n" + " collection.dropIndex(idx[i].iid);\n" + " }\n" + "\n" + " // delete all documents\n" + " var all = collection.all();\n" + "\n" + " while (all.hasNext()) {\n" + " var ref = all.nextRef();\n" + "\n" + " collection.delete(ref);\n" + " }\n" + "}\n" + "\n" + "AvocadoEdges.prototype._drop = AvocadoDatabase._drop;\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @}\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "// -----------------------------------------------------------------------------\n" + "// --SECTION-- AvocadoCollection\n" + "// -----------------------------------------------------------------------------\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @addtogroup V8Shell\n" + "/// @{\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @brief drops a collection\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "AvocadoCollection.prototype.drop = function() {\n" + " db._drop(this);\n" + "}\n" + "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "/// @}\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" "// Local Variables:\n" "// mode: outline-minor\n" "// outline-regexp: \"^\\\\(/// @brief\\\\|/// @addtogroup\\\\|// --SECTION--\\\\|/// @page\\\\|/// @}\\\\)\"\n" diff --git a/js/server/modules/simple-query.js b/js/server/modules/simple-query.js index 1cf26dce6d..f772469c52 100644 --- a/js/server/modules/simple-query.js +++ b/js/server/modules/simple-query.js @@ -786,7 +786,7 @@ SimpleQueryByExample.prototype.execute = function () { } } - var cursor = AQL_STATEMENT(db, queryString, undefined); + var cursor = AQL_STATEMENT(queryString, undefined); if (cursor instanceof AvocadoQueryError) { throw cursor.message; } diff --git a/js/server/server.js b/js/server/server.js index 5aff8d45e4..9d5d176bf1 100644 --- a/js/server/server.js +++ b/js/server/server.js @@ -25,27 +25,6 @@ /// @author Copyright 2011-2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @page JSModuleAvocadoTOC -/// -///
      -///
    1. @ref JSModuleAvocadoDefineHttpSystemAction "avocado.defineHttpSystemAction"
    2. -///
    -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @page JSModuleAvocado Module "avocado" -/// -/// The following functions are used avocadoly. -/// -///
    -/// @copydoc JSModuleAvocadoTOC -///
    -/// -/// @anchor JSModuleAvocadoDefineHttpSystemAction -/// @copydetails JS_DefineSystemAction -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- Module "internal" // ----------------------------------------------------------------------------- @@ -77,31 +56,6 @@ else { /// @} //////////////////////////////////////////////////////////////////////////////// -// ----------------------------------------------------------------------------- -// --SECTION-- Module "avocado" -// ----------------------------------------------------------------------------- - -//////////////////////////////////////////////////////////////////////////////// -/// @addtogroup V8ModuleAvocado -/// @{ -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -/// @brief avocado module -//////////////////////////////////////////////////////////////////////////////// - -ModuleCache["/avocado"] = new Module("/avocado"); - -if (typeof defineSystemAction == "function") { - ModuleCache["/avocado"].exports.defineHttpSystemAction = defineSystemAction; -} - -avocado = ModuleCache["/avocado"].exports; - -//////////////////////////////////////////////////////////////////////////////// -/// @} -//////////////////////////////////////////////////////////////////////////////// - // ----------------------------------------------------------------------------- // --SECTION-- Module "simple-query" // ----------------------------------------------------------------------------- @@ -123,7 +77,7 @@ catch (err) { //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- -// --SECTION-- PRINT +// --SECTION-- ShapedJson // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// @@ -148,6 +102,75 @@ ShapedJson.prototype._PRINT = function(seen, path, names) { /// @} //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- AvocadoDatabase +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Shell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drops a collection +//////////////////////////////////////////////////////////////////////////////// + +AvocadoDatabase.prototype._drop = function(name) { + var collection = name; + + if (typeof name === "string") { + collection = db[name]; + } + + // new born collection + if (collection.status() == 1) { + return; + } + + // drop all indexes + var idx = collection.getIndexes(); + + for (var i = 0; i < idx.length; ++i) { + collection.dropIndex(idx[i].iid); + } + + // delete all documents + var all = collection.all(); + + while (all.hasNext()) { + var ref = all.nextRef(); + + collection.delete(ref); + } +} + +AvocadoEdges.prototype._drop = AvocadoDatabase._drop; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- AvocadoCollection +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup V8Shell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief drops a collection +//////////////////////////////////////////////////////////////////////////////// + +AvocadoCollection.prototype.drop = function() { + db._drop(this); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)" diff --git a/js/server/tests/aql-bind.js b/js/server/tests/aql-bind.js index af135ffb46..a5c241b23b 100644 --- a/js/server/tests/aql-bind.js +++ b/js/server/tests/aql-bind.js @@ -60,7 +60,7 @@ function aqlBindParametersTestSuite () { //////////////////////////////////////////////////////////////////////////////// function executeQuery (query, bindParameters) { - var cursor = AQL_STATEMENT(db, query, bindParameters); + var cursor = AQL_STATEMENT(query, bindParameters); assertFalse(cursor instanceof AvocadoQueryError); return cursor; } diff --git a/js/server/tests/aql-joins.js b/js/server/tests/aql-joins.js index 0e9f8b65cf..6e736b4e5e 100644 --- a/js/server/tests/aql-joins.js +++ b/js/server/tests/aql-joins.js @@ -111,7 +111,7 @@ function aqlJoinsTestSuite () { //////////////////////////////////////////////////////////////////////////////// function executeQuery (query) { - var cursor = AQL_STATEMENT(db, query, undefined); + var cursor = AQL_STATEMENT(query, undefined); assertFalse(cursor instanceof AvocadoQueryError); return cursor; } diff --git a/js/server/tests/aql-keywords.js b/js/server/tests/aql-keywords.js index aed2932278..b10cd7aad3 100644 --- a/js/server/tests/aql-keywords.js +++ b/js/server/tests/aql-keywords.js @@ -76,7 +76,7 @@ function aqlKeywordsTestSuite () { //////////////////////////////////////////////////////////////////////////////// function executeQuery (query, expectError) { - var cursor = AQL_STATEMENT(db, query, undefined); + var cursor = AQL_STATEMENT(query, undefined); if (expectError) { assertTrue(cursor instanceof AvocadoQueryError); return null; diff --git a/js/server/tests/aql-simple.js b/js/server/tests/aql-simple.js index 5dc142b029..0a4fe54e79 100644 --- a/js/server/tests/aql-simple.js +++ b/js/server/tests/aql-simple.js @@ -60,7 +60,7 @@ function aqlSimpleTestSuite () { //////////////////////////////////////////////////////////////////////////////// function executeQuery (query) { - var cursor = AQL_STATEMENT(db, query, undefined); + var cursor = AQL_STATEMENT(query, undefined); if (cursor instanceof AvocadoQueryError) { print(query, cursor.message); }