summaryrefslogtreecommitdiff
path: root/src/mongo/tools
diff options
context:
space:
mode:
authorBenety Goh <benety@mongodb.com>2014-09-30 18:40:08 -0400
committerBenety Goh <benety@mongodb.com>2014-10-06 12:14:11 -0400
commit33c65901a5814298482d58005c5f83f013c28319 (patch)
tree1de6dd75fcfa1cc596333a92ac256d54474c6adc /src/mongo/tools
parent5363f0b64b80735f1e17154f21835650fe6ae887 (diff)
downloadmongo-33c65901a5814298482d58005c5f83f013c28319.tar.gz
SERVER-15487 added 'command' mode to run commands on database
extended processFile() and gotObject() to accept output stream removed 'applyOps' mode general code clean up - removed options that are either unused or replaced by --mode
Diffstat (limited to 'src/mongo/tools')
-rw-r--r--src/mongo/tools/bsondump.cpp4
-rw-r--r--src/mongo/tools/mongoshim_options.cpp179
-rw-r--r--src/mongo/tools/mongoshim_options.h21
-rw-r--r--src/mongo/tools/restore.cpp6
-rw-r--r--src/mongo/tools/shim.cpp447
-rw-r--r--src/mongo/tools/shim_mode.cpp50
-rw-r--r--src/mongo/tools/shim_mode.h63
-rw-r--r--src/mongo/tools/tool.cpp4
-rw-r--r--src/mongo/tools/tool.h10
9 files changed, 514 insertions, 270 deletions
diff --git a/src/mongo/tools/bsondump.cpp b/src/mongo/tools/bsondump.cpp
index d89253038e8..d7945034c25 100644
--- a/src/mongo/tools/bsondump.cpp
+++ b/src/mongo/tools/bsondump.cpp
@@ -69,7 +69,7 @@ public:
return 1;
}
- processFile( root );
+ processFile(root, NULL);
return 0;
}
@@ -138,7 +138,7 @@ public:
return true;
}
- virtual void gotObject( const BSONObj& o ) {
+ virtual void gotObject(const BSONObj& o, std::ostream* out) {
switch ( _type ) {
case JSON:
cout << o.jsonString( TenGen ) << endl;
diff --git a/src/mongo/tools/mongoshim_options.cpp b/src/mongo/tools/mongoshim_options.cpp
index 2e38b26ee2d..2ea23c01f30 100644
--- a/src/mongo/tools/mongoshim_options.cpp
+++ b/src/mongo/tools/mongoshim_options.cpp
@@ -28,13 +28,21 @@
* then also delete it in the license file.
*/
+#include "mongo/platform/basic.h"
+
#include "mongo/tools/mongoshim_options.h"
+#include <vector>
+
#include "mongo/base/status.h"
#include "mongo/db/json.h"
#include "mongo/util/options_parser/startup_options.h"
+#include "mongo/util/stringutils.h"
#include "mongo/util/text.h"
+using std::string;
+using std::vector;
+
namespace mongo {
MongoShimGlobalParams mongoShimGlobalParams;
@@ -65,54 +73,49 @@ namespace mongo {
return ret;
}
- const string modeDisplayFormat("find, insert, upsert, remove, repair or applyOps");
+ // Required option --mode.
+ // Refer to shim_mode.h for list of supported modes.
+ vector<string> modeStrings;
+ for (int i = 0; i <= int(ShimMode::kNumShimModes); ++i) {
+ ShimMode mode = static_cast<ShimMode::Value>(i);
+ modeStrings.push_back(mode.toString());
+ for (int i = 0; i <= int(ShimMode::kNumShimModes); ++i) {
+ ShimMode mode = static_cast<ShimMode::Value>(i);
+ modeStrings.push_back(mode.toString());
+ }
+ }
+ string modeDisplayFormat;
+ joinStringDelim(modeStrings, &modeDisplayFormat, '|');
options->addOptionChaining("mode", "mode", moe::String,
"Runs shim in one of several modes: " + modeDisplayFormat)
- .format("find|insert|upsert|remove|repair|applyOps",
- modeDisplayFormat)
- .incompatibleWith("load")
- .incompatibleWith("remove")
- .incompatibleWith("repair")
- .incompatibleWith("applyOps");
-
- options->addOptionChaining("load", "load", moe::Switch,
- "load data" )
- .hidden();
-
- options->addOptionChaining("remove", "remove", moe::Switch,
- "removes documents from collection matching query "
- "(or all documents if query is not provided)" )
- .incompatibleWith("load")
- .incompatibleWith("drop")
- .hidden();
-
- options->addOptionChaining("applyOps", "applyOps", moe::Switch,
- "apply oplog entries" )
- .incompatibleWith("load")
- .incompatibleWith("drop")
- .hidden();
+ .format(modeDisplayFormat,
+ modeDisplayFormat);
+ // Compatible with --mode=insert only.
+ // Used to drop collection before inserting documents read from input.
options->addOptionChaining("drop", "drop", moe::Switch,
- "drop collection before import" );
-
- options->addOptionChaining("upsert", "upsert", moe::Switch,
- "upsert instead of insert" )
- .requires("load")
- .hidden();
+ "drop collection before inserting documents");
+ // Compatible with --mode=upsert only.
+ // Used to specify fields for matching existing documents when
+ // updating/inserting.
options->addOptionChaining("upsertFields", "upsertFields", moe::String,
"comma-separated fields for the query part of the upsert. "
"Ensure these fields are indexed.");
+ // Used to filter document results before writing to to output.
options->addOptionChaining("query", "query,q", moe::String,
"query filter, as a JSON string, e.g., '{x:{$gt:1}}'");
- options->addOptionChaining("repair", "repair", moe::Switch,
- "try to recover a crashed collection")
- .incompatibleWith("applyOps")
- .incompatibleWith("load")
- .incompatibleWith("remove")
- .hidden();
+ options->addOptionChaining("skip", "skip", moe::Int, "documents to skip, default 0")
+ .setDefault(moe::Value(0));
+
+ options->addOptionChaining("limit", "limit", moe::Int,
+ "limit the numbers of documents returned, default all")
+ .setDefault(moe::Value(0));
+
+ options->addOptionChaining("sort", "sort", moe::String,
+ "sort order, as a JSON string, e.g., '{x:1}'");
// Used for testing.
options->addOptionChaining("in", "in", moe::String,
@@ -128,26 +131,8 @@ namespace mongo {
// Used for testing.
options->addOptionChaining("out", "out", moe::String,
"output file; if not specified, stdout is used")
- .incompatibleWith("in")
.hidden();
- options->addOptionChaining("slaveOk", "slaveOk,k", moe::Bool,
- "use secondaries for export if available, default true")
- .setDefault(moe::Value(true));
-
- options->addOptionChaining("forceTableScan", "forceTableScan", moe::Switch,
- "force a table scan (do not use $snapshot)");
-
- options->addOptionChaining("skip", "skip", moe::Int, "documents to skip, default 0")
- .setDefault(moe::Value(0));
-
- options->addOptionChaining("limit", "limit", moe::Int,
- "limit the numbers of documents returned, default all")
- .setDefault(moe::Value(0));
-
- options->addOptionChaining("sort", "sort", moe::String,
- "sort order, as a JSON string, e.g., '{x:1}'");
-
return Status::OK();
}
@@ -181,15 +166,6 @@ namespace mongo {
"MongoShim requires a --dbpath value to proceed");
}
- // Ensure that collection is specified.
- // Tool::getNs() validates --collection but error
- // is not propagated to calling process because Tool::main()
- // always exits cleanly when --dbpath is enabled.
- if (toolGlobalParams.coll.size() == 0) {
- return Status(ErrorCodes::BadValue,
- "MongoShim requires a --collection value to proceed");
- }
-
ret = storeFieldOptions(params, args);
if (!ret.isOK()) {
return ret;
@@ -200,39 +176,54 @@ namespace mongo {
return ret;
}
- mongoShimGlobalParams.load = params.count("load") > 0;
- mongoShimGlobalParams.remove = params.count("remove") > 0;
- mongoShimGlobalParams.applyOps = params.count("applyOps") > 0;
- mongoShimGlobalParams.repair = params.count("repair") > 0;
+ // Process shim mode.
+ if (params.count("mode") == 0) {
+ return Status(ErrorCodes::BadValue,
+ "MongoShim requires a --mode value to proceed");
+ }
+
+ const string modeParam = getParam("mode");
+ for (int i = 0; i <= int(ShimMode::kNumShimModes); ++i) {
+ ShimMode mode = static_cast<ShimMode::Value>(i);
+ if (modeParam == mode.toString()) {
+ mongoShimGlobalParams.mode = mode;
+ break;
+ }
+ }
+ // --mode value is enforced by format string in option description.
+ invariant(mongoShimGlobalParams.mode != ShimMode::kNumShimModes);
mongoShimGlobalParams.drop = params.count("drop") > 0;
- mongoShimGlobalParams.upsert = params.count("upsert") > 0;
- // Process shim mode. Using --mode is preferred to --load, --applyOps, --remove, ...
- if (params.count("mode") > 0) {
- const string mode = getParam("mode");
- if (mode == "find") {
- // No change to mongoShimGlobalParams.
- }
- else if (mode == "insert") {
- mongoShimGlobalParams.load = true;
- }
- else if (mode == "remove") {
- mongoShimGlobalParams.remove = true;
- }
- else if (mode == "repair") {
- mongoShimGlobalParams.repair = true;
- }
- else if (mode == "upsert") {
- mongoShimGlobalParams.load = true;
- mongoShimGlobalParams.upsert = true;
+ // Ensure that collection is specified when mode is not "command".
+ // Tool::getNs() validates --collection but error
+ // is not propagated to calling process because Tool::main()
+ // always exits cleanly when --dbpath is enabled.
+ if (toolGlobalParams.coll.size() == 0 &&
+ !(mongoShimGlobalParams.mode == ShimMode::kCommand)) {
+ return Status(ErrorCodes::BadValue,
+ "MongoShim requires a --collection value to proceed when not running "
+ "in \"command\" mode");
+ }
+ // --drop and --collection are not allowed in "command" mode.
+ if (mongoShimGlobalParams.mode == ShimMode::kCommand) {
+ if (!toolGlobalParams.coll.empty()) {
+ return Status(ErrorCodes::BadValue,
+ "--collection is not allowed in \"command\" mode");
}
- else if (mode == "applyOps") {
- mongoShimGlobalParams.applyOps = true;
+ if (mongoShimGlobalParams.drop) {
+ return Status(ErrorCodes::BadValue,
+ "--drop is not allowed in \"command\" mode");
}
}
- if (mongoShimGlobalParams.upsert) {
+ // Parse upsert fields.
+ // --upsertFields is illegal when not in "upsert" mode.
+ if (params.count("upsertFields") > 0) {
+ if (mongoShimGlobalParams.mode != ShimMode::kUpsert) {
+ return Status(ErrorCodes::BadValue,
+ "--upsertFields is not allowed when not in \"upsert\" mode");
+ }
string uf = getParam("upsertFields");
if (uf.empty()) {
mongoShimGlobalParams.upsertFields.push_back("_id");
@@ -254,17 +245,7 @@ namespace mongo {
mongoShimGlobalParams.inputDocuments = mongo::fromjson(getParam("inputDocuments", "{}"));
mongoShimGlobalParams.query = getParam("query", "");
- mongoShimGlobalParams.snapShotQuery = false;
-
- // Only allow snapshot query (requires _id idx scan) if following conditions are false
- if (!hasParam("query") &&
- !hasParam("sort") &&
- !hasParam("dbpath") &&
- !hasParam("forceTableScan")) {
- mongoShimGlobalParams.snapShotQuery = true;
- }
- mongoShimGlobalParams.slaveOk = params["slaveOk"].as<bool>();
mongoShimGlobalParams.limit = getParam("limit", 0);
mongoShimGlobalParams.skip = getParam("skip", 0);
mongoShimGlobalParams.sort = getParam("sort", "");
diff --git a/src/mongo/tools/mongoshim_options.h b/src/mongo/tools/mongoshim_options.h
index 5878022e35e..5c1c9a12c62 100644
--- a/src/mongo/tools/mongoshim_options.h
+++ b/src/mongo/tools/mongoshim_options.h
@@ -36,27 +36,20 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/tools/shim_mode.h"
#include "mongo/tools/tool_options.h"
namespace mongo {
struct MongoShimGlobalParams {
- bool load;
-
- // If true, removes all documents in collection.
- // If query is provided, only documents matching query will be removed.
- bool remove;
-
- // If true, read oplog entries from stdin to use as input to "applyOps" command.
- bool applyOps;
-
- // If true, attempts to repair bad data in collection.
- // Also writes contents of collection to output.
- bool repair;
+ MongoShimGlobalParams() : mode(ShimMode::kNumShimModes) { }
+ // Mongoshim can run in one mode per invocation.
+ // See shim_mode.h for list of supported modes.
+ ShimMode mode;
bool drop;
- bool upsert;
+
std::vector<std::string> upsertFields;
std::string query;
@@ -76,8 +69,6 @@ namespace mongo {
std::string outputFile;
bool outputFileSpecified;
- bool slaveOk;
- bool snapShotQuery;
unsigned int skip;
unsigned int limit;
std::string sort;
diff --git a/src/mongo/tools/restore.cpp b/src/mongo/tools/restore.cpp
index 340dfd2f926..ad4998dea74 100644
--- a/src/mongo/tools/restore.cpp
+++ b/src/mongo/tools/restore.cpp
@@ -294,7 +294,7 @@ public:
if (mongoRestoreGlobalParams.oplogReplay) {
toolInfoLog() << "\t Replaying oplog" << std::endl;
_curns = OPLOG_SENTINEL;
- processFile( root / "oplog.bson" );
+ processFile(root / "oplog.bson", NULL );
toolInfoLog() << "Applied " << _oplogEntryApplies << " oplog entries out of "
<< _oplogEntryApplies + _oplogEntrySkips << " (" << _oplogEntrySkips
<< " skipped)." << std::endl;
@@ -534,7 +534,7 @@ public:
}
// 3) Actually restore the BSONObjs inside the dump file
- processFile( root );
+ processFile(root, NULL);
// 4) If running with --drop, remove any users/roles that were in the system at the
// beginning of the restore but weren't found in the dump file
@@ -631,7 +631,7 @@ public:
}
}
- virtual void gotObject( const BSONObj& obj ) {
+ virtual void gotObject(const BSONObj& obj, std::ostream* out) {
if (_curns == OPLOG_SENTINEL) { // intentional ptr compare
if (obj["op"].valuestr()[0] == 'n') // skip no-ops
return;
diff --git a/src/mongo/tools/shim.cpp b/src/mongo/tools/shim.cpp
index 88d90b8ac43..5da90bfd7ae 100644
--- a/src/mongo/tools/shim.cpp
+++ b/src/mongo/tools/shim.cpp
@@ -62,173 +62,180 @@ using std::vector;
using namespace mongo;
-class Shim : public BSONTool {
+
+
+/**
+ * Mongoshim mode handlers
+ */
+class ShimHandler {
public:
- Shim() : BSONTool() { }
+ virtual ~ShimHandler() { }
- virtual void printHelp( ostream & out ) {
- printMongoShimHelp(&out);
- }
+ /**
+ * Returns true if this mode requires an output stream
+ */
+ virtual bool requiresOutputStream() const = 0;
- virtual void gotObject( const BSONObj& obj ) {
- if (mongoShimGlobalParams.upsert) {
- BSONObjBuilder b;
- invariant(!mongoShimGlobalParams.upsertFields.empty());
- for (vector<string>::const_iterator it = mongoShimGlobalParams.upsertFields.begin(),
- end = mongoShimGlobalParams.upsertFields.end(); it != end; ++it) {
- BSONElement e = obj.getFieldDotted(it->c_str());
- // If we cannot construct a valid query using the provided upsertFields,
- // insert the object and skip the rest of the fields.
- if (e.eoo()) {
- conn().insert(_ns, obj);
- return;
- }
- b.appendAs(e, *it);
- }
- Query query(b.obj());
- bool upsert = true;
- bool multi = false;
- conn().update(_ns, query, obj, upsert, multi);
- }
- else if (mongoShimGlobalParams.applyOps) {
- // A valid oplog entry contains a non-empty "ns" string field.
- // This does not apply to oplog entries of type 'n', which typically
- // have empty 'ns' field values. However, for the purposes of applyOps,
- // we ignore oplog entries of type 'n'.
- BSONElement nsElement = obj.getField("ns");
- if (nsElement.type() != mongo::String) {
- toolError() << "Skipping oplog entry without required \"ns\" field: " << obj;
- return;
- }
- else if (nsElement.String().empty()) {
- toolError() << "Skipping oplog entry with empty \"ns\" value: " << obj;
- return;
- }
+ /**
+ * If true, processes documents read from input in gotObject() - doRun() will not be called.
+ */
+ virtual bool acceptsInputDocuments() const = 0;
- BSONObjBuilder b(obj.objsize() + 32);
- BSONArrayBuilder updates(b.subarrayStart("applyOps"));
- updates.append(obj);
- updates.done();
+ /**
+ * Process input document.
+ * Results may be written to output stream if provided.
+ */
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const = 0;
- BSONObj c = b.obj();
+ /**
+ * Generate results for this mode and write results to output stream if applicable.
+ * During processing, only one of handleSingleInputDocument() or generateOutputDocuments()
+ * will be called as determined by the result of acceptsInputDocuments().
+ */
+ virtual void generateOutputDocuments(std::ostream* out) const = 0;
+};
- BSONObj res;
- bool ok = conn().runCommand("admin", c, res);
- if (!ok) {
- toolError() << "Failed to add oplog entry " << obj << ": " << res;
- }
+
+
+/**
+ * Find mode.
+ */
+class FindShimHandler : public ShimHandler {
+public:
+ FindShimHandler(DBClientBase& connection, const string& ns)
+ : _connection(connection), _ns(ns) { }
+
+ virtual bool requiresOutputStream() const { return true; }
+ virtual bool acceptsInputDocuments() const { return false; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const { }
+ virtual void generateOutputDocuments(std::ostream* out) const {
+ invariant(out);
+ Query q(mongoShimGlobalParams.query);
+ if (mongoShimGlobalParams.sort != "") {
+ BSONObj sortSpec = mongo::fromjson(mongoShimGlobalParams.sort);
+ q.sort(sortSpec);
}
- else {
- conn().insert(_ns, obj );
+
+ auto_ptr<DBClientCursor> cursor =
+ _connection.query(_ns, q, mongoShimGlobalParams.limit, mongoShimGlobalParams.skip,
+ NULL, 0, QueryOption_NoCursorTimeout);
+
+ while ( cursor->more() ) {
+ BSONObj obj = cursor->next();
+ out->write( obj.objdata(), obj.objsize() );
}
}
- int doRun() {
- // Flush stdout before returning from doRun().
- // XXX: This seems to be an issue under RHEL55 but not under other OSes or more recent
- // versions of Linux
- ON_BLOCK_EXIT(&std::ostream::flush, &cout);
+private:
+ DBClientBase& _connection;
+ string _ns;
+};
- try {
- _ns = getNS();
- }
- catch (...) {
- printHelp(cerr);
- return 1;
- }
- if (mongoShimGlobalParams.load ||
- mongoShimGlobalParams.applyOps) {
- if ( mongoShimGlobalParams.drop ) {
- conn().dropCollection( _ns );
- }
- // --inputDocuments and --in are used primarily for testing.
- if (!mongoShimGlobalParams.inputDocuments.isEmpty()) {
- BSONElement firstElement = mongoShimGlobalParams.inputDocuments.firstElement();
- if (firstElement.type() != Array) {
- toolError() << "first element of --inputDocuments has to be an array: "
- << firstElement;
- return -1;
- }
- BSONObjIterator i(firstElement.Obj());
- while ( i.more() ) {
- BSONElement e = i.next();
- if (!e.isABSONObj()) {
- toolError() << "skipping non-object in input documents: " << e;
- continue;
- }
- gotObject(e.Obj());
- }
- }
- else if (mongoShimGlobalParams.inputFileSpecified) {
- processFile(mongoShimGlobalParams.inputFile);
- }
- else {
- processFile("-");
- }
- }
- else if (mongoShimGlobalParams.remove) {
- // Removes all documents matching query
- bool justOne = false;
- conn().remove(_ns, mongoShimGlobalParams.query, justOne);
- }
- else if (mongoShimGlobalParams.repair) {
- // Repairs collection before writing documents to output.
- ostream *out = &cout;
- auto_ptr<ofstream> fileStream = _createOutputFile();
- if (fileStream.get()) {
- if (!fileStream->good()) {
- toolError() << "couldn't open [" << mongoShimGlobalParams.outputFile << "]";
- return -1;
- }
- out = fileStream.get();
- }
- _repair(*out);
+
+/**
+ * Insert mode.
+ */
+class InsertShimHandler : public ShimHandler {
+public:
+ InsertShimHandler(DBClientBase& connection, const string& ns)
+ : _connection(connection), _ns(ns) {
+ if (mongoShimGlobalParams.drop) {
+ invariant(!_ns.empty());
+ _connection.dropCollection(_ns);
}
- else {
- // Write results to stdout unless output file is specified using --out option.
- ostream *out = &cout;
- auto_ptr<ofstream> fileStream = _createOutputFile();
- if (fileStream.get()) {
- if (!fileStream->good()) {
- toolError() << "couldn't open [" << mongoShimGlobalParams.outputFile << "]";
- return -1;
- }
- out = fileStream.get();
- }
+ }
- Query q(mongoShimGlobalParams.query);
- if (mongoShimGlobalParams.sort != "") {
- BSONObj sortSpec = mongo::fromjson(mongoShimGlobalParams.sort);
- q.sort(sortSpec);
- }
+ virtual bool requiresOutputStream() const { return false; }
+ virtual bool acceptsInputDocuments() const { return true; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const {
+ _connection.insert(_ns, obj);
+ }
+ virtual void generateOutputDocuments(std::ostream* out) const { }
+
+private:
+ DBClientBase& _connection;
+ string _ns;
+};
- if (mongoShimGlobalParams.snapShotQuery) {
- q.snapshot();
- }
- auto_ptr<DBClientCursor> cursor = conn().query(_ns,
- q,
- mongoShimGlobalParams.limit,
- mongoShimGlobalParams.skip,
- NULL,
- 0,
- QueryOption_NoCursorTimeout);
-
- while ( cursor->more() ) {
- BSONObj obj = cursor->next();
- out->write( obj.objdata(), obj.objsize() );
+
+/**
+ * Upsert mode.
+ */
+class UpsertShimHandler : public ShimHandler {
+public:
+ UpsertShimHandler(DBClientBase& connection, const string& ns)
+ : _connection(connection), _ns(ns) { }
+
+ virtual bool requiresOutputStream() const { return false; }
+ virtual bool acceptsInputDocuments() const { return true; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const {
+ BSONObjBuilder b;
+ invariant(!mongoShimGlobalParams.upsertFields.empty());
+ for (vector<string>::const_iterator it = mongoShimGlobalParams.upsertFields.begin(),
+ end = mongoShimGlobalParams.upsertFields.end(); it != end; ++it) {
+ BSONElement e = obj.getFieldDotted(it->c_str());
+ // If we cannot construct a valid query using the provided upsertFields,
+ // insert the object and skip the rest of the fields.
+ if (e.eoo()) {
+ _connection.insert(_ns, obj);
+ return;
}
+ b.appendAs(e, *it);
}
+ Query query(b.obj());
+ bool upsert = true;
+ bool multi = false;
+ _connection.update(_ns, query, obj, upsert, multi);
+ }
+ virtual void generateOutputDocuments(std::ostream* out) const { }
- return 0;
+private:
+ DBClientBase& _connection;
+ string _ns;
+};
+
+
+
+/**
+ * Remove mode.
+ */
+class RemoveShimHandler : public ShimHandler {
+public:
+ RemoveShimHandler(DBClientBase& connection, const string& ns)
+ : _connection(connection), _ns(ns) { }
+
+ virtual bool requiresOutputStream() const { return false; }
+ virtual bool acceptsInputDocuments() const { return false; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const { }
+ virtual void generateOutputDocuments(std::ostream* out) const {
+ // Removes all documents matching query
+ bool justOne = false;
+ _connection.remove(_ns, mongoShimGlobalParams.query, justOne);
}
private:
- /**
- * Writes valid objects in collection to output.
- */
- void _repair(std::ostream& out) {
+ DBClientBase& _connection;
+ string _ns;
+};
+
+
+
+/**
+ * Repair mode.
+ * Writes valid objects in collection to output.
+ */
+class RepairShimHandler : public ShimHandler {
+public:
+ RepairShimHandler(DBClientBase& connection, const string& ns)
+ : _connection(connection), _ns(ns) { }
+
+ virtual bool requiresOutputStream() const { return true; }
+ virtual bool acceptsInputDocuments() const { return false; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const { }
+ virtual void generateOutputDocuments(std::ostream* out) const {
+ invariant(out);
toolInfoLog() << "going to try to recover data from: " << _ns << std::endl;
OperationContextImpl txn;
Client::WriteContext cx(&txn, toolGlobalParams.db);
@@ -266,7 +273,7 @@ private:
}
// Write valid object to output stream.
- out.write(obj.objdata(), obj.objsize());
+ out->write(obj.objdata(), obj.objsize());
}
catch (std::exception& ex) {
toolError() << "found invalid document @ " << currLoc << " " << ex.what();
@@ -287,6 +294,152 @@ private:
cx.commit();
}
+private:
+ DBClientBase& _connection;
+ string _ns;
+};
+
+
+
+/**
+ * Command mode.
+ */
+class CommandShimHandler : public ShimHandler {
+public:
+ CommandShimHandler(DBClientBase& connection)
+ : _connection(connection) { }
+
+ virtual bool requiresOutputStream() const { return true; }
+ virtual bool acceptsInputDocuments() const { return true; }
+ virtual void handleSingleInputDocument(const BSONObj& obj, std::ostream* out) const {
+ invariant(out);
+ // Every document read from input is a separate command object.
+ BSONObj res;
+ bool ok = _connection.runCommand(toolGlobalParams.db, obj, res);
+ if (!ok) {
+ toolError() << "Failed to run command " << obj << ": " << res;
+ }
+ invariant(res.isValid());
+ out->write(res.objdata(), res.objsize());
+ }
+ virtual void generateOutputDocuments(std::ostream* out) const { }
+
+private:
+ DBClientBase& _connection;
+};
+
+
+
+class Shim : public BSONTool {
+public:
+ Shim() : BSONTool() { }
+
+ virtual void printHelp( ostream & out ) {
+ printMongoShimHelp(&out);
+ }
+
+ virtual void gotObject(const BSONObj& obj, std::ostream* out) {
+ invariant(_shimHandler.get());
+ _shimHandler->handleSingleInputDocument(obj, out);
+ }
+
+ int doRun() {
+ // Flush stdout before returning from doRun().
+ // XXX: This seems to be an issue under RHEL55 but not under other OSes or more recent
+ // versions of Linux
+ ON_BLOCK_EXIT(&std::ostream::flush, &cout);
+
+ // getNS() could throw an exception.
+ string ns;
+ try {
+ if (mongoShimGlobalParams.mode != ShimMode::kCommand) {
+ ns = getNS();
+ }
+ }
+ catch (...) {
+ printHelp(cerr);
+ return EXIT_FAILURE;
+ }
+
+ switch (mongoShimGlobalParams.mode) {
+ case ShimMode::kFind:
+ _shimHandler.reset(new FindShimHandler(conn(), ns));
+ break;
+ case ShimMode::kInsert:
+ _shimHandler.reset(new InsertShimHandler(conn(), ns));
+ break;
+ case ShimMode::kUpsert:
+ _shimHandler.reset(new UpsertShimHandler(conn(), ns));
+ break;
+ case ShimMode::kRemove:
+ _shimHandler.reset(new RemoveShimHandler(conn(), ns));
+ break;
+ case ShimMode::kRepair:
+ _shimHandler.reset(new RepairShimHandler(conn(), ns));
+ break;
+ case ShimMode::kCommand:
+ _shimHandler.reset(new CommandShimHandler(conn()));
+ break;
+ case ShimMode::kNumShimModes:
+ invariant(false);
+ }
+
+ // Initialize output stream if handler needs it.
+ ostream *out = NULL;
+ auto_ptr<ofstream> fileStream;
+ if (_shimHandler->requiresOutputStream()) {
+ fileStream = _createOutputFile();
+ if (fileStream.get()) {
+ if (!fileStream->good()) {
+ toolError() << "couldn't open [" << mongoShimGlobalParams.outputFile << "]";
+ return EXIT_FAILURE;
+ }
+ out = fileStream.get();
+ }
+ else {
+ // Write results to stdout by default.
+ out = &cout;
+ }
+ }
+
+ // Skip doRun() if handler needs to read documents from input.
+ // The handler may still write results to output stream.
+ if (_shimHandler->acceptsInputDocuments()) {
+ // --inputDocuments and --in are used primarily for testing.
+ if (!mongoShimGlobalParams.inputDocuments.isEmpty()) {
+ BSONElement firstElement = mongoShimGlobalParams.inputDocuments.firstElement();
+ if (firstElement.type() != Array) {
+ toolError() << "first element of --inputDocuments has to be an array: "
+ << firstElement;
+ return EXIT_FAILURE;
+ }
+ BSONObjIterator i(firstElement.Obj());
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ if (!e.isABSONObj()) {
+ toolError() << "skipping non-object in input documents: " << e;
+ continue;
+ }
+ gotObject(e.Obj(), out);
+ }
+ }
+ else if (mongoShimGlobalParams.inputFileSpecified) {
+ processFile(mongoShimGlobalParams.inputFile, out);
+ }
+ else {
+ processFile("-", out);
+ }
+ }
+ else {
+ // 'out' may be NULL if not required by handler (eg. "remove" mode).
+ _shimHandler->generateOutputDocuments(out);
+ }
+
+ return EXIT_SUCCESS;
+ }
+
+private:
+
/**
* Returns a valid filestream if output file is specified and is not "-".
*/
@@ -304,7 +457,7 @@ private:
return fileStream;
}
- string _ns;
+ auto_ptr<ShimHandler> _shimHandler;
};
REGISTER_MONGO_TOOL(Shim);
diff --git a/src/mongo/tools/shim_mode.cpp b/src/mongo/tools/shim_mode.cpp
new file mode 100644
index 00000000000..23a43b49138
--- /dev/null
+++ b/src/mongo/tools/shim_mode.cpp
@@ -0,0 +1,50 @@
+/* Copyright 2014 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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/tools/shim_mode.h"
+
+#include "mongo/util/assert_util.h"
+
+namespace mongo {
+
+ std::string ShimMode::toString() const {
+ switch (_value) {
+ case kFind: return "find";
+ case kInsert: return "insert";
+ case kUpsert: return "upsert";
+ case kRemove: return "remove";
+ case kRepair: return "repair";
+ case kCommand: return "command";
+ case kNumShimModes: return "total";
+ // No default. Compiler should complain if there's a shim mode that's not handled.
+ }
+ invariant(false);
+ }
+
+} // mongo
diff --git a/src/mongo/tools/shim_mode.h b/src/mongo/tools/shim_mode.h
new file mode 100644
index 00000000000..ae5ad6bee8a
--- /dev/null
+++ b/src/mongo/tools/shim_mode.h
@@ -0,0 +1,63 @@
+/* Copyright 2014 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 <string>
+
+namespace mongo {
+
+ /**
+ * Mongoshim modes.
+ */
+ class ShimMode {
+ public:
+ enum Value {
+ kFind = 0, // Reads documents from collection.
+ kInsert = 1, // Inserts documents into collection.
+ kUpsert = 2, // Updates/inserts documents in collection.
+ kRemove = 3, // Removes documents from collection.
+ kRepair = 4, // Reads uncorrupted documents from collection record store.
+ kCommand = 5, // Runs a command on the database.
+ kNumShimModes
+ };
+
+ /* implicit */ ShimMode(Value value) : _value(value) {}
+
+ operator Value() const { return _value; }
+
+ /**
+ * Returns string representation shim mode.
+ * Used to generate list of choices for command line option.
+ */
+ std::string toString() const;
+
+ private:
+ Value _value;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/tools/tool.cpp b/src/mongo/tools/tool.cpp
index d6ab8437184..c80a140bffb 100644
--- a/src/mongo/tools/tool.cpp
+++ b/src/mongo/tools/tool.cpp
@@ -300,7 +300,7 @@ namespace mongo {
return doRun();
}
- long long BSONTool::processFile( const boost::filesystem::path& root ) {
+ long long BSONTool::processFile(const boost::filesystem::path& root, std::ostream* out) {
bool isFifoFile = boost::filesystem::status(root).type() == boost::filesystem::fifo_file;
bool isStdin = root == "-";
@@ -384,7 +384,7 @@ namespace mongo {
}
if (!bsonToolGlobalParams.hasFilter || _matcher->matches(o)) {
- gotObject( o );
+ gotObject(o, out);
processed++;
}
diff --git a/src/mongo/tools/tool.h b/src/mongo/tools/tool.h
index 235d2b46aab..df6bf671491 100644
--- a/src/mongo/tools/tool.h
+++ b/src/mongo/tools/tool.h
@@ -30,6 +30,7 @@
#pragma once
+#include <iosfwd>
#include <string>
#if defined(_WIN32)
@@ -96,11 +97,16 @@ namespace mongo {
BSONTool();
virtual int doRun() = 0;
- virtual void gotObject( const BSONObj& obj ) = 0;
+ virtual void gotObject(const BSONObj& obj, std::ostream* out) = 0;
virtual int run();
- long long processFile( const boost::filesystem::path& file );
+ /**
+ * Read BSON documents from file and invoke gotObject() for every document.
+ * Output stream will be passed along to gotObject().
+ * If output stream is not provided, a NULL output stream will be passed to gotObject().
+ */
+ long long processFile(const boost::filesystem::path& file, std::ostream* out);
};