feat(mongodb): support db.getSiblingDB()

This commit is contained in:
Loc Nguyen authored and GitHub committed 2026-09-18 15:38:53 +08:00
1 parent b44e1080a8
commit f1e0f54a2b
9 files changed
+262 -8

No files matched your search

+41 -1
View File
@@ -6,6 +6,7 @@ import {
evaluateMongoWriteSafety,
mongoAggregateWriteStage,
listChainedCalls,
MONGO_SHELL_COMMAND_HINT,
parseMongoFindExplainCommand,
mongoBulkWriteToQueryResult,
mongoCollectionStatsToQueryResult,
@@ -360,6 +361,45 @@ test("parseMongoFindCommand does not rewrite constructor text inside strings", (
assert.deepEqual(JSON.parse(command.filter), { label: "new Date()", note: "ObjectId()" });
});
test("parseMongoCommand wraps db.getSiblingDB() commands with the target database", () => {
// The exact command from #3936.
const parsed = parseMongoCommand(`db.getSiblingDB("iam_account").getCollection("user")
.find({_id: NumberLong('144115205316939462')})
.sort({phone: 1})
.limit(21);`);
assert.ok(parsed && parsed.command.kind === "inDatabase");
assert.equal(parsed.command.database, "iam_account");
assert.equal(parsed.command.command.kind, "find");
assert.equal((parsed.command.command as { limit: number }).limit, 21);
assert.equal((parsed.command.command as { collection: string }).collection, "user");
assert.equal(parseMongoCommand("db.getSiblingDB('other').orders.updateOne({a: 1}, {$set: {b: 2}})")?.command.kind, "inDatabase");
assert.equal(parseMongoCommand('db . getSiblingDB( "x" ) . stats()')?.command.kind, "inDatabase");
// A sibling command is one statement in a batch, and a plain one after it is unaffected.
assert.deepEqual(
splitMongoCommandRanges('db.getSiblingDB("a").c.find({});\ndb.d.find({})').map((range) => range.command.kind),
["inDatabase", "find"],
);
// A command that parses is not a failure, even when asked directly.
assert.equal(describeMongoCommandParseFailure('db.getSiblingDB("other").c.find({})'), MONGO_SHELL_COMMAND_HINT);
});
test("parseMongoCommand rejects malformed db.getSiblingDB() with a specific reason", () => {
for (const [source, expected] of [
['db.getSiblingDB("x")', /requires a database name string followed by a command/],
['db.getSiblingDB("").c.find({})', /requires a database name string/],
["db.getSiblingDB(1).c.find({})", /requires a database name string/],
['db.getSiblingDB("x", "y").c.find({})', /requires a database name string/],
['db.getSiblingDB("x").getSiblingDB("y").c.find({})', /cannot be chained/],
// Errors inside the wrapped command are reported as they would be without the prefix.
['db.getSiblingDB("x").c.replaceOne({a: 1}, {$set: {b: 2}})', /must not contain update operators such as \$set/],
['db.getSiblingDB("x").c.bogus()', /Collection method bogus\(\) is not supported/],
] as const) {
assert.equal(parseMongoCommand(source), null, source);
assert.match(describeMongoCommandParseFailure(source), expected, source);
}
});
test('parseMongoCommand accepts db["name"] bracket collection accessors', () => {
const find = parseMongoFindCommand('db["orders-2024"].find({a: 1})');
assert.ok(find);
@@ -1433,7 +1473,7 @@ test("describeMongoCommandParseFailure names unsupported methods and points at a
assert.match(describeMongoCommandParseFailure('db.getCollection("my-coll").watch()'), /watch\(\) is not supported/);
assert.match(describeMongoCommandParseFailure("db.getCollectionNames()"), /db\.getCollectionNames\(\) is not supported; collections are listed in the sidebar/);
assert.match(describeMongoCommandParseFailure('db.getSiblingDB("other").c.find({})'), /use <database>/);
assert.match(describeMongoCommandParseFailure("db.currentOp()"), /db\.currentOp\(\) is not supported/);
assert.match(describeMongoCommandParseFailure("db.adminCommand({ping: 1})"), /use db\.runCommand/);
assert.match(describeMongoCommandParseFailure("show collections"), /listed in the sidebar/);