From 665007aa5d70f018d14d44e120e05dae5b339e41 Mon Sep 17 00:00:00 2001 From: Andrew Tavera Date: Tue, 11 Jul 2017 03:13:17 -0400 Subject: [PATCH 1/2] StartsWith, EndsWith, and Contains generated SQL behaves like the System.String methods used to construct the query: Contains is case sensitive, using instr() instead of like(). ToUpper() or ToLower can be used to compare without case sensitivity. StartsWith and EndsWith is case sensitive unless a case insensitive StringComparison argument is passed using substr() = ? or the original like() depending upon case sensitivity --- src/SQLite.cs | 48 +++++++++++++++++++++++++++++++++++----- tests/StringQueryTest.cs | 40 ++++++++++++++++++++++++--------- 2 files changed, 72 insertions(+), 16 deletions(-) diff --git a/src/SQLite.cs b/src/SQLite.cs index 3ef06c5f..4735f532 100644 --- a/src/SQLite.cs +++ b/src/SQLite.cs @@ -3001,17 +3001,53 @@ private CompileResult CompileExpr (Expression expr, List queryArgs) } else if (call.Method.Name == "Contains" && args.Length == 1) { if (call.Object != null && call.Object.Type == typeof(string)) { - sqlCall = "(" + obj.CommandText + " like ('%' || " + args [0].CommandText + " || '%'))"; + sqlCall = "( instr(" + obj.CommandText + "," + args [0].CommandText + ") >0 )"; } else { sqlCall = "(" + args [0].CommandText + " in " + obj.CommandText + ")"; } } - else if (call.Method.Name == "StartsWith" && args.Length == 1) { - sqlCall = "(" + obj.CommandText + " like (" + args [0].CommandText + " || '%'))"; - } - else if (call.Method.Name == "EndsWith" && args.Length == 1) { - sqlCall = "(" + obj.CommandText + " like ('%' || " + args [0].CommandText + "))"; + else if (call.Method.Name == "StartsWith" && args.Length >= 1) + { + var startsWithCmpOp = StringComparison.CurrentCulture; + if (args.Length == 2) + { + startsWithCmpOp = (StringComparison) args[1].Value; + } + switch (startsWithCmpOp) + { + case StringComparison.Ordinal: + case StringComparison.CurrentCulture: + case StringComparison.InvariantCulture: + sqlCall = "( substr(" + obj.CommandText + ", 1, " + args[0].Value.ToString().Length + ") = " + args[0].CommandText + ")"; + break; + case StringComparison.OrdinalIgnoreCase: + case StringComparison.CurrentCultureIgnoreCase: + case StringComparison.InvariantCultureIgnoreCase: + sqlCall = "(" + obj.CommandText + " like (" + args[0].CommandText + " || '%'))"; + break; + } + + } + else if (call.Method.Name == "EndsWith" && args.Length >= 1) { + var endsWithCmpOp = StringComparison.CurrentCulture; + if (args.Length == 2) + { + endsWithCmpOp = (StringComparison)args[1].Value; + } + switch (endsWithCmpOp) + { + case StringComparison.Ordinal: + case StringComparison.CurrentCulture: + case StringComparison.InvariantCulture: + sqlCall = "( substr(" + obj.CommandText + ", length(" + obj.CommandText + ") - "+args[0].Value.ToString().Length+ "+1, " + args[0].Value.ToString().Length + ") = " + args[0].CommandText + ")"; + break; + case StringComparison.OrdinalIgnoreCase: + case StringComparison.CurrentCultureIgnoreCase: + case StringComparison.InvariantCultureIgnoreCase: + sqlCall = "(" + obj.CommandText + " like ('%' || " + args[0].CommandText + "))"; + break; + } } else if (call.Method.Name == "Equals" && args.Length == 1) { sqlCall = "(" + obj.CommandText + " = (" + args[0].CommandText + "))"; diff --git a/tests/StringQueryTest.cs b/tests/StringQueryTest.cs index 35976666..ae474b91 100644 --- a/tests/StringQueryTest.cs +++ b/tests/StringQueryTest.cs @@ -62,8 +62,16 @@ public void StartsWith () { var fs = db.Table ().Where (x => x.Name.StartsWith ("F")).ToList (); Assert.AreEqual (2, fs.Count); - - var bs = db.Table ().Where (x => x.Name.StartsWith ("B")).ToList (); + + var lfs = db.Table().Where(x => x.Name.StartsWith("f")).ToList(); + Assert.AreEqual(0, lfs.Count); + + + var lfs2 = db.Table().Where(x => x.Name.StartsWith("f",StringComparison.OrdinalIgnoreCase)).ToList(); + Assert.AreEqual(2, lfs2.Count); + + + var bs = db.Table ().Where (x => x.Name.StartsWith ("B")).ToList (); Assert.AreEqual (1, bs.Count); } @@ -71,20 +79,32 @@ public void StartsWith () public void EndsWith () { var fs = db.Table ().Where (x => x.Name.EndsWith ("ar")).ToList (); - Assert.AreEqual (2, fs.Count); - - var bs = db.Table ().Where (x => x.Name.EndsWith ("o")).ToList (); + Assert.AreEqual (2, fs.Count); + + var lfs = db.Table().Where(x => x.Name.EndsWith("Ar")).ToList(); + Assert.AreEqual(0, lfs.Count); + + var bs = db.Table ().Where (x => x.Name.EndsWith ("o")).ToList (); Assert.AreEqual (1, bs.Count); } [Test] public void Contains () { - var fs = db.Table ().Where (x => x.Name.Contains ("o")).ToList (); - Assert.AreEqual (2, fs.Count); - - var bs = db.Table ().Where (x => x.Name.Contains ("a")).ToList (); + var fs = db.Table().Where(x => x.Name.Contains("o")).ToList(); + Assert.AreEqual(2, fs.Count); + + var lfs = db.Table ().Where (x => x.Name.Contains ("O")).ToList (); + Assert.AreEqual (0, lfs.Count); + + var lfsu = db.Table().Where(x => x.Name.ToUpper().Contains("O")).ToList(); + Assert.AreEqual(2, lfsu.Count); + + var bs = db.Table ().Where (x => x.Name.Contains ("a")).ToList (); Assert.AreEqual (2, bs.Count); - } + + var zs = db.Table().Where(x => x.Name.Contains("z")).ToList(); + Assert.AreEqual(0, zs.Count); + } } } From feca677531a182d9cec06ed9eb229b50226ac07f Mon Sep 17 00:00:00 2001 From: Andrew Tavera Date: Wed, 12 Jul 2017 16:22:36 -0400 Subject: [PATCH 2/2] Remove StringComparison.InvariantCulture (not supported by PCL) --- src/SQLite.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/SQLite.cs b/src/SQLite.cs index 4735f532..a90df278 100644 --- a/src/SQLite.cs +++ b/src/SQLite.cs @@ -3018,12 +3018,10 @@ private CompileResult CompileExpr (Expression expr, List queryArgs) { case StringComparison.Ordinal: case StringComparison.CurrentCulture: - case StringComparison.InvariantCulture: sqlCall = "( substr(" + obj.CommandText + ", 1, " + args[0].Value.ToString().Length + ") = " + args[0].CommandText + ")"; break; case StringComparison.OrdinalIgnoreCase: case StringComparison.CurrentCultureIgnoreCase: - case StringComparison.InvariantCultureIgnoreCase: sqlCall = "(" + obj.CommandText + " like (" + args[0].CommandText + " || '%'))"; break; } @@ -3039,12 +3037,10 @@ private CompileResult CompileExpr (Expression expr, List queryArgs) { case StringComparison.Ordinal: case StringComparison.CurrentCulture: - case StringComparison.InvariantCulture: sqlCall = "( substr(" + obj.CommandText + ", length(" + obj.CommandText + ") - "+args[0].Value.ToString().Length+ "+1, " + args[0].Value.ToString().Length + ") = " + args[0].CommandText + ")"; break; case StringComparison.OrdinalIgnoreCase: case StringComparison.CurrentCultureIgnoreCase: - case StringComparison.InvariantCultureIgnoreCase: sqlCall = "(" + obj.CommandText + " like ('%' || " + args[0].CommandText + "))"; break; }