Skip to content
Browse files

Fix for issue #244

I assumed that SQL Ce supports DISTINCT when doing the regex replace -
to be fair it won't break anything if it doesn't!
  • Loading branch information...
1 parent c027ed7 commit 0f9d3c32a5ed16c48751203aac74157b088fbbfa @richardhopton richardhopton committed Nov 27, 2012
View
68 Simple.Data.SqlCe40/SqlCe40QueryPager.cs
@@ -1,34 +1,34 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.Composition;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Simple.Data.Ado;
-
-namespace Simple.Data.SqlCe40
-{
- [Export(typeof(IQueryPager))]
- public class SqlCe40QueryPager : IQueryPager
- {
- private static readonly Regex ColumnExtract = new Regex(@"SELECT\s*(.*)\s*(FROM.*)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
- private static readonly Regex SelectMatch = new Regex(@"^SELECT\s*", RegexOptions.IgnoreCase);
-
- public IEnumerable<string> ApplyLimit(string sql, int take)
- {
- yield return SelectMatch.Replace(sql, match => match.Value + " TOP(" + take + ") ");
- }
-
- public IEnumerable<string> ApplyPaging(string sql, string[] keys, int skip, int take)
- {
- if (sql.IndexOf("order by", StringComparison.InvariantCultureIgnoreCase) < 0)
- {
- var match = ColumnExtract.Match(sql);
- var columns = match.Groups[1].Value.Trim();
- sql += " ORDER BY " + columns.Split(',').First().Trim();
- }
-
- yield return string.Format("{0} OFFSET {1} ROWS FETCH NEXT {2} ROWS ONLY", sql, skip, take);
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Simple.Data.Ado;
+
+namespace Simple.Data.SqlCe40
+{
+ [Export(typeof(IQueryPager))]
+ public class SqlCe40QueryPager : IQueryPager
+ {
+ private static readonly Regex ColumnExtract = new Regex(@"SELECT\s*(.*)\s*(FROM.*)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
+ private static readonly Regex SelectMatch = new Regex(@"^SELECT\s*(DISTINCT)?", RegexOptions.IgnoreCase);
+
+ public IEnumerable<string> ApplyLimit(string sql, int take)
+ {
+ yield return SelectMatch.Replace(sql, match => match.Value + " TOP(" + take + ") ");
+ }
+
+ public IEnumerable<string> ApplyPaging(string sql, string[] keys, int skip, int take)
+ {
+ if (sql.IndexOf("order by", StringComparison.InvariantCultureIgnoreCase) < 0)
+ {
+ var match = ColumnExtract.Match(sql);
+ var columns = match.Groups[1].Value.Trim();
+ sql += " ORDER BY " + columns.Split(',').First().Trim();
+ }
+
+ yield return string.Format("{0} OFFSET {1} ROWS FETCH NEXT {2} ROWS ONLY", sql, skip, take);
+ }
+ }
+}
View
114 Simple.Data.SqlCe40Test/SqlCe40QueryPagerTest.cs
@@ -1,51 +1,63 @@
-using System.Text.RegularExpressions;
-using NUnit.Framework;
-using Simple.Data.SqlCe40;
-using System.Linq;
-
-namespace Simple.Data.SqlCe40Test
-{
- [TestFixture]
- public class SqlCe40QueryPagerTest
- {
- static readonly Regex Normalize = new Regex(@"\s+", RegexOptions.Multiline);
-
- [Test]
- public void ShouldApplyLimitUsingTop()
- {
- var sql = "select a,b,c from d where a = 1 order by c";
- var expected = new[] { "select top(5) a,b,c from d where a = 1 order by c" };
-
- var pagedSql = new SqlCe40QueryPager().ApplyLimit(sql, 5);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
-
- Assert.IsTrue(expected.SequenceEqual(modified));
- }
-
- [Test]
- public void ShouldApplyPagingUsingOrderBy()
- {
- var sql = "select a,b,c from d where a = 1 order by c";
- var expected = new[]{
- "select a,b,c from d where a = 1 order by c offset 5 rows fetch next 10 rows only"};
-
- var pagedSql = new SqlCe40QueryPager().ApplyPaging(sql, new string[0], 5, 10);
- var modified = pagedSql.Select(x=> Normalize.Replace(x, " ").ToLowerInvariant());
-
- Assert.IsTrue(expected.SequenceEqual(modified));
- }
-
- [Test]
- public void ShouldApplyPagingUsingOrderByFirstColumnIfNotAlreadyOrdered()
- {
- var sql = "select a,b,c from d where a = 1";
- var expected = new[]{
- "select a,b,c from d where a = 1 order by a offset 10 rows fetch next 20 rows only"};
-
- var pagedSql = new SqlCe40QueryPager().ApplyPaging(sql, new string[0], 10, 20);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
-
- Assert.IsTrue(expected.SequenceEqual(modified));
- }
- }
-}
+using System.Text.RegularExpressions;
+using NUnit.Framework;
+using Simple.Data.SqlCe40;
+using System.Linq;
+
+namespace Simple.Data.SqlCe40Test
+{
+ [TestFixture]
+ public class SqlCe40QueryPagerTest
+ {
+ static readonly Regex Normalize = new Regex(@"\s+", RegexOptions.Multiline);
+
+ [Test]
+ public void ShouldApplyLimitUsingTop()
+ {
+ var sql = "select a,b,c from d where a = 1 order by c";
+ var expected = new[] { "select top(5) a,b,c from d where a = 1 order by c" };
+
+ var pagedSql = new SqlCe40QueryPager().ApplyLimit(sql, 5);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+
+ [Test]
+ public void ShouldApplyLimitUsingTopWithDistinct()
+ {
+ var sql = "select distinct a,b,c from d where a = 1 order by c";
+ var expected = new[] { "select distinct top(5) a,b,c from d where a = 1 order by c" };
+
+ var pagedSql = new SqlCe40QueryPager().ApplyLimit(sql, 5);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+
+ [Test]
+ public void ShouldApplyPagingUsingOrderBy()
+ {
+ var sql = "select a,b,c from d where a = 1 order by c";
+ var expected = new[]{
+ "select a,b,c from d where a = 1 order by c offset 5 rows fetch next 10 rows only"};
+
+ var pagedSql = new SqlCe40QueryPager().ApplyPaging(sql, new string[0], 5, 10);
+ var modified = pagedSql.Select(x=> Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+
+ [Test]
+ public void ShouldApplyPagingUsingOrderByFirstColumnIfNotAlreadyOrdered()
+ {
+ var sql = "select a,b,c from d where a = 1";
+ var expected = new[]{
+ "select a,b,c from d where a = 1 order by a offset 10 rows fetch next 20 rows only"};
+
+ var pagedSql = new SqlCe40QueryPager().ApplyPaging(sql, new string[0], 10, 20);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+ }
+}
View
46 Simple.Data.SqlServer/SqlCommandOptimizer.cs
@@ -1,23 +1,23 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-namespace Simple.Data.SqlServer
-{
- using System.ComponentModel.Composition;
- using System.Data.SqlClient;
- using System.Text.RegularExpressions;
- using Ado;
-
- [Export(typeof(CommandOptimizer))]
- public class SqlCommandOptimizer : CommandOptimizer
- {
- public override System.Data.IDbCommand OptimizeFindOne(System.Data.IDbCommand command)
- {
- command.CommandText = Regex.Replace(command.CommandText, "^SELECT ", "SELECT TOP 1 ",
- RegexOptions.IgnoreCase);
- return command;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Simple.Data.SqlServer
+{
+ using System.ComponentModel.Composition;
+ using System.Data.SqlClient;
+ using System.Text.RegularExpressions;
+ using Ado;
+
+ [Export(typeof(CommandOptimizer))]
+ public class SqlCommandOptimizer : CommandOptimizer
+ {
+ public override System.Data.IDbCommand OptimizeFindOne(System.Data.IDbCommand command)
+ {
+ command.CommandText = Regex.Replace(command.CommandText, @"^SELECT\s*(DISTINCT)?", "SELECT $1 TOP 1 ",
+ RegexOptions.IgnoreCase);
+ return command;
+ }
+ }
+}
View
156 Simple.Data.SqlServer/SqlQueryPager.cs
@@ -1,78 +1,78 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.Composition;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Simple.Data.Ado;
-
-namespace Simple.Data.SqlServer
-{
- [Export(typeof(IQueryPager))]
- public class SqlQueryPager : IQueryPager
- {
- private static readonly Regex ColumnExtract = new Regex(@"SELECT\s*(.*)\s*(FROM.*)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
- private static readonly Regex SelectMatch = new Regex(@"^SELECT\s*", RegexOptions.IgnoreCase);
-
- public IEnumerable<string> ApplyLimit(string sql, int take)
- {
- yield return SelectMatch.Replace(sql, match => match.Value + " TOP " + take + " ");
- }
-
- public IEnumerable<string> ApplyPaging(string sql, string[] keys, int skip, int take)
- {
- var builder = new StringBuilder("WITH __Data AS (SELECT ");
-
- var match = ColumnExtract.Match(sql);
- var columns = match.Groups[1].Value.Trim();
- var fromEtc = match.Groups[2].Value.Trim();
-
- builder.Append(string.Join(",", keys));
-
- var orderBy = ExtractOrderBy(columns, keys, ref fromEtc);
-
- builder.AppendFormat(", ROW_NUMBER() OVER({0}) AS [_#_]", orderBy);
- builder.AppendLine();
- builder.Append(fromEtc);
- builder.AppendLine(")");
- builder.AppendFormat("SELECT {0} FROM __Data ", columns);
- builder.AppendFormat("JOIN {0} ON ",
- keys[0].Substring(0, keys[0].LastIndexOf(".", StringComparison.OrdinalIgnoreCase)));
- builder.AppendFormat(string.Join(" ", keys.Select(MakeDataJoin)));
- var rest = Regex.Replace(fromEtc, @"^from (\[.*?\]\.\[.*?\])", @"");
- builder.Append(rest);
-
- builder.AppendFormat(" AND [_#_] BETWEEN {0} AND {1}", skip + 1, skip + take);
-
- yield return builder.ToString();
- }
-
- private static string MakeDataJoin(string key)
- {
- return key + " = __Data" + key.Substring(key.LastIndexOf(".", StringComparison.OrdinalIgnoreCase));
- }
-
- private static string DequalifyColumns(string original)
- {
- var q = from part in original.Split(',')
- select part.Substring(Math.Max(part.LastIndexOf('.') + 1, part.LastIndexOf('[')));
- return string.Join(",", q);
- }
-
- private static string ExtractOrderBy(string columns, string[] keys, ref string fromEtc)
- {
- string orderBy;
- int index = fromEtc.IndexOf("ORDER BY", StringComparison.InvariantCultureIgnoreCase);
- if (index > -1)
- {
- orderBy = fromEtc.Substring(index).Trim();
- fromEtc = fromEtc.Remove(index).Trim();
- }
- else
- {
- orderBy = "ORDER BY " + string.Join(", ", keys);
- }
- return orderBy;
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Simple.Data.Ado;
+
+namespace Simple.Data.SqlServer
+{
+ [Export(typeof(IQueryPager))]
+ public class SqlQueryPager : IQueryPager
+ {
+ private static readonly Regex ColumnExtract = new Regex(@"SELECT\s*(.*)\s*(FROM.*)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
+ private static readonly Regex SelectMatch = new Regex(@"^SELECT\s*(DISTINCT)?", RegexOptions.IgnoreCase);
+
+ public IEnumerable<string> ApplyLimit(string sql, int take)
+ {
+ yield return SelectMatch.Replace(sql, match => match.Value + " TOP " + take + " ");
+ }
+
+ public IEnumerable<string> ApplyPaging(string sql, string[] keys, int skip, int take)
+ {
+ var builder = new StringBuilder("WITH __Data AS (SELECT ");
+
+ var match = ColumnExtract.Match(sql);
+ var columns = match.Groups[1].Value.Trim();
+ var fromEtc = match.Groups[2].Value.Trim();
+
+ builder.Append(string.Join(",", keys));
+
+ var orderBy = ExtractOrderBy(columns, keys, ref fromEtc);
+
+ builder.AppendFormat(", ROW_NUMBER() OVER({0}) AS [_#_]", orderBy);
+ builder.AppendLine();
+ builder.Append(fromEtc);
+ builder.AppendLine(")");
+ builder.AppendFormat("SELECT {0} FROM __Data ", columns);
+ builder.AppendFormat("JOIN {0} ON ",
+ keys[0].Substring(0, keys[0].LastIndexOf(".", StringComparison.OrdinalIgnoreCase)));
+ builder.AppendFormat(string.Join(" ", keys.Select(MakeDataJoin)));
+ var rest = Regex.Replace(fromEtc, @"^from (\[.*?\]\.\[.*?\])", @"");
+ builder.Append(rest);
+
+ builder.AppendFormat(" AND [_#_] BETWEEN {0} AND {1}", skip + 1, skip + take);
+
+ yield return builder.ToString();
+ }
+
+ private static string MakeDataJoin(string key)
+ {
+ return key + " = __Data" + key.Substring(key.LastIndexOf(".", StringComparison.OrdinalIgnoreCase));
+ }
+
+ private static string DequalifyColumns(string original)
+ {
+ var q = from part in original.Split(',')
+ select part.Substring(Math.Max(part.LastIndexOf('.') + 1, part.LastIndexOf('[')));
+ return string.Join(",", q);
+ }
+
+ private static string ExtractOrderBy(string columns, string[] keys, ref string fromEtc)
+ {
+ string orderBy;
+ int index = fromEtc.IndexOf("ORDER BY", StringComparison.InvariantCultureIgnoreCase);
+ if (index > -1)
+ {
+ orderBy = fromEtc.Substring(index).Trim();
+ fromEtc = fromEtc.Remove(index).Trim();
+ }
+ else
+ {
+ orderBy = "ORDER BY " + string.Join(", ", keys);
+ }
+ return orderBy;
+ }
+ }
+}
View
152 Simple.Data.SqlTest/SqlQueryPagerTest.cs
@@ -1,70 +1,82 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using NUnit.Framework;
-using Simple.Data.SqlServer;
-
-namespace Simple.Data.SqlTest
-{
- [TestFixture]
- public class SqlQueryPagerTest
- {
- static readonly Regex Normalize = new Regex(@"\s+", RegexOptions.Multiline);
-
- [Test]
- public void ShouldApplyLimitUsingTop()
- {
- var sql = "select a,b,c from d where a = 1 order by c";
- var expected = new[]{ "select top 5 a,b,c from d where a = 1 order by c"};
-
- var pagedSql = new SqlQueryPager().ApplyLimit(sql, 5);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
-
- Assert.IsTrue(expected.SequenceEqual(modified));
- }
-
- [Test]
- public void ShouldApplyPagingUsingOrderBy()
- {
- var sql = "select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1 order by [dbo].[d].[c]";
- var expected = new[]{
- "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[c]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
- + " select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 6 and 15"};
-
- var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[] {"[dbo].[d].[a]"}, 5, 10);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
-
- Assert.AreEqual(expected[0], modified[0]);
- }
-
- [Test]
- public void ShouldApplyPagingUsingOrderByKeysIfNotAlreadyOrdered()
- {
- var sql = "select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1";
- var expected = new[]{
- "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[a]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
- + " select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 11 and 30"};
-
- var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[] {"[dbo].[d].[a]"}, 10, 20);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
-
- Assert.AreEqual(expected[0], modified[0]);
- }
-
- [Test]
- public void ShouldCopeWithAliasedColumns()
- {
- var sql = "select [dbo].[d].[a],[dbo].[d].[b] as [foo],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1";
- var expected =new[]{
- "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[a]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
- + " select [dbo].[d].[a],[dbo].[d].[b] as [foo],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 21 and 25"};
-
- var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[]{"[dbo].[d].[a]"}, 20, 5);
- var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
-
- Assert.AreEqual(expected[0], modified[0]);
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using NUnit.Framework;
+using Simple.Data.SqlServer;
+
+namespace Simple.Data.SqlTest
+{
+ [TestFixture]
+ public class SqlQueryPagerTest
+ {
+ static readonly Regex Normalize = new Regex(@"\s+", RegexOptions.Multiline);
+
+ [Test]
+ public void ShouldApplyLimitUsingTop()
+ {
+ var sql = "select a,b,c from d where a = 1 order by c";
+ var expected = new[] { "select top 5 a,b,c from d where a = 1 order by c" };
+
+ var pagedSql = new SqlQueryPager().ApplyLimit(sql, 5);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+
+ [Test]
+ public void ShouldApplyLimitUsingTopWithDistinct()
+ {
+ var sql = "select distinct a,b,c from d where a = 1 order by c";
+ var expected = new[] { "select distinct top 5 a,b,c from d where a = 1 order by c" };
+
+ var pagedSql = new SqlQueryPager().ApplyLimit(sql, 5);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant());
+
+ Assert.IsTrue(expected.SequenceEqual(modified));
+ }
+
+ [Test]
+ public void ShouldApplyPagingUsingOrderBy()
+ {
+ var sql = "select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1 order by [dbo].[d].[c]";
+ var expected = new[]{
+ "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[c]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
+ + " select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 6 and 15"};
+
+ var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[] {"[dbo].[d].[a]"}, 5, 10);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
+
+ Assert.AreEqual(expected[0], modified[0]);
+ }
+
+ [Test]
+ public void ShouldApplyPagingUsingOrderByKeysIfNotAlreadyOrdered()
+ {
+ var sql = "select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1";
+ var expected = new[]{
+ "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[a]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
+ + " select [dbo].[d].[a],[dbo].[d].[b],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 11 and 30"};
+
+ var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[] {"[dbo].[d].[a]"}, 10, 20);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
+
+ Assert.AreEqual(expected[0], modified[0]);
+ }
+
+ [Test]
+ public void ShouldCopeWithAliasedColumns()
+ {
+ var sql = "select [dbo].[d].[a],[dbo].[d].[b] as [foo],[dbo].[d].[c] from [dbo].[d] where [dbo].[d].[a] = 1";
+ var expected =new[]{
+ "with __data as (select [dbo].[d].[a], row_number() over(order by [dbo].[d].[a]) as [_#_] from [dbo].[d] where [dbo].[d].[a] = 1)"
+ + " select [dbo].[d].[a],[dbo].[d].[b] as [foo],[dbo].[d].[c] from __data join [dbo].[d] on [dbo].[d].[a] = __data.[a] where [dbo].[d].[a] = 1 and [_#_] between 21 and 25"};
+
+ var pagedSql = new SqlQueryPager().ApplyPaging(sql, new[]{"[dbo].[d].[a]"}, 20, 5);
+ var modified = pagedSql.Select(x => Normalize.Replace(x, " ").ToLowerInvariant()).ToArray();
+
+ Assert.AreEqual(expected[0], modified[0]);
+ }
+ }
+}

0 comments on commit 0f9d3c3

Please sign in to comment.
Something went wrong with that request. Please try again.