Skip to content

Commit

Permalink
Push down derived tables under route when possible (#11422)
Browse files Browse the repository at this point in the history
Backport of #11379

* tests: add more plan_tests for derived tables and subqueries

Co-authored-by: Arthur Schreiber <arthurschreiber@github.com>
Signed-off-by: Andres Taylor <andres@planetscale.com>

* refactor: make switch pretty

Signed-off-by: Andres Taylor <andres@planetscale.com>

* feat: push derived table under the route

Signed-off-by: Harshit Gangal <harshit@planetscale.com>

* simplify logic

Signed-off-by: Andres Taylor <andres@planetscale.com>

* check for valid derived tables also when doing JOIN on the vtgate

Signed-off-by: Andres Taylor <andres@planetscale.com>

* comments

Signed-off-by: Andres Taylor <andres@planetscale.com>

* add more tests with derived tables

Signed-off-by: Andres Taylor <andres@planetscale.com>

* handle HAVING in derived tables correctly

Signed-off-by: Andres Taylor <andres@planetscale.com>

* merge tests into single file

Signed-off-by: Andres Taylor <andres@planetscale.com>

* rename test keypspace

Signed-off-by: Andres Taylor <andres@planetscale.com>

Signed-off-by: Andres Taylor <andres@planetscale.com>
Signed-off-by: Harshit Gangal <harshit@planetscale.com>
Co-authored-by: Arthur Schreiber <arthurschreiber@github.com>
Co-authored-by: Harshit Gangal <harshit@planetscale.com>

Signed-off-by: Andres Taylor <andres@planetscale.com>
Signed-off-by: Harshit Gangal <harshit@planetscale.com>
Co-authored-by: Arthur Schreiber <arthurschreiber@github.com>
Co-authored-by: Harshit Gangal <harshit@planetscale.com>
  • Loading branch information
3 people committed Oct 3, 2022
1 parent 4b5dd2b commit fc6a7d1
Show file tree
Hide file tree
Showing 14 changed files with 2,183 additions and 151 deletions.
43 changes: 0 additions & 43 deletions go/test/endtoend/vtgate/mysql80/derived/derived_test.go

This file was deleted.

99 changes: 99 additions & 0 deletions go/test/endtoend/vtgate/queries/derived/derived_test.go
@@ -0,0 +1,99 @@
/*
Copyright 2022 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package misc

import (
"testing"

"github.com/stretchr/testify/require"

"vitess.io/vitess/go/test/endtoend/cluster"
"vitess.io/vitess/go/test/endtoend/utils"
)

func start(t *testing.T) (utils.MySQLCompare, func()) {
mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams)
require.NoError(t, err)

deleteAll := func() {
tables := []string{"music"}
for _, table := range tables {
_, _ = mcmp.ExecAndIgnore("delete from " + table)
}
}

deleteAll()

return mcmp, func() {
deleteAll()
mcmp.Close()
cluster.PanicHandler(t)
}
}

func TestDerivedTableWithOrderByLimit(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)")
mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')")

mcmp.Exec("select /*vt+ PLANNER=Gen4 */ music.id from music join (select id,name from user order by id limit 2) as d on music.user_id = d.id")
}

func TestDerivedAggregationOnRHS(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)")
mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')")

mcmp.Exec("set sql_mode = ''")
mcmp.Exec("select /*vt+ PLANNER=Gen4 */ d.a from music join (select id, count(*) as a from user) as d on music.user_id = d.id group by 1")
}

func TestDerivedRemoveInnerOrderBy(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)")
mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')")

mcmp.Exec("select /*vt+ PLANNER=Gen4 */ count(*) from (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) as toto")
}

func TestDerivedTableWithHaving(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)")
mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')")

mcmp.Exec("set sql_mode = ''")

// this is probably flaky? the id returned from the derived table could be any of the ids from user.
// works on my machine (TM)
mcmp.Exec("select /*vt+ PLANNER=Gen4 */ * from (select id from user having count(*) >= 1) s")
}

func TestDerivedTableColumns(t *testing.T) {
mcmp, closer := start(t)
defer closer()

mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')")
mcmp.AssertMatches(`SELECT /*vt+ PLANNER=gen4 */ t.id FROM (SELECT id FROM user) AS t(id) ORDER BY t.id DESC`, `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`)
}
@@ -1,5 +1,5 @@
/*
Copyright 2021 The Vitess Authors.
Copyright 2022 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,83 +14,41 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package derived
package misc

import (
_ "embed"
"flag"
"fmt"
"os"
"testing"

"vitess.io/vitess/go/test/endtoend/utils"

"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/test/endtoend/cluster"
)

var (
clusterInstance *cluster.LocalProcessCluster
vtParams mysql.ConnParams
KeyspaceName = "ks_union"
Cell = "test_union"
SchemaSQL = `create table t1(
id1 bigint,
id2 bigint,
primary key(id1)
) Engine=InnoDB;
create table t1_id2_idx(
id2 bigint,
keyspace_id varbinary(10),
primary key(id2)
) Engine=InnoDB;
`

VSchema = `
{
"sharded": true,
"vindexes": {
"hash": {
"type": "hash"
},
"t1_id2_vdx": {
"type": "consistent_lookup_unique",
"params": {
"table": "t1_id2_idx",
"from": "id2",
"to": "keyspace_id"
},
"owner": "t1"
}
},
"tables": {
"t1": {
"column_vindexes": [
{
"column": "id1",
"name": "hash"
},
{
"column": "id2",
"name": "t1_id2_vdx"
}
]
},
"t1_id2_idx": {
"column_vindexes": [
{
"column": "id2",
"name": "hash"
}
]
}
}
}`
mysqlParams mysql.ConnParams
keyspaceName = "ks_derived"
cell = "test_derived"

//go:embed schema.sql
schemaSQL string

//go:embed vschema.json
vschema string
)

func TestMain(m *testing.M) {
defer cluster.PanicHandler(nil)
flag.Parse()

exitCode := func() int {
clusterInstance = cluster.NewCluster(Cell, "localhost")
clusterInstance = cluster.NewCluster(cell, "localhost")
defer clusterInstance.Teardown()

// Start topo server
Expand All @@ -101,12 +59,10 @@ func TestMain(m *testing.M) {

// Start keyspace
keyspace := &cluster.Keyspace{
Name: KeyspaceName,
SchemaSQL: SchemaSQL,
VSchema: VSchema,
Name: keyspaceName,
SchemaSQL: schemaSQL,
VSchema: vschema,
}
clusterInstance.VtGateExtraArgs = []string{"--schema_change_signal"}
clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal", "--queryserver-config-schema-change-signal-interval", "0.1"}
err = clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, true)
if err != nil {
return 1
Expand All @@ -123,6 +79,15 @@ func TestMain(m *testing.M) {
Host: clusterInstance.Hostname,
Port: clusterInstance.VtgateMySQLPort,
}

// create mysql instance and connection parameters
conn, closer, err := utils.NewMySQL(clusterInstance, keyspaceName, schemaSQL)
if err != nil {
fmt.Println(err)
return 1
}
defer closer()
mysqlParams = conn
return m.Run()
}()
os.Exit(exitCode)
Expand Down
13 changes: 13 additions & 0 deletions go/test/endtoend/vtgate/queries/derived/schema.sql
@@ -0,0 +1,13 @@
create table user
(
id bigint,
name varchar(255),
primary key (id)
) Engine = InnoDB;

create table music
(
id bigint,
user_id bigint,
primary key (id)
) Engine = InnoDB;
26 changes: 26 additions & 0 deletions go/test/endtoend/vtgate/queries/derived/vschema.json
@@ -0,0 +1,26 @@
{
"sharded": true,
"vindexes": {
"user_index": {
"type": "hash"
}
},
"tables": {
"user": {
"column_vindexes": [
{
"column": "id",
"name": "user_index"
}
]
},
"music": {
"column_vindexes": [
{
"column": "user_id",
"name": "user_index"
}
]
}
}
}
2 changes: 2 additions & 0 deletions go/vt/sqlparser/ast.go
Expand Up @@ -56,12 +56,14 @@ type (
AddOrder(*Order)
SetOrderBy(OrderBy)
GetOrderBy() OrderBy
GetLimit() *Limit
SetLimit(*Limit)
SetLock(lock Lock)
SetInto(into *SelectInto)
SetWith(with *With)
MakeDistinct()
GetColumnCount() int
GetColumns() SelectExprs
Commented
}

Expand Down
20 changes: 20 additions & 0 deletions go/vt/sqlparser/ast_funcs.go
Expand Up @@ -852,6 +852,11 @@ func (node *Select) SetLimit(limit *Limit) {
node.Limit = limit
}

// GetLimit gets the limit
func (node *Select) GetLimit() *Limit {
return node.Limit
}

// SetLock sets the lock clause
func (node *Select) SetLock(lock Lock) {
node.Lock = lock
Expand All @@ -877,6 +882,11 @@ func (node *Select) GetColumnCount() int {
return len(node.SelectExprs)
}

// GetColumns gets the columns
func (node *Select) GetColumns() SelectExprs {
return node.SelectExprs
}

// SetComments implements the SelectStatement interface
func (node *Select) SetComments(comments Comments) {
node.Comments = comments.Parsed()
Expand Down Expand Up @@ -964,6 +974,16 @@ func (node *Union) SetLimit(limit *Limit) {
node.Limit = limit
}

// GetLimit gets the limit
func (node *Union) GetLimit() *Limit {
return node.Limit
}

// GetColumns gets the columns
func (node *Union) GetColumns() SelectExprs {
return node.Left.GetColumns()
}

// SetLock sets the lock clause
func (node *Union) SetLock(lock Lock) {
node.Lock = lock
Expand Down

0 comments on commit fc6a7d1

Please sign in to comment.