feat(mongodb): support generic runCommand execution
This commit is contained in:
1 parent
2d00620fac
commit
9b24d962bb
27 files changed
+580
-18
No files matched your search
@@ -690,6 +690,7 @@ export const mongoDistinct = forward("mongoDistinct");
|
||||
export const mongoCollectionStats = forward("mongoCollectionStats");
|
||||
export const mongoCreateIndex = forward("mongoCreateIndex");
|
||||
export const mongoCreateUser = forward("mongoCreateUser");
|
||||
export const mongoRunCommand = forward("mongoRunCommand");
|
||||
export const mongoDropIndexes = forward("mongoDropIndexes");
|
||||
export const documentInsertDocument = forward("documentInsertDocument");
|
||||
export const mongoInsertDocument = forward("mongoInsertDocument");
|
||||
|
||||
@@ -3583,6 +3583,15 @@ export async function mongoCreateUser(connectionId: string, database: string, us
|
||||
});
|
||||
}
|
||||
|
||||
export async function mongoRunCommand(connectionId: string, database: string, commandJson: string, executionId?: string): Promise<MongoDocumentResult> {
|
||||
return post("/api/mongo/run-command", {
|
||||
connectionId,
|
||||
database,
|
||||
commandJson,
|
||||
executionId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function mongoDropIndexes(connectionId: string, database: string, collection: string, indexesJson?: string, single = false): Promise<MongoDropIndexesResult> {
|
||||
return post("/api/mongo/drop-indexes", {
|
||||
connectionId,
|
||||
|
||||
@@ -3527,6 +3527,15 @@ export async function mongoCreateUser(connectionId: string, database: string, us
|
||||
});
|
||||
}
|
||||
|
||||
export async function mongoRunCommand(connectionId: string, database: string, commandJson: string, executionId?: string): Promise<MongoDocumentResult> {
|
||||
return invoke("mongo_run_command", {
|
||||
connectionId,
|
||||
database,
|
||||
commandJson,
|
||||
executionId,
|
||||
});
|
||||
}
|
||||
|
||||
export async function mongoDropIndexes(connectionId: string, database: string, collection: string, indexesJson?: string, single = false): Promise<MongoDropIndexesResult> {
|
||||
return invoke("mongo_drop_indexes", {
|
||||
connectionId,
|
||||
|
||||
@@ -67,6 +67,10 @@ export interface MongoCreateUserCommand {
|
||||
writeConcernJson?: string;
|
||||
}
|
||||
|
||||
export interface MongoRunCommand {
|
||||
commandJson: string;
|
||||
}
|
||||
|
||||
export type MongoCollectionStatsMetric = "stats" | "dataSize" | "storageSize" | "totalIndexSize";
|
||||
|
||||
export interface MongoCollectionStatsCommand {
|
||||
@@ -81,7 +85,7 @@ export interface MongoDistinctCommand {
|
||||
filter?: string;
|
||||
}
|
||||
|
||||
type MongoWriteKind = "insert" | "update" | "delete" | "createIndex" | "createUser" | "dropIndex" | "dropIndexes" | "dropCollection" | "findOneAndUpdate" | "findOneAndReplace" | "findOneAndDelete";
|
||||
type MongoWriteKind = "runCommand" | "insert" | "update" | "delete" | "createIndex" | "createUser" | "dropIndex" | "dropIndexes" | "dropCollection" | "findOneAndUpdate" | "findOneAndReplace" | "findOneAndDelete";
|
||||
|
||||
export type MongoCommand =
|
||||
| ({ kind: "find" } & MongoFindCommand)
|
||||
@@ -94,6 +98,7 @@ export type MongoCommand =
|
||||
| ({ kind: "collectionStats" } & MongoCollectionStatsCommand)
|
||||
| ({ kind: "use" } & MongoUseCommand)
|
||||
| ({ kind: "createUser" } & MongoCreateUserCommand)
|
||||
| ({ kind: "runCommand" } & MongoRunCommand)
|
||||
| { kind: "insert"; collection: string; docsJson: string }
|
||||
| { kind: "update"; collection: string; filter: string; update: string; options?: string; many: boolean }
|
||||
| { kind: "delete"; collection: string; filter: string; many: boolean }
|
||||
@@ -499,6 +504,21 @@ export function parseMongoCreateUserCommand(input: string): MongoCreateUserComma
|
||||
};
|
||||
}
|
||||
|
||||
export function parseMongoRunCommand(input: string): MongoRunCommand | null {
|
||||
const source = input.trim().replace(/;$/, "").trim();
|
||||
const match = /^db\s*\.\s*runCommand\s*\(/i.exec(source);
|
||||
if (!match) return null;
|
||||
const openIndex = source.indexOf("(", match.index);
|
||||
const closeIndex = findMatchingParen(source, openIndex);
|
||||
if (closeIndex < 0 || source.slice(closeIndex + 1).trim()) return null;
|
||||
const args = splitTopLevel(source.slice(openIndex + 1, closeIndex));
|
||||
if (args.length !== 1) return null;
|
||||
const commandJson = parseMongoObjectArgument(args[0]);
|
||||
if (!commandJson) return null;
|
||||
const command = JSON.parse(commandJson) as Record<string, unknown>;
|
||||
return Object.keys(command).length > 0 ? { commandJson: JSON.stringify(command) } : null;
|
||||
}
|
||||
|
||||
export function parseMongoWriteCommand(input: string): MongoWriteCommand | null {
|
||||
const source = input.trim().replace(/;$/, "").trim();
|
||||
const insertOne = parseCollectionMethodTarget(source, "insertOne");
|
||||
@@ -607,6 +627,10 @@ export function parseMongoCommand(input: string): ParsedMongoCommand | null {
|
||||
const createUser = parseMongoCreateUserCommand(source);
|
||||
return createUser ? { kind: "createUser", ...createUser } : null;
|
||||
},
|
||||
(source) => {
|
||||
const runCommand = parseMongoRunCommand(source);
|
||||
return runCommand ? { kind: "runCommand", ...runCommand } : null;
|
||||
},
|
||||
(source) => {
|
||||
// Legacy Mongo shell uses count()/find().count(); keep accepting it
|
||||
// while mapping to DBX's countDocuments-compatible result path.
|
||||
|
||||
@@ -4204,6 +4204,26 @@ export const useQueryStore = defineStore("query", () => {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "runCommand": {
|
||||
if (options?.mongoSafety) {
|
||||
const safety = evaluateMongoWriteSafety(mongoCommand, options.mongoSafety);
|
||||
if (!safety.allowed) throw new Error(safety.reason);
|
||||
}
|
||||
queryExecutionLog("info", "mongo-run-command:start", {
|
||||
traceId,
|
||||
database: currentDatabase,
|
||||
});
|
||||
const result = await api.mongoRunCommand(executionConnectionId, currentDatabase, mongoCommand.commandJson, executionId);
|
||||
allResults.push(markQueryResultRowsRaw(annotateMongoResult(mongoDocumentsToQueryResult(result.documents, performance.now() - commandStartedAt, result.total, result.extended_documents, result.total_is_exact !== false))));
|
||||
mongoEditTarget = undefined;
|
||||
queryExecutionLog("info", "mongo-run-command:done", {
|
||||
traceId,
|
||||
database: currentDatabase,
|
||||
rowCount: result.documents.length,
|
||||
elapsed: elapsed(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "insert":
|
||||
case "update":
|
||||
case "delete":
|
||||
|
||||
Reference in new issue
Block a user