diff --git a/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs b/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs index 6be1e82e9c..ad9d02cab4 100644 --- a/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs +++ b/Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs @@ -495,7 +495,8 @@ private static SqlScalarExpression ApplyCustomConverters(Expression left, SqlLit memberExpression = left as MemberExpression; } - if (memberExpression != null) + if (memberExpression != null && + right.Literal is not SqlNullLiteral) { Type memberType = memberExpression.Type; if (memberType.IsNullable()) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDataMember.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDataMember.xml index 6734a125fa..215d593da2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDataMember.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDataMember.xml @@ -29,7 +29,7 @@ WHERE (root["numericFieldDataMember"] = 1)]]> +WHERE (root = {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null})]]> 1) ? {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null} : {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null}) +SELECT VALUE ((root["numericFieldDataMember"] > 1) ? {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null} : {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null}) FROM root]]> + + + + + + + (doc.DateTimeField != null))]]> + + + + + + + + + + + (doc.DataTypeField != null))]]> + + + + + + + + + + + (doc.DateTimeField == Convert(new DateTime(1970, 1, 1, 0, 0, 0, 0, Utc), Nullable`1)))]]> + + + + + + + + + + + (Convert(doc.DataTypeField, Nullable`1) == Convert(Point, Nullable`1)))]]> + + + + + + + + + + + (doc.StringField != null))]]> + + + + @@ -146,7 +251,7 @@ WHERE (root["NumericFieldDataMember"] = 1)]]> +WHERE (root = {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null})]]> 1) ? {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null} : {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null}) +SELECT VALUE ((root["NumericFieldDataMember"] > 1) ? {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null} : {"NumericFieldDataMember": 1, "StringFieldDataMember": "1", "id": null, "Pk": null, "DateTimeFieldDataMember": null, "DataTypeFieldDataMember": null}) FROM root]]> + + + + + + + (doc.DateTimeField != null))]]> + + + + + + + + + + + (doc.DataTypeField != null))]]> + + + + + + + + + + + (doc.DateTimeField == Convert(new DateTime(1970, 1, 1, 0, 0, 0, 0, Utc), Nullable`1)))]]> + + + + + + + + + + + (Convert(doc.DataTypeField, Nullable`1) == Convert(Point, Nullable`1)))]]> + + + + + + + + + + + (doc.StringField != null))]]> + + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDotNetCustomSerializer.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDotNetCustomSerializer.xml index a9c63ebf41..ca941f5bd0 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDotNetCustomSerializer.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerDotNetCustomSerializer.xml @@ -28,7 +28,7 @@ WHERE (root["NumberValueDotNet"] = 1)]]> +WHERE (root = {"NumberValueDotNet": 1, "StringValueDotNet": "1", "id": null, "Pk": null, "DateTimeFieldDotNet": null, "DataTypeField": null})]]> 1) ? {"NumberValueDotNet": 1, "StringValueDotNet": "1", "id": null, "Pk": null} : {"NumberValueDotNet": 1, "StringValueDotNet": "1", "id": null, "Pk": null}) +SELECT VALUE ((root["NumberValueDotNet"] > 1) ? {"NumberValueDotNet": 1, "StringValueDotNet": "1", "id": null, "Pk": null, "DateTimeFieldDotNet": null, "DataTypeField": null} : {"NumberValueDotNet": 1, "StringValueDotNet": "1", "id": null, "Pk": null, "DateTimeFieldDotNet": null, "DataTypeField": null}) FROM root]]> "false", "false", "true" +]]]> + + + + + + (doc.DateTimeField != null))]]> + + + + + + + + + + + (doc.DataTypeField != null))]]> + + + + + + + + + + + (doc.DateTimeField == Convert(new DateTime(1970, 1, 1, 0, 0, 0, 0, Utc), Nullable`1)))]]> + + + + + + + + + + + (Convert(doc.DataTypeField, Nullable`1) == Convert(Point, Nullable`1)))]]> + + + + + + + + + + + (doc.StringField != null))]]> + + + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerNewtonsoft.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerNewtonsoft.xml index 5ecf5171bc..83c0adb53b 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerNewtonsoft.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationWithCustomSerializerBaseline.TestMemberInitializerNewtonsoft.xml @@ -29,7 +29,7 @@ WHERE (root["numberValueNewtonsoft"] = 1)]]> +WHERE (root = {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null})]]> 1) ? {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null} : {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null}) +SELECT VALUE ((root["numberValueNewtonsoft"] > 1) ? {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null} : {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null}) FROM root]]> + + + + + + + (doc.DateTimeField != null))]]> + + + + + + + + + + + (doc.DataTypeField != null))]]> + + + + + + + + + + + (doc.DateTimeField == Convert(new DateTime(1970, 1, 1, 0, 0, 0, 0, Utc), Nullable`1)))]]> + + + + + + + + + + + (Convert(doc.DataTypeField, Nullable`1) == Convert(Point, Nullable`1)))]]> + + + + + + + + + + + (doc.StringField != null))]]> + + + + @@ -146,7 +251,7 @@ WHERE (root["NumberValueNewtonsoft"] = 1)]]> +WHERE (root = {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null})]]> 1) ? {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null} : {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null}) +SELECT VALUE ((root["NumberValueNewtonsoft"] > 1) ? {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null} : {"NumberValueNewtonsoft": 1, "StringValueNewtonsoft": "1", "id": null, "Pk": null, "DateTimeField": null, "DataTypeField": null}) FROM root]]> + + + + + + + (doc.DateTimeField != null))]]> + + + + + + + + + + + (doc.DataTypeField != null))]]> + + + + + + + + + + + (doc.DateTimeField == Convert(new DateTime(1970, 1, 1, 0, 0, 0, 0, Utc), Nullable`1)))]]> + + + + + + + + + + + (Convert(doc.DataTypeField, Nullable`1) == Convert(Point, Nullable`1)))]]> + + + + + + + + + + + (doc.StringField != null))]]> + + + + diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs index 817b030a60..91a537522c 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationWithCustomSerializerBaseline.cs @@ -18,6 +18,7 @@ namespace Microsoft.Azure.Cosmos.Services.Management.Tests.LinqProviderTests using Microsoft.Azure.Cosmos.SDK.EmulatorTests; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; + using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; [SDK.EmulatorTests.TestClass] @@ -102,6 +103,11 @@ public void TestMemberInitializerDotNetCustomSerializer() new LinqTestInput("Filter w/ DataObject initializer with member initialization", b => getQuery(b).Where(doc => doc == new DataObjectDotNet() { NumericField = doc.NumericField, StringField = doc.StringField }).Select(b => "A"), skipVerification : true, inputData: insertedData), new LinqTestInput("OrderBy query", b => getQuery(b).Select(x => x).OrderBy(x => x.NumericField).Take(5), skipVerification : true, inputData: insertedData), new LinqTestInput("Conditional", b => getQuery(b).Select(c => c.NumericField > 1 ? "true" : "false"), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable property", b => getQuery(b).Where(doc => doc.DateTimeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable enum", b => getQuery(b).Where(doc => doc.DataTypeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable property", b => getQuery(b).Where(doc => doc.DateTimeField == new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable enum", b => getQuery(b).Where(doc => doc.DataTypeField == DataType.Point), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ string null comparison", b => getQuery(b).Where(doc => doc.StringField != null), skipVerification : true, inputData: insertedData), }; this.ExecuteTestSuite(inputs); @@ -127,7 +133,12 @@ public void TestMemberInitializerNewtonsoft() new LinqTestInput("Filter w/ DataObject initializer with constant value, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectNewtonsoft() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), new LinqTestInput("Select w/ DataObject initializer, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Select(doc => new DataObjectNewtonsoft() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), new LinqTestInput("Deeper than top level reference, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Select(doc => doc.NumericField > 1 ? new DataObjectNewtonsoft() { NumericField = 1, StringField = "1" } : new DataObjectNewtonsoft() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), - new LinqTestInput("Filter w/ DataObject initializer with member initialization, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectNewtonsoft() { NumericField = doc.NumericField, StringField = doc.StringField }).Select(b => "A"), skipVerification : true, inputData: insertedData) + new LinqTestInput("Filter w/ DataObject initializer with member initialization, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectNewtonsoft() { NumericField = doc.NumericField, StringField = doc.StringField }).Select(b => "A"), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable property, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.DateTimeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable enum, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.DataTypeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable property", b => getQuery(b).Where(doc => doc.DateTimeField == new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable enum", b => getQuery(b).Where(doc => doc.DataTypeField == DataType.Point), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ string null comparison, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.StringField != null), skipVerification : true, inputData: insertedData), }; inputs.AddRange(camelCaseSettingInputs); @@ -156,7 +167,12 @@ public void TestMemberInitializerDataMember() new LinqTestInput("Filter w/ DataObject initializer with constant value, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectDataMember() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), new LinqTestInput("Select w/ DataObject initializer, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Select(doc => new DataObjectDataMember() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), new LinqTestInput("Deeper than top level reference, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Select(doc => doc.NumericField > 1 ? new DataObjectDataMember() { NumericField = 1, StringField = "1" } : new DataObjectDataMember() { NumericField = 1, StringField = "1" }), skipVerification : true, inputData: insertedData), - new LinqTestInput("Filter w/ DataObject initializer with member initialization, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectDataMember() { NumericField = doc.NumericField, StringField = doc.StringField }).Select(b => "A"), skipVerification : true, inputData: insertedData) + new LinqTestInput("Filter w/ DataObject initializer with member initialization, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc == new DataObjectDataMember() { NumericField = doc.NumericField, StringField = doc.StringField }).Select(b => "A"), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable property, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.DateTimeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ nullable enum, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.DataTypeField != null), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable property", b => getQuery(b).Where(doc => doc.DateTimeField == new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ non-null nullable enum", b => getQuery(b).Where(doc => doc.DataTypeField == DataType.Point), skipVerification : true, inputData: insertedData), + new LinqTestInput("Filter w/ string null comparison, camelcase = " + useCamelCaseSerializer, b => getQuery(b).Where(doc => doc.StringField != null), skipVerification : true, inputData: insertedData), }; inputs.AddRange(camelCaseSettingInputs); @@ -332,6 +348,12 @@ private class DataObjectDotNet : LinqTestObject public string Pk { get; set; } + [JsonPropertyName("DateTimeFieldDotNet")] + public DateTime? DateTimeField { get; set; } + + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] + public DataType? DataTypeField { get; set; } + public DataObjectDotNet() { } public DataObjectDotNet(double numericField, string stringField, string id, string pk) @@ -364,6 +386,12 @@ private class DataObjectNewtonsoft : LinqTestObject public string Pk { get; set; } + [Newtonsoft.Json.JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime? DateTimeField { get; set; } + + [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] + public DataType? DataTypeField { get; set; } + public DataObjectNewtonsoft() { } public DataObjectNewtonsoft(double numericField, string stringField, string id, string pk) @@ -396,6 +424,12 @@ private class DataObjectDataMember : LinqTestObject [DataMember(Name = "Pk")] public string Pk { get; set; } + [DataMember(Name = "DateTimeFieldDataMember")] + public DateTime? DateTimeField { get; set; } + + [DataMember(Name = "DataTypeFieldDataMember")] + public DataType? DataTypeField { get; set; } + public DataObjectDataMember() { } public DataObjectDataMember(double numericField, string stringField, string id, string pk)