Skip to content

Commit 32a374b

Browse files
frouiouisystay
andauthored
[planner bugfix] add expressions to HAVING (vitessio#12668) (vitessio#12699)
* [planner bugfix] add expressions to HAVING When a predicate contains aggregation, it should not be added to the WHERE clause. It should go to the * update test expecteations --------- Signed-off-by: Andres Taylor <andres@planetscale.com> Signed-off-by: Florent Poinsard <florent.poinsard@outlook.fr> Co-authored-by: Andres Taylor <andres@planetscale.com>
1 parent 0fbe50f commit 32a374b

File tree

4 files changed

+72
-33
lines changed

4 files changed

+72
-33
lines changed

go/vt/sqlparser/ast_funcs.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -991,10 +991,8 @@ func (node *Select) AddHaving(expr Expr) {
991991
}
992992
return
993993
}
994-
node.Having.Expr = &AndExpr{
995-
Left: node.Having.Expr,
996-
Right: expr,
997-
}
994+
exprs := SplitAndExpression(nil, node.Having.Expr)
995+
node.Having.Expr = AndExpressions(append(exprs, expr)...)
998996
}
999997

1000998
// AddGroupBy adds a grouping expression, unless it's already present

go/vt/sqlparser/ast_test.go

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -57,32 +57,22 @@ func TestSelect(t *testing.T) {
5757
sel.AddWhere(expr)
5858
buf := NewTrackedBuffer(nil)
5959
sel.Where.Format(buf)
60-
want := " where a = 1"
61-
if buf.String() != want {
62-
t.Errorf("where: %q, want %s", buf.String(), want)
63-
}
60+
assert.Equal(t, " where a = 1", buf.String())
6461
sel.AddWhere(expr)
6562
buf = NewTrackedBuffer(nil)
6663
sel.Where.Format(buf)
67-
want = " where a = 1"
68-
if buf.String() != want {
69-
t.Errorf("where: %q, want %s", buf.String(), want)
70-
}
64+
assert.Equal(t, " where a = 1", buf.String())
65+
7166
sel = &Select{}
7267
sel.AddHaving(expr)
7368
buf = NewTrackedBuffer(nil)
7469
sel.Having.Format(buf)
75-
want = " having a = 1"
76-
if buf.String() != want {
77-
t.Errorf("having: %q, want %s", buf.String(), want)
78-
}
70+
assert.Equal(t, " having a = 1", buf.String())
71+
7972
sel.AddHaving(expr)
8073
buf = NewTrackedBuffer(nil)
8174
sel.Having.Format(buf)
82-
want = " having a = 1 and a = 1"
83-
if buf.String() != want {
84-
t.Errorf("having: %q, want %s", buf.String(), want)
85-
}
75+
assert.Equal(t, " having a = 1", buf.String())
8676

8777
tree, err = Parse("select * from t where a = 1 or b = 1")
8878
require.NoError(t, err)
@@ -91,18 +81,14 @@ func TestSelect(t *testing.T) {
9181
sel.AddWhere(expr)
9282
buf = NewTrackedBuffer(nil)
9383
sel.Where.Format(buf)
94-
want = " where a = 1 or b = 1"
95-
if buf.String() != want {
96-
t.Errorf("where: %q, want %s", buf.String(), want)
97-
}
84+
assert.Equal(t, " where a = 1 or b = 1", buf.String())
85+
9886
sel = &Select{}
9987
sel.AddHaving(expr)
10088
buf = NewTrackedBuffer(nil)
10189
sel.Having.Format(buf)
102-
want = " having a = 1 or b = 1"
103-
if buf.String() != want {
104-
t.Errorf("having: %q, want %s", buf.String(), want)
105-
}
90+
assert.Equal(t, " having a = 1 or b = 1", buf.String())
91+
10692
}
10793

10894
func TestUpdate(t *testing.T) {

go/vt/vtgate/planbuilder/operator_to_query.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func buildQuery(op abstract.PhysicalOperator, qb *queryBuilder) {
8989
sel.Limit = opQuery.Limit
9090
sel.OrderBy = opQuery.OrderBy
9191
sel.GroupBy = opQuery.GroupBy
92-
sel.Having = opQuery.Having
92+
sel.Having = mergeHaving(sel.Having, opQuery.Having)
9393
sel.SelectExprs = opQuery.SelectExprs
9494
qb.addTableExpr(op.Alias, op.Alias, op.TableID(), &sqlparser.DerivedTable{
9595
Select: sel,
@@ -161,12 +161,16 @@ func (qb *queryBuilder) addPredicate(expr sqlparser.Expr) {
161161
}
162162

163163
sel := qb.sel.(*sqlparser.Select)
164-
if sel.Where == nil {
165-
sel.AddWhere(expr)
166-
return
164+
_, isSubQuery := expr.(*sqlparser.ExtractedSubquery)
165+
var addPred func(sqlparser.Expr)
166+
167+
if sqlparser.ContainsAggregation(expr) && !isSubQuery {
168+
addPred = sel.AddHaving
169+
} else {
170+
addPred = sel.AddWhere
167171
}
168172
for _, exp := range sqlparser.SplitAndExpression(nil, expr) {
169-
sel.AddWhere(exp)
173+
addPred(exp)
170174
}
171175
}
172176

@@ -288,3 +292,17 @@ func (ts *tableSorter) Less(i, j int) bool {
288292
func (ts *tableSorter) Swap(i, j int) {
289293
ts.sel.From[i], ts.sel.From[j] = ts.sel.From[j], ts.sel.From[i]
290294
}
295+
296+
func mergeHaving(h1, h2 *sqlparser.Where) *sqlparser.Where {
297+
switch {
298+
case h1 == nil && h2 == nil:
299+
return nil
300+
case h1 == nil:
301+
return h2
302+
case h2 == nil:
303+
return h1
304+
default:
305+
h1.Expr = sqlparser.AndExpressions(h1.Expr, h2.Expr)
306+
return h1
307+
}
308+
}

go/vt/vtgate/planbuilder/testdata/aggr_cases.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4996,5 +4996,42 @@
49964996
"user.user"
49974997
]
49984998
}
4999+
},
5000+
{
5001+
"comment": "when pushing predicates into derived tables, make sure to put them in HAVING when they contain aggregations",
5002+
"query": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) as t1 where count >= :v2",
5003+
"v3-plan": {
5004+
"QueryType": "SELECT",
5005+
"Original": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) as t1 where count >= :v2",
5006+
"Instructions": {
5007+
"OperatorType": "Route",
5008+
"Variant": "Scatter",
5009+
"Keyspace": {
5010+
"Name": "user",
5011+
"Sharded": true
5012+
},
5013+
"FieldQuery": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where 1 != 1 group by user_id, flowId) as t1 where 1 != 1",
5014+
"Query": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where localDate > :v1 group by user_id, flowId order by null) as t1 where `count` >= :v2",
5015+
"Table": "user_extra"
5016+
}
5017+
},
5018+
"gen4-plan": {
5019+
"QueryType": "SELECT",
5020+
"Original": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) as t1 where count >= :v2",
5021+
"Instructions": {
5022+
"OperatorType": "Route",
5023+
"Variant": "Scatter",
5024+
"Keyspace": {
5025+
"Name": "user",
5026+
"Sharded": true
5027+
},
5028+
"FieldQuery": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where 1 != 1 group by user_id, flowId) as t1 where 1 != 1",
5029+
"Query": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where localDate > :v1 group by user_id, flowId having count(*) >= :v2 order by null) as t1",
5030+
"Table": "user_extra"
5031+
},
5032+
"TablesUsed": [
5033+
"user.user_extra"
5034+
]
5035+
}
49995036
}
50005037
]

0 commit comments

Comments
 (0)