Skip to content

Commit 27ef426

Browse files
committed
fix(clickhouse): preserve grouped aggregate dimensions
1 parent 47f06ea commit 27ef426

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

core/internal/dialect/clickhouse.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,15 @@ func (b *chBuilder) collectFields(sel *qcode.Select, n *chNode) error {
300300
n.Aggregates = append(n.Aggregates, chAggregate{Fn: f.Func.Name, Col: col, Alias: f.FieldName})
301301
}
302302
}
303+
// Mixed dimension+aggregate selections use the same grouping columns the
304+
// shared compiler calculated for SQL dialects. Without this, ClickHouse's
305+
// aggregate projection drops regular fields and silently collapses the
306+
// result to a single global aggregate row.
307+
if sel.GroupCols && !sel.GlobalAgg && len(n.Aggregates) > 0 {
308+
for _, col := range sel.BCols {
309+
n.GroupBy = ensureCol(n.GroupBy, col.Col.Name)
310+
}
311+
}
303312
for _, dc := range sel.DistinctOn {
304313
add(dc.Name)
305314
n.GroupBy = append(n.GroupBy, dc.Name)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dialect
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/dosco/graphjin/core/v3/internal/qcode"
8+
"github.com/dosco/graphjin/core/v3/internal/sdata"
9+
)
10+
11+
func TestClickHouseCollectFieldsGroupsCompilerBaseColumns(t *testing.T) {
12+
nameCol := sdata.DBColumn{Name: "name"}
13+
sel := &qcode.Select{
14+
GroupCols: true,
15+
Fields: []qcode.Field{
16+
{Type: qcode.FieldTypeCol, Col: nameCol, FieldName: "name"},
17+
{Type: qcode.FieldTypeFunc, Func: sdata.DBFunction{Name: "count", Agg: true}, FieldName: "count_id"},
18+
},
19+
BCols: []qcode.Column{{Col: nameCol}},
20+
}
21+
22+
node := &chNode{}
23+
if err := (&chBuilder{}).collectFields(sel, node); err != nil {
24+
t.Fatal(err)
25+
}
26+
if want := []string{"name"}; !reflect.DeepEqual(node.GroupBy, want) {
27+
t.Fatalf("group_by = %v, want %v", node.GroupBy, want)
28+
}
29+
if len(node.Aggregates) != 1 || node.Aggregates[0].Alias != "count_id" {
30+
t.Fatalf("aggregates = %+v", node.Aggregates)
31+
}
32+
}
33+
34+
func TestClickHouseCollectFieldsKeepsGlobalAggregateUngrouped(t *testing.T) {
35+
sel := &qcode.Select{
36+
GroupCols: true,
37+
GlobalAgg: true,
38+
Fields: []qcode.Field{
39+
{Type: qcode.FieldTypeFunc, Func: sdata.DBFunction{Name: "count", Agg: true}, FieldName: "count_id"},
40+
},
41+
}
42+
43+
node := &chNode{}
44+
if err := (&chBuilder{}).collectFields(sel, node); err != nil {
45+
t.Fatal(err)
46+
}
47+
if len(node.GroupBy) != 0 {
48+
t.Fatalf("global aggregate group_by = %v, want none", node.GroupBy)
49+
}
50+
}

tests/query_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2683,8 +2683,8 @@ func TestResultTruncatedRoots(t *testing.T) {
26832683
t.Fatalf("limit-clamped list not flagged: %+v", truncated)
26842684
}
26852685

2686-
if dbType == "mongodb" {
2687-
return // aggregate functions are not supported on the MongoDB harness
2686+
if dbType == "mongodb" || dbType == "cassandra" {
2687+
return // aggregate/group queries are not supported on these harnesses
26882688
}
26892689

26902690
res, err = gj.GraphQL(context.Background(),

0 commit comments

Comments
 (0)