Skip to content

Commit

Permalink
Merge pull request #6932 from planetscale/information_schema_routing_…
Browse files Browse the repository at this point in the history
…rules

Route INFORMATION_SCHEMA queries
  • Loading branch information
systay authored Nov 2, 2020
2 parents 4b2190c + ec443f1 commit 5ab4af4
Show file tree
Hide file tree
Showing 14 changed files with 563 additions and 176 deletions.
5 changes: 5 additions & 0 deletions go/test/endtoend/cluster/vtctlclient_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ func (vtctlclient *VtctlClientProcess) ApplyVSchema(Keyspace string, JSON string
)
}

// ApplyRoutingRules does it
func (vtctlclient *VtctlClientProcess) ApplyRoutingRules(JSON string) (err error) {
return vtctlclient.ExecuteCommand("ApplyRoutingRules", "-rules", JSON)
}

// OnlineDDLShowRecent responds with recent schema migration list
func (vtctlclient *VtctlClientProcess) OnlineDDLShowRecent(Keyspace string) (result string, err error) {
return vtctlclient.ExecuteCommandWithOutput(
Expand Down
116 changes: 116 additions & 0 deletions go/test/endtoend/vtgate/information_schema_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
Copyright 2020 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 vtgate

import (
"context"
"testing"

"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"

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

// TestCheckConstraint test check constraints on CREATE TABLE
// This feature is supported from MySQL 8.0.16 and MariaDB 10.2.1.
func TestCheckConstraint(t *testing.T) {
// Skipping as tests are run against MySQL 5.7
t.Skip()

conn, err := mysql.Connect(context.Background(), &vtParams)
require.NoError(t, err)
defer conn.Close()

query := `CREATE TABLE t7 (CHECK (c1 <> c2), c1 INT CHECK (c1 > 10), c2 INT CONSTRAINT c2_positive CHECK (c2 > 0), c3 INT CHECK (c3 < 100), CONSTRAINT c1_nonzero CHECK (c1 <> 0), CHECK (c1 > c3));`
exec(t, conn, query)

checkQuery := `SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 't7';`
expected := `[[VARCHAR("t7_chk_1")] [VARCHAR("t7_chk_2")] [VARCHAR("c2_positive")] [VARCHAR("t7_chk_3")] [VARCHAR("c1_nonzero")] [VARCHAR("t7_chk_4")]]`

assertMatches(t, conn, checkQuery, expected)

cleanup := `DROP TABLE t7`
exec(t, conn, cleanup)
}

func TestDbNameOverride(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.Nil(t, err)
defer conn.Close()
qr, err := conn.ExecuteFetch("SELECT distinct database() FROM information_schema.tables WHERE table_schema = database()", 1000, true)

require.Nil(t, err)
assert.Equal(t, 1, len(qr.Rows), "did not get enough rows back")
assert.Equal(t, "vt_ks", qr.Rows[0][0].ToString())
}

func TestInformationSchemaQuery(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.NoError(t, err)
defer conn.Close()

assertSingleRowIsReturned(t, conn, "table_schema = 'ks'")
assertSingleRowIsReturned(t, conn, "table_schema = 'vt_ks'")
assertResultIsEmpty(t, conn, "table_schema = 'NONE'")
}

func assertResultIsEmpty(t *testing.T, conn *mysql.Conn, pre string) {
t.Run(pre, func(t *testing.T) {
qr, err := conn.ExecuteFetch("SELECT distinct table_schema FROM information_schema.tables WHERE "+pre, 1000, true)
require.NoError(t, err)
assert.Empty(t, qr.Rows)
})
}

func assertSingleRowIsReturned(t *testing.T, conn *mysql.Conn, predicate string) {
t.Run(predicate, func(t *testing.T) {
qr, err := conn.ExecuteFetch("SELECT distinct table_schema FROM information_schema.tables WHERE "+predicate, 1000, true)
require.NoError(t, err)
assert.Equal(t, 1, len(qr.Rows), "did not get enough rows back")
assert.Equal(t, "vt_ks", qr.Rows[0][0].ToString())
})
}

func TestInformationSchemaWithSubquery(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.NoError(t, err)
defer conn.Close()

result := exec(t, conn, "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = (SELECT SCHEMA()) AND TABLE_NAME = 'not_exists'")
assert.Empty(t, result.Rows)
}

func TestInformationSchemaQueryGetsRoutedToTheRightTableAndKeyspace(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.NoError(t, err)
defer conn.Close()

_ = exec(t, conn, "SELECT * FROM t1000") // test that the routed table is available to us
result := exec(t, conn, "SELECT * FROM information_schema.tables WHERE table_schema = database() and table_name='t1000'")
assert.NotEmpty(t, result.Rows)
}
18 changes: 18 additions & 0 deletions go/test/endtoend/vtgate/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,14 @@ create table t7_xxhash_idx(
}
}
}`
routingRules = `
{"rules": [
{
"from_table": "ks.t1000",
"to_tables": ["ks.t1"]
}
]}
`
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -383,6 +391,16 @@ func TestMain(m *testing.M) {
return 1
}

err = clusterInstance.VtctlclientProcess.ApplyRoutingRules(routingRules)
if err != nil {
return 1
}

err = clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildVSchemaGraph")
if err != nil {
return 1
}

// Start vtgate
err = clusterInstance.StartVtgate()
if err != nil {
Expand Down
68 changes: 0 additions & 68 deletions go/test/endtoend/vtgate/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,28 +133,6 @@ func TestUnion(t *testing.T) {
assertMatches(t, conn, `(SELECT 1,'a' order by 1) union (SELECT 1,'a' ORDER BY 1)`, `[[INT64(1) VARCHAR("a")]]`)
}

// TestCheckConstraint test check constraints on CREATE TABLE
// This feature is supported from MySQL 8.0.16 and MariaDB 10.2.1.
func TestCheckConstraint(t *testing.T) {
// Skipping as tests are run against MySQL 5.7
t.Skip()

conn, err := mysql.Connect(context.Background(), &vtParams)
require.NoError(t, err)
defer conn.Close()

query := `CREATE TABLE t7 (CHECK (c1 <> c2), c1 INT CHECK (c1 > 10), c2 INT CONSTRAINT c2_positive CHECK (c2 > 0), c3 INT CHECK (c3 < 100), CONSTRAINT c1_nonzero CHECK (c1 <> 0), CHECK (c1 > c3));`
exec(t, conn, query)

checkQuery := `SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 't7';`
expected := `[[VARCHAR("t7_chk_1")] [VARCHAR("t7_chk_2")] [VARCHAR("c2_positive")] [VARCHAR("t7_chk_3")] [VARCHAR("c1_nonzero")] [VARCHAR("t7_chk_4")]]`

assertMatches(t, conn, checkQuery, expected)

cleanup := `DROP TABLE t7`
exec(t, conn, cleanup)
}

func TestSavepointInTx(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
Expand Down Expand Up @@ -310,41 +288,6 @@ func TestShowTablesWithWhereClause(t *testing.T) {
assertMatches(t, conn, "show tables from ks where Tables_in_ks='t3'", `[[VARCHAR("t3")]]`)
}

func TestDbNameOverride(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.Nil(t, err)
defer conn.Close()
qr, err := conn.ExecuteFetch("SELECT distinct database() FROM information_schema.tables WHERE table_schema = database()", 1000, true)

require.Nil(t, err)
assert.Equal(t, 1, len(qr.Rows), "did not get enough rows back")
assert.Equal(t, "vt_ks", qr.Rows[0][0].ToString())
}

func TestInformationSchemaQuery(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.Nil(t, err)
defer conn.Close()

qr, err := conn.ExecuteFetch("SELECT distinct table_schema FROM information_schema.tables WHERE table_schema = 'ks'", 1000, true)
require.Nil(t, err)
assert.Equal(t, 1, len(qr.Rows), "did not get enough rows back")
assert.Equal(t, "vt_ks", qr.Rows[0][0].ToString())

qr, err = conn.ExecuteFetch("SELECT distinct table_schema FROM information_schema.tables WHERE table_schema = 'vt_ks'", 1000, true)
require.Nil(t, err)
assert.Equal(t, 1, len(qr.Rows), "did not get enough rows back")
assert.Equal(t, "vt_ks", qr.Rows[0][0].ToString())

qr, err = conn.ExecuteFetch("SELECT distinct table_schema FROM information_schema.tables WHERE table_schema = 'NONE'", 1000, true)
require.Nil(t, err)
assert.Empty(t, qr.Rows)
}

func TestOffsetAndLimitWithOLAP(t *testing.T) {
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
Expand Down Expand Up @@ -394,17 +337,6 @@ func TestUseStmtInOLAP(t *testing.T) {
}
}

func TestInformationSchemaWithSubquery(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.NoError(t, err)
defer conn.Close()

result := exec(t, conn, "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = (SELECT SCHEMA()) AND TABLE_NAME = 'not_exists'")
assert.Empty(t, result.Rows)
}

func TestInsertStmtInOLAP(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
Expand Down
33 changes: 27 additions & 6 deletions go/vt/vtgate/engine/fake_vcursor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import (
"fmt"
"reflect"
"sort"
"strings"
"testing"
"time"

"github.com/google/go-cmp/cmp"
"vitess.io/vitess/go/test/utils"

"vitess.io/vitess/go/vt/vtgate/vindexes"

"golang.org/x/sync/errgroup"

Expand Down Expand Up @@ -78,6 +81,10 @@ func (t noopVCursor) InTransactionAndIsDML() bool {
panic("implement me")
}

func (t noopVCursor) FindRoutedTable(sqlparser.TableName) (*vindexes.Table, error) {
panic("implement me")
}

func (t noopVCursor) ExecuteLock(rs *srvtopo.ResolvedShard, query *querypb.BoundQuery) (*sqltypes.Result, error) {
panic("implement me")
}
Expand Down Expand Up @@ -222,6 +229,12 @@ type loggingVCursor struct {
log []string

resolvedTargetTabletType topodatapb.TabletType

tableRoutes tableRoutes
}

type tableRoutes struct {
tbl *vindexes.Table
}

func (f *loggingVCursor) SetFoundRows(u uint64) {
Expand All @@ -232,6 +245,10 @@ func (f *loggingVCursor) InTransactionAndIsDML() bool {
return false
}

func (f *loggingVCursor) LookupRowLockShardSession() vtgatepb.CommitOrder {
panic("implement me")
}

func (f *loggingVCursor) SetUDV(key string, value interface{}) error {
f.log = append(f.log, fmt.Sprintf("UDV set with (%s,%v)", key, value))
return nil
Expand Down Expand Up @@ -399,14 +416,13 @@ func (f *loggingVCursor) ResolveDestinations(keyspace string, ids []*querypb.Val

func (f *loggingVCursor) ExpectLog(t *testing.T, want []string) {
t.Helper()
if len(want) == 0 && len(f.log) == 0 {
// both are empty. no need to compare empty array with nil
if len(f.log) == 0 && len(want) == 0 {
return
}
diff := cmp.Diff(want, f.log)
if diff != "" {
t.Fatalf("log not what was expected: %s", diff)
if !reflect.DeepEqual(f.log, want) {
t.Errorf("got:\n%s\nwant:\n%s", strings.Join(f.log, "\n"), strings.Join(want, "\n"))
}
utils.MustMatch(t, want, f.log, "")
}

func (f *loggingVCursor) ExpectWarnings(t *testing.T, want []*querypb.QueryWarning) {
Expand Down Expand Up @@ -447,6 +463,11 @@ func (f *loggingVCursor) SetWorkload(querypb.ExecuteOptions_Workload) {
panic("implement me")
}

func (f *loggingVCursor) FindRoutedTable(tbl sqlparser.TableName) (*vindexes.Table, error) {
f.log = append(f.log, fmt.Sprintf("FindTable(%s)", sqlparser.String(tbl)))
return f.tableRoutes.tbl, nil
}

func (f *loggingVCursor) nextResult() (*sqltypes.Result, error) {
if f.results == nil || f.curResult >= len(f.results) {
return &sqltypes.Result{}, f.resultErr
Expand Down
5 changes: 4 additions & 1 deletion go/vt/vtgate/engine/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import (
"sync"
"time"

"golang.org/x/sync/errgroup"
"vitess.io/vitess/go/vt/vtgate/vindexes"

"golang.org/x/sync/errgroup"
"vitess.io/vitess/go/vt/sqlparser"

"golang.org/x/net/context"
Expand Down Expand Up @@ -93,6 +94,8 @@ type (
InTransactionAndIsDML() bool

LookupRowLockShardSession() vtgatepb.CommitOrder

FindRoutedTable(tablename sqlparser.TableName) (*vindexes.Table, error)
}

//SessionActions gives primitives ability to interact with the session state
Expand Down
Loading

0 comments on commit 5ab4af4

Please sign in to comment.