Skip to content

Commit 28f9f52

Browse files
authored
fix(shell-api): Fix invalid regular expression error in db.currentOp() MONGOSH-1703 (#2187)
Fixes a bug where a regular expression error in db.currentOp() would cause it to error.
1 parent 591aaa5 commit 28f9f52

File tree

4 files changed

+87
-8
lines changed

4 files changed

+87
-8
lines changed

packages/e2e-tests/test/e2e.spec.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { createServer as createHTTPServer } from 'http';
2121
import { once } from 'events';
2222
import type { AddressInfo } from 'net';
2323
const { EJSON } = bson;
24+
import { sleep } from './util-helpers';
2425

2526
const jsContextFlagCombinations: `--jsContext=${'plain-vm' | 'repl'}`[][] = [
2627
[],
@@ -1938,4 +1939,73 @@ describe('e2e', function () {
19381939
shell.assertContainsOutput('610');
19391940
});
19401941
});
1942+
1943+
describe('currentOp', function () {
1944+
context('with 2 shells', function () {
1945+
let helperShell: TestShell;
1946+
let currentOpShell: TestShell;
1947+
1948+
const CURRENT_OP_WAIT_TIME = 400;
1949+
const OPERATION_TIME = CURRENT_OP_WAIT_TIME * 2;
1950+
1951+
beforeEach(async function () {
1952+
helperShell = this.startTestShell({
1953+
args: [await testServer.connectionString()],
1954+
});
1955+
currentOpShell = this.startTestShell({
1956+
args: [await testServer.connectionString()],
1957+
});
1958+
await helperShell.waitForPrompt();
1959+
await currentOpShell.waitForPrompt();
1960+
1961+
// Insert a dummy object so find commands will actually run with the delay.
1962+
await helperShell.executeLine('db.coll.insertOne({})');
1963+
});
1964+
1965+
it('should return the current operation and clear when it is complete', async function () {
1966+
const currentCommand = helperShell.executeLine(
1967+
`db.coll.find({$where: function() { sleep(${OPERATION_TIME}) }}).projection({testProjection: 1})`
1968+
);
1969+
helperShell.assertNoErrors();
1970+
await sleep(CURRENT_OP_WAIT_TIME);
1971+
let currentOpCall = await currentOpShell.executeLine(`db.currentOp()`);
1972+
1973+
currentOpShell.assertNoErrors();
1974+
1975+
expect(currentOpCall).to.include('testProjection');
1976+
1977+
await currentCommand;
1978+
1979+
currentOpCall = await currentOpShell.executeLine(`db.currentOp()`);
1980+
1981+
currentOpShell.assertNoErrors();
1982+
expect(currentOpCall).not.to.include('testProjection');
1983+
});
1984+
1985+
it('should work when the operation contains regex', async function () {
1986+
const regExpString = String.raw`^(?i)\Qchho0842\E`;
1987+
1988+
// Stringify the reg exp and drop the quotation marks.
1989+
// Meant to account for JS escaping behavior and to compare with output later.
1990+
const stringifiedRegExpString = `${JSON.stringify(regExpString)}`.slice(
1991+
1,
1992+
-1
1993+
);
1994+
1995+
void helperShell.executeLine(
1996+
`db.coll.find({$where: function() { sleep(${OPERATION_TIME}) }}).projection({re: BSONRegExp('${stringifiedRegExpString}')})`
1997+
);
1998+
helperShell.assertNoErrors();
1999+
2000+
await sleep(CURRENT_OP_WAIT_TIME);
2001+
2002+
const currentOpCall = await currentOpShell.executeLine(
2003+
`db.currentOp()`
2004+
);
2005+
currentOpShell.assertNoErrors();
2006+
2007+
expect(currentOpCall).to.include(stringifiedRegExpString);
2008+
});
2009+
});
2010+
});
19412011
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function sleep(ms: number): Promise<void> {
2+
return new Promise((resolve) => setTimeout(resolve, ms));
3+
}

packages/shell-api/src/database.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,10 +1796,11 @@ describe('Database', function () {
17961796
},
17971797
});
17981798

1799-
const READ_PREFERENCE = {
1799+
const AGGREGATE_OPTIONS = {
18001800
$readPreference: {
18011801
mode: 'primaryPreferred',
18021802
},
1803+
bsonRegExp: true,
18031804
};
18041805

18051806
beforeEach(function () {
@@ -1824,7 +1825,7 @@ describe('Database', function () {
18241825
})
18251826
);
18261827
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1827-
READ_PREFERENCE
1828+
AGGREGATE_OPTIONS
18281829
);
18291830
}
18301831
});
@@ -1846,7 +1847,7 @@ describe('Database', function () {
18461847
})
18471848
);
18481849
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1849-
READ_PREFERENCE
1850+
AGGREGATE_OPTIONS
18501851
);
18511852
});
18521853
});
@@ -1868,7 +1869,7 @@ describe('Database', function () {
18681869
);
18691870
expect(matchStage).to.deep.equals({ $match: {} });
18701871
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1871-
READ_PREFERENCE
1872+
AGGREGATE_OPTIONS
18721873
);
18731874
});
18741875
});
@@ -1890,7 +1891,7 @@ describe('Database', function () {
18901891
);
18911892
expect(matchStage).to.deep.equals({ $match: {} });
18921893
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1893-
READ_PREFERENCE
1894+
AGGREGATE_OPTIONS
18941895
);
18951896
});
18961897
});
@@ -1914,7 +1915,7 @@ describe('Database', function () {
19141915
);
19151916
expect(matchStage).to.deep.equals({ $match: {} });
19161917
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1917-
READ_PREFERENCE
1918+
AGGREGATE_OPTIONS
19181919
);
19191920
});
19201921
}
@@ -1945,7 +1946,7 @@ describe('Database', function () {
19451946
$match: { waitingForLock: true },
19461947
});
19471948
expect(serviceProvider.aggregateDb.firstCall.args[2]).to.deep.equal(
1948-
READ_PREFERENCE
1949+
AGGREGATE_OPTIONS
19491950
);
19501951
});
19511952

packages/shell-api/src/database.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,12 @@ export default class Database extends ShellApiWithMongoClass {
10631063
}
10641064

10651065
const adminDb = this.getSiblingDB('admin');
1066-
const aggregateOptions = { $readPreference: { mode: 'primaryPreferred' } };
1066+
const aggregateOptions = {
1067+
$readPreference: { mode: 'primaryPreferred' },
1068+
// Regex patterns should be instances of BSONRegExp
1069+
// as there can be issues during conversion otherwise.
1070+
bsonRegExp: true,
1071+
};
10671072

10681073
try {
10691074
const cursor = await adminDb.aggregate(pipeline, aggregateOptions);

0 commit comments

Comments
 (0)