Skip to content

Commit 644cd0c

Browse files
jsteemannMongoDB Bot
authored andcommitted
SERVER-100489 Use BSON builder to build change streams namespace regex matchers (#32137)
GitOrigin-RevId: 577a911581fdbf671065f33aeeb69902510a8fdf
1 parent 830214f commit 644cd0c

File tree

3 files changed

+377
-22
lines changed

3 files changed

+377
-22
lines changed

etc/backports_required_for_multiversion_tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ last-continuous:
575575
ticket: SERVER-73641
576576
- test_file: jstests/core/timeseries/timeseries_index_partial.js
577577
ticket: SERVER-73641
578+
- test_file: jstests/change_streams/change_streams_namespace_match_expressions.js
579+
ticket: SERVER-100489
578580
suites: null
579581
last-lts:
580582
all:
@@ -1202,4 +1204,6 @@ last-lts:
12021204
ticket: SERVER-73641
12031205
- test_file: jstests/core/timeseries/timeseries_index_partial.js
12041206
ticket: SERVER-73641
1207+
- test_file: jstests/change_streams/change_streams_namespace_match_expressions.js
1208+
ticket: SERVER-100489
12051209
suites: null
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
// Tests regular expressions for namespace matching in change streams.
2+
3+
(function() {
4+
"use strict";
5+
6+
load("jstests/libs/change_stream_util.js");
7+
load("jstests/libs/collection_drop_recreate.js");
8+
9+
// Database name with special characters inside it.
10+
// According to https://www.mongodb.com/docs/manual/reference/limits/#naming-restrictions, many
11+
// special characters are disallowed here.
12+
const kDBName = jsTestName() + "]]][}}{{{{{{{{";
13+
// Collection name with special characters inside it.
14+
const kCollName = "booooo '\"bar baz]]][}}}}\\{{{{{{{{{{{{{{{{{{{{{{{{{";
15+
const kCollNameOther = "other '\"]]]]";
16+
17+
const testDB = db.getSiblingDB(kDBName);
18+
19+
function runTest(db, collection, runOperations, pipelineStages, expectedChanges) {
20+
assertDropAndRecreateCollection(db, kCollName);
21+
assertDropAndRecreateCollection(db, kCollNameOther);
22+
23+
const cst = new ChangeStreamTest(db);
24+
const pipeline = [{$changeStream: {}}].concat(pipelineStages);
25+
26+
jsTestLog("Running test with pipeline: " + tojson(pipeline));
27+
let cursor = cst.startWatchingChanges({pipeline, collection});
28+
assert.eq(0, cursor.firstBatch.length, "Cursor had changes: " + tojson(cursor));
29+
30+
runOperations(db);
31+
32+
cst.assertNextChangesEqual({cursor, expectedChanges});
33+
cst.cleanUp();
34+
}
35+
36+
// Pipeline using a string literal to filter on the entire namespace.
37+
runTest(testDB,
38+
kCollName,
39+
(db) => {
40+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
41+
assert.commandWorked(db[kCollName].updateOne({_id: "test"}, {$set: {updated: true}}));
42+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
43+
},
44+
[
45+
{$match: {"ns": {db: kDBName, coll: kCollName}}},
46+
],
47+
[
48+
{
49+
operationType: "insert",
50+
fullDocument: {_id: "test"},
51+
ns: {db: kDBName, coll: kCollName},
52+
documentKey: {_id: "test"},
53+
},
54+
{
55+
operationType: "update",
56+
ns: {db: kDBName, coll: kCollName},
57+
documentKey: {_id: "test"},
58+
updateDescription: {
59+
updatedFields: {
60+
updated: true,
61+
},
62+
removedFields: [],
63+
truncatedArrays: [],
64+
},
65+
},
66+
{
67+
operationType: "delete",
68+
ns: {db: kDBName, coll: kCollName},
69+
documentKey: {_id: "test"},
70+
},
71+
]);
72+
73+
// Pipeline using a string literal to filter on the database name.
74+
runTest(testDB,
75+
kCollName,
76+
(db) => {
77+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
78+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
79+
},
80+
[
81+
{$match: {"ns.db": kDBName}},
82+
],
83+
[
84+
{
85+
operationType: "insert",
86+
fullDocument: {_id: "test"},
87+
ns: {db: kDBName, coll: kCollName},
88+
documentKey: {_id: "test"},
89+
},
90+
{
91+
operationType: "delete",
92+
ns: {db: kDBName, coll: kCollName},
93+
documentKey: {_id: "test"},
94+
},
95+
]);
96+
97+
// Pipeline using a regular expression to filter on the database name.
98+
runTest(testDB,
99+
kCollName,
100+
(db) => {
101+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
102+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
103+
},
104+
[
105+
{$match: {"ns.db": new RegExp(RegExp.escape(kDBName))}},
106+
],
107+
[
108+
{
109+
operationType: "insert",
110+
fullDocument: {_id: "test"},
111+
ns: {db: kDBName, coll: kCollName},
112+
documentKey: {_id: "test"},
113+
},
114+
{
115+
operationType: "delete",
116+
ns: {db: kDBName, coll: kCollName},
117+
documentKey: {_id: "test"},
118+
},
119+
]);
120+
121+
// Pipeline using a regular expression to filter on the database name.
122+
runTest(testDB,
123+
kCollName,
124+
(db) => {
125+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
126+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
127+
},
128+
[
129+
{$match: {"ns.db": new RegExp(RegExp.escape(kDBName) + "[^'\"\\ {}]*")}},
130+
],
131+
[
132+
{
133+
operationType: "insert",
134+
fullDocument: {_id: "test"},
135+
ns: {db: kDBName, coll: kCollName},
136+
documentKey: {_id: "test"},
137+
},
138+
{
139+
operationType: "delete",
140+
ns: {db: kDBName, coll: kCollName},
141+
documentKey: {_id: "test"},
142+
},
143+
]);
144+
145+
// Pipeline using a literal string to filter on the collection name.
146+
runTest(testDB,
147+
kCollName,
148+
(db) => {
149+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
150+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
151+
},
152+
[
153+
{$match: {"ns.coll": kCollName}},
154+
],
155+
[
156+
{
157+
operationType: "insert",
158+
fullDocument: {_id: "test"},
159+
ns: {db: kDBName, coll: kCollName},
160+
documentKey: {_id: "test"},
161+
},
162+
{
163+
operationType: "delete",
164+
ns: {db: kDBName, coll: kCollName},
165+
documentKey: {_id: "test"},
166+
},
167+
]);
168+
169+
// Pipeline using a regular expression to filter on the collection name.
170+
runTest(testDB,
171+
kCollName,
172+
(db) => {
173+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
174+
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
175+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
176+
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
177+
},
178+
[
179+
{$match: {"ns.coll": new RegExp(RegExp.escape(kCollName))}},
180+
],
181+
[
182+
{
183+
operationType: "insert",
184+
fullDocument: {_id: "test"},
185+
ns: {db: kDBName, coll: kCollName},
186+
documentKey: {_id: "test"},
187+
},
188+
{
189+
operationType: "delete",
190+
ns: {db: kDBName, coll: kCollName},
191+
documentKey: {_id: "test"},
192+
},
193+
]);
194+
195+
// Pipeline using a regular expression to filter on the collection name.
196+
runTest(testDB,
197+
kCollName,
198+
(db) => {
199+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
200+
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
201+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
202+
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
203+
},
204+
[
205+
{$match: {"ns.coll": new RegExp("^" + RegExp.escape(kCollName) + "[a-zA-Z]*")}},
206+
],
207+
[
208+
{
209+
operationType: "insert",
210+
fullDocument: {_id: "test"},
211+
ns: {db: kDBName, coll: kCollName},
212+
documentKey: {_id: "test"},
213+
},
214+
{
215+
operationType: "delete",
216+
ns: {db: kDBName, coll: kCollName},
217+
documentKey: {_id: "test"},
218+
},
219+
]);
220+
221+
runTest(testDB,
222+
kCollName,
223+
(db) => {
224+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
225+
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
226+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
227+
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
228+
},
229+
[
230+
{$match: {"ns.coll": new RegExp(RegExp.escape(kCollName) + "[^'\"\\ {}]*")}},
231+
],
232+
[
233+
{
234+
operationType: "insert",
235+
fullDocument: {_id: "test"},
236+
ns: {db: kDBName, coll: kCollName},
237+
documentKey: {_id: "test"},
238+
},
239+
{
240+
operationType: "delete",
241+
ns: {db: kDBName, coll: kCollName},
242+
documentKey: {_id: "test"},
243+
},
244+
]);
245+
246+
// Pipeline using a regular expression to filter on multiple collections.
247+
runTest(testDB,
248+
1 /* Use entire database */,
249+
(db) => {
250+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
251+
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
252+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
253+
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
254+
},
255+
[
256+
{
257+
$match: {
258+
"ns.coll": new RegExp("^(" + RegExp.escape(kCollName) + "|" +
259+
RegExp.escape(kCollNameOther) + ")$")
260+
}
261+
},
262+
],
263+
[
264+
{
265+
operationType: "insert",
266+
fullDocument: {_id: "test"},
267+
ns: {db: kDBName, coll: kCollName},
268+
documentKey: {_id: "test"},
269+
},
270+
{
271+
operationType: "insert",
272+
fullDocument: {_id: "test"},
273+
ns: {db: kDBName, coll: kCollNameOther},
274+
documentKey: {_id: "test"},
275+
},
276+
{
277+
operationType: "delete",
278+
ns: {db: kDBName, coll: kCollName},
279+
documentKey: {_id: "test"},
280+
},
281+
{
282+
operationType: "delete",
283+
ns: {db: kDBName, coll: kCollNameOther},
284+
documentKey: {_id: "test"},
285+
},
286+
]);
287+
288+
// Pipeline using a regular expression to filter on multiple collections.
289+
runTest(testDB,
290+
1 /* Use entire database */,
291+
(db) => {
292+
assert.commandWorked(db[kCollName].insert({_id: "test"}));
293+
assert.commandWorked(db[kCollNameOther].insert({_id: "test"}));
294+
assert.commandWorked(db[kCollName].deleteOne({_id: "test"}));
295+
assert.commandWorked(db[kCollNameOther].deleteOne({_id: "test"}));
296+
},
297+
[
298+
{
299+
$match: {
300+
"ns.coll": {
301+
$in: [
302+
new RegExp("^" + RegExp.escape(kCollName) + "$"),
303+
new RegExp("^" + RegExp.escape(kCollNameOther) + "$"),
304+
],
305+
}
306+
}
307+
},
308+
],
309+
[
310+
{
311+
operationType: "insert",
312+
fullDocument: {_id: "test"},
313+
ns: {db: kDBName, coll: kCollName},
314+
documentKey: {_id: "test"},
315+
},
316+
{
317+
operationType: "insert",
318+
fullDocument: {_id: "test"},
319+
ns: {db: kDBName, coll: kCollNameOther},
320+
documentKey: {_id: "test"},
321+
},
322+
{
323+
operationType: "delete",
324+
ns: {db: kDBName, coll: kCollName},
325+
documentKey: {_id: "test"},
326+
},
327+
{
328+
operationType: "delete",
329+
ns: {db: kDBName, coll: kCollNameOther},
330+
documentKey: {_id: "test"},
331+
},
332+
]);
333+
334+
assert.commandWorked(testDB.dropDatabase());
335+
})();

0 commit comments

Comments
 (0)