Skip to content

Commit c97627f

Browse files
[release-22.0] bugfix: allow window functions when possible to push down (#18103) (#18106)
Signed-off-by: Andres Taylor <[email protected]> Co-authored-by: vitess-bot[bot] <108069721+vitess-bot[bot]@users.noreply.github.com>
1 parent a373cad commit c97627f

File tree

6 files changed

+78
-31
lines changed

6 files changed

+78
-31
lines changed

go/vt/vtgate/planbuilder/operators/plan_query.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,36 @@ func PlanQuery(ctx *plancontext.PlanningContext, stmt sqlparser.Statement) (resu
8282

8383
// checkSingleRouteError checks if the query has a NotSingleRouteErr and more than one route, and returns an error if it does
8484
func checkSingleRouteError(ctx *plancontext.PlanningContext, op Operator) error {
85-
if ctx.SemTable.NotSingleRouteErr == nil {
85+
if ctx.SemTable.NotSingleRouteErr == nil && ctx.SemTable.NotSingleShardErr == nil {
8686
return nil
8787
}
88+
err := ctx.SemTable.NotSingleRouteErr
89+
if err == nil {
90+
err = ctx.SemTable.NotSingleShardErr
91+
}
8892
routes := 0
93+
var singleShard bool
8994
visitF := func(op Operator, _ semantics.TableSet, _ bool) (Operator, *ApplyResult) {
90-
switch op.(type) {
95+
switch op := op.(type) {
9196
case *Route:
97+
9298
routes++
99+
singleShard = op.IsSingleShard()
93100
}
94101
return op, NoRewrite
95102
}
96103

97104
// we'll walk the tree and count the number of routes
98105
TopDown(op, TableID, visitF, stopAtRoute)
99106

100-
if routes <= 1 {
101-
return nil
107+
if routes > 1 {
108+
return err
102109
}
103110

104-
return ctx.SemTable.NotSingleRouteErr
111+
if ctx.SemTable.NotSingleShardErr != nil && !singleShard {
112+
return ctx.SemTable.NotSingleShardErr
113+
}
114+
return nil
105115
}
106116

107117
func PanicHandler(err *error) {

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6391,6 +6391,33 @@
63916391
},
63926392
"skip_e2e": true
63936393
},
6394+
{
6395+
"comment": "Over clause in a equal unique query going to a single shard",
6396+
"query": "SELECT Sum(id) OVER ( PARTITION BY name ) from user where id = 5",
6397+
"plan": {
6398+
"Type": "Passthrough",
6399+
"QueryType": "SELECT",
6400+
"Original": "SELECT Sum(id) OVER ( PARTITION BY name ) from user where id = 5",
6401+
"Instructions": {
6402+
"OperatorType": "Route",
6403+
"Variant": "EqualUnique",
6404+
"Keyspace": {
6405+
"Name": "user",
6406+
"Sharded": true
6407+
},
6408+
"FieldQuery": "select sum(id) over ( partition by `name`) from `user` where 1 != 1",
6409+
"Query": "select sum(id) over ( partition by `name`) from `user` where id = 5",
6410+
"Table": "`user`",
6411+
"Values": [
6412+
"5"
6413+
],
6414+
"Vindex": "user_index"
6415+
},
6416+
"TablesUsed": [
6417+
"user.user"
6418+
]
6419+
}
6420+
},
63946421
{
63956422
"comment": "join with derived table with alias and join condition - merge into route",
63966423
"query": "select 1 from user join (select id as uid from user) as t where t.uid = user.id",

go/vt/vtgate/semantics/analyzer.go

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ type analyzer struct {
4444
inProjection int
4545

4646
notSingleRouteErr error
47+
notSingleShardErr error
4748
unshardedErr error
4849
warning string
4950
canShortcut bool
@@ -144,6 +145,7 @@ func (a *analyzer) newSemTable(
144145
Collation: coll,
145146
ExprTypes: map[sqlparser.Expr]evalengine.Type{},
146147
NotSingleRouteErr: a.notSingleRouteErr,
148+
NotSingleShardErr: a.notSingleShardErr,
147149
NotUnshardedErr: a.unshardedErr,
148150
Recursive: ExprDependencies{},
149151
Direct: ExprDependencies{},
@@ -177,6 +179,7 @@ func (a *analyzer) newSemTable(
177179
DMLTargets: a.binder.targets,
178180
NotSingleRouteErr: a.notSingleRouteErr,
179181
NotUnshardedErr: a.unshardedErr,
182+
NotSingleShardErr: a.notSingleShardErr,
180183
Warning: a.warning,
181184
Comments: comments,
182185
ExprEqualities: NewTransitiveClosures(),
@@ -198,6 +201,8 @@ func (a *analyzer) setError(err error) {
198201
a.notSingleRouteErr = err.Inner
199202
case ShardedError:
200203
a.unshardedErr = err.Inner
204+
case NotSingleShardError:
205+
a.notSingleShardErr = err.Inner
201206
default:
202207
if a.inProjection > 0 && vterrors.ErrState(err) == vterrors.NonUniqError {
203208
a.notSingleRouteErr = err
@@ -491,26 +496,37 @@ func (a *analyzer) getError() error {
491496
return a.err
492497
}
493498

494-
// NotSingleRouteErr is used to mark an error as something that should only be returned
495-
// if the planner fails to merge everything down to a single route
496-
type NotSingleRouteErr struct {
497-
Inner error
498-
}
499+
type (
500+
// NotSingleRouteErr is used to mark an error as something that should only be returned
501+
// if the planner fails to merge everything down to a single route
502+
NotSingleRouteErr struct {
503+
Inner error
504+
}
505+
// ShardedError is used to mark an error as something that should only be returned
506+
// if the query is not unsharded
507+
ShardedError struct {
508+
Inner error
509+
}
510+
// NotSingleShardError is used to mark an error as something that should only be returned
511+
// if the query fails to be planned into a single shard query
512+
NotSingleShardError struct {
513+
Inner error
514+
}
515+
)
499516

500517
func (p NotSingleRouteErr) Error() string {
501518
return p.Inner.Error()
502519
}
520+
func (p NotSingleRouteErr) Unwrap() error { return p.Inner }
503521

504-
// ShardedError is used to mark an error as something that should only be returned
505-
// if the query is not unsharded
506-
type ShardedError struct {
507-
Inner error
522+
func (p ShardedError) Error() string {
523+
return p.Inner.Error()
508524
}
509-
510525
func (p ShardedError) Unwrap() error {
511526
return p.Inner
512527
}
513528

514-
func (p ShardedError) Error() string {
529+
func (p NotSingleShardError) Error() string {
515530
return p.Inner.Error()
516531
}
532+
func (p NotSingleShardError) Unwrap() error { return p.Inner }

go/vt/vtgate/semantics/binder.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package semantics
1818

1919
import (
20+
"errors"
2021
"strings"
2122

2223
"vitess.io/vitess/go/vt/sqlparser"
@@ -303,22 +304,11 @@ func (b *binder) resolveColumn(colName *sqlparser.ColName, current *scope, allow
303304
return dependency{}, ShardedError{ColumnNotFoundError{Column: colName, Table: tableName}}
304305
}
305306

306-
func isColumnNotFound(err error) bool {
307-
switch err := err.(type) {
308-
case ColumnNotFoundError:
309-
return true
310-
case ShardedError:
311-
return isColumnNotFound(err.Inner)
312-
default:
313-
return false
314-
}
315-
}
316-
317307
func (b *binder) resolveColumnInHaving(colName *sqlparser.ColName, current *scope, allowMulti bool) (dependency, error) {
318308
if current.inHavingAggr {
319309
// when inside an aggregation, we'll search the FROM clause before the SELECT expressions
320310
deps, err := b.resolveColumn(colName, current.parent, allowMulti, true)
321-
if deps.direct.NotEmpty() || (err != nil && !isColumnNotFound(err)) {
311+
if deps.direct.NotEmpty() || (err != nil && !errors.As(err, &ColumnNotFoundError{})) {
322312
return deps, err
323313
}
324314
}
@@ -354,7 +344,7 @@ func (b *binder) resolveColumnInHaving(colName *sqlparser.ColName, current *scop
354344

355345
if !current.inHavingAggr && sel.GroupBy == nil {
356346
// if we are not inside an aggregation, and there is no GROUP BY, we consider the FROM clause before failing
357-
if deps.direct.NotEmpty() || (err != nil && !isColumnNotFound(err)) {
347+
if deps.direct.NotEmpty() || (err != nil && !errors.As(err, &ColumnNotFoundError{})) {
358348
return deps, err
359349
}
360350
}
@@ -429,7 +419,7 @@ func (b *binder) resolveColInGroupBy(
429419
return dependency{}, err
430420
}
431421
if dependencies.empty() {
432-
if isColumnNotFound(firstErr) {
422+
if errors.As(firstErr, &ColumnNotFoundError{}) {
433423
return dependency{}, &ColumnNotFoundClauseError{Column: colName.Name.String(), Clause: "group statement"}
434424
}
435425
return deps, firstErr

go/vt/vtgate/semantics/check_invalid.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ func (a *analyzer) checkForInvalidConstructs(cursor *sqlparser.Cursor) error {
5454
}
5555
case *sqlparser.OverClause:
5656
if !a.singleUnshardedKeyspace {
57-
return ShardedError{Inner: &UnsupportedConstruct{errString: "OVER CLAUSE with sharded keyspace"}}
57+
return NotSingleShardError{Inner: &UnsupportedConstruct{errString: "OVER CLAUSE with sharded keyspace"}}
5858
}
5959
}
6060

go/vt/vtgate/semantics/semantic_table.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ type (
122122
// MySQL engine to handle errors appropriately.
123123
NotUnshardedErr error
124124

125+
// If there are constructs in this query that we know we only support if we can push them down
126+
// unbroken to mysql, this field will contain an error that is produced when we fail to do so.
127+
NotSingleShardErr error
128+
125129
// Recursive contains dependencies from the expression to the actual tables
126130
// in the query (excluding derived tables). For columns in derived tables,
127131
// this map holds the accumulated dependencies for the column expression.

0 commit comments

Comments
 (0)