Skip to content

Commit ced67a1

Browse files
authored
CSHARP-3399: Mitigate pain of using field names with dots and dollars. (#568)
1 parent 806a0d4 commit ced67a1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+753
-1279
lines changed

src/MongoDB.Bson/IO/NoOpElementNameValidator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
*/
1515

1616

17+
using System;
18+
1719
namespace MongoDB.Bson.IO
1820
{
1921
/// <summary>
@@ -54,6 +56,10 @@ public IElementNameValidator GetValidatorForChildContent(string elementName)
5456
/// <returns>True if the element name is valid.</returns>
5557
public bool IsValidElementName(string elementName)
5658
{
59+
if (elementName == null)
60+
{
61+
throw new ArgumentNullException(nameof(elementName), "Value cannot be null.");
62+
}
5763
return true;
5864
}
5965
}

src/MongoDB.Driver.Core/Core/Operations/ElementNameValidators/ElementNameValidatorFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public static IElementNameValidator ForUpdateType(UpdateType updateType)
3232
switch (updateType)
3333
{
3434
case UpdateType.Replacement:
35-
return CollectionElementNameValidator.Instance;
35+
return ReplacementElementNameValidator.Instance;
3636
case UpdateType.Update:
3737
return UpdateElementNameValidator.Instance;
3838
default:
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2013-present MongoDB Inc.
1+
/* Copyright 2021-present MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -13,29 +13,27 @@
1313
* limitations under the License.
1414
*/
1515

16-
using System.Collections.Generic;
1716
using MongoDB.Bson.IO;
1817
using MongoDB.Driver.Core.Misc;
1918

2019
namespace MongoDB.Driver.Core.Operations.ElementNameValidators
2120
{
2221
/// <summary>
23-
/// Represents an element name validator that checks that element names are valid for MongoDB collections.
22+
/// Represents an element name validator for replace operations.
2423
/// </summary>
25-
public class CollectionElementNameValidator : IElementNameValidator
24+
public class ReplacementElementNameValidator : IElementNameValidator
2625
{
2726
// private static fields
28-
private static readonly List<string> __exceptions = new List<string> { "$db", "$ref", "$id", "$code", "$scope" };
29-
private static readonly CollectionElementNameValidator __instance = new CollectionElementNameValidator();
27+
private static readonly ReplacementElementNameValidator __instance = new ReplacementElementNameValidator();
3028

3129
// public static fields
3230
/// <summary>
33-
/// Gets a pre-created instance of a CollectionElementNameValidator.
31+
/// Gets a pre-created instance of an ReplacementElementNameValidator.
3432
/// </summary>
3533
/// <value>
3634
/// The pre-created instance.
3735
/// </value>
38-
public static CollectionElementNameValidator Instance
36+
public static ReplacementElementNameValidator Instance
3937
{
4038
get { return __instance; }
4139
}
@@ -44,31 +42,14 @@ public static CollectionElementNameValidator Instance
4442
/// <inheritdoc/>
4543
public IElementNameValidator GetValidatorForChildContent(string elementName)
4644
{
47-
return this;
45+
return NoOpElementNameValidator.Instance;
4846
}
4947

5048
/// <inheritdoc/>
5149
public bool IsValidElementName(string elementName)
5250
{
5351
Ensure.IsNotNull(elementName, nameof(elementName));
54-
55-
if (elementName.Length == 0)
56-
{
57-
// the server seems to allow empty element names, but in 1.x we did not
58-
return false;
59-
}
60-
61-
if (elementName.IndexOf('.') != -1)
62-
{
63-
return false;
64-
}
65-
66-
if (elementName[0] == '$' && !__exceptions.Contains(elementName))
67-
{
68-
return false;
69-
}
70-
71-
return true;
52+
return elementName.Length > 0 && elementName[0] != '$';
7253
}
7354
}
7455
}

src/MongoDB.Driver.Core/Core/Operations/ElementNameValidators/UpdateElementNameValidator.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using MongoDB.Bson.IO;
17+
using MongoDB.Driver.Core.Misc;
1718

1819
namespace MongoDB.Driver.Core.Operations.ElementNameValidators
1920
{
@@ -47,6 +48,7 @@ public IElementNameValidator GetValidatorForChildContent(string elementName)
4748
/// <inheritdoc/>
4849
public bool IsValidElementName(string elementName)
4950
{
51+
Ensure.IsNotNull(elementName, nameof(elementName));
5052
return elementName.Length > 0 && elementName[0] == '$';
5153
}
5254
}

src/MongoDB.Driver.Core/Core/Operations/ElementNameValidators/UpdateOrReplacementElementNameValidator.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,17 @@ public IElementNameValidator GetValidatorForChildContent(string elementName)
4444
/// <inheritdoc/>
4545
public bool IsValidElementName(string elementName)
4646
{
47+
Ensure.IsNotNull(elementName, nameof(elementName));
4748
// the first elementName we see determines whether we are validating an update or a replacement document
4849
if (_chosenValidator == null)
4950
{
5051
if (elementName.Length > 0 && elementName[0] == '$')
5152
{
52-
_chosenValidator = UpdateElementNameValidator.Instance; ;
53+
_chosenValidator = UpdateElementNameValidator.Instance;
5354
}
5455
else
5556
{
56-
_chosenValidator = CollectionElementNameValidator.Instance;
57+
_chosenValidator = ReplacementElementNameValidator.Instance;
5758
}
5859
}
5960
return _chosenValidator.IsValidElementName(elementName);

src/MongoDB.Driver.Core/Core/Operations/FindOneAndReplaceOperation.cs

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -202,27 +202,7 @@ internal override BsonDocument CreateCommand(ICoreSessionHandle session, Connect
202202
/// <inheritdoc/>
203203
protected override IElementNameValidator GetCommandValidator()
204204
{
205-
return Validator.Instance;
206-
}
207-
208-
private class Validator : IElementNameValidator
209-
{
210-
public readonly static Validator Instance = new Validator();
211-
212-
public IElementNameValidator GetValidatorForChildContent(string elementName)
213-
{
214-
if (elementName == "update")
215-
{
216-
return CollectionElementNameValidator.Instance;
217-
}
218-
219-
return NoOpElementNameValidator.Instance;
220-
}
221-
222-
public bool IsValidElementName(string elementName)
223-
{
224-
return true;
225-
}
205+
return NoOpElementNameValidator.Instance;
226206
}
227207
}
228208
}

src/MongoDB.Driver.Core/Core/Operations/FindOneAndUpdateOperation.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ public IElementNameValidator GetValidatorForChildContent(string elementName)
268268

269269
public bool IsValidElementName(string elementName)
270270
{
271+
Ensure.IsNotNull(elementName, nameof(elementName));
271272
return true;
272273
}
273274
}

src/MongoDB.Driver.Core/Core/Operations/RetryableInsertCommandOperation.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
using MongoDB.Driver.Core.Bindings;
2323
using MongoDB.Driver.Core.Connections;
2424
using MongoDB.Driver.Core.Misc;
25-
using MongoDB.Driver.Core.Operations.ElementNameValidators;
2625
using MongoDB.Driver.Core.WireProtocol.Messages;
2726
using MongoDB.Driver.Core.WireProtocol.Messages.Encoders;
2827

@@ -133,8 +132,8 @@ protected override IEnumerable<Type1CommandMessageSection> CreateCommandPayloads
133132
{
134133
documents = new BatchableSource<TDocument>(_documents.Items, _documents.Offset, _documents.ProcessedCount, canBeSplit: false);
135134
}
136-
var isSystemIndexesCollection = _collectionNamespace.Equals(CollectionNamespace.DatabaseNamespace.SystemIndexesCollection);
137-
var elementNameValidator = isSystemIndexesCollection ? (IElementNameValidator)NoOpElementNameValidator.Instance : CollectionElementNameValidator.Instance;
135+
136+
var elementNameValidator = NoOpElementNameValidator.Instance;
138137
var maxBatchCount = Math.Min(MaxBatchCount ?? int.MaxValue, channel.ConnectionDescription.MaxBatchCount);
139138
var maxDocumentSize = channel.ConnectionDescription.MaxDocumentSize;
140139
var payload = new Type1CommandMessageSection<TDocument>("documents", documents, _documentSerializer, elementNameValidator, maxBatchCount, maxDocumentSize);

src/MongoDB.Driver.Core/Core/WireProtocol/Messages/Encoders/BinaryEncoders/InsertMessageBinaryEncoder.cs

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -142,49 +142,37 @@ private void WriteDocuments(BsonBinaryWriter writer, long messageStartPosition,
142142
var stream = writer.BsonStream;
143143
var context = BsonSerializationContext.CreateRoot(writer);
144144

145-
var collectionNamespace = message.CollectionNamespace;
146-
var isSystemIndexesCollection = collectionNamespace.Equals(collectionNamespace.DatabaseNamespace.SystemIndexesCollection);
147-
var elementNameValidator = isSystemIndexesCollection ? (IElementNameValidator)NoOpElementNameValidator.Instance : CollectionElementNameValidator.Instance;
148-
149-
writer.PushElementNameValidator(elementNameValidator);
150-
try
145+
var documentSource = message.DocumentSource;
146+
var batchCount = Math.Min(documentSource.Count, message.MaxBatchCount);
147+
if (batchCount < documentSource.Count && !documentSource.CanBeSplit)
151148
{
152-
var documentSource = message.DocumentSource;
153-
var batchCount = Math.Min(documentSource.Count, message.MaxBatchCount);
154-
if (batchCount < documentSource.Count && !documentSource.CanBeSplit)
155-
{
156-
throw new BsonSerializationException("Batch is too large.");
157-
}
149+
throw new BsonSerializationException("Batch is too large.");
150+
}
158151

159-
for (var i = 0; i < batchCount; i++)
160-
{
161-
var document = documentSource.Items[documentSource.Offset + i];
162-
var documentStartPosition = stream.Position;
152+
for (var i = 0; i < batchCount; i++)
153+
{
154+
var document = documentSource.Items[documentSource.Offset + i];
155+
var documentStartPosition = stream.Position;
163156

164-
_serializer.Serialize(context, document);
157+
_serializer.Serialize(context, document);
165158

166-
var messageSize = stream.Position - messageStartPosition;
167-
if (messageSize > message.MaxMessageSize)
159+
var messageSize = stream.Position - messageStartPosition;
160+
if (messageSize > message.MaxMessageSize)
161+
{
162+
if (i > 0 && documentSource.CanBeSplit)
163+
{
164+
stream.Position = documentStartPosition;
165+
stream.SetLength(documentStartPosition);
166+
documentSource.SetProcessedCount(i);
167+
return;
168+
}
169+
else
168170
{
169-
if (i > 0 && documentSource.CanBeSplit)
170-
{
171-
stream.Position = documentStartPosition;
172-
stream.SetLength(documentStartPosition);
173-
documentSource.SetProcessedCount(i);
174-
return;
175-
}
176-
else
177-
{
178-
throw new BsonSerializationException("Batch is too large.");
179-
}
171+
throw new BsonSerializationException("Batch is too large.");
180172
}
181173
}
182-
documentSource.SetProcessedCount(batchCount);
183-
}
184-
finally
185-
{
186-
writer.PopElementNameValidator();
187174
}
175+
documentSource.SetProcessedCount(batchCount);
188176
}
189177

190178
// explicit interface implementations

tests/MongoDB.Driver.Legacy.Tests/Jira/CSharp253Tests.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,6 @@ public void TestInsertClass()
4949
_collection.Insert(c);
5050
}
5151

52-
[Fact]
53-
public void TestInsertDollar()
54-
{
55-
Assert.Throws<BsonSerializationException>(() => { _collection.Insert(new BsonDocument("$x", 1)); });
56-
Assert.Throws<BsonSerializationException>(() => { _collection.Insert(new BsonDocument("x", new BsonDocument("$x", 1))); });
57-
}
58-
59-
[Fact]
60-
public void TestInsertPeriod()
61-
{
62-
Assert.Throws<BsonSerializationException>(() => { _collection.Insert(new BsonDocument("a.b", 1)); });
63-
Assert.Throws<BsonSerializationException>(() => { _collection.Insert(new BsonDocument("a", new BsonDocument("b.c", 1))); });
64-
}
65-
6652
[Fact]
6753
public void TestLegacyDollar()
6854
{

0 commit comments

Comments
 (0)