Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions go/test/endtoend/vtgate/system_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ func TestInformationSchemaQuery(t *testing.T) {
assertResultIsEmpty(t, conn, "table_schema = 'PERFORMANCE_SCHEMA'")
assertSingleRowIsReturned(t, conn, "table_schema = 'performance_schema' and table_name = 'users'", "performance_schema")
assertResultIsEmpty(t, conn, "table_schema = 'performance_schema' and table_name = 'foo'")
assertSingleRowIsReturned(t, conn, "table_schema = 'vt_ks' and table_name = 't1'", "vt_ks")
assertSingleRowIsReturned(t, conn, "table_schema = 'ks' and table_name = 't1'", "vt_ks")
}

func assertResultIsEmpty(t *testing.T, conn *mysql.Conn, pre string) {
Expand Down Expand Up @@ -187,3 +189,29 @@ func TestSystemSchemaQueryWithoutQualifier(t *testing.T) {
qr3 := exec(t, conn2, queryWithoutQualifier)
require.Equal(t, qr2, qr3)
}

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

query := fmt.Sprintf("select t.table_schema,t.table_name,c.column_name,c.column_type "+
"from information_schema.tables t "+
"join information_schema.columns c "+
"on c.table_schema = t.table_schema and c.table_name = t.table_name "+
"where t.table_schema = '%s' and c.table_schema = '%s' and c.table_schema = '%s' and c.table_schema = '%s'", KeyspaceName, KeyspaceName, KeyspaceName, KeyspaceName)
qr1 := exec(t, conn, query)
require.EqualValues(t, 4, len(qr1.Fields))

// test a query with two keyspace names
query = fmt.Sprintf("select t.table_schema,t.table_name,c.column_name,c.column_type "+
"from information_schema.tables t "+
"join information_schema.columns c "+
"on c.table_schema = t.table_schema and c.table_name = t.table_name "+
"where t.table_schema = '%s' and c.table_schema = '%s' and c.table_schema = '%s'", KeyspaceName, KeyspaceName, "a")
_, err = conn.ExecuteFetch(query, 1000, true)
require.Error(t, err)
require.Contains(t, err.Error(), "specifying two different database in the query is not supported")
}
65 changes: 51 additions & 14 deletions go/vt/vtgate/engine/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ type Route struct {
ScatterErrorsAsWarnings bool

// The following two fields are used when routing information_schema queries
SysTableTableSchema evalengine.Expr
SysTableTableName evalengine.Expr
SysTableTableSchema []evalengine.Expr
SysTableTableName []evalengine.Expr

// Route does not take inputs
noInputs
Expand Down Expand Up @@ -408,7 +408,7 @@ func (route *Route) routeInfoSchemaQuery(vcursor VCursor, bindVars map[string]*q
return destinations, vterrors.Wrapf(err, "failed to find information about keyspace `%s`", ks)
}

if route.SysTableTableName == nil && route.SysTableTableSchema == nil {
if len(route.SysTableTableName) == 0 && len(route.SysTableTableSchema) == 0 {
return defaultRoute()
}

Expand All @@ -418,22 +418,38 @@ func (route *Route) routeInfoSchemaQuery(vcursor VCursor, bindVars map[string]*q
}

var specifiedKS string
if route.SysTableTableSchema != nil {
result, err := route.SysTableTableSchema.Evaluate(env)
for _, tableSchema := range route.SysTableTableSchema {
result, err := tableSchema.Evaluate(env)
if err != nil {
return nil, err
}
specifiedKS = result.Value().ToString()
ks := result.Value().ToString()
if specifiedKS == "" {
specifiedKS = ks
}
if specifiedKS != ks {
return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "specifying two different database in the query is not supported")
}
}
if specifiedKS != "" {
bindVars[sqltypes.BvSchemaName] = sqltypes.StringBindVariable(specifiedKS)
}

var tableName string
if route.SysTableTableName != nil {
val, err := route.SysTableTableName.Evaluate(env)
for _, sysTableName := range route.SysTableTableName {
val, err := sysTableName.Evaluate(env)
if err != nil {
return nil, err
}
tableName = val.Value().ToString()
tabName := val.Value().ToString()
if tableName == "" {
tableName = tabName
}
if tableName != tabName {
return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "two predicates for table_name not supported")
}
}
if tableName != "" {
bindVars[BvTableName] = sqltypes.StringBindVariable(tableName)
}

Expand All @@ -446,6 +462,11 @@ func (route *Route) routeInfoSchemaQuery(vcursor VCursor, bindVars map[string]*q
if tableName != "" {
rss, err := route.paramsRoutedTable(vcursor, bindVars, specifiedKS, tableName)
if err != nil {
// Only if keyspace is not found in vschema, we try with default keyspace.
// As the in the table_schema predicates for a keyspace 'ks' it can contain 'vt_ks'.
if vterrors.Code(err) == vtrpcpb.Code_NOT_FOUND {
return defaultRoute()
}
return nil, err
}
if rss != nil {
Expand Down Expand Up @@ -723,11 +744,27 @@ func (route *Route) description() PrimitiveDescription {
if len(route.Values) > 0 {
other["Values"] = route.Values
}
if route.SysTableTableSchema != nil {
other["SysTableTableSchema"] = route.SysTableTableSchema.String()
}
if route.SysTableTableName != nil {
other["SysTableTableName"] = route.SysTableTableName.String()
if len(route.SysTableTableSchema) != 0 {
sysTabSchema := "["
for idx, tableSchema := range route.SysTableTableSchema {
if idx != 0 {
sysTabSchema += ", "
}
sysTabSchema += tableSchema.String()
}
sysTabSchema += "]"
other["SysTableTableSchema"] = sysTabSchema
}
if len(route.SysTableTableName) != 0 {
sysTableName := "["
for idx, tableName := range route.SysTableTableName {
if idx != 0 {
sysTableName += ", "
}
sysTableName += tableName.String()
}
sysTableName += "]"
other["SysTableTableName"] = sysTableName
}
orderBy := GenericJoin(route.OrderBy, orderByToString)
if orderBy != "" {
Expand Down
48 changes: 32 additions & 16 deletions go/vt/vtgate/engine/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,56 +78,72 @@ func TestSelectUnsharded(t *testing.T) {
}

func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T) {
stringToExpr := func(in string) evalengine.Expr {
var schema evalengine.Expr
if in != "" {
schema = evalengine.NewLiteralString([]byte(in))
stringListToExprList := func(in []string) []evalengine.Expr {
var schema []evalengine.Expr
for _, s := range in {
schema = append(schema, evalengine.NewLiteralString([]byte(s)))
}
return schema
}

type testCase struct {
tableSchema, tableName, testName string
expectedLog []string
routed bool
tableSchema, tableName []string
testName string
expectedLog []string
routed bool
}
tests := []testCase{{
testName: "both schema and table predicates - routed table",
tableSchema: "schema",
tableName: "table",
tableSchema: []string{"schema"},
tableName: []string{"table"},
routed: true,
expectedLog: []string{
"FindTable(`schema`.`table`)",
"ResolveDestinations routedKeyspace [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard routedKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" __vttablename: type:VARBINARY value:\"routedTable\" } false false"},
}, {
testName: "both schema and table predicates - not routed",
tableSchema: "schema",
tableName: "table",
tableSchema: []string{"schema"},
tableName: []string{"table"},
routed: false,
expectedLog: []string{
"FindTable(`schema`.`table`)",
"ResolveDestinations schema [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard schema.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" __vttablename: type:VARBINARY value:\"table\" } false false"},
}, {
testName: "multiple schema and table predicates",
tableSchema: []string{"schema", "schema", "schema"},
tableName: []string{"table", "table", "table"},
routed: false,
expectedLog: []string{
"FindTable(`schema`.`table`)",
"ResolveDestinations schema [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard schema.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" __vttablename: type:VARBINARY value:\"table\" } false false"},
}, {
testName: "table name predicate - routed table",
tableName: "tableName",
tableName: []string{"tableName"},
routed: true,
expectedLog: []string{
"FindTable(tableName)",
"ResolveDestinations routedKeyspace [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard routedKeyspace.1: dummy_select {__vttablename: type:VARBINARY value:\"routedTable\" } false false"},
}, {
testName: "table name predicate - not routed",
tableName: "tableName",
tableName: []string{"tableName"},
routed: false,
expectedLog: []string{
"FindTable(tableName)",
"ResolveDestinations ks [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard ks.1: dummy_select {__vttablename: type:VARBINARY value:\"tableName\" } false false"},
}, {
testName: "schema predicate",
tableSchema: "myKeyspace",
tableSchema: []string{"myKeyspace"},
expectedLog: []string{
"ResolveDestinations myKeyspace [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard myKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" } false false"},
}, {
testName: "multiple schema predicates",
tableSchema: []string{"myKeyspace", "myKeyspace", "myKeyspace", "myKeyspace"},
expectedLog: []string{
"ResolveDestinations myKeyspace [] Destinations:DestinationAnyShard()",
"ExecuteMultiShard myKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" } false false"},
Expand All @@ -148,8 +164,8 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
},
Query: "dummy_select",
FieldQuery: "dummy_select_field",
SysTableTableSchema: stringToExpr(tc.tableSchema),
SysTableTableName: stringToExpr(tc.tableName),
SysTableTableSchema: stringListToExprList(tc.tableSchema),
SysTableTableName: stringListToExprList(tc.tableName),
}
vc := &loggingVCursor{
shards: []string{"1"},
Expand Down
12 changes: 0 additions & 12 deletions go/vt/vtgate/evalengine/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,15 +327,3 @@ func evaluateByType(val *querypb.BindVariable) (EvalResult, error) {
func (e *EvalResult) debugString() string {
return fmt.Sprintf("(%s) %d %d %f %s", querypb.Type_name[int32(e.typ)], e.ival, e.uval, e.fval, string(e.bytes))
}

// AreExprEqual checks if the provided Expr are the same or not
func AreExprEqual(expr1 Expr, expr2 Expr) bool {
// Check the types of the two expressions, if they don't match then the two are not equal
if fmt.Sprintf("%T", expr1) != fmt.Sprintf("%T", expr2) {
return false
}
if expr1.String() == expr2.String() {
return true
}
return false
}
31 changes: 29 additions & 2 deletions go/vt/vtgate/executor_select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ func TestSelectDBA(t *testing.T) {
require.NoError(t, err)
wantQueries := []*querypb.BoundQuery{{Sql: query, BindVariables: map[string]*querypb.BindVariable{}}}
utils.MustMatch(t, wantQueries, sbc1.Queries)
sbc1.Queries = nil

sbc1.Queries = nil
query = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = 'performance_schema' AND table_name = 'foo'"
_, err = executor.Execute(context.Background(), "TestSelectDBA",
NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}),
Expand All @@ -84,6 +84,33 @@ func TestSelectDBA(t *testing.T) {
}}}
utils.MustMatch(t, wantQueries, sbc1.Queries)

sbc1.Queries = nil
query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks' and table_name = 'user'"
_, err = executor.Execute(context.Background(), "TestSelectDBA",
NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}),
query, map[string]*querypb.BindVariable{},
)
require.NoError(t, err)
wantQueries = []*querypb.BoundQuery{{Sql: "select 1 from information_schema.table_constraints where constraint_schema = :__vtschemaname and table_name = :__vttablename",
BindVariables: map[string]*querypb.BindVariable{
"__vtschemaname": sqltypes.StringBindVariable("vt_ks"),
"__vttablename": sqltypes.StringBindVariable("user"),
}}}
utils.MustMatch(t, wantQueries, sbc1.Queries)

sbc1.Queries = nil
query = "select 1 from information_schema.table_constraints where constraint_schema = 'vt_ks'"
_, err = executor.Execute(context.Background(), "TestSelectDBA",
NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}),
query, map[string]*querypb.BindVariable{},
)
require.NoError(t, err)
wantQueries = []*querypb.BoundQuery{{Sql: "select 1 from information_schema.table_constraints where constraint_schema = :__vtschemaname",
BindVariables: map[string]*querypb.BindVariable{
"__vtschemaname": sqltypes.StringBindVariable("vt_ks"),
}}}
utils.MustMatch(t, wantQueries, sbc1.Queries)

}

func TestUnsharded(t *testing.T) {
Expand Down Expand Up @@ -2290,7 +2317,7 @@ func TestSelectFromInformationSchema(t *testing.T) {
// check failure when trying to query two keyspaces
_, err := exec(executor, session, "SELECT B.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES AS A, INFORMATION_SCHEMA.COLUMNS AS B WHERE A.TABLE_SCHEMA = 'TestExecutor' AND A.TABLE_SCHEMA = 'TestXBadSharding'")
require.Error(t, err)
require.Contains(t, err.Error(), "two predicates for specifying the database are not supported")
require.Contains(t, err.Error(), "specifying two different database in the query is not supported")

// we pick a keyspace and query for table_schema = database(). should be routed to the picked keyspace
session.TargetString = "TestExecutor"
Expand Down
10 changes: 1 addition & 9 deletions go/vt/vtgate/planbuilder/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,15 +392,7 @@ func (rb *route) JoinCanMerge(pb *primitiveBuilder, rrb *route, ajoin *sqlparser
if where == nil {
return true
}
hasRuntimeRoutingPredicates := false
sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
col, ok := node.(*sqlparser.ColName)
if ok {
hasRuntimeRoutingPredicates = hasRuntimeRoutingPredicates || isTableNameCol(col) || isDbNameCol(col)
}
return !hasRuntimeRoutingPredicates, nil
}, where)
return !hasRuntimeRoutingPredicates
return ajoin != nil
}
if ajoin == nil {
return false
Expand Down
12 changes: 2 additions & 10 deletions go/vt/vtgate/planbuilder/system_tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ package planbuilder

import (
"vitess.io/vitess/go/sqltypes"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vtgate/engine"
"vitess.io/vitess/go/vt/vtgate/evalengine"
)
Expand All @@ -36,15 +34,9 @@ func (pb *primitiveBuilder) findSysInfoRoutingPredicates(expr sqlparser.Expr, ru
}

if isTableSchema {
if rut.eroute.SysTableTableSchema != nil && !evalengine.AreExprEqual(rut.eroute.SysTableTableSchema, out) {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "two predicates for specifying the database are not supported")
}
rut.eroute.SysTableTableSchema = out
rut.eroute.SysTableTableSchema = append(rut.eroute.SysTableTableSchema, out)
} else {
if rut.eroute.SysTableTableName != nil && !evalengine.AreExprEqual(rut.eroute.SysTableTableName, out) {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "two predicates for table_name not supported")
}
rut.eroute.SysTableTableName = out
rut.eroute.SysTableTableName = append(rut.eroute.SysTableTableName, out)
}

return nil
Expand Down
Loading