diff options
-rw-r--r-- | jstests/core/list_collections1.js | 5 | ||||
-rw-r--r-- | jstests/core/list_indexes.js | 5 | ||||
-rw-r--r-- | src/mongo/scripting/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/cursor_handle.cpp | 101 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/cursor_handle.h | 72 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.cpp | 2 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/implscope.h | 6 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.cpp | 29 | ||||
-rw-r--r-- | src/mongo/scripting/mozjs/mongo.h | 3 | ||||
-rw-r--r-- | src/mongo/shell/query.js | 10 |
10 files changed, 225 insertions, 9 deletions
diff --git a/jstests/core/list_collections1.js b/jstests/core/list_collections1.js index 0a9a2d4c563..70ca144bfbd 100644 --- a/jstests/core/list_collections1.js +++ b/jstests/core/list_collections1.js @@ -214,10 +214,9 @@ // // Test killCursors against a listCollections cursor. - // TODO: re-enable after implementing SERVER-19570. // - /*assert.commandWorked(mydb.dropDatabase()); + assert.commandWorked(mydb.dropDatabase()); assert.commandWorked(mydb.createCollection("foo")); assert.commandWorked(mydb.createCollection("bar")); assert.commandWorked(mydb.createCollection("baz")); @@ -228,5 +227,5 @@ cursor = null; gc(); // Shell will send a killCursors message when cleaning up underlying cursor. cursor = new DBCommandCursor(mydb.getMongo(), res, 2); - assert.throws(function() { cursor.hasNext(); });*/ + assert.throws(function() { cursor.hasNext(); }); }()); diff --git a/jstests/core/list_indexes.js b/jstests/core/list_indexes.js index 7f0a9e8a45c..db3f895bc20 100644 --- a/jstests/core/list_indexes.js +++ b/jstests/core/list_indexes.js @@ -151,10 +151,9 @@ // // Test killCursors against a listCollections cursor. - // TODO: re-enable after implementing SERVER-19570. // - /*coll.drop(); + coll.drop(); assert.commandWorked(coll.getDB().createCollection(coll.getName())); assert.commandWorked(coll.ensureIndex({a: 1}, {unique: true})); assert.commandWorked(coll.ensureIndex({b: 1}, {unique: true})); @@ -165,5 +164,5 @@ cursor = null; gc(); // Shell will send a killCursors message when cleaning up underlying cursor. cursor = new DBCommandCursor(coll.getDB().getMongo(), res, 2); - assert.throws(function() { cursor.hasNext(); });*/ + assert.throws(function() { cursor.hasNext(); }); }()); diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript index e81f2e4c705..25dd86201af 100644 --- a/src/mongo/scripting/SConscript +++ b/src/mongo/scripting/SConscript @@ -116,6 +116,7 @@ elif usemozjs: 'mozjs/bson.cpp', 'mozjs/countdownlatch.cpp', 'mozjs/cursor.cpp', + 'mozjs/cursor_handle.cpp', 'mozjs/dbcollection.cpp', 'mozjs/db.cpp', 'mozjs/dbpointer.cpp', diff --git a/src/mongo/scripting/mozjs/cursor_handle.cpp b/src/mongo/scripting/mozjs/cursor_handle.cpp new file mode 100644 index 00000000000..597ebe2ccd6 --- /dev/null +++ b/src/mongo/scripting/mozjs/cursor_handle.cpp @@ -0,0 +1,101 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kQuery + +#include "mongo/platform/basic.h" + +#include "mongo/scripting/mozjs/cursor_handle.h" + +#include "mongo/scripting/mozjs/implscope.h" +#include "mongo/util/log.h" + +namespace mongo { +namespace mozjs { + +const JSFunctionSpec CursorHandleInfo::methods[2] = { + MONGO_ATTACH_JS_FUNCTION(zeroCursorId), JS_FS_END, +}; + +const char* const CursorHandleInfo::className = "CursorHandle"; + +namespace { + +long long* getCursorId(JSObject* thisv) { + CursorHandleInfo::CursorTracker* tracker = + static_cast<CursorHandleInfo::CursorTracker*>(JS_GetPrivate(thisv)); + if (tracker) { + return &tracker->cursorId; + } + + return nullptr; +} + +long long* getCursorId(JS::CallArgs& args) { + return getCursorId(args.thisv().toObjectOrNull()); +} + +} // namespace + +void CursorHandleInfo::finalize(JSFreeOp* fop, JSObject* obj) { + auto cursorTracker = static_cast<CursorHandleInfo::CursorTracker*>(JS_GetPrivate(obj)); + if (cursorTracker) { + const long long cursorId = cursorTracker->cursorId; + if (cursorId) { + try { + cursorTracker->client->killCursor(cursorId); + } catch (...) { + auto status = exceptionToStatus(); + + try { + LOG(0) << "Failed to kill cursor " << cursorId << " due to " << status; + } catch (...) { + // This is here in case logging fails. + } + } + } + + delete cursorTracker; + } +} + +void CursorHandleInfo::construct(JSContext* cx, JS::CallArgs args) { + auto scope = getScope(cx); + + scope->getCursorHandleProto().newObject(args.rval()); +} + +void CursorHandleInfo::Functions::zeroCursorId(JSContext* cx, JS::CallArgs args) { + long long* cursorId = getCursorId(args); + if (cursorId) { + *cursorId = 0; + } +} + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/cursor_handle.h b/src/mongo/scripting/mozjs/cursor_handle.h new file mode 100644 index 00000000000..95f00a3d964 --- /dev/null +++ b/src/mongo/scripting/mozjs/cursor_handle.h @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2015 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects + * for all of the code used other than as permitted herein. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you do not + * wish to do so, delete this exception statement from your version. If you + * delete this exception statement from all source files in the program, + * then also delete it in the license file. + */ + +#pragma once + +#include "mongo/client/dbclientcursor.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/scripting/mozjs/wraptype.h" + +namespace mongo { +namespace mozjs { + +/** + * Wraps a DBClientCursor in javascript. + * + * Note that the install is private, so this class should only be constructible from C++. Current + * callers are all via the Mongo object. + */ +struct CursorHandleInfo : public BaseInfo { + static void construct(JSContext* cx, JS::CallArgs args); + static void finalize(JSFreeOp* fop, JSObject* obj); + + struct Functions { + MONGO_DEFINE_JS_FUNCTION(zeroCursorId); + }; + + static const JSFunctionSpec methods[2]; + + static const char* const className; + static const unsigned classFlags = JSCLASS_HAS_PRIVATE; + static const InstallType installType = InstallType::Private; + + /** + * We need this because the DBClientBase can go out of scope before all of its children (as + * in global shutdown). So we have to manage object lifetimes in C++ land. + */ + struct CursorTracker { + CursorTracker(long long curId, std::shared_ptr<DBClientBase> client) + : client(std::move(client)), cursorId(curId) {} + + std::shared_ptr<DBClientBase> client; + long long cursorId; + }; +}; + +} // namespace mozjs +} // namespace mongo diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp index d594623a31e..398eb70c018 100644 --- a/src/mongo/scripting/mozjs/implscope.cpp +++ b/src/mongo/scripting/mozjs/implscope.cpp @@ -231,6 +231,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine) _bsonProto(_context), _countDownLatchProto(_context), _cursorProto(_context), + _cursorHandleProto(_context), _dbCollectionProto(_context), _dbPointerProto(_context), _dbQueryProto(_context), @@ -672,6 +673,7 @@ void MozJSImplScope::installBSONTypes() { void MozJSImplScope::installDBAccess() { _cursorProto.install(_global); + _cursorHandleProto.install(_global); _dbProto.install(_global); _dbQueryProto.install(_global); _dbCollectionProto.install(_global); diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h index 30106006d43..b9a3e53c469 100644 --- a/src/mongo/scripting/mozjs/implscope.h +++ b/src/mongo/scripting/mozjs/implscope.h @@ -35,6 +35,7 @@ #include "mongo/scripting/mozjs/bson.h" #include "mongo/scripting/mozjs/countdownlatch.h" #include "mongo/scripting/mozjs/cursor.h" +#include "mongo/scripting/mozjs/cursor_handle.h" #include "mongo/scripting/mozjs/db.h" #include "mongo/scripting/mozjs/dbcollection.h" #include "mongo/scripting/mozjs/dbpointer.h" @@ -159,6 +160,10 @@ public: return _cursorProto; } + WrapType<CursorHandleInfo>& getCursorHandleProto() { + return _cursorHandleProto; + } + WrapType<DBCollectionInfo>& getDbCollectionProto() { return _dbCollectionProto; } @@ -306,6 +311,7 @@ private: WrapType<BSONInfo> _bsonProto; WrapType<CountDownLatchInfo> _countDownLatchProto; WrapType<CursorInfo> _cursorProto; + WrapType<CursorHandleInfo> _cursorHandleProto; WrapType<DBCollectionInfo> _dbCollectionProto; WrapType<DBPointerInfo> _dbPointerProto; WrapType<DBQueryInfo> _dbQueryProto; diff --git a/src/mongo/scripting/mozjs/mongo.cpp b/src/mongo/scripting/mozjs/mongo.cpp index ca8c5a35f93..2fcb59a7c35 100644 --- a/src/mongo/scripting/mozjs/mongo.cpp +++ b/src/mongo/scripting/mozjs/mongo.cpp @@ -49,6 +49,7 @@ const JSFunctionSpec MongoBase::methods[] = { MONGO_ATTACH_JS_FUNCTION(auth), MONGO_ATTACH_JS_FUNCTION(copyDatabaseWithSCRAM), MONGO_ATTACH_JS_FUNCTION(cursorFromId), + MONGO_ATTACH_JS_FUNCTION(cursorHandleFromId), MONGO_ATTACH_JS_FUNCTION(find), MONGO_ATTACH_JS_FUNCTION(getClientRPCProtocols), MONGO_ATTACH_JS_FUNCTION(getServerRPCProtocols), @@ -83,6 +84,14 @@ void setCursor(JS::HandleObject target, // Copy the client shared pointer to up the refcount JS_SetPrivate(target, new CursorInfo::CursorHolder(std::move(cursor), *client)); } + +void setCursorHandle(JS::HandleObject target, long long cursorId, JS::CallArgs& args) { + auto client = + static_cast<std::shared_ptr<DBClientBase>*>(JS_GetPrivate(args.thisv().toObjectOrNull())); + + // Copy the client shared pointer to up the refcount. + JS_SetPrivate(target, new CursorHandleInfo::CursorTracker(cursorId, *client)); +} } // namespace void MongoBase::finalize(JSFreeOp* fop, JSObject* obj) { @@ -399,6 +408,26 @@ void MongoBase::Functions::cursorFromId(JSContext* cx, JS::CallArgs args) { args.rval().setObjectOrNull(c); } +void MongoBase::Functions::cursorHandleFromId(JSContext* cx, JS::CallArgs args) { + auto scope = getScope(cx); + + if (args.length() != 1) { + uasserted(ErrorCodes::BadValue, "cursorHandleFromId needs 1 arg"); + } + if (!scope->getNumberLongProto().instanceOf(args.get(0))) { + uasserted(ErrorCodes::BadValue, "1st arg must be a NumberLong"); + } + + long long cursorId = NumberLongInfo::ToNumberLong(cx, args.get(0)); + + JS::RootedObject c(cx); + scope->getCursorHandleProto().newInstance(&c); + + setCursorHandle(c, cursorId, args); + + args.rval().setObjectOrNull(c); +} + void MongoBase::Functions::copyDatabaseWithSCRAM(JSContext* cx, JS::CallArgs args) { auto conn = getConnection(args); diff --git a/src/mongo/scripting/mozjs/mongo.h b/src/mongo/scripting/mozjs/mongo.h index 33ca0502018..bb9057cbd9d 100644 --- a/src/mongo/scripting/mozjs/mongo.h +++ b/src/mongo/scripting/mozjs/mongo.h @@ -47,6 +47,7 @@ struct MongoBase : public BaseInfo { MONGO_DEFINE_JS_FUNCTION(auth); MONGO_DEFINE_JS_FUNCTION(copyDatabaseWithSCRAM); MONGO_DEFINE_JS_FUNCTION(cursorFromId); + MONGO_DEFINE_JS_FUNCTION(cursorHandleFromId); MONGO_DEFINE_JS_FUNCTION(find); MONGO_DEFINE_JS_FUNCTION(getClientRPCProtocols); MONGO_DEFINE_JS_FUNCTION(getServerRPCProtocols); @@ -59,7 +60,7 @@ struct MongoBase : public BaseInfo { MONGO_DEFINE_JS_FUNCTION(update); }; - static const JSFunctionSpec methods[14]; + static const JSFunctionSpec methods[15]; static const char* const className; static const unsigned classFlags = JSCLASS_HAS_PRIVATE; diff --git a/src/mongo/shell/query.js b/src/mongo/shell/query.js index 26f6ea4d43d..250a7013e73 100644 --- a/src/mongo/shell/query.js +++ b/src/mongo/shell/query.js @@ -648,8 +648,13 @@ function DBCommandCursor(mongo, cmdResult, batchSize) { this._ns = cmdResult.cursor.ns; this._db = mongo.getDB(this._ns.substr(0, this._ns.indexOf("."))); this._collName = this._ns.substr(this._ns.indexOf(".") + 1); - } - else { + + if (cmdResult.cursor.id) { + // Note that setting this._cursorid to 0 should be accompanied by + // this._cursorHandle.zeroCursorId(). + this._cursorHandle = mongo.cursorHandleFromId(cmdResult.cursor.id); + } + } else { this._cursor = mongo.cursorFromId(cmdResult.cursor.ns, cmdResult.cursor.id, batchSize); } } @@ -683,6 +688,7 @@ DBCommandCursor.prototype._runGetMoreCommand = function() { } if (!cmdRes.cursor.id.compare(NumberLong("0"))) { + this._cursorHandle.zeroCursorId(); this._cursorid = NumberLong("0"); } else if (this._cursorid.compare(cmdRes.cursor.id)) { |