From f6e2b563f568076948eebb91a72ef75567fdb813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Fri, 25 Aug 2023 11:24:02 +0200 Subject: [PATCH 1/8] infoschema,executor: Add CHECK_CONSTRAINT I_S table --- executor/builder.go | 3 ++- infoschema/tables.go | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/executor/builder.go b/executor/builder.go index c4dca3e29958e..b48dca24a6553 100644 --- a/executor/builder.go +++ b/executor/builder.go @@ -2115,7 +2115,8 @@ func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) exec.Ex strings.ToLower(infoschema.ClusterTableMemoryUsage), strings.ToLower(infoschema.ClusterTableMemoryUsageOpsHistory), strings.ToLower(infoschema.TableResourceGroups), - strings.ToLower(infoschema.TableRunawayWatches): + strings.ToLower(infoschema.TableRunawayWatches), + strings.ToLower(infoschema.TableCheckConstraints): return &MemTableReaderExec{ BaseExecutor: exec.NewBaseExecutor(b.ctx, v.Schema(), v.ID()), table: v.Table, diff --git a/infoschema/tables.go b/infoschema/tables.go index ba9881f53be6c..48cfb69046660 100644 --- a/infoschema/tables.go +++ b/infoschema/tables.go @@ -206,6 +206,8 @@ const ( TableResourceGroups = "RESOURCE_GROUPS" // TableRunawayWatches is the query list of runaway watch. TableRunawayWatches = "RUNAWAY_WATCHES" + // TableCheckConstraints is the list of CHECK constraints. + TableCheckConstraints = "CHECK_CONSTRAINTS" ) const ( @@ -314,6 +316,7 @@ var tableIDMap = map[string]int64{ ClusterTableMemoryUsageOpsHistory: autoid.InformationSchemaDBID + 87, TableResourceGroups: autoid.InformationSchemaDBID + 88, TableRunawayWatches: autoid.InformationSchemaDBID + 89, + TableCheckConstraints: autoid.InformationSchemaDBID + 90, } // columnInfo represents the basic column information of all kinds of INFORMATION_SCHEMA tables @@ -1625,6 +1628,13 @@ var tableRunawayWatchListCols = []columnInfo{ {name: "ACTION", tp: mysql.TypeVarchar, size: 12, flag: mysql.NotNullFlag}, } +var tableCheckConstraintsCols = []columnInfo{ + {name: "CONSTRAINT_CATALOG", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CONSTRAINT_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "CHECK_CLAUSE", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, +} + // GetShardingInfo returns a nil or description string for the sharding information of given TableInfo. // The returned description string may be: // - "NOT_SHARDED": for tables that SHARD_ROW_ID_BITS is not specified. @@ -2138,6 +2148,7 @@ var tableNameToColumns = map[string][]columnInfo{ TableMemoryUsageOpsHistory: tableMemoryUsageOpsHistoryCols, TableResourceGroups: tableResourceGroupsCols, TableRunawayWatches: tableRunawayWatchListCols, + TableCheckConstraints: tableCheckConstraintsCols, } func createInfoSchemaTable(_ autoid.Allocators, meta *model.TableInfo) (table.Table, error) { From 73c648c58a01242ce5feb4219bf534d82430cfbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Fri, 25 Aug 2023 12:48:59 +0200 Subject: [PATCH 2/8] Populate information_schema.CHECK_CONSTRAINTS --- executor/infoschema_reader.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index 1518abfaf0b8e..4790155b4018c 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -194,6 +194,8 @@ func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Contex err = e.setDataFromResourceGroups() case infoschema.TableRunawayWatches: err = e.setDataFromRunawayWatches(sctx) + case infoschema.TableCheckConstraints: + err = e.setDataFromCheckConstraints(ctx, sctx, dbs) } if err != nil { return nil, err @@ -627,6 +629,31 @@ func (e *memtableRetriever) setDataFromTables(ctx context.Context, sctx sessionc return nil } +func (e *memtableRetriever) setDataFromCheckConstraints(ctx context.Context, sctx sessionctx.Context, schemas []*model.DBInfo) error { + var rows [][]types.Datum + checker := privilege.GetPrivilegeManager(sctx) + for _, schema := range schemas { + for _, table := range schema.Tables { + if len(table.Constraints) > 0 { + if checker != nil && !checker.RequestVerification(sctx.GetSessionVars().ActiveRoles, schema.Name.L, table.Name.L, "", mysql.SelectPriv) { + continue + } + for _, constraint := range table.Constraints { + record := types.MakeDatums( + infoschema.CatalogVal, // CONSTRAINT_CATALOG + schema.Name.O, // CONSTRAINT_SCHEMA + constraint.Name.O, // CONSTRAINT_NAME + fmt.Sprintf("(%s)", constraint.ExprString), // CHECK_CLAUSE + ) + rows = append(rows, record) + } + } + } + } + e.rows = rows + return nil +} + func (e *hugeMemTableRetriever) setDataForColumns(ctx context.Context, sctx sessionctx.Context, extractor *plannercore.ColumnsTableExtractor) error { checker := privilege.GetPrivilegeManager(sctx) e.rows = e.rows[:0] From 25bcd6cc97db6cfc38f2abd007e1c68b67093c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Fri, 25 Aug 2023 13:14:04 +0200 Subject: [PATCH 3/8] Add test --- infoschema/test/clustertablestest/tables_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/infoschema/test/clustertablestest/tables_test.go b/infoschema/test/clustertablestest/tables_test.go index a1d3e2cccf368..39a6e5c623cfc 100644 --- a/infoschema/test/clustertablestest/tables_test.go +++ b/infoschema/test/clustertablestest/tables_test.go @@ -1811,3 +1811,16 @@ func TestAddFieldsForBinding(t *testing.T) { require.Equal(t, rows[0][7], "use_index(@`sel_1` `test`.`t` ), ignore_index(`t` `a`)") require.Equal(t, rows[0][8], "select * from `t` where `a` = ?") } + +func TestCheckConstraints(t *testing.T) { + store := testkit.CreateMockStore(t) + + tk := testkit.NewTestKit(t, store) + tk.MustExec("SET GLOBAL tidb_enable_check_constraint = ON") + tk.MustExec("CREATE TABLE test.t1 (id INT PRIMARY KEY, CHECK (id<10))") + rows := tk.MustQuery("SELECT * FROM information_schema.CHECK_CONSTRAINTS").Rows() + require.Equal(t, rows[0][0], "def") + require.Equal(t, rows[0][1], "test") + require.Equal(t, rows[0][2], "t1_chk_1") + require.Equal(t, rows[0][3], "(`id` < 10)") +} From 4e52baf70ab31ce1f6bbd497d1e9d0e968c35c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Fri, 25 Aug 2023 13:37:14 +0200 Subject: [PATCH 4/8] Fixup for nogo issue --- executor/infoschema_reader.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index 4790155b4018c..b24506a3aa77b 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -195,7 +195,7 @@ func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Contex case infoschema.TableRunawayWatches: err = e.setDataFromRunawayWatches(sctx) case infoschema.TableCheckConstraints: - err = e.setDataFromCheckConstraints(ctx, sctx, dbs) + err = e.setDataFromCheckConstraints(sctx, dbs) } if err != nil { return nil, err @@ -629,7 +629,7 @@ func (e *memtableRetriever) setDataFromTables(ctx context.Context, sctx sessionc return nil } -func (e *memtableRetriever) setDataFromCheckConstraints(ctx context.Context, sctx sessionctx.Context, schemas []*model.DBInfo) error { +func (e *memtableRetriever) setDataFromCheckConstraints(sctx sessionctx.Context, schemas []*model.DBInfo) error { var rows [][]types.Datum checker := privilege.GetPrivilegeManager(sctx) for _, schema := range schemas { From ab498cc9ea1bd9c045c6fc1c18b9bed169c2e967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 7 Sep 2023 09:29:32 +0200 Subject: [PATCH 5/8] Update based on review --- executor/infoschema_reader.go | 3 +++ .../test/clustertablestest/tables_test.go | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index b24506a3aa77b..115ce18ab3f7c 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -639,6 +639,9 @@ func (e *memtableRetriever) setDataFromCheckConstraints(sctx sessionctx.Context, continue } for _, constraint := range table.Constraints { + if constraint.State != model.StatePublic { + continue + } record := types.MakeDatums( infoschema.CatalogVal, // CONSTRAINT_CATALOG schema.Name.O, // CONSTRAINT_SCHEMA diff --git a/infoschema/test/clustertablestest/tables_test.go b/infoschema/test/clustertablestest/tables_test.go index 39a6e5c623cfc..f7c886b382a78 100644 --- a/infoschema/test/clustertablestest/tables_test.go +++ b/infoschema/test/clustertablestest/tables_test.go @@ -1814,13 +1814,30 @@ func TestAddFieldsForBinding(t *testing.T) { func TestCheckConstraints(t *testing.T) { store := testkit.CreateMockStore(t) - tk := testkit.NewTestKit(t, store) tk.MustExec("SET GLOBAL tidb_enable_check_constraint = ON") + tk.MustExec("CREATE TABLE test.t1 (id INT PRIMARY KEY, CHECK (id<10))") rows := tk.MustQuery("SELECT * FROM information_schema.CHECK_CONSTRAINTS").Rows() + require.Equal(t, len(rows), 1) require.Equal(t, rows[0][0], "def") require.Equal(t, rows[0][1], "test") require.Equal(t, rows[0][2], "t1_chk_1") require.Equal(t, rows[0][3], "(`id` < 10)") + + tk.MustExec("ALTER TABLE test.t1 DROP CONSTRAINT t1_chk_1") + rows = tk.MustQuery("SELECT * FROM information_schema.CHECK_CONSTRAINTS").Rows() + require.Equal(t, len(rows), 0) + + tk.MustExec("CREATE TABLE test.t2 (id INT PRIMARY KEY, CHECK (id<20))") + rows = tk.MustQuery("SELECT * FROM information_schema.CHECK_CONSTRAINTS").Rows() + require.Equal(t, len(rows), 1) + require.Equal(t, rows[0][0], "def") + require.Equal(t, rows[0][1], "test") + require.Equal(t, rows[0][2], "t2_chk_1") + require.Equal(t, rows[0][3], "(`id` < 20)") + + tk.MustExec("DROP TABLE test.t2") + rows = tk.MustQuery("SELECT * FROM information_schema.CHECK_CONSTRAINTS").Rows() + require.Equal(t, len(rows), 0) } From d8bf5d356afaa0bfeac5d4068f31bbfd3ff8fd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 7 Sep 2023 11:08:42 +0200 Subject: [PATCH 6/8] Add test for setDataFromCheckConstraints() --- executor/infoschema_reader_internal_test.go | 59 +++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 executor/infoschema_reader_internal_test.go diff --git a/executor/infoschema_reader_internal_test.go b/executor/infoschema_reader_internal_test.go new file mode 100644 index 0000000000000..ea0c146af2a2a --- /dev/null +++ b/executor/infoschema_reader_internal_test.go @@ -0,0 +1,59 @@ +package executor + +import ( + "testing" + + "github.com/pingcap/tidb/parser/model" + "github.com/pingcap/tidb/types" + "github.com/stretchr/testify/require" +) + +func TestSetDataFromCheckConstraints(t *testing.T) { + mt := memtableRetriever{} + sctx := defaultCtx() + dbs := []*model.DBInfo{ + { + ID: 1, + Name: model.NewCIStr("test"), + Tables: []*model.TableInfo{ + { + ID: 1, + Name: model.NewCIStr("t1"), + }, + { + ID: 2, + Name: model.NewCIStr("t2"), + Constraints: []*model.ConstraintInfo{ + { + Name: model.NewCIStr("t2_c1"), + Table: model.NewCIStr("t2"), + ExprString: "id<10", + State: model.StatePublic, + }, + }, + }, + { + ID: 3, + Name: model.NewCIStr("t3"), + Constraints: []*model.ConstraintInfo{ + { + Name: model.NewCIStr("t3_c1"), + Table: model.NewCIStr("t3"), + ExprString: "id<10", + State: model.StateDeleteOnly, + }, + }, + }, + }, + }, + } + err := mt.setDataFromCheckConstraints(sctx, dbs) + require.NoError(t, err) + + require.Equal(t, 1, len(mt.rows)) + require.Equal(t, 4, len(mt.rows[0])) + require.Equal(t, types.NewStringDatum("def"), mt.rows[0][0]) + require.Equal(t, types.NewStringDatum("test"), mt.rows[0][1]) + require.Equal(t, types.NewStringDatum("t2_c1"), mt.rows[0][2]) + require.Equal(t, types.NewStringDatum("(id<10)"), mt.rows[0][3]) +} From 83e5930ae99add8b72201b4e57a25a41959c4a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 7 Sep 2023 11:15:03 +0200 Subject: [PATCH 7/8] Update BUILD.bazel --- executor/BUILD.bazel | 1 + executor/infoschema_reader_internal_test.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/executor/BUILD.bazel b/executor/BUILD.bazel index 1296e90df8685..a8821b6c19c64 100644 --- a/executor/BUILD.bazel +++ b/executor/BUILD.bazel @@ -317,6 +317,7 @@ go_test( "index_lookup_join_test.go", "index_lookup_merge_join_test.go", "infoschema_cluster_table_test.go", + "infoschema_reader_internal_test.go", "infoschema_reader_test.go", "insert_test.go", "inspection_common_test.go", diff --git a/executor/infoschema_reader_internal_test.go b/executor/infoschema_reader_internal_test.go index ea0c146af2a2a..6db80411d1667 100644 --- a/executor/infoschema_reader_internal_test.go +++ b/executor/infoschema_reader_internal_test.go @@ -50,8 +50,8 @@ func TestSetDataFromCheckConstraints(t *testing.T) { err := mt.setDataFromCheckConstraints(sctx, dbs) require.NoError(t, err) - require.Equal(t, 1, len(mt.rows)) - require.Equal(t, 4, len(mt.rows[0])) + require.Equal(t, 1, len(mt.rows)) // 1 row + require.Equal(t, 4, len(mt.rows[0])) // 4 columns require.Equal(t, types.NewStringDatum("def"), mt.rows[0][0]) require.Equal(t, types.NewStringDatum("test"), mt.rows[0][1]) require.Equal(t, types.NewStringDatum("t2_c1"), mt.rows[0][2]) From 7eb28387bbf899cb31dfa543b9e868edd5f8c0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20Eeden?= Date: Thu, 7 Sep 2023 11:24:46 +0200 Subject: [PATCH 8/8] Add license header to infoschema_reader_internal_test.go --- executor/infoschema_reader_internal_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/executor/infoschema_reader_internal_test.go b/executor/infoschema_reader_internal_test.go index 6db80411d1667..b942fca493a19 100644 --- a/executor/infoschema_reader_internal_test.go +++ b/executor/infoschema_reader_internal_test.go @@ -1,3 +1,17 @@ +// Copyright 2023 PingCAP, Inc. +// +// 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 executor import (