summaryrefslogtreecommitdiff
path: root/src/mongo/scripting/v8_db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/scripting/v8_db.cpp')
-rw-r--r--src/mongo/scripting/v8_db.cpp183
1 files changed, 123 insertions, 60 deletions
diff --git a/src/mongo/scripting/v8_db.cpp b/src/mongo/scripting/v8_db.cpp
index ea6cfe33591..a20aa099591 100644
--- a/src/mongo/scripting/v8_db.cpp
+++ b/src/mongo/scripting/v8_db.cpp
@@ -13,17 +13,30 @@
*
* 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.
*/
#include "mongo/scripting/v8_db.h"
#include <iostream>
+#include <iomanip>
#include <boost/scoped_array.hpp>
#include "mongo/base/init.h"
#include "mongo/client/sasl_client_authenticate.h"
#include "mongo/client/syncclusterconnection.h"
-#include "mongo/db/namespacestring.h"
+#include "mongo/db/namespace_string.h"
#include "mongo/s/d_logic.h"
#include "mongo/scripting/engine_v8.h"
#include "mongo/scripting/v8_utils.h"
@@ -99,6 +112,7 @@ namespace mongo {
scope->injectV8Method("update", mongoUpdate, proto);
scope->injectV8Method("auth", mongoAuth, proto);
scope->injectV8Method("logout", mongoLogout, proto);
+ scope->injectV8Method("cursorFromId", mongoCursorFromId, proto);
fassert(16468, _mongoPrototypeManipulatorsFrozen);
for (size_t i = 0; i < _mongoPrototypeManipulators.size(); ++i)
@@ -129,18 +143,18 @@ namespace mongo {
return v8AssertionException(errmsg);
}
- DBClientWithCommands* conn;
+ DBClientBase* conn;
conn = cs.connect(errmsg);
if (!conn) {
return v8AssertionException(errmsg);
}
v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This());
- scope->dbClientWithCommandsTracker.track(self, conn);
+ v8::Local<v8::External> connHandle = scope->dbClientBaseTracker.track(self, conn);
ScriptEngine::runConnectCallback(*conn);
- args.This()->SetInternalField(0, v8::External::New(conn));
+ args.This()->SetInternalField(0, connHandle);
args.This()->ForceSet(scope->v8StringData("slaveOk"), v8::Boolean::New(false));
args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData(host));
@@ -157,22 +171,23 @@ namespace mongo {
DBClientBase* conn = createDirectClient();
v8::Persistent<v8::Object> self = v8::Persistent<v8::Object>::New(args.This());
- scope->dbClientBaseTracker.track(self, conn);
+ v8::Local<v8::External> connHandle = scope->dbClientBaseTracker.track(self, conn);
- args.This()->SetInternalField(0, v8::External::New(conn));
+ args.This()->SetInternalField(0, connHandle);
args.This()->ForceSet(scope->v8StringData("slaveOk"), v8::Boolean::New(false));
args.This()->ForceSet(scope->v8StringData("host"), scope->v8StringData("EMBEDDED"));
return v8::Undefined();
}
- DBClientBase* getConnection(V8Scope* scope, const v8::Arguments& args) {
+ boost::shared_ptr<DBClientBase> getConnection(V8Scope* scope, const v8::Arguments& args) {
verify(scope->MongoFT()->HasInstance(args.This()));
verify(args.This()->InternalFieldCount() == 1);
v8::Local<v8::External> c = v8::External::Cast(*(args.This()->GetInternalField(0)));
- DBClientBase* conn = (DBClientBase*)(c->Value());
- massert(16667, "Unable to get db client connection", conn);
- return conn;
+ boost::shared_ptr<DBClientBase>* conn =
+ static_cast<boost::shared_ptr<DBClientBase>*>(c->Value());
+ massert(16667, "Unable to get db client connection", conn && conn->get());
+ return *conn;
}
/**
@@ -181,7 +196,7 @@ namespace mongo {
v8::Handle<v8::Value> mongoFind(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 7, "find needs 7 args")
argumentCheck(args[1]->IsObject(), "needs to be an object")
- DBClientBase * conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
const string ns = toSTLString(args[0]);
BSONObj fields;
BSONObj q = scope->v8ToMongo(args[1]->ToObject());
@@ -190,7 +205,7 @@ namespace mongo {
if (haveFields)
fields = scope->v8ToMongo(args[2]->ToObject());
- auto_ptr<mongo::DBClientCursor> cursor;
+ boost::shared_ptr<mongo::DBClientCursor> cursor;
int nToReturn = args[3]->Int32Value();
int nToSkip = args[4]->Int32Value();
int batchSize = args[5]->Int32Value();
@@ -202,9 +217,35 @@ namespace mongo {
}
v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
- v8::Persistent<v8::Object> c = v8::Persistent<v8::Object>::New(cons->NewInstance());
+ v8::Handle<v8::Object> c = cons->NewInstance();
+ c->SetInternalField(0, v8::External::New(cursor.get()));
+ scope->dbConnectionAndCursor.track(
+ v8::Persistent<v8::Value>::New(c),
+ new V8Scope::DBConnectionAndCursor(conn, cursor));
+ return c;
+ }
+
+ v8::Handle<v8::Value> mongoCursorFromId(V8Scope* scope, const v8::Arguments& args) {
+ argumentCheck(args.Length() == 2 || args.Length() == 3, "cursorFromId needs 2 or 3 args")
+ argumentCheck(scope->NumberLongFT()->HasInstance(args[1]), "2nd arg must be a NumberLong")
+ argumentCheck(args[2]->IsUndefined() || args[2]->IsNumber(), "3rd arg must be a js Number")
+
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
+ const string ns = toSTLString(args[0]);
+ long long cursorId = numberLongVal(scope, args[1]->ToObject());
+
+ boost::shared_ptr<mongo::DBClientCursor> cursor(
+ new DBClientCursor(conn.get(), ns, cursorId, 0, 0));
+
+ if (!args[2]->IsUndefined())
+ cursor->setBatchSize(args[2]->Int32Value());
+
+ v8::Handle<v8::Function> cons = scope->InternalCursorFT()->GetFunction();
+ v8::Handle<v8::Object> c = cons->NewInstance();
c->SetInternalField(0, v8::External::New(cursor.get()));
- scope->dbClientCursorTracker.track(c, cursor.release());
+ scope->dbConnectionAndCursor.track(
+ v8::Persistent<v8::Value>::New(c),
+ new V8Scope::DBConnectionAndCursor(conn, cursor));
return c;
}
@@ -218,7 +259,7 @@ namespace mongo {
return v8AssertionException("js db in read only mode");
}
- DBClientBase * conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
const string ns = toSTLString(args[0]);
v8::Handle<v8::Integer> flags = args[2]->ToInteger();
@@ -266,7 +307,7 @@ namespace mongo {
return v8AssertionException("js db in read only mode");
}
- DBClientBase * conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
const string ns = toSTLString(args[0]);
v8::Handle<v8::Object> in = args[1]->ToObject();
@@ -292,7 +333,7 @@ namespace mongo {
return v8AssertionException("js db in read only mode");
}
- DBClientBase * conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
const string ns = toSTLString(args[0]);
v8::Handle<v8::Object> q = args[1]->ToObject();
@@ -308,7 +349,7 @@ namespace mongo {
}
v8::Handle<v8::Value> mongoAuth(V8Scope* scope, const v8::Arguments& args) {
- DBClientWithCommands* conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
if (NULL == conn)
return v8AssertionException("no connection");
@@ -319,8 +360,8 @@ namespace mongo {
break;
case 3:
params = BSON(saslCommandMechanismFieldName << "MONGODB-CR" <<
- saslCommandPrincipalSourceFieldName << toSTLString(args[0]) <<
- saslCommandPrincipalFieldName << toSTLString(args[1]) <<
+ saslCommandUserDBFieldName << toSTLString(args[0]) <<
+ saslCommandUserFieldName << toSTLString(args[1]) <<
saslCommandPasswordFieldName << toSTLString(args[2]));
break;
default:
@@ -337,7 +378,7 @@ namespace mongo {
v8::Handle<v8::Value> mongoLogout(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 1, "logout needs 1 arg")
- DBClientBase* conn = getConnection(scope, args);
+ boost::shared_ptr<DBClientBase> conn = getConnection(scope, args);
const string db = toSTLString(args[0]);
BSONObj ret;
conn->logout(db, ret);
@@ -708,7 +749,7 @@ namespace mongo {
return v8AssertionException("Timestamp increment must be a number");
}
int64_t t = args[0]->IntegerValue();
- int64_t largestVal = ((2039LL-1970LL) *365*24*60*60); //seconds between 1970-2038
+ int64_t largestVal = int64_t(OpTime::max().getSecs());
if( t > largestVal )
return v8AssertionException( str::stream()
<< "The first argument must be in seconds; "
@@ -732,24 +773,22 @@ namespace mongo {
v8::Local<v8::Object> it = args.This();
verify(scope->BinDataFT()->HasInstance(it));
- if (args.Length() == 2) {
- // 2 args: type, base64 string
- v8::Handle<v8::Value> type = args[0];
- if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
- return v8AssertionException(
- "BinData subtype must be a Number between 0 and 255 inclusive)");
- }
- v8::String::Utf8Value utf(args[1]);
- // uassert if invalid base64 string
- string tmpBase64 = base64::decode(*utf);
- // length property stores the decoded length
- it->ForceSet(scope->v8StringData("len"), v8::Number::New(tmpBase64.length()));
- it->ForceSet(scope->v8StringData("type"), type);
- it->SetInternalField(0, args[1]);
- }
- else if (args.Length() != 0) {
- return v8AssertionException("BinData takes 2 arguments -- BinData(subtype,data)");
+ argumentCheck(args.Length() == 2, "BinData takes 2 arguments -- BinData(subtype,data)");
+
+ // 2 args: type, base64 string
+ v8::Handle<v8::Value> type = args[0];
+ if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
+ return v8AssertionException(
+ "BinData subtype must be a Number between 0 and 255 inclusive)");
}
+ v8::String::Utf8Value utf(args[1]);
+ // uassert if invalid base64 string
+ string tmpBase64 = base64::decode(*utf);
+ // length property stores the decoded length
+ it->ForceSet(scope->v8StringData("len"), v8::Number::New(tmpBase64.length()),
+ v8::ReadOnly);
+ it->ForceSet(scope->v8StringData("type"), type, v8::ReadOnly);
+ it->SetInternalField(0, args[1]);
return it;
}
@@ -775,23 +814,22 @@ namespace mongo {
v8::Handle<v8::Value> binDataToHex(V8Scope* scope, const v8::Arguments& args) {
v8::Handle<v8::Object> it = args.This();
verify(scope->BinDataFT()->HasInstance(it));
- int len = v8::Handle<v8::Number>::Cast(it->Get(v8::String::New("len")))->Int32Value();
verify(it->InternalFieldCount() == 1);
string data = base64::decode(toSTLString(it->GetInternalField(0)));
stringstream ss;
ss.setf (ios_base::hex, ios_base::basefield);
ss.fill ('0');
ss.setf (ios_base::right, ios_base::adjustfield);
- for(int i = 0; i < len; i++) {
- unsigned v = (unsigned char) data[i];
+ for(std::string::iterator it = data.begin(); it != data.end(); ++it) {
+ unsigned v = (unsigned char) *it;
ss << setw(2) << v;
}
return v8::String::New(ss.str().c_str());
}
- static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, v8::Local<v8::Object> it, int type,
- string hexstr) {
- verify(scope->BinDataFT()->HasInstance(it));
+ static v8::Handle<v8::Value> hexToBinData(V8Scope* scope, int type, string hexstr) {
+ // SERVER-9686: This function does not correctly check to make sure hexstr is actually made
+ // up of valid hex digits, and fails in the hex utility functions
int len = hexstr.length() / 2;
scoped_array<char> data(new char[len]);
@@ -801,38 +839,35 @@ namespace mongo {
}
string encoded = base64::encode(data.get(), len);
- it->ForceSet(v8::String::New("len"), v8::Number::New(len));
- it->ForceSet(v8::String::New("type"), v8::Number::New(type));
- it->SetInternalField(0, v8::String::New(encoded.c_str(), encoded.length()));
- return it;
+ v8::Handle<v8::Value> argv[2];
+ argv[0] = v8::Number::New(type);
+ argv[1] = v8::String::New(encoded.c_str());
+ return scope->BinDataFT()->GetFunction()->NewInstance(2, argv);
}
v8::Handle<v8::Value> uuidInit(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 1, "UUID needs 1 argument")
v8::String::Utf8Value utf(args[0]);
argumentCheck(utf.length() == 32, "UUID string must have 32 characters")
-
- v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction();
- v8::Local<v8::Object> it = f->NewInstance();
- return hexToBinData(scope, it, bdtUUID, *utf);
+ return hexToBinData(scope, bdtUUID, *utf);
}
v8::Handle<v8::Value> md5Init(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 1, "MD5 needs 1 argument")
v8::String::Utf8Value utf(args[0]);
argumentCheck(utf.length() == 32, "MD5 string must have 32 characters")
-
- v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction();
- v8::Local<v8::Object> it = f->NewInstance();
- return hexToBinData(scope, it, MD5Type, *utf);
+ return hexToBinData(scope, MD5Type, *utf);
}
v8::Handle<v8::Value> hexDataInit(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 2, "HexData needs 2 arguments")
+ v8::Handle<v8::Value> type = args[0];
+ if (!type->IsNumber() || type->Int32Value() < 0 || type->Int32Value() > 255) {
+ return v8AssertionException(
+ "HexData subtype must be a Number between 0 and 255 inclusive");
+ }
v8::String::Utf8Value utf(args[1]);
- v8::Handle<v8::Function> f = scope->BinDataFT()->GetFunction();
- v8::Local<v8::Object> it = f->NewInstance();
- return hexToBinData(scope, it, args[0]->IntegerValue(), *utf);
+ return hexToBinData(scope, type->Int32Value(), *utf);
}
v8::Handle<v8::Value> numberLongInit(V8Scope* scope, const v8::Arguments& args) {
@@ -969,6 +1004,22 @@ namespace mongo {
return v8::String::New(ret.c_str());
}
+ v8::Handle<v8::Value> v8ObjectInvalidForStorage(V8Scope* scope, const v8::Arguments& args) {
+ argumentCheck(args.Length() == 1, "invalidForStorage needs 1 argument")
+ if (args[0]->IsNull()) {
+ return v8::Null();
+ }
+ argumentCheck(args[0]->IsObject(), "argument to invalidForStorage has to be an object")
+ Status validForStorage = scope->v8ToMongo(args[0]->ToObject()).storageValid(true);
+ if (validForStorage.isOK()) {
+ return v8::Null();
+ }
+
+ std::string errmsg = str::stream() << validForStorage.codeString()
+ << ": "<< validForStorage.reason();
+ return v8::String::New(errmsg.c_str());
+ }
+
v8::Handle<v8::Value> bsonsize(V8Scope* scope, const v8::Arguments& args) {
argumentCheck(args.Length() == 1, "bsonsize needs 1 argument")
if (args[0]->IsNull()) {
@@ -978,4 +1029,16 @@ namespace mongo {
return v8::Number::New(scope->v8ToMongo(args[0]->ToObject()).objsize());
}
+ v8::Handle<v8::Value> bsonWoCompare(V8Scope* scope, const v8::Arguments& args) {
+ argumentCheck(args.Length() == 2, "bsonWoCompare needs 2 argument");
+
+ argumentCheck(args[0]->IsObject(), "first argument to bsonWoCompare has to be an object");
+ argumentCheck(args[1]->IsObject(), "second argument to bsonWoCompare has to be an object");
+
+ BSONObj firstObject(scope->v8ToMongo(args[0]->ToObject()));
+ BSONObj secondObject(scope->v8ToMongo(args[1]->ToObject()));
+
+ return v8::Number::New(firstObject.woCompare(secondObject));
+ }
+
}