Skip to content

Commit

Permalink
Merge pull request #373 from mk3008/372-i-want-to-convert-ienumerable…
Browse files Browse the repository at this point in the history
…t-to-a-query

Added query conversion functionality for IEnumerable<T>.
  • Loading branch information
mk3008 authored Mar 30, 2024
2 parents 091c305 + 42443af commit cad23f3
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 5 deletions.
49 changes: 49 additions & 0 deletions src/Carbunql/Building/IEnumerableExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Carbunql.Analysis.Parser;
using Carbunql.Clauses;
using Carbunql.Values;

namespace Carbunql.Building;

public static class IEnumerableExtension
{
public static ValuesQuery ToValuesQuery<T>(this IEnumerable<T> source)
{
return ToValuesQuery(source, DbmsConfiguration.PlaceholderIdentifier);
}

public static ValuesQuery ToValuesQuery<T>(this IEnumerable<T> source, string placeholderIndentifer)
{
var vq = new ValuesQuery();
var r = 0;
foreach (var row in source)
{
var lst = new List<ValueBase>();
var c = 0;
foreach (var column in typeof(T).GetProperties().Where(x => x.CanRead && x.CanWrite).Select(x => x.GetValue(row)))
{
var name = $"r{r}c{c}";
var v = placeholderIndentifer + name;
lst.Add(ValueParser.Parse(v));
vq.AddParameter(name, column);
c++;
}
vq.Rows.Add(new ValueCollection(lst));
r++;
}
return vq;
}

public static SelectQuery ToSelectQuery<T>(this IEnumerable<T> source)
{
return source.ToSelectQuery(DbmsConfiguration.PlaceholderIdentifier, x => x.ToLowerSnakeCase());
}

public static SelectQuery ToSelectQuery<T>(this IEnumerable<T> source, string placeholderIndentifer, Func<string, string> propertyNameConverter)
{
var columns = typeof(T).GetProperties().Where(x => x.CanRead && x.CanWrite).Select(x => propertyNameConverter(x.Name)).ToList();
var vq = source.ToValuesQuery(placeholderIndentifer);
var sq = vq.ToSelectQuery(columns);

return sq;
}
}
2 changes: 1 addition & 1 deletion src/Carbunql/Carbunql.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Title></Title>
<Copyright>mk3008net</Copyright>
<Description>Carbunql provides query parsing and building functionality.</Description>
<Version>0.7.4</Version>
<Version>0.7.5</Version>
<Authors>mk3008net</Authors>
<PackageProjectUrl>https://github.com/mk3008/Carbunql</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand Down
4 changes: 2 additions & 2 deletions src/Carbunql/IQueryCommandable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ public static QueryCommand ToOneLineCommand(this IQueryCommandable source)
return new QueryCommand(source.GetTokens().ToText(), source.GetParameters());
}

public static string ToText(this IQueryCommandable source)
public static string ToText(this IQueryCommandable source, bool exportParameterInfo = true)
{
var cmd = source.ToCommand();
var text = cmd.CommandText;

if (!cmd.Parameters.Any()) return text;
if (!cmd.Parameters.Any() || !exportParameterInfo) return text;

var head = GetParameterText(cmd);
if (string.IsNullOrEmpty(head)) return text;
Expand Down
11 changes: 9 additions & 2 deletions src/Carbunql/ValuesQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public ValuesQuery(string query)
LimitClause = q.LimitClause;
}


[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(IEnumerable<object> matrix)
{
foreach (var row in matrix)
Expand All @@ -38,20 +40,21 @@ public ValuesQuery(IEnumerable<object> matrix)

foreach (var column in row.GetType().GetProperties().Where(x => x.CanRead && x.CanWrite).Select(x => x.GetValue(row)))
{
if (column == null)
if (column == null || column.ToString() == null)
{
lst.Add(new LiteralValue("null"));
}
else
{
var v = $"\"{column}\"";
lst.Add(ValueParser.Parse(v));
lst.Add(ValueParser.Parse(column.ToString()!));
}
}
Rows.Add(new ValueCollection(lst));
}
}

[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(IEnumerable<object> matrix, string placeholderIndentifer)
{
var r = 0;
Expand All @@ -73,6 +76,7 @@ public ValuesQuery(IEnumerable<object> matrix, string placeholderIndentifer)
}
}

[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(string[,] matrix)
{
for (int row = 0; row < matrix.GetLength(0); row++)
Expand All @@ -88,6 +92,7 @@ public ValuesQuery(string[,] matrix)
}
}

[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(IEnumerable<IEnumerable<string>> matrix)
{
foreach (var row in matrix)
Expand All @@ -102,6 +107,7 @@ public ValuesQuery(IEnumerable<IEnumerable<string>> matrix)
}
}

[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(string[,] matrix, string placeholderIndentifer)
{
for (int row = 0; row < matrix.GetLength(0); row++)
Expand All @@ -119,6 +125,7 @@ public ValuesQuery(string[,] matrix, string placeholderIndentifer)
}
}

[Obsolete("This feature has been deprecated. Consider building it outside of class.")]
public ValuesQuery(IEnumerable<IEnumerable<string>> matrix, string placeholderIndentifer)
{
var r = 0;
Expand Down
148 changes: 148 additions & 0 deletions test/Carbunql.Building.Test/IEnumerableTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using Xunit.Abstractions;

namespace Carbunql.Building.Test;

public class IEnumerableTest
{
private readonly QueryCommandMonitor Monitor;

public IEnumerableTest(ITestOutputHelper output)
{
Monitor = new QueryCommandMonitor(output);
Output = output;
}

private ITestOutputHelper Output { get; set; }

[Fact]
public void ToValuesQuery()
{
var results = new List<ApiResult>()
{
new (){ ResultId = 1 , ResultText = "a", ResultValue = 10, ResultDate = new DateTime(2000,1,1), ResultBool= true },
new (){ ResultId = 2 , ResultText = "b", ResultValue = 20, ResultDate = new DateTime(2010,10,10), ResultBool= false },
new (){ ResultId = 3 , ResultText = null, ResultValue = null, ResultDate = null, ResultBool= null},
};

var q = results.ToValuesQuery();

var actual = q.ToText(true);
Output.WriteLine(actual);

var expect = @"/*
r0c0 = 1
r0c1 = 'a'
r0c2 = 10
r0c3 = 2000/01/01 0:00:00
r0c4 = True
r1c0 = 2
r1c1 = 'b'
r1c2 = 20
r1c3 = 2010/10/10 0:00:00
r1c4 = False
r2c0 = 3
r2c1 is NULL
r2c2 is NULL
r2c3 is NULL
r2c4 is NULL
*/
VALUES
(:r0c0, :r0c1, :r0c2, :r0c3, :r0c4),
(:r1c0, :r1c1, :r1c2, :r1c3, :r1c4),
(:r2c0, :r2c1, :r2c2, :r2c3, :r2c4)";

Assert.Equal(expect, actual, true, true, true);
}

[Fact]
public void ToSelectQuery()
{
var results = new List<ApiResult>()
{
new (){ ResultId = 1 , ResultText = "a", ResultValue = 10, ResultDate = new DateTime(2000,1,1), ResultBool= true },
new (){ ResultId = 2 , ResultText = "b", ResultValue = 20, ResultDate = new DateTime(2010,10,10), ResultBool= false },
new (){ ResultId = 3 , ResultText = null, ResultValue = null, ResultDate = null, ResultBool= null},
};

var q = results.ToSelectQuery();

var actual = q.ToText(true);
Output.WriteLine(actual);

var expect = @"/*
r0c0 = 1
r0c1 = 'a'
r0c2 = 10
r0c3 = 2000/01/01 0:00:00
r0c4 = True
r1c0 = 2
r1c1 = 'b'
r1c2 = 20
r1c3 = 2010/10/10 0:00:00
r1c4 = False
r2c0 = 3
r2c1 is NULL
r2c2 is NULL
r2c3 is NULL
r2c4 is NULL
*/
SELECT
v.result_id,
v.result_text,
v.result_value,
v.result_date,
v.result_bool
FROM
(
VALUES
(:r0c0, :r0c1, :r0c2, :r0c3, :r0c4),
(:r1c0, :r1c1, :r1c2, :r1c3, :r1c4),
(:r2c0, :r2c1, :r2c2, :r2c3, :r2c4)
) AS v (
result_id, result_text, result_value, result_date, result_bool
)";

Assert.Equal(expect, actual, true, true, true);
}

[Fact]
public void ToSelectQuery_50k()
{
var results = GenerateDummyResults(50000);

var q = results.ToSelectQuery();

var actual = q.ToText(false);
Output.WriteLine(actual);
}

private static List<ApiResult> GenerateDummyResults(int count)
{
var dummyResults = new List<ApiResult>();

for (int i = 0; i < count; i++)
{
var result = new ApiResult
{
ResultId = i + 1,
ResultText = "dummy",
ResultValue = i * 10,
ResultDate = DateTime.Now.AddDays(i),
ResultBool = i % 2 == 0
};

dummyResults.Add(result);
}

return dummyResults;
}

public class ApiResult
{
public int ResultId { get; set; }
public string? ResultText { get; set; }
public long? ResultValue { get; set; }
public DateTime? ResultDate { get; set; }
public bool? ResultBool { get; set; }
}
}

0 comments on commit cad23f3

Please sign in to comment.