From be05efc39ca5b8b549886afe44261f318559595e Mon Sep 17 00:00:00 2001 From: Nikola Irinchev Date: Fri, 21 Jun 2024 17:05:19 +0200 Subject: [PATCH] RNET-1155: Add more validation to .Filter arguments (#3620) * Add more validation to .Filter arguments * Fix expected exception type --- CHANGELOG.md | 1 + Realm/Realm/DatabaseTypes/QueryArgument.cs | 14 +++++++-- Tests/Realm.Tests/Database/CollectionTests.cs | 31 ++++++++++++++++++- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e190dc9c9f..2ec4e04185 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Fixed * A `ForCurrentlyOutstandingWork` progress notifier would not immediately call its callback after registration. Instead you would have to wait for some data to be received to get your first update - if you were already caught up when you registered the notifier you could end up waiting a long time for the server to deliver a download that would call/expire your notifier. (Core 14.8.0) * After compacting, a file upgrade would be triggered. This could cause loss of data if `ShouldDeleteIfMigrationNeeded` is set to `true`. (Issue [#3583](https://github.com/realm/realm-dotnet/issues/3583), Core 14.9.0) +* Passing in a deleted object as a substitution argument to `.Filter()` would throw a confusing error with a message starting with `invalid RQL for table`. It now throws a more descriptive error instead. (Issue [#3619](https://github.com/realm/realm-dotnet/issues/3619)) ### Compatibility * Realm Studio: 15.0.0 or later. diff --git a/Realm/Realm/DatabaseTypes/QueryArgument.cs b/Realm/Realm/DatabaseTypes/QueryArgument.cs index d92e1c4958..8c954ee9a3 100644 --- a/Realm/Realm/DatabaseTypes/QueryArgument.cs +++ b/Realm/Realm/DatabaseTypes/QueryArgument.cs @@ -19,7 +19,6 @@ using System; using System.Linq; using MongoDB.Bson; -using Realms.Exceptions; using Realms.Native; namespace Realms @@ -318,9 +317,18 @@ private QueryArgument(RealmValue? realmValue = null, GeoShapeBase? geoValue = nu if (RealmValue != null) { var primitive = RealmValue.Value; - if (primitive.Type == RealmValueType.Object && !primitive.AsIRealmObject().IsManaged) + if (primitive.Type == RealmValueType.Object) { - throw new RealmException("Can't use unmanaged object as argument of Filter"); + var obj = primitive.AsIRealmObject(); + if (!obj.IsManaged) + { + throw new ArgumentException("Can't use unmanaged object as argument of Filter"); + } + + if (!obj.IsValid) + { + throw new ArgumentException("Can't use removed object as argument of Filter"); + } } var (primitiveValue, handles) = primitive.ToNative(); diff --git a/Tests/Realm.Tests/Database/CollectionTests.cs b/Tests/Realm.Tests/Database/CollectionTests.cs index d375801a63..8f518c1cce 100644 --- a/Tests/Realm.Tests/Database/CollectionTests.cs +++ b/Tests/Realm.Tests/Database/CollectionTests.cs @@ -1120,6 +1120,35 @@ public void Results_GetFiltered_SanityTest() Assert.That(query.ToArray().All(i => i.Int >= 5)); } + [Test] + public void Results_Filter_WhenArgumentIsDeletedObject_Throws() + { + var targetObj = _realm.Write(() => + { + var intPropertyObject = _realm.Add(new IntPropertyObject()); + + for (var i = 0; i < 10; i++) + { + _realm.Add(new ObjectWithObjectProperties + { + StandaloneObject = i % 2 == 0 ? intPropertyObject : null + }); + } + + return intPropertyObject; + }); + + var query = _realm.All().Filter($"{nameof(ObjectWithObjectProperties.StandaloneObject)} = $0", targetObj); + Assert.That(query.Count(), Is.EqualTo(5)); + + _realm.Write(() => + { + _realm.Remove(targetObj); + }); + + Assert.Throws(() => _realm.All().Filter($"{nameof(ObjectWithObjectProperties.StandaloneObject)} = $0", targetObj)); + } + public readonly struct StringQueryNumericData { public readonly string PropertyName; @@ -1375,7 +1404,7 @@ public void QueryFilter_WithArgumentsUnmanagedObjects_ShouldThrow() _realm.Add(new Owner { TopDog = new Dog { Name = "Doge", Color = "almost yellow", Vaccinated = true } }); }); - Assert.Throws(() => _realm.All().Filter("TopDog = $0", new Dog { Name = "Doge", Color = "almost yellow", Vaccinated = true })); + Assert.Throws(() => _realm.All().Filter("TopDog = $0", new Dog { Name = "Doge", Color = "almost yellow", Vaccinated = true })); } [Test]