Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,21 @@ public static SqlScalarExpression VisitBuiltinFunctionCall(MethodCallExpression
return MathBuiltinFunctions.Visit(methodCallExpression, context);
}

// String functions
if (declaringType == typeof(string))
// ToString with String and Guid only becomes passthrough
if (methodCallExpression.Method.Name == "ToString" &&
methodCallExpression.Arguments.Count == 0 &&
methodCallExpression.Object != null &&
((methodCallExpression.Object.Type == typeof(string)) ||
(methodCallExpression.Object.Type == typeof(Guid))))
{
return ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Object, context);
}

// String functions or ToString with Objects that are not strings and guids
if ((declaringType == typeof(string)) ||
(methodCallExpression.Method.Name == "ToString" &&
methodCallExpression.Arguments.Count == 0 &&
methodCallExpression.Object != null))
{
return StringBuiltinFunctions.Visit(methodCallExpression, context);
}
Expand All @@ -83,14 +96,6 @@ public static SqlScalarExpression VisitBuiltinFunctionCall(MethodCallExpression
return SpatialBuiltinFunctions.Visit(methodCallExpression, context);
}

// ToString with Objects (String and Guid only)
if (methodCallExpression.Method.Name == "ToString" &&
methodCallExpression.Arguments.Count == 0 &&
methodCallExpression.Object != null)
{
return ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Object, context);
}

throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.MethodNotSupported, methodCallExpression.Method.Name));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,27 @@ protected override SqlScalarExpression VisitExplicit(MethodCallExpression method
}
}

private class StringVisitToString : SqlBuiltinFunctionVisitor
{
public StringVisitToString()
: base("ToString",
true,
null)
{
}

protected override SqlScalarExpression VisitImplicit(MethodCallExpression methodCallExpression, TranslationContext context)
{
if ((methodCallExpression.Arguments.Count == 0) && (methodCallExpression.Object != null))
{
SqlScalarExpression str = ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Object, context);
return SqlFunctionCallScalarExpression.CreateBuiltin(SqlFunctionCallScalarExpression.Names.ToString, str);
}

return null;
}
}

static StringBuiltinFunctions()
{
StringBuiltinFunctionDefinitions = new Dictionary<string, BuiltinFunctionVisitor>
Expand Down Expand Up @@ -386,6 +407,10 @@ static StringBuiltinFunctions()
"Reverse",
new StringVisitReverse()
},
{
"ToString",
new StringVisitToString()
},
{
"TrimEnd",
new StringVisitTrimEnd()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ JOIN (
SELECT VALUE MAX(t0["Amount"])
FROM root
JOIN t0 IN root["Records"]["Transactions"])) AS v0
ORDER BY (root["IsRegistered"] ? root["FamilyId"] : root["Int"]) DESC, (v0[0] % 1000) ASC
ORDER BY (root["IsRegistered"] ? root["FamilyId"] : ToString(root["Int"])) DESC, (v0[0] % 1000) ASC
]]></SqlQuery>
<ErrorMessage><![CDATA[Status Code: BadRequest,{"errors":[{"severity":"Error","code":2206,"message":"Unsupported ORDER BY clause. ORDER BY item expression could not be mapped to a document path."}]},0x800A0B00]]></ErrorMessage>
</Output>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,50 @@ FROM root]]></SqlQuery>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE SUBSTRING(root["StringField"], 0, 1)
FROM root]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[String constant StartsWith]]></Description>
<Expression><![CDATA[query.Select(doc => "str".StartsWith(doc.StringField.ToString()))]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE STARTSWITH("str", root["StringField"])
FROM root]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[ToString]]></Description>
<Expression><![CDATA[query.Select(doc => doc.StringField.ToString())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE root["StringField"]
FROM root]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[ToString]]></Description>
<Expression><![CDATA[query.Select(doc => doc.NumericField.ToString())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE ToString(root["NumericField"])
FROM root]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[ToString]]></Description>
<Expression><![CDATA[query.Select(doc => doc.GuidField.ToString())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE root["GuidField"]
FROM root]]></SqlQuery>
</Output>
</Result>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ internal class DataObject : LinqTestObject
public int? NullableField;
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value false
public bool BooleanField;
public Guid GuidField;
#pragma warning restore // Field is never assigned to, and will always have its default value false

[JsonConverter(typeof(StringEnumConverter))]
Expand Down Expand Up @@ -663,7 +664,11 @@ private Func<bool, IQueryable<DataObject>> CreateDataTestStringFunctions()
{
StringField = sb.ToString(),
Id = Guid.NewGuid().ToString(),
Pk = "Test"
Pk = "Test",

// For ToString tests
ArrayField = new int[] {},
Point = new Point(0, 0)
};
};
Func<bool, IQueryable<DataObject>> getQuery = LinqTestsCommon.GenerateTestCosmosData(createDataObj, Records, testContainer);
Expand Down Expand Up @@ -757,13 +762,23 @@ public void TestStringFunctions()
new LinqTestInput("String constant StartsWith (case-insensitive)", b => getQuery(b).Select(doc => "sTr".StartsWith(doc.StringField, StringComparison.OrdinalIgnoreCase))),
// Substring
new LinqTestInput("Substring", b => getQuery(b).Select(doc => doc.StringField.Substring(0, 1))),
// ToString
new LinqTestInput("String constant StartsWith", b => getQuery(b).Select(doc => "str".StartsWith(doc.StringField.ToString()))),
new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.StringField.ToString())),
new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.NumericField.ToString())),
new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.GuidField.ToString())),
// For these fields, .NET ToString and CosmosDB ToString don't produce the same behavior. Manually verified that BE behavior is as expected
//new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.ArrayField.ToString())),
//new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.Point.ToString())),
//new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.BooleanField.ToString())),
//new LinqTestInput("ToString", b => getQuery(b).Select(doc => doc.UnixTime.ToString())),
// ToUpper
new LinqTestInput("ToUpper", b => getQuery(b).Select(doc => doc.StringField.ToUpper()))
};
this.ExecuteTestSuite(inputs);
}
}

[TestMethod]
[TestMethod]
public void TestArrayFunctions()
{
const int Records = 100;
Expand Down