diff --git a/data/test/vtgate/postprocess_cases.txt b/data/test/vtgate/postprocess_cases.txt index b7a34e16bc6..88823d612b2 100644 --- a/data/test/vtgate/postprocess_cases.txt +++ b/data/test/vtgate/postprocess_cases.txt @@ -196,6 +196,63 @@ } } +# ORDER BY RAND() +"select col from user order by RAND()" +{ + "Original": "select col from user order by RAND()", + "Instructions": { + "Opcode": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select col from user order by rand()", + "FieldQuery": "select col from user where 1 != 1" + } +} + +# ORDER BY RAND() for join +"select user.col1 as a, user.col2, music.col3 from user join music on user.id = music.id where user.id = 1 order by RAND()" +{ + "Original": "select user.col1 as a, user.col2, music.col3 from user join music on user.id = music.id where user.id = 1 order by RAND()", + "Instructions": { + "Opcode": "Join", + "Left": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select user.col1 as a, user.col2, user.id from user where user.id = 1 order by rand()", + "FieldQuery": "select user.col1 as a, user.col2, user.id from user where 1 != 1", + "Vindex": "user_index", + "Values": [1] + }, + "Right": { + "Opcode": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Query": "select music.col3 from music where music.id = :user_id order by rand()", + "FieldQuery": "select music.col3 from music where 1 != 1", + "Vindex": "music_user_map", + "Values": [":user_id"], + "JoinVars": { + "user_id": {} + } + }, + "Cols": [ + -1, + -2, + 1 + ], + "Vars": { + "user_id": 2 + } + } +} + # Order by, '*' expression "select * from user where id = 5 order by col" { diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index bd93f3ec4be..cb863a2f367 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -2566,6 +2566,13 @@ func (node *Order) Format(buf *TrackedBuffer) { buf.Myprintf("%v", node) return } + if node, ok := node.Expr.(*FuncExpr); ok { + if node.Name.Lowered() == "rand" { + buf.Myprintf("%v", node) + return + } + } + buf.Myprintf("%v %s", node.Expr, node.Direction) } diff --git a/go/vt/vtgate/planbuilder/builder.go b/go/vt/vtgate/planbuilder/builder.go index caa626c7691..47593114336 100644 --- a/go/vt/vtgate/planbuilder/builder.go +++ b/go/vt/vtgate/planbuilder/builder.go @@ -73,6 +73,10 @@ type builder interface { // just on optimization hint. PushOrderByNull() + // PushOrderByRand pushes the special case ORDER BY RAND() to + // all primitives. + PushOrderByRand() + // SetUpperLimit is an optimization hint that tells that primitive // that it does not need to return more than the specified number of rows. // A primitive that cannot perform this can ignore the request. diff --git a/go/vt/vtgate/planbuilder/join.go b/go/vt/vtgate/planbuilder/join.go index fe1c46e69a1..c49c2bfa159 100644 --- a/go/vt/vtgate/planbuilder/join.go +++ b/go/vt/vtgate/planbuilder/join.go @@ -201,6 +201,12 @@ func (jb *join) PushOrderByNull() { jb.Right.PushOrderByNull() } +// PushOrderByRand satisfies the builder interface. +func (jb *join) PushOrderByRand() { + jb.Left.PushOrderByRand() + jb.Right.PushOrderByRand() +} + // SetUpperLimit satisfies the builder interface. // The call is ignored because results get multiplied // as they join with others. So, it's hard to reliably diff --git a/go/vt/vtgate/planbuilder/limit.go b/go/vt/vtgate/planbuilder/limit.go index 650cbe54439..2706983c52c 100644 --- a/go/vt/vtgate/planbuilder/limit.go +++ b/go/vt/vtgate/planbuilder/limit.go @@ -104,6 +104,11 @@ func (l *limit) PushOrderByNull() { panic("BUG: unreachable") } +// PushOrderByRand satisfies the builder interface. +func (l *limit) PushOrderByRand() { + panic("BUG: unreachable") +} + // SetLimit sets the limit for the primitive. It calls the underlying // primitive's SetUpperLimit, which is an optimization hint that informs // the underlying primitive that it doesn't need to return more rows than diff --git a/go/vt/vtgate/planbuilder/ordered_aggregate.go b/go/vt/vtgate/planbuilder/ordered_aggregate.go index d9595fad8bb..5e4e269600e 100644 --- a/go/vt/vtgate/planbuilder/ordered_aggregate.go +++ b/go/vt/vtgate/planbuilder/ordered_aggregate.go @@ -446,6 +446,11 @@ func (oa *orderedAggregate) PushOrderByNull() { panic("BUG: unreachable") } +// PushOrderByRand satisfies the builder interface. +func (oa *orderedAggregate) PushOrderByRand() { + panic("BUG: unreachable") +} + // SetUpperLimit satisfies the builder interface. func (oa *orderedAggregate) SetUpperLimit(count *sqlparser.SQLVal) { oa.input.SetUpperLimit(count) diff --git a/go/vt/vtgate/planbuilder/postprocess.go b/go/vt/vtgate/planbuilder/postprocess.go index 6967956a7de..8152c03a88a 100644 --- a/go/vt/vtgate/planbuilder/postprocess.go +++ b/go/vt/vtgate/planbuilder/postprocess.go @@ -75,6 +75,11 @@ func pushOrderBy(orderBy sqlparser.OrderBy, bldr builder) error { if _, ok := orderBy[0].Expr.(*sqlparser.NullVal); ok { bldr.PushOrderByNull() return nil + } else if f, ok := orderBy[0].Expr.(*sqlparser.FuncExpr); ok { + if f.Name.Lowered() == "rand" { + bldr.PushOrderByRand() + return nil + } } } diff --git a/go/vt/vtgate/planbuilder/route.go b/go/vt/vtgate/planbuilder/route.go index e908f5030d0..9a69b48d289 100644 --- a/go/vt/vtgate/planbuilder/route.go +++ b/go/vt/vtgate/planbuilder/route.go @@ -450,6 +450,11 @@ func (rb *route) PushOrderByNull() { rb.Select.(*sqlparser.Select).OrderBy = sqlparser.OrderBy{&sqlparser.Order{Expr: &sqlparser.NullVal{}}} } +// PushOrderByRand satisfies the builder interface. +func (rb *route) PushOrderByRand() { + rb.Select.(*sqlparser.Select).OrderBy = sqlparser.OrderBy{&sqlparser.Order{Expr: &sqlparser.FuncExpr{Name: sqlparser.NewColIdent("rand")}}} +} + // SetLimit adds a LIMIT clause to the route. func (rb *route) SetLimit(limit *sqlparser.Limit) { rb.Select.SetLimit(limit) diff --git a/go/vt/vtgate/planbuilder/subquery.go b/go/vt/vtgate/planbuilder/subquery.go index a15c6fde65e..d3ef501010e 100644 --- a/go/vt/vtgate/planbuilder/subquery.go +++ b/go/vt/vtgate/planbuilder/subquery.go @@ -140,6 +140,10 @@ func (sq *subquery) PushSelect(expr *sqlparser.AliasedExpr, _ columnOriginator) func (sq *subquery) PushOrderByNull() { } +// PushOrderByRand satisfies the builder interface. +func (sq *subquery) PushOrderByRand() { +} + // SetUpperLimit satisfies the builder interface. // For now, the call is ignored because the // repercussions of pushing this limit down diff --git a/go/vt/vtgate/planbuilder/vindex_func.go b/go/vt/vtgate/planbuilder/vindex_func.go index 303144bd10e..3d03ad269fa 100644 --- a/go/vt/vtgate/planbuilder/vindex_func.go +++ b/go/vt/vtgate/planbuilder/vindex_func.go @@ -182,6 +182,10 @@ func (vf *vindexFunc) PushSelect(expr *sqlparser.AliasedExpr, _ columnOriginator func (vf *vindexFunc) PushOrderByNull() { } +// PushOrderByRand satisfies the builder interface. +func (vf *vindexFunc) PushOrderByRand() { +} + // SetUpperLimit satisfies the builder interface. func (vf *vindexFunc) SetUpperLimit(_ *sqlparser.SQLVal) { }