diff --git a/executor/infoschema_reader.go b/executor/infoschema_reader.go index 64aef3ba4ace1..8c78c6ac50aec 100644 --- a/executor/infoschema_reader.go +++ b/executor/infoschema_reader.go @@ -22,7 +22,6 @@ import ( "fmt" "io" "net/http" - "regexp" "sort" "strconv" "strings" @@ -682,7 +681,7 @@ func (e *hugeMemTableRetriever) dataForColumnsInTable(ctx context.Context, sctx sctx.GetSessionVars().StmtCtx.AppendWarning(err) return } - var tableSchemaRegexp, tableNameRegexp, columnsRegexp []*regexp.Regexp + var tableSchemaRegexp, tableNameRegexp, columnsRegexp []collate.WildcardPattern var tableSchemaFilterEnable, tableNameFilterEnable, columnsFilterEnable bool if !extractor.SkipRequest { @@ -690,21 +689,24 @@ func (e *hugeMemTableRetriever) dataForColumnsInTable(ctx context.Context, sctx tableNameFilterEnable = extractor.TableName.Count() > 0 columnsFilterEnable = extractor.ColumnName.Count() > 0 if len(extractor.TableSchemaPatterns) > 0 { - tableSchemaRegexp = make([]*regexp.Regexp, len(extractor.TableSchemaPatterns)) + tableSchemaRegexp = make([]collate.WildcardPattern, len(extractor.TableSchemaPatterns)) for i, pattern := range extractor.TableSchemaPatterns { - tableSchemaRegexp[i] = regexp.MustCompile(pattern) + tableSchemaRegexp[i] = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + tableSchemaRegexp[i].Compile(pattern, byte('\\')) } } if len(extractor.TableNamePatterns) > 0 { - tableNameRegexp = make([]*regexp.Regexp, len(extractor.TableNamePatterns)) + tableNameRegexp = make([]collate.WildcardPattern, len(extractor.TableNamePatterns)) for i, pattern := range extractor.TableNamePatterns { - tableNameRegexp[i] = regexp.MustCompile(pattern) + tableNameRegexp[i] = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + tableNameRegexp[i].Compile(pattern, byte('\\')) } } if len(extractor.ColumnNamePatterns) > 0 { - columnsRegexp = make([]*regexp.Regexp, len(extractor.ColumnNamePatterns)) + columnsRegexp = make([]collate.WildcardPattern, len(extractor.ColumnNamePatterns)) for i, pattern := range extractor.ColumnNamePatterns { - columnsRegexp[i] = regexp.MustCompile(pattern) + columnsRegexp[i] = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + columnsRegexp[i].Compile(pattern, byte('\\')) } } } @@ -724,17 +726,17 @@ ForColumnsTag: continue } for _, re := range tableSchemaRegexp { - if !re.MatchString(schema.Name.L) { + if !re.DoMatch(schema.Name.L) { continue ForColumnsTag } } for _, re := range tableNameRegexp { - if !re.MatchString(tbl.Name.L) { + if !re.DoMatch(tbl.Name.L) { continue ForColumnsTag } } for _, re := range columnsRegexp { - if !re.MatchString(col.Name.L) { + if !re.DoMatch(col.Name.L) { continue ForColumnsTag } } diff --git a/executor/show.go b/executor/show.go index 08dbdf634972e..e9cb3bf606430 100644 --- a/executor/show.go +++ b/executor/show.go @@ -19,7 +19,6 @@ import ( "context" gjson "encoding/json" "fmt" - "regexp" "sort" "strconv" "strings" @@ -430,15 +429,16 @@ func (e *ShowExec) fetchShowTables() error { tableNames := make([]string, 0, len(schemaTables)) activeRoles := e.ctx.GetSessionVars().ActiveRoles var ( - tableTypes = make(map[string]string) - fieldPatternsRegexp *regexp.Regexp - FieldFilterEnable bool - fieldFilter string + tableTypes = make(map[string]string) + fieldPatternsLike collate.WildcardPattern + FieldFilterEnable bool + fieldFilter string ) if e.Extractor != nil { extractor := (e.Extractor).(*plannercore.ShowTablesTableExtractor) if extractor.FieldPatterns != "" { - fieldPatternsRegexp = regexp.MustCompile(extractor.FieldPatterns) + fieldPatternsLike = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + fieldPatternsLike.Compile(extractor.FieldPatterns, byte('\\')) } FieldFilterEnable = extractor.Field != "" fieldFilter = extractor.Field @@ -450,7 +450,7 @@ func (e *ShowExec) fetchShowTables() error { continue } else if FieldFilterEnable && v.Meta().Name.L != fieldFilter { continue - } else if fieldPatternsRegexp != nil && !fieldPatternsRegexp.MatchString(v.Meta().Name.L) { + } else if fieldPatternsLike != nil && !fieldPatternsLike.DoMatch(v.Meta().Name.L) { continue } tableNames = append(tableNames, v.Meta().Name.O) @@ -528,14 +528,15 @@ func (e *ShowExec) fetchShowColumns(ctx context.Context) error { return errors.Trace(err) } var ( - fieldPatternsRegexp *regexp.Regexp - FieldFilterEnable bool - fieldFilter string + fieldPatternsLike collate.WildcardPattern + FieldFilterEnable bool + fieldFilter string ) if e.Extractor != nil { extractor := (e.Extractor).(*plannercore.ShowColumnsTableExtractor) if extractor.FieldPatterns != "" { - fieldPatternsRegexp = regexp.MustCompile(extractor.FieldPatterns) + fieldPatternsLike = collate.GetCollatorByID(collate.CollationName2ID(mysql.UTF8MB4DefaultCollation)).Pattern() + fieldPatternsLike.Compile(extractor.FieldPatterns, byte('\\')) } FieldFilterEnable = extractor.Field != "" fieldFilter = extractor.Field @@ -561,7 +562,7 @@ func (e *ShowExec) fetchShowColumns(ctx context.Context) error { for _, col := range cols { if FieldFilterEnable && col.Name.L != fieldFilter { continue - } else if fieldPatternsRegexp != nil && !fieldPatternsRegexp.MatchString(col.Name.L) { + } else if fieldPatternsLike != nil && !fieldPatternsLike.DoMatch(col.Name.L) { continue } desc := table.NewColDesc(col) diff --git a/planner/core/memtable_predicate_extractor.go b/planner/core/memtable_predicate_extractor.go index 72ecd4520567f..3dd916e6f11d4 100644 --- a/planner/core/memtable_predicate_extractor.go +++ b/planner/core/memtable_predicate_extractor.go @@ -237,6 +237,7 @@ func (helper extractHelper) extractLikePatternCol( predicates []expression.Expression, extractColName string, toLower bool, + needLike2Regexp bool, ) ( remained []expression.Expression, patterns []string, @@ -263,13 +264,13 @@ func (helper extractHelper) extractLikePatternCol( // We use '|' to combine DNF regular expression: .*a.*|.*b.* // e.g: // SELECT * FROM t WHERE c LIKE '%a%' OR c LIKE '%b%' - if fn.FuncName.L == ast.LogicOr { - canBuildPattern, pattern = helper.extractOrLikePattern(fn, extractColName, extractCols) + if fn.FuncName.L == ast.LogicOr && !toLower { + canBuildPattern, pattern = helper.extractOrLikePattern(fn, extractColName, extractCols, needLike2Regexp) } else { - canBuildPattern, pattern = helper.extractLikePattern(fn, extractColName, extractCols) + canBuildPattern, pattern = helper.extractLikePattern(fn, extractColName, extractCols, needLike2Regexp) } if canBuildPattern && toLower { - pattern = "(?i)" + pattern + pattern = strings.ToLower(pattern) } if canBuildPattern { patterns = append(patterns, pattern) @@ -284,6 +285,7 @@ func (helper extractHelper) extractOrLikePattern( orFunc *expression.ScalarFunction, extractColName string, extractCols map[int64]*types.FieldName, + needLike2Regexp bool, ) ( ok bool, pattern string, @@ -300,7 +302,7 @@ func (helper extractHelper) extractOrLikePattern( return false, "" } - ok, partPattern := helper.extractLikePattern(fn, extractColName, extractCols) + ok, partPattern := helper.extractLikePattern(fn, extractColName, extractCols, needLike2Regexp) if !ok { return false, "" } @@ -313,6 +315,7 @@ func (helper extractHelper) extractLikePattern( fn *expression.ScalarFunction, extractColName string, extractCols map[int64]*types.FieldName, + needLike2Regexp bool, ) ( ok bool, pattern string, @@ -328,7 +331,10 @@ func (helper extractHelper) extractLikePattern( case ast.EQ: return true, "^" + regexp.QuoteMeta(datums[0].GetString()) + "$" case ast.Like: - return true, stringutil.CompileLike2Regexp(datums[0].GetString()) + if needLike2Regexp { + return true, stringutil.CompileLike2Regexp(datums[0].GetString()) + } + return true, datums[0].GetString() case ast.Regexp: return true, datums[0].GetString() default: @@ -682,7 +688,7 @@ func (e *ClusterLogTableExtractor) Extract( return nil } - remained, patterns := e.extractLikePatternCol(schema, names, remained, "message", false) + remained, patterns := e.extractLikePatternCol(schema, names, remained, "message", false, true) e.Patterns = patterns return remained } @@ -1506,9 +1512,9 @@ func (e *ColumnsTableExtractor) Extract(_ sessionctx.Context, if e.SkipRequest { return } - remained, tableSchemaPatterns := e.extractLikePatternCol(schema, names, remained, "table_schema", true) - remained, tableNamePatterns := e.extractLikePatternCol(schema, names, remained, "table_name", true) - remained, columnNamePatterns := e.extractLikePatternCol(schema, names, remained, "column_name", true) + remained, tableSchemaPatterns := e.extractLikePatternCol(schema, names, remained, "table_schema", true, false) + remained, tableNamePatterns := e.extractLikePatternCol(schema, names, remained, "table_name", true, false) + remained, columnNamePatterns := e.extractLikePatternCol(schema, names, remained, "column_name", true, false) e.ColumnName = columnName e.TableName = tableName diff --git a/planner/core/memtable_predicate_extractor_test.go b/planner/core/memtable_predicate_extractor_test.go index 40ab99e289111..83cb9b4225610 100644 --- a/planner/core/memtable_predicate_extractor_test.go +++ b/planner/core/memtable_predicate_extractor_test.go @@ -1598,23 +1598,23 @@ func TestColumns(t *testing.T) { }, { sql: `select * from information_schema.COLUMNS where table_name like 'T%';`, - tableNamePattern: []string{"(?i)T.*"}, + tableNamePattern: []string{"t%"}, }, { sql: `select * from information_schema.COLUMNS where column_name like 'T%';`, - columnNamePattern: []string{"(?i)T.*"}, + columnNamePattern: []string{"t%"}, }, { sql: `select * from information_schema.COLUMNS where column_name like 'i%';`, - columnNamePattern: []string{"(?i)i.*"}, + columnNamePattern: []string{"i%"}, }, { sql: `select * from information_schema.COLUMNS where column_name like 'abc%' or column_name like "def%";`, - columnNamePattern: []string{"(?i)abc.*|def.*"}, + columnNamePattern: []string{}, }, { sql: `select * from information_schema.COLUMNS where column_name like 'abc%' and column_name like "%def";`, - columnNamePattern: []string{"(?i)abc.*", "(?i).*def"}, + columnNamePattern: []string{"abc%", "%def"}, }, } parser := parser.New() @@ -1659,37 +1659,39 @@ func TestPredicateQuery(t *testing.T) { tk := testkit.NewTestKit(t, store) tk.MustExec("use test") - tk.MustExec("create table t(id int, abclmn int);") + tk.MustExec("create table t(id int, abctime int,DATETIME_PRECISION int);") tk.MustExec("create table abclmn(a int);") tk.MustQuery("select TABLE_NAME from information_schema.columns where table_schema = 'test' and column_name like 'i%'").Check(testkit.Rows("t")) tk.MustQuery("select TABLE_NAME from information_schema.columns where table_schema = 'TEST' and column_name like 'I%'").Check(testkit.Rows("t")) tk.MustQuery("select TABLE_NAME from information_schema.columns where table_schema = 'TEST' and column_name like 'ID'").Check(testkit.Rows("t")) tk.MustQuery("select TABLE_NAME from information_schema.columns where table_schema = 'TEST' and column_name like 'id'").Check(testkit.Rows("t")) - tk.MustQuery("select column_name from information_schema.columns where table_schema = 'TEST' and (column_name like 'I%' or column_name like '%D')").Check(testkit.Rows("id")) - tk.MustQuery("select column_name from information_schema.columns where table_schema = 'TEST' and (column_name like 'abc%' and column_name like '%lmn')").Check(testkit.Rows("abclmn")) - tk.MustQuery("describe t").Check(testkit.Rows("id int(11) YES ", "abclmn int(11) YES ")) + tk.MustQuery("select column_name from information_schema.columns where table_schema = 'TEST' and (column_name like 'i%' or column_name like '%d')").Check(testkit.Rows("id")) + tk.MustQuery("select column_name from information_schema.columns where table_schema = 'TEST' and (column_name like 'abc%' and column_name like '%time')").Check(testkit.Rows("abctime")) + result := tk.MustQuery("select TABLE_NAME, column_name from information_schema.columns where table_schema = 'TEST' and column_name like '%time';") + require.Len(t, result.Rows(), 1) + tk.MustQuery("describe t").Check(testkit.Rows("id int(11) YES ", "abctime int(11) YES ", "DATETIME_PRECISION int(11) YES ")) tk.MustQuery("describe t id").Check(testkit.Rows("id int(11) YES ")) tk.MustQuery("describe t ID").Check(testkit.Rows("id int(11) YES ")) tk.MustGetErrCode("describe t 'I%'", errno.ErrParse) tk.MustGetErrCode("describe t I%", errno.ErrParse) - tk.MustQuery("show columns from t like 'abclmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t like 'ABCLMN'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t like 'abc%'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t like 'ABC%'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t like '%lmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t like '%LMN'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns in t like '%lmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns in t like '%LMN'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show fields in t like '%lmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show fields in t like '%LMN'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) + tk.MustQuery("show columns from t like 'abctime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t like 'ABCTIME'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t like 'abc%'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t like 'ABC%'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t like '%ime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t like '%IME'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns in t like '%ime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns in t like '%IME'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show fields in t like '%ime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show fields in t like '%IME'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) - tk.MustQuery("show columns from t where field like '%lmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns from t where field = 'abclmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show columns in t where field = 'abclmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show fields from t where field = 'abclmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("show fields in t where field = 'abclmn'").Check(testkit.RowsWithSep(",", "abclmn,int(11),YES,,,")) - tk.MustQuery("explain t").Check(testkit.Rows("id int(11) YES ", "abclmn int(11) YES ")) + tk.MustQuery("show columns from t where field like '%time'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns from t where field = 'abctime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show columns in t where field = 'abctime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show fields from t where field = 'abctime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("show fields in t where field = 'abctime'").Check(testkit.RowsWithSep(",", "abctime,int(11),YES,,,")) + tk.MustQuery("explain t").Check(testkit.Rows("id int(11) YES ", "abctime int(11) YES ", "DATETIME_PRECISION int(11) YES ")) tk.MustGetErrCode("show columns from t like id", errno.ErrBadField) tk.MustGetErrCode("show columns from t like `id`", errno.ErrBadField) diff --git a/planner/core/show_predicate_extractor.go b/planner/core/show_predicate_extractor.go index 103c4107c7f5e..424c190afe673 100644 --- a/planner/core/show_predicate_extractor.go +++ b/planner/core/show_predicate_extractor.go @@ -21,7 +21,6 @@ import ( "github.com/pingcap/tidb/parser/ast" driver "github.com/pingcap/tidb/types/parser_driver" - "github.com/pingcap/tidb/util/collate" "github.com/pingcap/tidb/util/stringutil" ) @@ -60,12 +59,11 @@ func (e *ShowColumnsTableExtractor) Extract(show *ast.ShowStmt) bool { // It is used in `SHOW COLUMNS FROM t LIKE `abc``. ptn := pattern.Pattern.(*driver.ValueExpr).GetString() patValue, patTypes := stringutil.CompilePattern(ptn, pattern.Escape) - if !collate.NewCollationEnabled() && stringutil.IsExactMatch(patTypes) { + if stringutil.IsExactMatch(patTypes) { e.Field = strings.ToLower(string(patValue)) return true } - // (?i) mean to be case-insensitive. - e.FieldPatterns = "(?i)" + stringutil.CompileLike2Regexp(string(patValue)) + e.FieldPatterns = strings.ToLower(string(patValue)) return true case *ast.ColumnNameExpr: // It is used in `SHOW COLUMNS FROM t LIKE abc`. @@ -116,8 +114,7 @@ func (e *ShowTablesTableExtractor) Extract(show *ast.ShowStmt) bool { e.Field = strings.ToLower(string(patValue)) return true } - // (?i) mean to be case-insensitive. - e.FieldPatterns = "(?i)" + stringutil.CompileLike2Regexp(string(patValue)) + e.FieldPatterns = strings.ToLower(string(patValue)) return true } } diff --git a/planner/core/stringer_test.go b/planner/core/stringer_test.go index b356dbe4ca3da..6433ad59b158c 100644 --- a/planner/core/stringer_test.go +++ b/planner/core/stringer_test.go @@ -38,11 +38,11 @@ func TestPlanStringer(t *testing.T) { }{ { sql: "show columns from t like 'a'", - plan: "Show(field_pattern:[(?i)a])", + plan: "Show(field:[a])", }, { sql: "show columns from t like 'a%'", - plan: "Show(field_pattern:[(?i)a.*])", + plan: "Show(field_pattern:[a%])", }, { sql: "show columns from t where field = 'a'", @@ -66,11 +66,11 @@ func TestPlanStringer(t *testing.T) { }, { sql: "show tables in test like 't%'", - plan: "Show(table_pattern:[(?i)t.*])", + plan: "Show(table_pattern:[t%])", }, { sql: "show tables in test like '%T%'", - plan: "Show(table_pattern:[(?i).*T.*])", + plan: "Show(table_pattern:[%t%])", }, } parser := parser.New()