From b3d7a8e85fb0b4fe44a536e5bfdb8b358174a324 Mon Sep 17 00:00:00 2001 From: e11jah <30852919+e11jah@users.noreply.github.com> Date: Mon, 30 May 2022 13:58:26 +0800 Subject: [PATCH] executor: fix show database like case sensitive issue#34766 (#34925) close pingcap/tidb#34766 --- executor/show.go | 19 +++++++++++++++++++ executor/show_test.go | 17 +++++++++++++++++ planner/core/planbuilder.go | 7 +++++++ planner/core/show_predicate_extractor.go | 23 +++++++++++++++++++++++ planner/core/stringer_test.go | 16 ++++++++++++++++ 5 files changed, 82 insertions(+) diff --git a/executor/show.go b/executor/show.go index 79d466aa71d2e..d4568e86526cb 100644 --- a/executor/show.go +++ b/executor/show.go @@ -402,11 +402,30 @@ func (e *ShowExec) fetchShowDatabases() error { dbs := e.is.AllSchemaNames() checker := privilege.GetPrivilegeManager(e.ctx) sort.Strings(dbs) + var ( + fieldPatternsLike collate.WildcardPattern + FieldFilterEnable bool + fieldFilter string + ) + + if e.Extractor != nil { + extractor := (e.Extractor).(*plannercore.ShowDatabaseExtractor) + if extractor.FieldPatterns != "" { + fieldPatternsLike = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + fieldPatternsLike.Compile(extractor.FieldPatterns, byte('\\')) + } + FieldFilterEnable = extractor.Field != "" + fieldFilter = extractor.Field + } // let information_schema be the first database moveInfoSchemaToFront(dbs) for _, d := range dbs { if checker != nil && !checker.DBIsVisible(e.ctx.GetSessionVars().ActiveRoles, d) { continue + } else if FieldFilterEnable && strings.ToLower(d) != fieldFilter { + continue + } else if fieldPatternsLike != nil && !fieldPatternsLike.DoMatch(strings.ToLower(d)) { + continue } e.appendRow([]interface{}{ d, diff --git a/executor/show_test.go b/executor/show_test.go index e4e45bb4b9bb6..a1e511785b9b1 100644 --- a/executor/show_test.go +++ b/executor/show_test.go @@ -1831,3 +1831,20 @@ func TestShowBindingCacheStatus(t *testing.T) { tk.MustQuery("show binding_cache status").Check(testkit.Rows( "1 1 198 Bytes 250 Bytes")) } + +func TestShowDatabasesLike(t *testing.T) { + store, clean := testkit.CreateMockStore(t) + defer clean() + + tk := testkit.NewTestKit(t, store) + require.True(t, tk.Session().Auth(&auth.UserIdentity{ + Username: "root", Hostname: "%"}, nil, nil)) + + tk.MustExec("DROP DATABASE IF EXISTS `TEST_$1`") + tk.MustExec("DROP DATABASE IF EXISTS `test_$2`") + tk.MustExec("CREATE DATABASE `TEST_$1`;") + tk.MustExec("CREATE DATABASE `test_$2`;") + + tk.MustQuery("SHOW DATABASES LIKE 'TEST_%'").Check(testkit.Rows("TEST_$1", "test_$2")) + tk.MustQuery("SHOW DATABASES LIKE 'test_%'").Check(testkit.Rows("TEST_$1", "test_$2")) +} diff --git a/planner/core/planbuilder.go b/planner/core/planbuilder.go index 5f73a1b8778a4..22f105d9fe6c1 100644 --- a/planner/core/planbuilder.go +++ b/planner/core/planbuilder.go @@ -3012,6 +3012,13 @@ func (b *PlanBuilder) buildShow(ctx context.Context, show *ast.ShowStmt) (Plan, if tableInfo.Meta().TempTableType != model.TempTableNone { return nil, ErrOptOnTemporaryTable.GenWithStackByArgs("show table regions") } + case ast.ShowDatabases: + var extractor ShowDatabaseExtractor + if extractor.Extract(show) { + p.Extractor = &extractor + // Avoid building Selection. + show.Pattern = nil + } } if show.Tp == ast.ShowVariables { var extractor ShowVariablesExtractor diff --git a/planner/core/show_predicate_extractor.go b/planner/core/show_predicate_extractor.go index 352b38b4f8b8c..eb9806c44ee5c 100644 --- a/planner/core/show_predicate_extractor.go +++ b/planner/core/show_predicate_extractor.go @@ -28,6 +28,7 @@ var ( _ ShowPredicateExtractor = &ShowColumnsTableExtractor{} _ ShowPredicateExtractor = &ShowTablesTableExtractor{} _ ShowPredicateExtractor = &ShowVariablesExtractor{} + _ ShowPredicateExtractor = &ShowDatabaseExtractor{} ) // ShowPredicateExtractor is used to extract some predicates from `PatternLikeExpr` clause @@ -167,3 +168,25 @@ func (e *ShowVariablesExtractor) explainInfo() string { } return s } + +// ShowDatabaseExtractor is used to extract some predicates of databases. +type ShowDatabaseExtractor struct { + ShowBaseExtractor +} + +func (e *ShowDatabaseExtractor) explainInfo() string { + r := new(bytes.Buffer) + if len(e.Field) > 0 { + r.WriteString(fmt.Sprintf("database:[%s], ", e.Field)) + } + if len(e.FieldPatterns) > 0 { + r.WriteString(fmt.Sprintf("database_pattern:[%s], ", e.FieldPatterns)) + } + + // remove the last ", " in the message info + s := r.String() + if len(s) > 2 { + return s[:len(s)-2] + } + return s +} diff --git a/planner/core/stringer_test.go b/planner/core/stringer_test.go index 6433ad59b158c..374c0589694ca 100644 --- a/planner/core/stringer_test.go +++ b/planner/core/stringer_test.go @@ -72,6 +72,22 @@ func TestPlanStringer(t *testing.T) { sql: "show tables in test like '%T%'", plan: "Show(table_pattern:[%t%])", }, + { + sql: "show databases like 't'", + plan: "Show(database:[t])", + }, + { + sql: "show databases like 'T'", + plan: "Show(database:[t])", + }, + { + sql: "show databases like 't%'", + plan: "Show(database_pattern:[t%])", + }, + { + sql: "show databases like '%T%'", + plan: "Show(database_pattern:[%t%])", + }, } parser := parser.New() for _, tt := range tests {