diff options
author | Benety Goh <benety@mongodb.com> | 2014-09-30 18:40:08 -0400 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2014-10-06 12:14:11 -0400 |
commit | 33c65901a5814298482d58005c5f83f013c28319 (patch) | |
tree | 1de6dd75fcfa1cc596333a92ac256d54474c6adc /src/mongo/tools | |
parent | 5363f0b64b80735f1e17154f21835650fe6ae887 (diff) | |
download | mongo-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.cpp | 4 | ||||
-rw-r--r-- | src/mongo/tools/mongoshim_options.cpp | 179 | ||||
-rw-r--r-- | src/mongo/tools/mongoshim_options.h | 21 | ||||
-rw-r--r-- | src/mongo/tools/restore.cpp | 6 | ||||
-rw-r--r-- | src/mongo/tools/shim.cpp | 447 | ||||
-rw-r--r-- | src/mongo/tools/shim_mode.cpp | 50 | ||||
-rw-r--r-- | src/mongo/tools/shim_mode.h | 63 | ||||
-rw-r--r-- | src/mongo/tools/tool.cpp | 4 | ||||
-rw-r--r-- | src/mongo/tools/tool.h | 10 |
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); }; |