Files
tidb/planner/core/show_predicate_extractor.go
2022-01-26 20:30:45 +08:00

144 lines
4.3 KiB
Go

// Copyright 2022 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 core
import (
"bytes"
"fmt"
"strings"
"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"
)
var (
_ ShowPredicateExtractor = &ShowColumnsTableExtractor{}
_ ShowPredicateExtractor = &ShowTablesTableExtractor{}
)
// ShowPredicateExtractor is used to extract some predicates from `PatternLikeExpr` clause
// and push the predicates down to the data retrieving on reading memory table stage when use ShowStmt.
//
// e.g:
// SHOW COLUMNS FROM t LIKE '%abc%'
// We must request all components from the memory table, and filter the result by the PatternLikeExpr predicate.
//
// it is a way to fix https://github.com/pingcap/tidb/issues/29910.
type ShowPredicateExtractor interface {
// Extracts predicates which can be pushed down and returns whether the extractor can extract predicates.
Extract(show *ast.ShowStmt) bool
explainInfo() string
}
// ShowColumnsTableExtractor is used to extract some predicates of tables table.
type ShowColumnsTableExtractor struct {
Field string
FieldPatterns string
}
// Extract implements the MemTablePredicateExtractor Extract interface
func (e *ShowColumnsTableExtractor) Extract(show *ast.ShowStmt) bool {
if show.Pattern != nil && show.Pattern.Pattern != nil {
pattern := show.Pattern
switch pattern.Pattern.(type) {
case *driver.ValueExpr:
// 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) {
e.Field = strings.ToLower(string(patValue))
return true
}
// (?i) mean to be case-insensitive.
e.FieldPatterns = "(?i)" + stringutil.CompileLike2Regexp(string(patValue))
return true
case *ast.ColumnNameExpr:
// It is used in `SHOW COLUMNS FROM t LIKE abc`.
// MySQL do not support this syntax and return the error.
return false
}
} else if show.Column != nil && show.Column.Name.L != "" {
// it is used in `DESCRIBE t COLUMN`.
e.Field = show.Column.Name.L
return true
}
return false
}
func (e *ShowColumnsTableExtractor) explainInfo() string {
r := new(bytes.Buffer)
if len(e.Field) > 0 {
r.WriteString(fmt.Sprintf("field:[%s], ", e.Field))
}
if len(e.FieldPatterns) > 0 {
r.WriteString(fmt.Sprintf("field_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
}
// ShowTablesTableExtractor is used to extract some predicates of tables.
type ShowTablesTableExtractor struct {
ShowColumnsTableExtractor
}
// Extract implements the ShowTablesTableExtractor Extract interface
func (e *ShowTablesTableExtractor) Extract(show *ast.ShowStmt) bool {
if show.Pattern != nil && show.Pattern.Pattern != nil {
pattern := show.Pattern
switch pattern.Pattern.(type) {
case *driver.ValueExpr:
// It is used in `SHOW TABLE in t LIKE `abc``.
ptn := pattern.Pattern.(*driver.ValueExpr).GetString()
patValue, patTypes := stringutil.CompilePattern(ptn, pattern.Escape)
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))
return true
}
}
return false
}
func (e *ShowTablesTableExtractor) explainInfo() string {
r := new(bytes.Buffer)
if len(e.Field) > 0 {
r.WriteString(fmt.Sprintf("table:[%s], ", e.Field))
}
if len(e.FieldPatterns) > 0 {
r.WriteString(fmt.Sprintf("table_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
}