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
26 changes: 26 additions & 0 deletions go/test/endtoend/vtgate/system_schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,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")
}
24 changes: 17 additions & 7 deletions go/vt/vtgate/engine/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 46 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 @@ -415,7 +415,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 @@ -425,22 +425,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 Down Expand Up @@ -729,11 +745,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 @@ -77,56 +77,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 @@ -147,8 +163,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
}
2 changes: 1 addition & 1 deletion go/vt/vtgate/executor_select_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2321,7 +2321,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
18 changes: 1 addition & 17 deletions go/vt/vtgate/planbuilder/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,23 +440,7 @@ func (rb *route) JoinCanMerge(pb *primitiveBuilder, rrb *route, ajoin *sqlparser
if where == nil {
return true
}
tableWithRoutingPredicates := make(map[sqlparser.TableName]struct{})
_ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
col, ok := node.(*sqlparser.ColName)
if ok {
hasRuntimeRoutingPredicates := isTableNameCol(col) || isDbNameCol(col)
if hasRuntimeRoutingPredicates && pb.st.tables[col.Qualifier] != nil {
tableWithRoutingPredicates[col.Qualifier] = struct{}{}
}
}
return true, nil
}, where)
// Routes can be merged if only 1 table is used in the predicates that are used for routing
// TODO :- Even if more table are present in the routing, we can merge if they agree
if len(tableWithRoutingPredicates) <= 1 {
return true
}
return len(tableWithRoutingPredicates) == 0
return ajoin != nil
}
if ajoin == nil {
return false
Expand Down
4 changes: 2 additions & 2 deletions go/vt/vtgate/planbuilder/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ func shouldRetryWithCNFRewriting(plan logicalPlan) bool {
}
// if we have a I_S query, but have not found table_schema or table_name, let's try CNF
return routePlan.eroute.Opcode == engine.SelectDBA &&
routePlan.eroute.SysTableTableName == nil &&
routePlan.eroute.SysTableTableSchema == nil
len(routePlan.eroute.SysTableTableName) == 0 &&
len(routePlan.eroute.SysTableTableSchema) == 0

}

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_UNIMPLEMENTED, "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_UNIMPLEMENTED, "two predicates for table_name not supported")
}
rut.eroute.SysTableTableName = out
rut.eroute.SysTableTableName = append(rut.eroute.SysTableTableName, out)
}

return nil
Expand Down
Loading