Skip to content

Commit bbe07bb

Browse files
author
Robert Stam
committed
Implemented CSHARP-428. Added IsDatabaseNameValid to MongoServer and IsCollectionNameValid to MongoDatabase.
1 parent a389dc6 commit bbe07bb

File tree

8 files changed

+172
-59
lines changed

8 files changed

+172
-59
lines changed

Driver/Core/MongoCollection.cs

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,20 @@ public abstract class MongoCollection
4949
/// <param name="settings">The settings to use to access this collection.</param>
5050
protected MongoCollection(MongoDatabase database, MongoCollectionSettings settings)
5151
{
52-
ValidateCollectionName(settings.CollectionName);
52+
if (database == null)
53+
{
54+
throw new ArgumentNullException("database");
55+
}
56+
if (settings == null)
57+
{
58+
throw new ArgumentNullException("settings");
59+
}
60+
string message;
61+
if (!database.IsCollectionNameValid(settings.CollectionName, out message))
62+
{
63+
throw new ArgumentOutOfRangeException("settings", message);
64+
}
65+
5366
_server = database.Server;
5467
_database = database;
5568
_settings = settings.FrozenCopy();
@@ -1596,20 +1609,6 @@ private string GetIndexName(string[] keyNames)
15961609
}
15971610
return sb.ToString();
15981611
}
1599-
1600-
internal static void ValidateCollectionName(string name)
1601-
{
1602-
if (name == null)
1603-
{
1604-
throw new ArgumentNullException("name");
1605-
}
1606-
if (name == "" ||
1607-
name.IndexOf('\0') != -1 ||
1608-
Encoding.UTF8.GetBytes(name).Length > 121)
1609-
{
1610-
throw new ArgumentException("Invalid collection name", "name");
1611-
}
1612-
}
16131612
}
16141613

16151614
// this subclass provides a default document type for Find methods

Driver/Core/MongoDatabase.cs

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ namespace MongoDB.Driver
3232
/// </summary>
3333
public class MongoDatabase
3434
{
35-
// private static fields
36-
private static HashSet<char> __invalidDatabaseNameChars;
37-
3835
// private fields
3936
private object _databaseLock = new object();
4037
private MongoServer _server;
@@ -44,15 +41,6 @@ public class MongoDatabase
4441
private MongoCollection<BsonDocument> _commandCollection;
4542
private MongoGridFS _gridFS;
4643

47-
// static constructor
48-
static MongoDatabase()
49-
{
50-
// MongoDB itself prohibits some characters and the rest are prohibited by the Windows restrictions on filenames
51-
__invalidDatabaseNameChars = new HashSet<char>() { '\0', ' ', '.', '$', '/', '\\' };
52-
foreach (var c in Path.GetInvalidPathChars()) { __invalidDatabaseNameChars.Add(c); }
53-
foreach (var c in Path.GetInvalidFileNameChars()) { __invalidDatabaseNameChars.Add(c); }
54-
}
55-
5644
// constructors
5745
/// <summary>
5846
/// Creates a new instance of MongoDatabase. Normally you would call one of the indexers or GetDatabase methods
@@ -62,7 +50,20 @@ static MongoDatabase()
6250
/// <param name="settings">The settings to use to access this database.</param>
6351
public MongoDatabase(MongoServer server, MongoDatabaseSettings settings)
6452
{
65-
ValidateDatabaseName(settings.DatabaseName);
53+
if (server == null)
54+
{
55+
throw new ArgumentNullException("server");
56+
}
57+
if (settings == null)
58+
{
59+
throw new ArgumentNullException("settings");
60+
}
61+
string message;
62+
if (!server.IsDatabaseNameValid(settings.DatabaseName, out message))
63+
{
64+
throw new ArgumentOutOfRangeException(message);
65+
}
66+
6667
_server = server;
6768
_settings = settings.FrozenCopy();
6869
_name = settings.DatabaseName;
@@ -735,6 +736,41 @@ public virtual DatabaseStatsResult GetStats()
735736
return RunCommandAs<DatabaseStatsResult>("dbstats");
736737
}
737738

739+
/// <summary>
740+
/// Checks whether a given collection name is valid in this database.
741+
/// </summary>
742+
/// <param name="collectionName">The collection name.</param>
743+
/// <param name="message">An error message if the collection name is not valid.</param>
744+
/// <returns>True if the collection name is valid; otherwise, false.</returns>
745+
public virtual bool IsCollectionNameValid(string collectionName, out string message)
746+
{
747+
if (collectionName == null)
748+
{
749+
throw new ArgumentNullException("collectionName");
750+
}
751+
752+
if (collectionName == "")
753+
{
754+
message = "Collection name cannot be empty.";
755+
return false;
756+
}
757+
758+
if (collectionName.IndexOf('\0') != -1)
759+
{
760+
message = "Collection name cannot contain null characters.";
761+
return false;
762+
}
763+
764+
if (Encoding.UTF8.GetBytes(collectionName).Length > 121)
765+
{
766+
message = "Collection name cannot exceed 121 bytes (after encoding to UTF-8).";
767+
return false;
768+
}
769+
770+
message = null;
771+
return true;
772+
}
773+
738774
// TODO: mongo shell has IsMaster at database level?
739775

740776
/// <summary>
@@ -794,7 +830,20 @@ public virtual CommandResult RenameCollection(
794830
bool dropTarget,
795831
MongoCredentials adminCredentials)
796832
{
797-
MongoCollection.ValidateCollectionName(newCollectionName);
833+
if (oldCollectionName == null)
834+
{
835+
throw new ArgumentNullException("oldCollectionName");
836+
}
837+
if (newCollectionName == null)
838+
{
839+
throw new ArgumentNullException("newCollectionName");
840+
}
841+
string message;
842+
if (!IsCollectionNameValid(newCollectionName, out message))
843+
{
844+
throw new ArgumentOutOfRangeException("newCollectionName", message);
845+
}
846+
798847
var command = new CommandDocument
799848
{
800849
{ "renameCollection", string.Format("{0}.{1}", _name, oldCollectionName) },
@@ -980,33 +1029,5 @@ public override string ToString()
9801029
{
9811030
return _name;
9821031
}
983-
984-
// private methods
985-
private void ValidateDatabaseName(string name)
986-
{
987-
if (name == null)
988-
{
989-
throw new ArgumentNullException("name");
990-
}
991-
if (name == "")
992-
{
993-
throw new ArgumentException("Database name is empty.");
994-
}
995-
foreach (var c in name)
996-
{
997-
if (__invalidDatabaseNameChars.Contains(c))
998-
{
999-
var bytes = new byte[] { (byte)((int)c >> 8), (byte)((int)c & 255) };
1000-
var hex = BsonUtils.ToHexString(bytes);
1001-
var message = string.Format("Database name '{0}' is not valid. The character 0x{1} '{2}' is not allowed in database names.", name, hex, c);
1002-
throw new ArgumentException(message);
1003-
}
1004-
}
1005-
if (Encoding.UTF8.GetBytes(name).Length > 64)
1006-
{
1007-
var message = string.Format("Database name '{0}' exceeds 64 bytes (after encoding to UTF8).", name);
1008-
throw new ArgumentException(message);
1009-
}
1010-
}
10111032
}
10121033
}

Driver/Core/MongoServer.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class MongoServer
3737
private static Dictionary<MongoServerSettings, MongoServer> __servers = new Dictionary<MongoServerSettings, MongoServer>();
3838
private static int __nextSequentialId;
3939
private static int __maxServerCount = 100;
40+
private static HashSet<char> __invalidDatabaseNameChars;
4041

4142
// private fields
4243
private object _serverLock = new object();
@@ -56,6 +57,12 @@ public class MongoServer
5657
// static constructor
5758
static MongoServer()
5859
{
60+
// MongoDB itself prohibits some characters and the rest are prohibited by the Windows restrictions on filenames
61+
// the C# driver checks that the database name is valid on any of the supported platforms
62+
__invalidDatabaseNameChars = new HashSet<char>() { '\0', ' ', '.', '$', '/', '\\' };
63+
foreach (var c in Path.GetInvalidPathChars()) { __invalidDatabaseNameChars.Add(c); }
64+
foreach (var c in Path.GetInvalidFileNameChars()) { __invalidDatabaseNameChars.Add(c); }
65+
5966
BsonSerializer.RegisterSerializer(typeof(MongoDBRef), new MongoDBRefSerializer());
6067
BsonSerializer.RegisterSerializer(typeof(SystemProfileInfo), new SystemProfileInfoSerializer());
6168
}
@@ -915,6 +922,46 @@ public virtual GetLastErrorResult GetLastError(MongoCredentials adminCredentials
915922
return adminDatabase.GetLastError();
916923
}
917924

925+
/// <summary>
926+
/// Checks whether a given database name is valid on this server.
927+
/// </summary>
928+
/// <param name="databaseName">The database name.</param>
929+
/// <param name="message">An error message if the database name is not valid.</param>
930+
/// <returns>True if the database name is valid; otherwise, false.</returns>
931+
public virtual bool IsDatabaseNameValid(string databaseName, out string message)
932+
{
933+
if (databaseName == null)
934+
{
935+
throw new ArgumentNullException("databaseName");
936+
}
937+
938+
if (databaseName == "")
939+
{
940+
message = "Database name is empty.";
941+
return false;
942+
}
943+
944+
foreach (var c in databaseName)
945+
{
946+
if (__invalidDatabaseNameChars.Contains(c))
947+
{
948+
var bytes = new byte[] { (byte)((int)c >> 8), (byte)((int)c & 255) };
949+
var hex = BsonUtils.ToHexString(bytes);
950+
message = string.Format("Database name '{0}' is not valid. The character 0x{1} '{2}' is not allowed in database names.", databaseName, hex, c);
951+
return false;
952+
}
953+
}
954+
955+
if (Encoding.UTF8.GetBytes(databaseName).Length > 64)
956+
{
957+
message = string.Format("Database name '{0}' exceeds 64 bytes (after encoding to UTF8).", databaseName);
958+
return false;
959+
}
960+
961+
message = null;
962+
return true;
963+
}
964+
918965
/// <summary>
919966
/// Checks whether the server is alive (throws an exception if not). If server is a replica set, pings all members one at a time.
920967
/// </summary>

DriverUnitTests/Core/MongoCollectionTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ public void Setup()
5050

5151
// TODO: more tests for MongoCollection
5252

53+
[Test]
54+
public void TestConstructorArgumentChecking()
55+
{
56+
var settings = new MongoCollectionSettings<BsonDocument>(_database, "");
57+
Assert.Throws<ArgumentNullException>(() => { new MongoCollection<BsonDocument>(null, settings); });
58+
Assert.Throws<ArgumentNullException>(() => { new MongoCollection<BsonDocument>(_database, null); });
59+
Assert.Throws<ArgumentOutOfRangeException>(() => { new MongoCollection<BsonDocument>(_database, settings); });
60+
}
61+
5362
[Test]
5463
public void TestCountZero()
5564
{

DriverUnitTests/Core/MongoDatabaseTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,15 @@ public void TestCollectionExists()
5252
Assert.IsTrue(_database.CollectionExists(collectionName));
5353
}
5454

55+
[Test]
56+
public void TestConstructorArgumentChecking()
57+
{
58+
var settings = new MongoDatabaseSettings(_server, "");
59+
Assert.Throws<ArgumentNullException>(() => { new MongoDatabase(null, settings); });
60+
Assert.Throws<ArgumentNullException>(() => { new MongoDatabase(_server, null); });
61+
Assert.Throws<ArgumentOutOfRangeException>(() => { new MongoDatabase(_server, settings); });
62+
}
63+
5564
[Test]
5665
public void TestCreateCollection()
5766
{
@@ -174,6 +183,16 @@ public void TestGetProfilingInfo()
174183
Assert.IsTrue(info.Duration >= TimeSpan.Zero);
175184
}
176185

186+
[Test]
187+
public void TestIsCollectionNameValid()
188+
{
189+
string message;
190+
Assert.Throws<ArgumentNullException>(() => { _database.IsCollectionNameValid(null, out message); });
191+
Assert.IsFalse(_database.IsCollectionNameValid("", out message));
192+
Assert.IsFalse(_database.IsCollectionNameValid("a\0b", out message));
193+
Assert.IsFalse(_database.IsCollectionNameValid(new string('x', 128), out message));
194+
}
195+
177196
[Test]
178197
public void TestRenameCollection()
179198
{
@@ -191,6 +210,14 @@ public void TestRenameCollection()
191210
Assert.IsTrue(_database.CollectionExists(collectionName2));
192211
}
193212

213+
[Test]
214+
public void TestRenameCollectionArgumentChecking()
215+
{
216+
Assert.Throws<ArgumentNullException>(() => { _database.RenameCollection(null, "new"); });
217+
Assert.Throws<ArgumentNullException>(() => { _database.RenameCollection("old", null); });
218+
Assert.Throws<ArgumentOutOfRangeException>(() => { _database.RenameCollection("old", ""); });
219+
}
220+
194221
[Test]
195222
public void TestRenameCollectionDropTarget()
196223
{

DriverUnitTests/Core/MongoServerTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ public void TestGetDatabaseNames()
100100
var databaseNames = _server.GetDatabaseNames();
101101
}
102102

103+
[Test]
104+
public void TestIsDatabaseNameValid()
105+
{
106+
string message;
107+
Assert.Throws<ArgumentNullException>(() => { _server.IsDatabaseNameValid(null, out message); });
108+
Assert.IsFalse(_server.IsDatabaseNameValid("", out message));
109+
Assert.IsFalse(_server.IsDatabaseNameValid("/", out message));
110+
Assert.IsFalse(_server.IsDatabaseNameValid(new string('x', 128), out message));
111+
}
112+
103113
[Test]
104114
public void TestReconnect()
105115
{

DriverUnitTests/Jira/CSharp325Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public void TestValidateDatabaseName()
4747
foreach (var c in invalidChars)
4848
{
4949
var databaseName = new string(new char[] { 'x', c });
50-
Assert.Throws<ArgumentException>(() => { var database = _server[databaseName]; });
50+
Assert.Throws<ArgumentOutOfRangeException>(() => { var database = _server[databaseName]; });
5151
}
5252
}
5353
}

DriverUnitTests/Jira/CSharp361Tests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public void TestInsertUpdateAndSaveWithElementNameStartingWithDollarSign()
4141
collection.Drop();
4242

4343
collection.Insert(new BsonDocument("_id", 1));
44-
Assert.Throws<ArgumentException>(() => { database.RenameCollection("test", ""); });
44+
Assert.Throws<ArgumentOutOfRangeException>(() => { database.RenameCollection("test", ""); });
4545
}
4646
}
4747
}

0 commit comments

Comments
 (0)