Skip to content
Permalink
Browse files

Merge pull request #535 from promatcloud/development

Integration Test demonstrating defect generating sql Count
  • Loading branch information...
pleb committed Jun 11, 2019
2 parents 20df502 + 0f6a653 commit 96639bdf1909c422f776665d00cac1496bffb01b
Showing with 54 additions and 15 deletions.
  1. +32 −0 PetaPoco.Tests.Integration/Databases/BaseQueryTests.cs
  2. +22 −15 PetaPoco/Utilities/PagingHelper.cs
@@ -5,6 +5,7 @@
using PetaPoco.Core;
using PetaPoco.Providers;
using PetaPoco.Tests.Integration.Models;
using PetaPoco.Utilities;
using Shouldly;
using Xunit;

@@ -17,6 +18,37 @@ protected BaseQueryTests(DBTestProvider provider)
{
}

[Fact]
public virtual void Page_SqlCountStatmentNotWoksCorrectlyWithGroupBy()
{
// Add duplicate names
AddPeople(15, 5);
AddPeople(5, 3);

var pd = PocoData.ForType(typeof(Person), DB.DefaultMapper);
var columnName = DB.Provider.EscapeSqlIdentifier(pd.Columns.Values.First(c => c.PropertyInfo.Name == "Name").ColumnName);
var tableName = DB.Provider.EscapeSqlIdentifier(pd.TableInfo.TableName);
var sql = Sql.Builder
.Select(columnName)
.From(tableName)
.Where($"{columnName} = @0", "Peta1")
.GroupBy(columnName)
.OrderBy(columnName);

// Obtain items
var fetchResult = DB.Fetch<string>(sql);

PagingHelper.Instance.SplitSQL(sql.SQL, out var sqlParts);

var correctSyntax = $"SELECT COUNT(*) FROM (SELECT {sqlParts.SqlSelectRemoved.Replace(sqlParts.SqlOrderBy, string.Empty)}) countAlias";

var correctNumberOfTotalItems = DB.Single<int>(correctSyntax, sql.Arguments);
var page = DB.Page<Person>(2, 3, sql);

fetchResult.Count.ShouldBe(correctNumberOfTotalItems);
page.TotalItems.ShouldBe(fetchResult.Count, $"Statment {sqlParts.SqlCount} is not correct. Correct syntax is {correctSyntax}");
}

[Fact]
public virtual void Page_ForPocoGivenSqlWithoutOrderByParameterPageItemAndPerPage_ShouldReturnValidPocoCollection()
{
@@ -17,6 +17,10 @@ public class PagingHelper : IPagingHelper
public Regex SimpleRegexOrderBy = new Regex(@"\bORDER\s+BY\s+",
RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);

public Regex RegexGroupBy = new Regex(@"\bGROUP\s+BY\s+(?!.*?(?:\)|\s+)AS\s)(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\[\]`""\w\(\)\.])+(?:)?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\[\]`""\w\(\)\.])+(?:)?)*",
RegexOptions.RightToLeft | RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);


public static IPagingHelper Instance { get; private set; }

static PagingHelper()
@@ -34,30 +38,33 @@ public bool SplitSQL(string sql, out SQLParts parts)
{
parts.Sql = sql;
parts.SqlSelectRemoved = null;
parts.SqlCount = null;
parts.SqlCount = sql;
parts.SqlOrderBy = null;

// Extract the columns from "SELECT <whatever> FROM"
var m = RegexColumns.Match(sql);
if (!m.Success)
var columnsMatch = RegexColumns.Match(sql);
if (!columnsMatch.Success)
return false;

// Look for the last "ORDER BY <whatever>" clause not part of a ROW_NUMBER expression
var orderByMatch = RegexOrderBy.Match(sql);
if (orderByMatch.Success)
{
parts.SqlOrderBy = orderByMatch.Value;
parts.SqlCount = sql.Replace(orderByMatch.Value, string.Empty);
}

// Save column list and replace with COUNT(*)
var g = m.Groups[1];
parts.SqlSelectRemoved = sql.Substring(g.Index);
var columnsGroup = columnsMatch.Groups[1];
parts.SqlSelectRemoved = sql.Substring(columnsGroup.Index);

if (RegexDistinct.IsMatch(parts.SqlSelectRemoved))
parts.SqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
if (RegexDistinct.IsMatch(parts.SqlSelectRemoved) || RegexGroupBy.IsMatch(parts.SqlSelectRemoved))
{
parts.SqlCount = sql.Substring(0, columnsGroup.Index) + "COUNT(*) FROM (" + parts.SqlCount + ") countAlias";
}
else
parts.SqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);

// Look for the last "ORDER BY <whatever>" clause not part of a ROW_NUMBER expression
m = SimpleRegexOrderBy.Match(parts.SqlCount);
if (m.Success)
{
g = m.Groups[0];
parts.SqlOrderBy = g + parts.SqlCount.Substring(g.Index + g.Length);
parts.SqlCount = parts.SqlCount.Substring(0, g.Index);
parts.SqlCount = sql.Substring(0, columnsGroup.Index) + "COUNT(*) " + parts.SqlCount.Substring(columnsGroup.Index + columnsGroup.Length);
}

return true;

0 comments on commit 96639bd

Please sign in to comment.
You can’t perform that action at this time.