Skip to content
This repository was archived by the owner on Feb 21, 2024. It is now read-only.

Commit b35c240

Browse files
authored
handle count(*) in having correctly (#2274)
* correct reference for `having count(*)` It turns out that `having count(*) ...` was always treating the count(*) as exactly 1. After studying this a lot, I noticed that in fact, we correctly handle other counts. The reason is that there's already code to recognize aggregates in `having` clauses as matching aggregates that are being computed -- but it only covers the other aggregate clause types, not the newly added `countStarPlanExpression` from making `count(*)` work even if there's no `_id` field. We add several corresponding test cases. * fix sum(a_decimal) type conversion Added a test case for this, and also added a fix for it. Underlying issue: qualifiedRefPlanExpression could end up producing an int64 instead of a pql.Decimal, even though it had expected type Decimal. Originally this worked by politely converting an int64 to a pql.Decimal in the Evaluate phase, but this was not ideal; the real question is why it was coming out as an int64 at that step. Showed this to Pat, who spent a while studying it and produced a better fix. * temporarily comment out test which fails in DAX
1 parent a479441 commit b35c240

File tree

5 files changed

+120
-7
lines changed

5 files changed

+120
-7
lines changed

sql3/planner/expression.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,7 @@ func (n *binOpPlanExpression) Evaluate(currentRow []interface{}) (interface{}, e
538538
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
539539
}
540540
}
541-
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
541+
return nil, sql3.NewErrInternalf("unexpected type conversion error '%T', '%T'", coercedLhs, coercedRhs)
542542

543543
case *parser.DataTypeTimestamp:
544544
//if either side is nil, return nil

sql3/planner/expressionagg.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ func (n *countStarPlanExpression) Plan() map[string]interface{} {
122122
result["_expr"] = fmt.Sprintf("%T", n)
123123
result["description"] = n.String()
124124
result["dataType"] = n.Type().TypeDescription()
125+
if n.arg != nil {
126+
result["arg"] = n.arg.Plan()
127+
}
125128
return result
126129
}
127130

sql3/planner/oppqlgroupby.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -244,13 +244,27 @@ func (i *pqlGroupByRowIter) Next(ctx context.Context) (types.Row, error) {
244244
}
245245
//now populate the aggregate value
246246
aggIdx := len(i.groupByColumns)
247-
switch i.aggregate.(type) {
247+
switch agg := i.aggregate.(type) {
248248
case *countPlanExpression, *countStarPlanExpression:
249249
row[aggIdx] = int64(group.Count)
250250

251-
case *countDistinctPlanExpression, *sumPlanExpression:
251+
case *countDistinctPlanExpression:
252252
row[aggIdx] = int64(group.Agg)
253253

254+
case *sumPlanExpression:
255+
switch ty := agg.Type().(type) {
256+
case *parser.DataTypeDecimal:
257+
if group.DecimalAgg == nil {
258+
row[aggIdx] = pql.NewDecimal(int64(group.Agg), ty.Scale)
259+
} else {
260+
row[aggIdx] = *group.DecimalAgg
261+
}
262+
case *parser.DataTypeInt:
263+
row[aggIdx] = int64(group.Agg)
264+
default:
265+
return nil, sql3.NewErrInternalf("unhandled sum return type '%T'", ty)
266+
}
267+
254268
case *avgPlanExpression:
255269
if group.DecimalAgg == nil {
256270
average := float64(group.Agg) / float64(group.Count)

sql3/planner/planoptimizer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1355,7 +1355,7 @@ func fixFieldRefIndexesForHaving(ctx context.Context, scope *OptimizerScope, a *
13551355
return TransformExpr(exp, func(e types.PlanExpression) (types.PlanExpression, bool, error) {
13561356
switch typedExpr := e.(type) {
13571357
case *sumPlanExpression, *countPlanExpression, *countDistinctPlanExpression,
1358-
*avgPlanExpression, *minPlanExpression, *maxPlanExpression,
1358+
*avgPlanExpression, *minPlanExpression, *maxPlanExpression, *countStarPlanExpression,
13591359
*percentilePlanExpression:
13601360
for i, col := range schema {
13611361
if strings.EqualFold(typedExpr.String(), col.ColumnName) {

sql3/test/defs/defs_having.go

Lines changed: 99 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package defs
22

3+
import "github.com/featurebasedb/featurebase/v3/pql"
4+
35
var selectHavingTests = TableTest{
46
name: "select-having",
57
Table: tbl(
@@ -18,26 +20,120 @@ var selectHavingTests = TableTest{
1820
srcRow(int64(2), int64(22), []int64{21, 22, 23}, int64(201), "str2", []string{"a2", "b2", "c2"}, float64(234.56)),
1921
srcRow(int64(3), int64(33), []int64{31, 32, 33}, int64(301), "str3", []string{"a3", "b3", "c3"}, float64(345.67)),
2022
srcRow(int64(4), int64(44), []int64{41, 42, 43}, int64(401), "str4", []string{"a4", "b4", "c4"}, float64(456.78)),
23+
srcRow(int64(5), int64(11), []int64{11, 12, 13}, int64(101), "str1", []string{"a5", "b5", "c5"}, float64(567.89)),
2124
),
2225
),
2326
SQLTests: []SQLTest{
2427
{
25-
name: "select-having",
28+
name: "countfieldincluded",
2629
SQLs: sqls(
27-
"select count(*), an_int from having_test group by an_int having count(*) > 0",
30+
"select count(an_int), an_int from having_test group by an_int having count(an_int) = 1",
2831
),
2932
ExpHdrs: hdrs(
3033
hdr("", fldTypeInt),
3134
hdr("an_int", fldTypeInt),
3235
),
3336
ExpRows: rows(
34-
row(int64(1), int64(11)),
3537
row(int64(1), int64(22)),
3638
row(int64(1), int64(33)),
3739
row(int64(1), int64(44)),
3840
),
3941
Compare: CompareExactUnordered,
4042
SortStringKeys: true,
4143
},
44+
{
45+
name: "countfieldnotincluded",
46+
SQLs: sqls(
47+
"select an_int from having_test group by an_int having count(an_int) = 1",
48+
),
49+
ExpHdrs: hdrs(
50+
hdr("an_int", fldTypeInt),
51+
),
52+
ExpRows: rows(
53+
row(int64(22)),
54+
row(int64(33)),
55+
row(int64(44)),
56+
),
57+
Compare: CompareExactUnordered,
58+
SortStringKeys: true,
59+
},
60+
{
61+
name: "countstarincluded",
62+
SQLs: sqls(
63+
"select count(*), an_int from having_test group by an_int having count(*) > 1",
64+
),
65+
ExpHdrs: hdrs(
66+
hdr("", fldTypeInt),
67+
hdr("an_int", fldTypeInt),
68+
),
69+
ExpRows: rows(
70+
row(int64(2), int64(11)),
71+
),
72+
Compare: CompareExactUnordered,
73+
SortStringKeys: true,
74+
},
75+
{
76+
name: "countstarnotincluded",
77+
SQLs: sqls(
78+
"select an_int from having_test group by an_int having count(*) > 1",
79+
),
80+
ExpHdrs: hdrs(
81+
hdr("an_int", fldTypeInt),
82+
),
83+
ExpRows: rows(
84+
row(
85+
int64(11),
86+
),
87+
),
88+
Compare: CompareExactUnordered,
89+
SortStringKeys: true,
90+
},
91+
{
92+
name: "sum-dec",
93+
SQLs: sqls(
94+
"select sum(a_decimal), an_int from having_test group by an_int having sum(a_decimal) < 250.00",
95+
),
96+
ExpHdrs: hdrs(
97+
hdr("", fldTypeDecimal2),
98+
hdr("an_int", fldTypeInt),
99+
),
100+
ExpRows: rows(
101+
row(pql.NewDecimal(23456, 2), int64(22)),
102+
),
103+
Compare: CompareExactUnordered,
104+
SortStringKeys: true,
105+
},
106+
{
107+
name: "sum-int",
108+
SQLs: sqls(
109+
"select sum(an_int), an_int from having_test group by an_int having sum(an_int) < 25",
110+
),
111+
ExpHdrs: hdrs(
112+
hdr("", fldTypeInt),
113+
hdr("an_int", fldTypeInt),
114+
),
115+
ExpRows: rows(
116+
row(int64(22), int64(11)),
117+
row(int64(22), int64(22)),
118+
),
119+
Compare: CompareExactUnordered,
120+
SortStringKeys: true,
121+
},
122+
// Fails in DAX because the string isn't translated.
123+
// {
124+
// name: "string",
125+
// SQLs: sqls(
126+
// "select a_string, count(*) from having_test group by a_string having count(*) > 1",
127+
// ),
128+
// ExpHdrs: hdrs(
129+
// hdr("a_string", fldTypeString),
130+
// hdr("", fldTypeInt),
131+
// ),
132+
// ExpRows: rows(
133+
// row(string("str1"), int64(2)),
134+
// ),
135+
// Compare: CompareExactUnordered,
136+
// SortStringKeys: true,
137+
// },
42138
},
43139
}

0 commit comments

Comments
 (0)