From cf822745c799b950409438a0ad0dc1e325af6ec9 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Wed, 1 Feb 2023 18:11:25 +0100 Subject: [PATCH] Use schema for the information_schema views (#12171) * feat: fail queries from information_schema which aren't querying valid table names Signed-off-by: Manan Gupta Signed-off-by: Andres Taylor * more columns that can be treated as table and schema names Signed-off-by: Andres Taylor * renamed test case files Signed-off-by: Andres Taylor * refactor: clean up code Signed-off-by: Andres Taylor * clean up types Signed-off-by: Andres Taylor * test: add query with missing info_schema table Signed-off-by: Andres Taylor * refactor: clean up method Signed-off-by: Andres Taylor --------- Signed-off-by: Manan Gupta Signed-off-by: Andres Taylor Co-authored-by: Manan Gupta --- go/vt/vtgate/executor_select_test.go | 5 +- go/vt/vtgate/planbuilder/operators/route.go | 2 +- .../planbuilder/operators/route_planning.go | 21 ++--- .../planbuilder/operators/system_tables.go | 91 ++++++++++--------- go/vt/vtgate/planbuilder/plan_test.go | 4 +- go/vt/vtgate/planbuilder/system_tables.go | 73 +++++++++------ .../planbuilder/testdata/from_cases.json | 46 +++++----- ..._cases57.json => info_schema57_cases.json} | 50 ++++++++-- ..._cases80.json => info_schema80_cases.json} | 50 ++++++++-- .../planbuilder/testdata/union_cases.json | 48 +++++----- go/vt/vtgate/semantics/info_schema.go | 8 +- 11 files changed, 248 insertions(+), 150 deletions(-) rename go/vt/vtgate/planbuilder/testdata/{systemtables_cases57.json => info_schema57_cases.json} (97%) rename go/vt/vtgate/planbuilder/testdata/{systemtables_cases80.json => info_schema80_cases.json} (97%) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 9531d9b539b..3eb44b060e4 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -459,13 +459,14 @@ func TestGen4SelectDBA(t *testing.T) { executor.normalize = true executor.pv = querypb.ExecuteOptions_Gen4 - query := "select * from INFORMATION_SCHEMA.foo" + query := "select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" _, err := executor.Execute(context.Background(), "TestSelectDBA", NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"}), query, map[string]*querypb.BindVariable{}, ) require.NoError(t, err) - wantQueries := []*querypb.BoundQuery{{Sql: query, BindVariables: map[string]*querypb.BindVariable{}}} + expected := "select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_SCHEMA, TABLE_NAME, CONSTRAINT_TYPE, `ENFORCED` from INFORMATION_SCHEMA.TABLE_CONSTRAINTS" + wantQueries := []*querypb.BoundQuery{{Sql: expected, BindVariables: map[string]*querypb.BindVariable{}}} utils.MustMatch(t, wantQueries, sbc1.Queries) sbc1.Queries = nil diff --git a/go/vt/vtgate/planbuilder/operators/route.go b/go/vt/vtgate/planbuilder/operators/route.go index 1f28da11ef8..46d9515b707 100644 --- a/go/vt/vtgate/planbuilder/operators/route.go +++ b/go/vt/vtgate/planbuilder/operators/route.go @@ -729,7 +729,7 @@ func createRoute( ctx *plancontext.PlanningContext, queryTable *QueryTable, solves semantics.TableSet, -) (*Route, error) { +) (ops.Operator, error) { if queryTable.IsInfSchema { return createInfSchemaRoute(ctx, queryTable) } diff --git a/go/vt/vtgate/planbuilder/operators/route_planning.go b/go/vt/vtgate/planbuilder/operators/route_planning.go index 8bcd18a0361..5e9460dc7ab 100644 --- a/go/vt/vtgate/planbuilder/operators/route_planning.go +++ b/go/vt/vtgate/planbuilder/operators/route_planning.go @@ -269,7 +269,7 @@ func seedOperatorList(ctx *plancontext.PlanningContext, qg *QueryGraph) ([]ops.O return nil, err } if qg.NoDeps != nil { - plan.Source, err = plan.Source.AddPredicate(ctx, qg.NoDeps) + plan, err = plan.AddPredicate(ctx, qg.NoDeps) if err != nil { return nil, err } @@ -279,22 +279,21 @@ func seedOperatorList(ctx *plancontext.PlanningContext, qg *QueryGraph) ([]ops.O return plans, nil } -func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) (*Route, error) { +func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) (ops.Operator, error) { ks, err := ctx.VSchema.AnyKeyspace() if err != nil { return nil, err } - var src ops.Operator = &Table{ - QTable: table, - VTable: &vindexes.Table{ - Name: table.Table.Name, - Keyspace: ks, - }, - } r := &Route{ RouteOpCode: engine.DBA, - Source: src, - Keyspace: ks, + Source: &Table{ + QTable: table, + VTable: &vindexes.Table{ + Name: table.Table.Name, + Keyspace: ks, + }, + }, + Keyspace: ks, } for _, pred := range table.Predicates { isTableSchema, bvName, out, err := extractInfoSchemaRoutingPredicate(pred, ctx.ReservedVars) diff --git a/go/vt/vtgate/planbuilder/operators/system_tables.go b/go/vt/vtgate/planbuilder/operators/system_tables.go index 6f8fc2e7882..8486805a853 100644 --- a/go/vt/vtgate/planbuilder/operators/system_tables.go +++ b/go/vt/vtgate/planbuilder/operators/system_tables.go @@ -37,60 +37,69 @@ func (r *Route) findSysInfoRoutingPredicatesGen4(predicates []sqlparser.Expr, re continue } + if r.SysTableTableName == nil { + r.SysTableTableName = map[string]evalengine.Expr{} + } + if isTableSchema { r.SysTableTableSchema = append(r.SysTableTableSchema, out) } else { - if r.SysTableTableName == nil { - r.SysTableTableName = map[string]evalengine.Expr{} - } r.SysTableTableName[bvName] = out } } return nil } -func extractInfoSchemaRoutingPredicate(in sqlparser.Expr, reservedVars *sqlparser.ReservedVars) (bool, string, evalengine.Expr, error) { - switch cmp := in.(type) { - case *sqlparser.ComparisonExpr: - if cmp.Operator == sqlparser.EqualOp { - isSchemaName, col, other, replaceOther := findOtherComparator(cmp) - if col != nil && shouldRewrite(other) { - evalExpr, err := evalengine.Translate(other, ¬ImplementedSchemaInfoConverter{}) - if err != nil { - if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) { - // This just means we can't rewrite this particular expression, - // not that we have to exit altogether - return false, "", nil, nil - } - return false, "", nil, err - } - var name string - if isSchemaName { - name = sqltypes.BvSchemaName - } else { - name = reservedVars.ReserveColName(col.(*sqlparser.ColName)) - } - replaceOther(sqlparser.NewArgument(name)) - return isSchemaName, name, evalExpr, nil - } +func extractInfoSchemaRoutingPredicate( + in sqlparser.Expr, + reservedVars *sqlparser.ReservedVars, +) (isSchemaName bool, name string, evalExpr evalengine.Expr, err error) { + cmp, ok := in.(*sqlparser.ComparisonExpr) + if !ok || cmp.Operator != sqlparser.EqualOp { + return + } + + isSchemaName, col := isTableOrSchemaRouteable(cmp) + if col == nil || !shouldRewrite(cmp.Right) { + return + } + + evalExpr, err = evalengine.Translate(cmp.Right, ¬ImplementedSchemaInfoConverter{}) + if err != nil { + if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) { + // This just means we can't rewrite this particular expression, + // not that we have to exit altogether + err = nil + return } + return + } + if isSchemaName { + name = sqltypes.BvSchemaName + } else { + name = reservedVars.ReserveColName(col) } - return false, "", nil, nil + cmp.Right = sqlparser.NewArgument(name) + return isSchemaName, name, evalExpr, nil } -func findOtherComparator(cmp *sqlparser.ComparisonExpr) (bool, sqlparser.Expr, sqlparser.Expr, func(arg sqlparser.Argument)) { - if schema, table := isTableSchemaOrName(cmp.Left); schema || table { - return schema, cmp.Left, cmp.Right, func(arg sqlparser.Argument) { - cmp.Right = arg - } +// isTableOrSchemaRouteable searches for a comparison where one side is a table or schema name column. +// if it finds the correct column name being used, +// it also makes sure that the LHS of the comparison contains the column, and the RHS the value sought after +func isTableOrSchemaRouteable(cmp *sqlparser.ComparisonExpr) ( + isSchema bool, // tells if we are dealing with a table or a schema name comparator + col *sqlparser.ColName, // which is the colName we are comparing against +) { + if col, schema, table := isTableSchemaOrName(cmp.Left); schema || table { + return schema, col } - if schema, table := isTableSchemaOrName(cmp.Right); schema || table { - return schema, cmp.Right, cmp.Left, func(arg sqlparser.Argument) { - cmp.Left = arg - } + if col, schema, table := isTableSchemaOrName(cmp.Right); schema || table { + // to make the rest of the code easier, we shuffle these around so the ColName is always on the LHS + cmp.Right, cmp.Left = cmp.Left, cmp.Right + return schema, col } - return false, nil, nil, nil + return false, nil } func shouldRewrite(e sqlparser.Expr) bool { @@ -102,12 +111,12 @@ func shouldRewrite(e sqlparser.Expr) bool { return true } -func isTableSchemaOrName(e sqlparser.Expr) (isTableSchema bool, isTableName bool) { +func isTableSchemaOrName(e sqlparser.Expr) (col *sqlparser.ColName, isTableSchema bool, isTableName bool) { col, ok := e.(*sqlparser.ColName) if !ok { - return false, false + return nil, false, false } - return isDbNameCol(col), isTableNameCol(col) + return col, isDbNameCol(col), isTableNameCol(col) } func isDbNameCol(col *sqlparser.ColName) bool { diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 162ef84d503..ed84e11429d 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -250,7 +250,7 @@ func TestPlan(t *testing.T) { testFile(t, "flush_cases_no_default_keyspace.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "show_cases_no_default_keyspace.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "stream_cases.json", testOutputTempDir, vschemaWrapper, false) - testFile(t, "systemtables_cases80.json", testOutputTempDir, vschemaWrapper, false) + testFile(t, "info_schema80_cases.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "reference_cases.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "vexplain_cases.json", testOutputTempDir, vschemaWrapper, false) } @@ -261,7 +261,7 @@ func TestSystemTables57(t *testing.T) { defer servenv.SetMySQLServerVersionForTest("") vschemaWrapper := &vschemaWrapper{v: loadSchema(t, "vschemas/schema.json", true)} testOutputTempDir := makeTestOutput(t) - testFile(t, "systemtables_cases57.json", testOutputTempDir, vschemaWrapper, false) + testFile(t, "info_schema57_cases.json", testOutputTempDir, vschemaWrapper, false) } func TestSysVarSetDisabled(t *testing.T) { diff --git a/go/vt/vtgate/planbuilder/system_tables.go b/go/vt/vtgate/planbuilder/system_tables.go index b6fe4c2fddc..ba061af909f 100644 --- a/go/vt/vtgate/planbuilder/system_tables.go +++ b/go/vt/vtgate/planbuilder/system_tables.go @@ -87,41 +87,60 @@ func isTableSchemaOrName(e sqlparser.Expr) (isTableSchema bool, isTableName bool return isDbNameCol(col), isTableNameCol(col) } +var schemaColumns = map[string]any{ + "table_schema": nil, + "constraint_schema": nil, + "schema_name": nil, + "routine_schema": nil, + "specific_schema": nil, + "event_schema": nil, + "referenced_table_schema": nil, + "index_schema": nil, + "trigger_schema": nil, + "event_object_schema": nil, +} + func isDbNameCol(col *sqlparser.ColName) bool { - return col.Name.EqualString("table_schema") || col.Name.EqualString("constraint_schema") || col.Name.EqualString("schema_name") || col.Name.EqualString("routine_schema") + _, found := schemaColumns[col.Name.Lowered()] + return found } func isTableNameCol(col *sqlparser.ColName) bool { - return col.Name.EqualString("table_name") + return col.Name.EqualString("table_name") || col.Name.EqualString("referenced_table_name") } -func extractInfoSchemaRoutingPredicate(in sqlparser.Expr, reservedVars *sqlparser.ReservedVars) (bool, string, evalengine.Expr, error) { - switch cmp := in.(type) { - case *sqlparser.ComparisonExpr: - if cmp.Operator == sqlparser.EqualOp { - isSchemaName, col, other, replaceOther := findOtherComparator(cmp) - if col != nil && shouldRewrite(other) { - evalExpr, err := evalengine.Translate(other, ¬ImplementedSchemaInfoConverter{}) - if err != nil { - if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) { - // This just means we can't rewrite this particular expression, - // not that we have to exit altogether - return false, "", nil, nil - } - return false, "", nil, err - } - var name string - if isSchemaName { - name = sqltypes.BvSchemaName - } else { - name = reservedVars.ReserveColName(col.(*sqlparser.ColName)) - } - replaceOther(sqlparser.NewArgument(name)) - return isSchemaName, name, evalExpr, nil - } +func extractInfoSchemaRoutingPredicate( + in sqlparser.Expr, + reservedVars *sqlparser.ReservedVars, +) (isSchemaName bool, name string, evalExpr evalengine.Expr, err error) { + cmp, ok := in.(*sqlparser.ComparisonExpr) + if !ok || cmp.Operator != sqlparser.EqualOp { + return + } + + isSchemaName, col, other, replaceOther := findOtherComparator(cmp) + if col == nil || !shouldRewrite(other) { + return + } + + evalExpr, err = evalengine.Translate(other, ¬ImplementedSchemaInfoConverter{}) + if err != nil { + if strings.Contains(err.Error(), evalengine.ErrTranslateExprNotSupported) { + // This just means we can't rewrite this particular expression, + // not that we have to exit altogether + err = nil + return } + return false, "", nil, err + } + + if isSchemaName { + name = sqltypes.BvSchemaName + } else { + name = reservedVars.ReserveColName(col.(*sqlparser.ColName)) } - return false, "", nil, nil + replaceOther(sqlparser.NewArgument(name)) + return isSchemaName, name, evalExpr, nil } func shouldRewrite(e sqlparser.Expr) bool { diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 91270d4e1f2..f47dd91b30b 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -2935,15 +2935,15 @@ }, { "comment": "join of information_schema with normal table", - "query": "select unsharded.foo from information_schema.a join unsharded", + "query": "select unsharded.foo from information_schema.CHARACTER_SETS join unsharded", "v3-plan": { "QueryType": "SELECT", - "Original": "select unsharded.foo from information_schema.a join unsharded", + "Original": "select unsharded.foo from information_schema.CHARACTER_SETS join unsharded", "Instructions": { "OperatorType": "Join", "Variant": "Join", "JoinColumnIndexes": "R:0", - "TableName": "information_schema.a_unsharded", + "TableName": "information_schema.CHARACTER_SETS_unsharded", "Inputs": [ { "OperatorType": "Route", @@ -2952,9 +2952,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select 1 from information_schema.a where 1 != 1", - "Query": "select 1 from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select 1 from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select 1 from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" }, { "OperatorType": "Route", @@ -2972,12 +2972,12 @@ }, "gen4-plan": { "QueryType": "SELECT", - "Original": "select unsharded.foo from information_schema.a join unsharded", + "Original": "select unsharded.foo from information_schema.CHARACTER_SETS join unsharded", "Instructions": { "OperatorType": "Join", "Variant": "Join", "JoinColumnIndexes": "R:0", - "TableName": "information_schema.a_unsharded", + "TableName": "information_schema.CHARACTER_SETS_unsharded", "Inputs": [ { "OperatorType": "Route", @@ -2986,9 +2986,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select 1 from information_schema.a where 1 != 1", - "Query": "select 1 from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select 1 from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select 1 from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" }, { "OperatorType": "Route", @@ -3010,15 +3010,15 @@ }, { "comment": "join of normal table with information_schema", - "query": "select unsharded.foo from unsharded join information_schema.a", + "query": "select unsharded.foo from unsharded join information_schema.CHARACTER_SETS", "v3-plan": { "QueryType": "SELECT", - "Original": "select unsharded.foo from unsharded join information_schema.a", + "Original": "select unsharded.foo from unsharded join information_schema.CHARACTER_SETS", "Instructions": { "OperatorType": "Join", "Variant": "Join", "JoinColumnIndexes": "L:0", - "TableName": "unsharded_information_schema.a", + "TableName": "unsharded_information_schema.CHARACTER_SETS", "Inputs": [ { "OperatorType": "Route", @@ -3038,21 +3038,21 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select 1 from information_schema.a where 1 != 1", - "Query": "select 1 from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select 1 from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select 1 from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" } ] } }, "gen4-plan": { "QueryType": "SELECT", - "Original": "select unsharded.foo from unsharded join information_schema.a", + "Original": "select unsharded.foo from unsharded join information_schema.CHARACTER_SETS", "Instructions": { "OperatorType": "Join", "Variant": "Join", "JoinColumnIndexes": "L:0", - "TableName": "unsharded_information_schema.a", + "TableName": "unsharded_information_schema.CHARACTER_SETS", "Inputs": [ { "OperatorType": "Route", @@ -3072,9 +3072,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select 1 from information_schema.a where 1 != 1", - "Query": "select 1 from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select 1 from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select 1 from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" } ] }, @@ -6165,7 +6165,7 @@ } }, { - "comment": "select * from (select bar as push_it from (select foo as bar from (select id as foo from user) as t1) as t2) as t3 where push_it = 12", + "comment": "derived table inside derived table with a where clause depending on columns from the derived table", "query": "select * from (select bar as push_it from (select foo as bar from (select id as foo from user) as t1) as t2) as t3 where push_it = 12", "v3-plan": { "QueryType": "SELECT", diff --git a/go/vt/vtgate/planbuilder/testdata/systemtables_cases57.json b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json similarity index 97% rename from go/vt/vtgate/planbuilder/testdata/systemtables_cases57.json rename to go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json index 90cd293c332..c3798bbc2fe 100644 --- a/go/vt/vtgate/planbuilder/testdata/systemtables_cases57.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json @@ -262,9 +262,9 @@ "Sharded": false }, "FieldQuery": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where 1 != 1", - "Query": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where KCU.TABLE_SCHEMA = :__vtschemaname and KCU.TABLE_NAME = :KCU_TABLE_NAME and KCU.COLUMN_NAME = 'id' and KCU.REFERENCED_TABLE_SCHEMA = 'test' and KCU.CONSTRAINT_NAME = 'data_type_table_id_fkey' order by KCU.CONSTRAINT_NAME asc, KCU.COLUMN_NAME asc", + "Query": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where KCU.TABLE_SCHEMA = :__vtschemaname and KCU.TABLE_NAME = :KCU_TABLE_NAME and KCU.COLUMN_NAME = 'id' and KCU.REFERENCED_TABLE_SCHEMA = :__vtschemaname and KCU.CONSTRAINT_NAME = 'data_type_table_id_fkey' order by KCU.CONSTRAINT_NAME asc, KCU.COLUMN_NAME asc", "SysTableTableName": "[KCU_TABLE_NAME:VARCHAR(\"data_type_table\")]", - "SysTableTableSchema": "[VARCHAR(\"test\")]", + "SysTableTableSchema": "[VARCHAR(\"test\"), VARCHAR(\"test\")]", "Table": "INFORMATION_SCHEMA.KEY_COLUMN_USAGE, INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS" } }, @@ -1052,10 +1052,25 @@ }, { "comment": "join of information_schema queries with select stars exprs", - "query": "select a.*, b.* from information_schema.a a, information_schema.b b", - "plan": { + "query": "select a.*, b.* from information_schema.GLOBAL_STATUS a, information_schema.CHARACTER_SETS b", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select a.*, b.* from information_schema.GLOBAL_STATUS a, information_schema.CHARACTER_SETS b", + "Instructions": { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select a.*, b.* from information_schema.GLOBAL_STATUS as a, information_schema.CHARACTER_SETS as b where 1 != 1", + "Query": "select a.*, b.* from information_schema.GLOBAL_STATUS as a, information_schema.CHARACTER_SETS as b", + "Table": "information_schema.GLOBAL_STATUS, information_schema.CHARACTER_SETS" + } + }, + "gen4-plan": { "QueryType": "SELECT", - "Original": "select a.*, b.* from information_schema.a a, information_schema.b b", + "Original": "select a.*, b.* from information_schema.GLOBAL_STATUS a, information_schema.CHARACTER_SETS b", "Instructions": { "OperatorType": "Route", "Variant": "DBA", @@ -1063,9 +1078,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select a.*, b.* from information_schema.a as a, information_schema.b as b where 1 != 1", - "Query": "select a.*, b.* from information_schema.a as a, information_schema.b as b", - "Table": "information_schema.a, information_schema.b" + "FieldQuery": "select a.VARIABLE_NAME as VARIABLE_NAME, a.VARIABLE_VALUE as VARIABLE_VALUE, b.CHARACTER_SET_NAME as CHARACTER_SET_NAME, b.DEFAULT_COLLATE_NAME as DEFAULT_COLLATE_NAME, b.DESCRIPTION as DESCRIPTION, b.MAXLEN as MAXLEN from information_schema.GLOBAL_STATUS as a, information_schema.CHARACTER_SETS as b where 1 != 1", + "Query": "select a.VARIABLE_NAME as VARIABLE_NAME, a.VARIABLE_VALUE as VARIABLE_VALUE, b.CHARACTER_SET_NAME as CHARACTER_SET_NAME, b.DEFAULT_COLLATE_NAME as DEFAULT_COLLATE_NAME, b.DESCRIPTION as DESCRIPTION, b.MAXLEN as MAXLEN from information_schema.GLOBAL_STATUS as a, information_schema.CHARACTER_SETS as b", + "Table": "information_schema.CHARACTER_SETS, information_schema.GLOBAL_STATUS" } } }, @@ -1544,5 +1559,24 @@ "Table": "INFORMATION_SCHEMA.`TABLES`" } } + }, + { + "comment": "Non-existing information_schema table is still OK", + "query": "select TABLE_NAME from information_schema.apa", + "plan": { + "QueryType": "SELECT", + "Original": "select TABLE_NAME from information_schema.apa", + "Instructions": { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select TABLE_NAME from information_schema.apa where 1 != 1", + "Query": "select TABLE_NAME from information_schema.apa", + "Table": "information_schema.apa" + } + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/systemtables_cases80.json b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json similarity index 97% rename from go/vt/vtgate/planbuilder/testdata/systemtables_cases80.json rename to go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json index cd2d3645c08..919ce017add 100644 --- a/go/vt/vtgate/planbuilder/testdata/systemtables_cases80.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json @@ -262,9 +262,9 @@ "Sharded": false }, "FieldQuery": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where 1 != 1", - "Query": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where KCU.TABLE_SCHEMA = :__vtschemaname and KCU.TABLE_NAME = :KCU_TABLE_NAME and KCU.COLUMN_NAME = 'id' and KCU.REFERENCED_TABLE_SCHEMA = 'test' and KCU.CONSTRAINT_NAME = 'data_type_table_id_fkey' order by KCU.CONSTRAINT_NAME asc, KCU.COLUMN_NAME asc", + "Query": "select RC.CONSTRAINT_NAME, ORDINAL_POSITION from INFORMATION_SCHEMA.KEY_COLUMN_USAGE as KCU join INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as RC on KCU.CONSTRAINT_NAME = RC.CONSTRAINT_NAME where KCU.TABLE_SCHEMA = :__vtschemaname and KCU.TABLE_NAME = :KCU_TABLE_NAME and KCU.COLUMN_NAME = 'id' and KCU.REFERENCED_TABLE_SCHEMA = :__vtschemaname and KCU.CONSTRAINT_NAME = 'data_type_table_id_fkey' order by KCU.CONSTRAINT_NAME asc, KCU.COLUMN_NAME asc", "SysTableTableName": "[KCU_TABLE_NAME:VARCHAR(\"data_type_table\")]", - "SysTableTableSchema": "[VARCHAR(\"test\")]", + "SysTableTableSchema": "[VARCHAR(\"test\"), VARCHAR(\"test\")]", "Table": "INFORMATION_SCHEMA.KEY_COLUMN_USAGE, INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS" } }, @@ -1110,10 +1110,25 @@ }, { "comment": "join of information_schema queries with select stars exprs", - "query": "select a.*, b.* from information_schema.a a, information_schema.b b", - "plan": { + "query": "select a.*, b.* from information_schema.CHECK_CONSTRAINTS a, information_schema.CHARACTER_SETS b", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select a.*, b.* from information_schema.CHECK_CONSTRAINTS a, information_schema.CHARACTER_SETS b", + "Instructions": { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select a.*, b.* from information_schema.CHECK_CONSTRAINTS as a, information_schema.CHARACTER_SETS as b where 1 != 1", + "Query": "select a.*, b.* from information_schema.CHECK_CONSTRAINTS as a, information_schema.CHARACTER_SETS as b", + "Table": "information_schema.CHECK_CONSTRAINTS, information_schema.CHARACTER_SETS" + } + }, + "gen4-plan": { "QueryType": "SELECT", - "Original": "select a.*, b.* from information_schema.a a, information_schema.b b", + "Original": "select a.*, b.* from information_schema.CHECK_CONSTRAINTS a, information_schema.CHARACTER_SETS b", "Instructions": { "OperatorType": "Route", "Variant": "DBA", @@ -1121,9 +1136,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select a.*, b.* from information_schema.a as a, information_schema.b as b where 1 != 1", - "Query": "select a.*, b.* from information_schema.a as a, information_schema.b as b", - "Table": "information_schema.a, information_schema.b" + "FieldQuery": "select a.CONSTRAINT_CATALOG as CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA as CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME as CONSTRAINT_NAME, a.CHECK_CLAUSE as CHECK_CLAUSE, b.CHARACTER_SET_NAME as CHARACTER_SET_NAME, b.DEFAULT_COLLATE_NAME as DEFAULT_COLLATE_NAME, b.DESCRIPTION as DESCRIPTION, b.MAXLEN as MAXLEN from information_schema.CHECK_CONSTRAINTS as a, information_schema.CHARACTER_SETS as b where 1 != 1", + "Query": "select a.CONSTRAINT_CATALOG as CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA as CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME as CONSTRAINT_NAME, a.CHECK_CLAUSE as CHECK_CLAUSE, b.CHARACTER_SET_NAME as CHARACTER_SET_NAME, b.DEFAULT_COLLATE_NAME as DEFAULT_COLLATE_NAME, b.DESCRIPTION as DESCRIPTION, b.MAXLEN as MAXLEN from information_schema.CHECK_CONSTRAINTS as a, information_schema.CHARACTER_SETS as b", + "Table": "information_schema.CHARACTER_SETS, information_schema.CHECK_CONSTRAINTS" } } }, @@ -1659,5 +1674,24 @@ "Table": "performance_schema.error_log" } } + }, + { + "comment": "Non-existing information_schema table is still OK", + "query": "select TABLE_NAME from information_schema.apa", + "plan": { + "QueryType": "SELECT", + "Original": "select TABLE_NAME from information_schema.apa", + "Instructions": { + "OperatorType": "Route", + "Variant": "DBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select TABLE_NAME from information_schema.apa where 1 != 1", + "Query": "select TABLE_NAME from information_schema.apa", + "Table": "information_schema.apa" + } + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.json b/go/vt/vtgate/planbuilder/testdata/union_cases.json index d8a90c39ab4..5aba78fcb40 100644 --- a/go/vt/vtgate/planbuilder/testdata/union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/union_cases.json @@ -587,10 +587,10 @@ }, { "comment": "union of information_schema with normal table", - "query": "select * from information_schema.a union select * from unsharded", + "query": "select CHARACTER_SET_NAME from information_schema.CHARACTER_SETS union select user_name from unsharded", "v3-plan": { "QueryType": "SELECT", - "Original": "select * from information_schema.a union select * from unsharded", + "Original": "select CHARACTER_SET_NAME from information_schema.CHARACTER_SETS union select user_name from unsharded", "Instructions": { "OperatorType": "Distinct", "Inputs": [ @@ -604,9 +604,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from information_schema.a where 1 != 1", - "Query": "select * from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select CHARACTER_SET_NAME from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select CHARACTER_SET_NAME from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" }, { "OperatorType": "Route", @@ -615,8 +615,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from unsharded where 1 != 1", - "Query": "select * from unsharded", + "FieldQuery": "select user_name from unsharded where 1 != 1", + "Query": "select user_name from unsharded", "Table": "unsharded" } ] @@ -626,9 +626,13 @@ }, "gen4-plan": { "QueryType": "SELECT", - "Original": "select * from information_schema.a union select * from unsharded", + "Original": "select CHARACTER_SET_NAME from information_schema.CHARACTER_SETS union select user_name from unsharded", "Instructions": { "OperatorType": "Distinct", + "Collations": [ + "(0:1)" + ], + "ResultColumns": 1, "Inputs": [ { "OperatorType": "Concatenate", @@ -640,9 +644,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from information_schema.a where 1 != 1", - "Query": "select distinct * from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select CHARACTER_SET_NAME, weight_string(CHARACTER_SET_NAME) from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select distinct CHARACTER_SET_NAME, weight_string(CHARACTER_SET_NAME) from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" }, { "OperatorType": "Route", @@ -651,8 +655,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from unsharded where 1 != 1", - "Query": "select distinct * from unsharded", + "FieldQuery": "select user_name, weight_string(user_name) from unsharded where 1 != 1", + "Query": "select distinct user_name, weight_string(user_name) from unsharded", "Table": "unsharded" } ] @@ -666,10 +670,10 @@ }, { "comment": "union of information_schema with normal table", - "query": "select * from unsharded union select * from information_schema.a", + "query": "select * from unsharded union select * from information_schema.CHARACTER_SETS", "v3-plan": { "QueryType": "SELECT", - "Original": "select * from unsharded union select * from information_schema.a", + "Original": "select * from unsharded union select * from information_schema.CHARACTER_SETS", "Instructions": { "OperatorType": "Distinct", "Inputs": [ @@ -694,9 +698,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from information_schema.a where 1 != 1", - "Query": "select * from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select * from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select * from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" } ] } @@ -705,7 +709,7 @@ }, "gen4-plan": { "QueryType": "SELECT", - "Original": "select * from unsharded union select * from information_schema.a", + "Original": "select * from unsharded union select * from information_schema.CHARACTER_SETS", "Instructions": { "OperatorType": "Distinct", "Inputs": [ @@ -730,9 +734,9 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select * from information_schema.a where 1 != 1", - "Query": "select distinct * from information_schema.a", - "Table": "information_schema.a" + "FieldQuery": "select CHARACTER_SET_NAME, DEFAULT_COLLATE_NAME, DESCRIPTION, MAXLEN from information_schema.CHARACTER_SETS where 1 != 1", + "Query": "select distinct CHARACTER_SET_NAME, DEFAULT_COLLATE_NAME, DESCRIPTION, MAXLEN from information_schema.CHARACTER_SETS", + "Table": "information_schema.CHARACTER_SETS" } ] } diff --git a/go/vt/vtgate/semantics/info_schema.go b/go/vt/vtgate/semantics/info_schema.go index f834bb52f35..b27e197c16f 100644 --- a/go/vt/vtgate/semantics/info_schema.go +++ b/go/vt/vtgate/semantics/info_schema.go @@ -1689,15 +1689,13 @@ func (i *infoSchemaWithColumns) FindTableOrVindex(tbl sqlparser.TableName) (*vin return i.inner.FindTableOrVindex(tbl) } - ks := vindexes.Keyspace{ - Name: "information_schema", - Sharded: false, + cols, found := i.infoSchemaData[strings.ToUpper(tbl.Name.String())] + if !found { + return nil, nil, "", topodatapb.TabletType_UNKNOWN, nil, vindexes.NotFoundError{TableName: tbl.Name.String()} } - cols := i.infoSchemaData[strings.ToUpper(tbl.Name.String())] vtbl := &vindexes.Table{ Type: "View", Name: sqlparser.NewIdentifierCS(tbl.Name.String()), - Keyspace: &ks, Columns: cols, ColumnListAuthoritative: true, }