Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 0f9d3c3
Showing
5 changed files
with
280 additions
and
256 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
} |
Oops, something went wrong.