diff --git a/executor/builder.go b/executor/builder.go index 224ba7d1da1f2..1e10e3640f802 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -1574,6 +1574,7 @@ func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) Executo strings.ToLower(infoschema.TableTiDBIndexes), strings.ToLower(infoschema.TableViews), strings.ToLower(infoschema.TableTables), + strings.ToLower(infoschema.TableReferConst), strings.ToLower(infoschema.TableSequences), strings.ToLower(infoschema.TablePartitions), strings.ToLower(infoschema.TableEngines), diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index ddc6bfb9f6ea4..30060fca8d3b1 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/errors" "github.com/pingcap/failpoint" "github.com/pingcap/kvproto/pkg/deadlock" + "github.com/pingcap/parser/ast" "github.com/pingcap/parser/charset" "github.com/pingcap/parser/model" "github.com/pingcap/parser/mysql" @@ -103,6 +104,8 @@ func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Contex e.setDataForStatistics(sctx, dbs) case infoschema.TableTables: err = e.setDataFromTables(ctx, sctx, dbs) + case infoschema.TableReferConst: + err = e.setDataFromReferConst(ctx, sctx, dbs) case infoschema.TableSequences: e.setDataFromSequences(sctx, dbs) case infoschema.TablePartitions: @@ -463,6 +466,46 @@ func (e *memtableRetriever) setDataForStatisticsInTable(schema *model.DBInfo, ta e.rows = append(e.rows, rows...) } +func (e *memtableRetriever) setDataFromReferConst(ctx context.Context, sctx sessionctx.Context, schemas []*model.DBInfo) error { + checker := privilege.GetPrivilegeManager(sctx) + var rows [][]types.Datum + for _, schema := range schemas { + for _, table := range schema.Tables { + if !table.IsBaseTable() { + continue + } + if checker != nil && !checker.RequestVerification(sctx.GetSessionVars().ActiveRoles, schema.Name.L, table.Name.L, "", mysql.AllPrivMask) { + continue + } + for _, fk := range table.ForeignKeys { + updateRule, deleteRule := "NO ACTION", "NO ACTION" + if ast.ReferOptionType(fk.OnUpdate) != 0 { + updateRule = ast.ReferOptionType(fk.OnUpdate).String() + } + if ast.ReferOptionType(fk.OnDelete) != 0 { + deleteRule = ast.ReferOptionType(fk.OnDelete).String() + } + record := types.MakeDatums( + infoschema.CatalogVal, // CONSTRAINT_CATALOG + schema.Name.O, // CONSTRAINT_SCHEMA + fk.Name.O, // CONSTRAINT_NAME + infoschema.CatalogVal, // UNIQUE_CONSTRAINT_CATALOG + schema.Name.O, // UNIQUE_CONSTRAINT_SCHEMA + "PRIMARY", // UNIQUE_CONSTRAINT_NAME + "NONE", // MATCH_OPTION + updateRule, // UPDATE_RULE + deleteRule, // DELETE_RULE + table.Name.O, // TABLE_NAME + fk.RefTable.O, // REFERENCED_TABLE_NAME + ) + rows = append(rows, record) + } + } + } + e.rows = rows + return nil +} + func (e *memtableRetriever) setDataFromTables(ctx context.Context, sctx sessionctx.Context, schemas []*model.DBInfo) error { tableRowsMap, colLengthMap, err := tableStatsCache.get(ctx, sctx) if err != nil { diff --git a/expression/integration_test.go b/expression/integration_test.go index 25c5972c0c789..ed233391e1a3f 100644 --- a/expression/integration_test.go +++ b/expression/integration_test.go @@ -10054,6 +10054,41 @@ func (s *testIntegrationSuite) TestTimestampIssue25093(c *C) { tk.MustQuery("select timestamp(101.234) from t;").Check(testkit.Rows("2000-01-01 00:00:00.000")) } +// issue https://github.com/pingcap/tidb/issues/26111 +func (s *testIntegrationSuite) TestRailsFKUsage(c *C) { + defer s.cleanEnv(c) + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec(`CREATE TABLE author_addresses ( + id bigint(20) NOT NULL AUTO_INCREMENT, + PRIMARY KEY (id) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`) + tk.MustExec(`CREATE TABLE authors ( + id bigint(20) NOT NULL AUTO_INCREMENT, + name varchar(255) NOT NULL, + author_address_id bigint(20) DEFAULT NULL, + author_address_extra_id bigint(20) DEFAULT NULL, + organization_id varchar(255) DEFAULT NULL, + owned_essay_id varchar(255) DEFAULT NULL, + PRIMARY KEY (id), + KEY index_authors_on_author_address_id (author_address_id), + KEY index_authors_on_author_address_extra_id (author_address_extra_id), + CONSTRAINT fk_rails_94423a17a3 FOREIGN KEY (author_address_id) REFERENCES author_addresses (id) ON UPDATE CASCADE ON DELETE RESTRICT + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`) + tk.MustQuery(`SELECT fk.referenced_table_name AS 'to_table', + fk.referenced_column_name AS 'primary_key', + fk.column_name AS 'column', + fk.constraint_name AS 'name', + rc.update_rule AS 'on_update', + rc.delete_rule AS 'on_delete' + FROM information_schema.referential_constraints rc + JOIN information_schema.key_column_usage fk + USING (constraint_schema, constraint_name) + WHERE fk.referenced_column_name IS NOT NULL + AND fk.table_schema = database() + AND fk.table_name = 'authors';`).Check(testkit.Rows("author_addresses id author_address_id fk_rails_94423a17a3 CASCADE RESTRICT")) +} + func (s *testIntegrationSuite) TestTranslate(c *C) { cases := []string{"'ABC'", "'AABC'", "'A.B.C'", "'aaaaabbbbb'", "'abc'", "'aaa'", "NULL"} tk := testkit.NewTestKit(c, s.store) diff --git a/infoschema/tables.go b/infoschema/tables.go index 72e1a93f2a43d..140a1d15446c7 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -72,8 +72,9 @@ const ( // TablePartitions is the string constant of infoschema table. TablePartitions = "PARTITIONS" // TableKeyColumn is the string constant of KEY_COLUMN_USAGE. - TableKeyColumn = "KEY_COLUMN_USAGE" - tableReferConst = "REFERENTIAL_CONSTRAINTS" + TableKeyColumn = "KEY_COLUMN_USAGE" + // TableReferConst is the string constant of REFERENTIAL_CONSTRAINTS. + TableReferConst = "REFERENTIAL_CONSTRAINTS" // TableSessionVar is the string constant of SESSION_VARIABLES. TableSessionVar = "SESSION_VARIABLES" tablePlugins = "PLUGINS" @@ -205,7 +206,7 @@ var tableIDMap = map[string]int64{ TableProfiling: autoid.InformationSchemaDBID + 10, TablePartitions: autoid.InformationSchemaDBID + 11, TableKeyColumn: autoid.InformationSchemaDBID + 12, - tableReferConst: autoid.InformationSchemaDBID + 13, + TableReferConst: autoid.InformationSchemaDBID + 13, TableSessionVar: autoid.InformationSchemaDBID + 14, tablePlugins: autoid.InformationSchemaDBID + 15, TableConstraints: autoid.InformationSchemaDBID + 16, @@ -1752,7 +1753,7 @@ var tableNameToColumns = map[string][]columnInfo{ TableProfiling: profilingCols, TablePartitions: partitionsCols, TableKeyColumn: keyColumnUsageCols, - tableReferConst: referConstCols, + TableReferConst: referConstCols, TableSessionVar: sessionVarCols, tablePlugins: pluginsCols, TableConstraints: tableConstraintsCols, @@ -1851,7 +1852,6 @@ func (it *infoschemaTable) getRows(ctx sessionctx.Context, cols []*table.Column) sort.Sort(SchemasSorter(dbs)) switch it.meta.Name.O { case tableFiles: - case tableReferConst: case tablePlugins, tableTriggers: case tableRoutines: // TODO: Fill the following tables. diff --git a/infoschema/tables_test.go b/infoschema/tables_test.go index fa54b8f0da380..2033786541f30 100644 --- a/infoschema/tables_test.go +++ b/infoschema/tables_test.go @@ -1886,3 +1886,15 @@ func (s *testClusterTableSuite) TestDataLockWaitsPrivilege(c *C) { }, nil, nil), IsTrue) _ = tk.MustQuery("select * from information_schema.DATA_LOCK_WAITS") } + +func (s *testTableSuite) TestReferentialConstraints(c *C) { + tk := testkit.NewTestKit(c, s.store) + + tk.MustExec("CREATE DATABASE referconstraints") + tk.MustExec("use referconstraints") + + tk.MustExec("CREATE TABLE t1 (id INT NOT NULL PRIMARY KEY)") + tk.MustExec("CREATE TABLE t2 (id INT NOT NULL PRIMARY KEY, t1_id INT DEFAULT NULL, INDEX (t1_id), CONSTRAINT `fk_to_t1` FOREIGN KEY (`t1_id`) REFERENCES `t1` (`id`))") + + tk.MustQuery(`SELECT * FROM information_schema.referential_constraints WHERE table_name='t2'`).Check(testkit.Rows("def referconstraints fk_to_t1 def referconstraints PRIMARY NONE NO ACTION NO ACTION t2 t1")) +}