diff --git a/src/Carbunql/Building/SelectClauseExtension.cs b/src/Carbunql/Building/SelectClauseExtension.cs index 4be2bb6d..152c99b9 100644 --- a/src/Carbunql/Building/SelectClauseExtension.cs +++ b/src/Carbunql/Building/SelectClauseExtension.cs @@ -182,4 +182,10 @@ public static void Order(this SelectQuery source, string table, string column) source.OrderClause ??= new(); source.OrderClause.Add(item); } + + public static void Window(this SelectQuery source, NamedWindowDefinition namedWindow) + { + source.WindowClause ??= new(); + source.WindowClause.Add(namedWindow); + } } \ No newline at end of file diff --git a/src/Carbunql/Building/WindowFunctionExtension.cs b/src/Carbunql/Building/WindowFunctionExtension.cs index 91b5732c..a9954df6 100644 --- a/src/Carbunql/Building/WindowFunctionExtension.cs +++ b/src/Carbunql/Building/WindowFunctionExtension.cs @@ -1,34 +1,121 @@ using Carbunql.Clauses; +using Carbunql.Values; namespace Carbunql.Building; public static class WindowFunctionExtension { - public static void AddPartition(this OverClause source, ValueBase partition) - { - source.WindowDefinition ??= new(); - source.WindowDefinition.PartitionBy ??= new(); - source.WindowDefinition.PartitionBy.Add(partition); - } + public static void Partition(this OverClause source, ValueBase partition) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(partition); + } + + public static void Partition(this OverClause source, Func partitionbuilder) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(partitionbuilder()); + } + + public static void Partition(this OverClause source, SelectableItem order) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(order.Value); + } + + public static void Order(this OverClause source, ColumnValue order) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Order(order.ToSortable()); + } + + public static void Order(this OverClause source, SortableItem order) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Order(order); + } + + public static void Order(this OverClause source, Func orderbuilder) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Order(orderbuilder()); + } + + public static void Partition(this WindowDefinition source, ValueBase partition) + { + source.PartitionBy ??= new(); + source.PartitionBy.Add(partition); + } + + public static void Partition(this WindowDefinition source, Func partitionbuilder) + { + source.PartitionBy ??= new(); + source.PartitionBy.Add(partitionbuilder()); + } + + public static void Partition(this WindowDefinition source, SelectableItem order) + { + source.PartitionBy ??= new(); + source.PartitionBy.Add(order.Value); + } + + public static void Order(this WindowDefinition source, ColumnValue order) + { + source.OrderBy ??= new(); + source.OrderBy.Add(order.ToSortable()); + } + + public static void Order(this WindowDefinition source, SortableItem order) + { + source.OrderBy ??= new(); + source.OrderBy.Add(order); + } + + public static void Order(this WindowDefinition source, Func orderbuilder) + { + source.OrderBy ??= new(); + source.OrderBy.Add(orderbuilder()); + } + + public static void Partition(this NamedWindowDefinition source, ValueBase partition) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(partition); + } + + public static void Partition(this NamedWindowDefinition source, Func partitionbuilder) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(partitionbuilder()); + } + + public static void Partition(this NamedWindowDefinition source, SelectableItem order) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Partition(order.Value); + } + + public static void Order(this NamedWindowDefinition source, ColumnValue order) + { + source.WindowDefinition ??= new(); + source.WindowDefinition.Order(order.ToSortable()); + } - public static void AddPartition(this OverClause source, Func partitionbuilder) - { + public static void Order(this NamedWindowDefinition source, SelectableItem order) + { source.WindowDefinition ??= new(); - source.WindowDefinition.PartitionBy ??= new(); - source.WindowDefinition.PartitionBy.Add(partitionbuilder()); - } + source.WindowDefinition.Order(order.Value.ToSortable()); + } - public static void AddOrder(this OverClause source, SortableItem order) - { + public static void Order(this NamedWindowDefinition source, SortableItem order) + { source.WindowDefinition ??= new(); - source.WindowDefinition.OrderBy ??= new(); - source.WindowDefinition.OrderBy.Add(order); - } + source.WindowDefinition.Order(order); + } - public static void AddOrder(this OverClause source, Func orderbuilder) - { + public static void Order(this NamedWindowDefinition source, Func orderbuilder) + { source.WindowDefinition ??= new(); - source.WindowDefinition.OrderBy ??= new(); - source.WindowDefinition.OrderBy.Add(orderbuilder()); - } + source.WindowDefinition.Order(orderbuilder()); + } } diff --git a/src/Carbunql/Clauses/NamedWindowDefinition.cs b/src/Carbunql/Clauses/NamedWindowDefinition.cs index d4c3be0f..52c627b5 100644 --- a/src/Carbunql/Clauses/NamedWindowDefinition.cs +++ b/src/Carbunql/Clauses/NamedWindowDefinition.cs @@ -12,6 +12,12 @@ public NamedWindowDefinition() WindowDefinition = null!; } + public NamedWindowDefinition(string alias) + { + Alias = alias; + WindowDefinition = null!; + } + public NamedWindowDefinition(string alias, WindowDefinition definition) { Alias = alias; diff --git a/src/Carbunql/Clauses/OverClause.cs b/src/Carbunql/Clauses/OverClause.cs index 60850264..77fe02dd 100644 --- a/src/Carbunql/Clauses/OverClause.cs +++ b/src/Carbunql/Clauses/OverClause.cs @@ -16,6 +16,11 @@ public OverClause(WindowDefinition definition) WindowDefinition = definition; } + public OverClause(NamedWindowDefinition definition) + { + WindowDefinition = new WindowDefinition(definition.Alias); + } + public WindowDefinition WindowDefinition { get; set; } public IEnumerable GetCommonTables() diff --git a/test/Carbunql.Building.Test/FunctionTest.cs b/test/Carbunql.Building.Test/FunctionTest.cs index 4e3cc101..528b8e02 100644 --- a/test/Carbunql.Building.Test/FunctionTest.cs +++ b/test/Carbunql.Building.Test/FunctionTest.cs @@ -48,8 +48,8 @@ public void RowNumberTest() sq.Select(() => { var wf = new OverClause(); - wf.AddPartition(new ColumnValue(a, "parent_id")); - wf.AddOrder(new ColumnValue(a, "id").ToSortable()); + wf.Partition(new ColumnValue(a, "parent_id")); + wf.Order(new ColumnValue(a, "id").ToSortable()); return new FunctionValue("row_number", wf); }).As("val"); diff --git a/test/Carbunql.Building.Test/SerializeTest.ValueBase.cs b/test/Carbunql.Building.Test/SerializeTest.ValueBase.cs index b1bde723..36c42f05 100644 --- a/test/Carbunql.Building.Test/SerializeTest.ValueBase.cs +++ b/test/Carbunql.Building.Test/SerializeTest.ValueBase.cs @@ -231,8 +231,8 @@ public void QueryContainer() public void WindowFunction() { var win = new OverClause(); - win.AddPartition(new ColumnValue("shop_id")); - win.AddOrder(new Clauses.SortableItem(new ColumnValue("order_id"))); + win.Partition(new ColumnValue("shop_id")); + win.Order(new SortableItem(new ColumnValue("order_id"))); var sq = new FunctionValue("row_number", win); diff --git a/test/Carbunql.Building.Test/WindowTest.cs b/test/Carbunql.Building.Test/WindowTest.cs new file mode 100644 index 00000000..4be9fe47 --- /dev/null +++ b/test/Carbunql.Building.Test/WindowTest.cs @@ -0,0 +1,111 @@ +using Carbunql.Clauses; +using Carbunql.Values; +using Xunit.Abstractions; + +namespace Carbunql.Building.Test; + +public class WindowTest +{ + private readonly QueryCommandMonitor Monitor; + + public WindowTest(ITestOutputHelper output) + { + Monitor = new QueryCommandMonitor(output); + } + + [Fact] + public void Default() + { + var sq = new SelectQuery(); + var (_, a) = sq.From("table_a").As("a"); + + var pc = new PartitionClause + { + new ColumnValue(a, "name") + }; + var oc = new OrderClause { + new ColumnValue(a, "a_id").ToSortable() + }; + var wd = new WindowDefinition(pc, oc); + var w1 = new NamedWindowDefinition("w1", wd); + + sq.WindowClause ??= new(); + sq.WindowClause.Add(w1); + + sq.Select(new FunctionValue("row_number", new OverClause(w1))).As("row_num"); + + Monitor.Log(sq); + + var expect = @"SELECT + ROW_NUMBER() OVER w1 AS row_num +FROM + table_a AS a +WINDOW + w1 AS ( + PARTITION BY + a.name + ORDER BY + a.a_id + )"; + + Assert.Equal(expect, sq.ToText().ToString(), true, true, true); + } + + [Fact] + public void NamedWindow() + { + var sq = new SelectQuery(); + var (_, a) = sq.From("table_a").As("a"); + + var w = new NamedWindowDefinition("w1"); + w.Partition(new ColumnValue(a, "name")); + w.Order(new ColumnValue(a, "a_id")); + + sq.Window(w); + + sq.Select(new FunctionValue("row_number", new OverClause(w))).As("row_num"); + + Monitor.Log(sq); + + var expect = @"SELECT + ROW_NUMBER() OVER w1 AS row_num +FROM + table_a AS a +WINDOW + w1 AS ( + PARTITION BY + a.name + ORDER BY + a.a_id + )"; + + Assert.Equal(expect, sq.ToText().ToString(), true, true, true); + } + + [Fact] + public void Window() + { + var sq = new SelectQuery(); + var (_, a) = sq.From("table_a").As("a"); + + var w = new WindowDefinition(); + w.Partition(new ColumnValue(a, "name")); + w.Order(new ColumnValue(a, "a_id")); + + sq.Select(new FunctionValue("row_number", new OverClause(w))).As("row_num"); + + Monitor.Log(sq); + + var expect = @"SELECT + ROW_NUMBER() OVER( + PARTITION BY + a.name + ORDER BY + a.a_id + ) AS row_num +FROM + table_a AS a"; + + Assert.Equal(expect, sq.ToText().ToString(), true, true, true); + } +} \ No newline at end of file