From 77a01803ec4c1d11a23f705e618c361add940a67 Mon Sep 17 00:00:00 2001 From: Hasan Khan Date: Wed, 8 Oct 2014 11:57:20 -0700 Subject: [PATCH] [client][managed][offline] fixed the like implementation in sqlite store --- .../SqlQueryFormatter.cs | 55 +++++++++---------- .../SqlQueryFormatterTests.cs | 2 +- .../UnitTests/SQLiteStoreTests.Query.cs | 23 +++++--- 3 files changed, 44 insertions(+), 36 deletions(-) diff --git a/sdk/Managed/src/Microsoft.WindowsAzure.MobileServices.SQLiteStore/SqlQueryFormatter.cs b/sdk/Managed/src/Microsoft.WindowsAzure.MobileServices.SQLiteStore/SqlQueryFormatter.cs index 94c2e98bc..b83a73ce3 100644 --- a/sdk/Managed/src/Microsoft.WindowsAzure.MobileServices.SQLiteStore/SqlQueryFormatter.cs +++ b/sdk/Managed/src/Microsoft.WindowsAzure.MobileServices.SQLiteStore/SqlQueryFormatter.cs @@ -6,17 +6,16 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; using Microsoft.WindowsAzure.MobileServices.Query; using Newtonsoft.Json.Linq; namespace Microsoft.WindowsAzure.MobileServices.SQLiteStore { - internal class SqlQueryFormatter: QueryNodeVisitor + internal class SqlQueryFormatter : QueryNodeVisitor { private MobileServiceTableQueryDescription query; private StringBuilder sql; - + public IDictionary Parameters { get; private set; } public SqlQueryFormatter(MobileServiceTableQueryDescription query) @@ -47,7 +46,7 @@ public string FormatSelectCount() if (this.query.IncludeTotalCount) { - this.FormatCountQuery(); + this.FormatCountQuery(); } return GetSql(); @@ -60,7 +59,7 @@ public string FormatDelete() delQuery.Selection.Clear(); delQuery.Selection.Add(MobileServiceSystemColumns.Id); delQuery.IncludeTotalCount = false; - + var formatter = new SqlQueryFormatter(delQuery); string selectIdQuery = formatter.FormatSelect(); string idMemberName = SqlHelpers.FormatMember(MobileServiceSystemColumns.Id); @@ -74,7 +73,7 @@ public string FormatDelete() private string FormatQuery(string command) { Reset(); - + this.sql.Append(command); string tableName = SqlHelpers.FormatTableName(this.query.TableName); @@ -158,9 +157,9 @@ public override QueryNode Visit(BinaryOperatorNode nodeIn) this.sql.Append("("); QueryNode left = nodeIn.LeftOperand; - QueryNode right = nodeIn.RightOperand; - - if (left != null) + QueryNode right = nodeIn.RightOperand; + + if (left != null) { // modulo requires the dividend to be an integer, monetary or numeric // rewrite the expression to convert to numeric, allowing the DB to apply @@ -175,22 +174,22 @@ public override QueryNode Visit(BinaryOperatorNode nodeIn) } var rightConstant = right as ConstantNode; - if (rightConstant != null && rightConstant.Value == null) + if (rightConstant != null && rightConstant.Value == null) { // inequality expressions against a null literal have a special // translation in SQL - if (nodeIn.OperatorKind == BinaryOperatorKind.Equal) + if (nodeIn.OperatorKind == BinaryOperatorKind.Equal) { this.sql.Append(" IS NULL"); } - else if (nodeIn.OperatorKind == BinaryOperatorKind.NotEqual) + else if (nodeIn.OperatorKind == BinaryOperatorKind.NotEqual) { this.sql.Append(" IS NOT NULL"); } } - else + else { - switch (nodeIn.OperatorKind) + switch (nodeIn.OperatorKind) { case BinaryOperatorKind.Equal: this.sql.Append(" = "); @@ -232,7 +231,7 @@ public override QueryNode Visit(BinaryOperatorNode nodeIn) this.sql.Append(" % "); break; } - + if (right != null) { right = right.Accept(this); @@ -259,10 +258,10 @@ public override QueryNode Visit(ConstantNode nodeIn) { this.sql.Append(this.CreateParameter(nodeIn.Value)); } - + return nodeIn; - } + } public override QueryNode Visit(MemberAccessNode nodeIn) { @@ -303,11 +302,11 @@ public override QueryNode Visit(FunctionCallNode nodeIn) case "trim": return this.FormatStringFunction("TRIM", nodeIn); case "substringof": - return this.FormatLikeFunction(true, nodeIn, true); + return this.FormatLikeFunction(true, nodeIn, 0, 1, true); case "startswith": - return this.FormatLikeFunction(false, nodeIn, true); + return this.FormatLikeFunction(false, nodeIn, 1, 0, true); case "endswith": - return this.FormatLikeFunction(true, nodeIn, false); + return this.FormatLikeFunction(true, nodeIn, 1, 0, false); case "concat": return this.FormatConcatFunction(nodeIn); case "indexof": @@ -321,21 +320,21 @@ public override QueryNode Visit(FunctionCallNode nodeIn) throw new NotImplementedException(); } - private QueryNode FormatLikeFunction(bool startAny, FunctionCallNode nodeIn, bool endAny) + private QueryNode FormatLikeFunction(bool startAny, FunctionCallNode nodeIn, int patternIndex, int valueIndex, bool endAny) { - // like('%pattern%', column) + // like('%pattern%', value) this.sql.Append("LIKE("); if (startAny) { this.sql.Append("'%' || "); } - nodeIn.Arguments[1].Accept(this); + nodeIn.Arguments[patternIndex].Accept(this); if (endAny) { this.sql.Append(" || '%'"); } this.sql.Append(", "); - nodeIn.Arguments[0].Accept(this); + nodeIn.Arguments[valueIndex].Accept(this); this.sql.Append(")"); return nodeIn; @@ -391,7 +390,7 @@ private QueryNode FormatStringFunction(string fn, FunctionCallNode nodeIn) separator = ", "; } this.sql.Append(")"); - + return nodeIn; } @@ -474,7 +473,7 @@ public override QueryNode Visit(UnaryOperatorNode nodeIn) return new UnaryOperatorNode(nodeIn.OperatorKind, operand); } - return nodeIn; + return nodeIn; } public override QueryNode Visit(ConvertNode nodeIn) @@ -482,7 +481,7 @@ public override QueryNode Visit(ConvertNode nodeIn) this.sql.Append("CAST("); QueryNode source = nodeIn.Source.Accept(this); - + this.sql.Append(" AS "); string sqlType = SqlHelpers.GetColumnType(nodeIn.TargetType); @@ -504,6 +503,6 @@ private string CreateParameter(object value) string paramName = "@p" + paramNumber; this.Parameters.Add(paramName, SqlHelpers.SerializeValue(new JValue(value), allowNull: true)); return paramName; - } + } } } diff --git a/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.Unit/SqlQueryFormatterTests.cs b/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.Unit/SqlQueryFormatterTests.cs index f8882b49e..e4e2ccd74 100644 --- a/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.Unit/SqlQueryFormatterTests.cs +++ b/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.Unit/SqlQueryFormatterTests.cs @@ -146,7 +146,7 @@ public void FormatSelect_String_Trim() [TestMethod] public void FormatSelect_String_SubstringOf() { - TestSelectStringFunction("substringof(name, 'khan')", "LIKE('%' || @p1 || '%', [name])", "khan"); + TestSelectStringFunction("substringof('khan', name)", "LIKE('%' || @p1 || '%', [name])", "khan"); } [TestMethod] diff --git a/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test/UnitTests/SQLiteStoreTests.Query.cs b/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test/UnitTests/SQLiteStoreTests.Query.cs index 465e5b5d8..99ed834e9 100644 --- a/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test/UnitTests/SQLiteStoreTests.Query.cs +++ b/sdk/Managed/test/Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test/UnitTests/SQLiteStoreTests.Query.cs @@ -3,17 +3,12 @@ // ---------------------------------------------------------------------------- using System; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.WindowsAzure.MobileServices.Query; -using Microsoft.WindowsAzure.MobileServices.SQLiteStore; using Microsoft.WindowsAzure.MobileServices.TestFramework; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Newtonsoft.Json.Schema; -using SQLitePCL; namespace Microsoft.WindowsAzure.MobileServices.SQLiteStore.Test.UnitTests { @@ -145,8 +140,22 @@ public async Task Query_OnString_IndexOf() [AsyncTestMethod] public async Task Query_OnString_SubstringOf() { - await TestQuery("$filter=substringof(col1, 'ump')", 1); - await TestQuery("$filter=substringof(col1, 'umx')", 0); + await TestQuery("$filter=substringof('ump', col1)", 1); + await TestQuery("$filter=substringof('umx', col1)", 0); + } + + [AsyncTestMethod] + public async Task Query_OnString_StartsWith() + { + await TestQuery("$filter=startswith(col1, 'jum')", 1); + await TestQuery("$filter=startswith(col1, 'pum')", 0); + } + + [AsyncTestMethod] + public async Task Query_OnString_EndsWith() + { + await TestQuery("$filter=endswith(col1, 'umped')", 1); + await TestQuery("$filter=endswith(col1, 'umxed')", 0); } [AsyncTestMethod]