Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/importsdk/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ go_test(
"//pkg/lightning/config",
"//pkg/lightning/log",
"//pkg/lightning/mydump",
"//pkg/parser/ast",
"//pkg/parser/mysql",
"//pkg/util/table-filter",
"@com_github_data_dog_go_sqlmock//:go-sqlmock",
Expand Down
14 changes: 14 additions & 0 deletions pkg/meta/model/column.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,20 @@ func (c *ColumnInfo) IsVirtualGenerated() bool {
return c.IsGenerated() && !c.GeneratedStored
}

// IsVirtualGeneratedTemporalWithDateColumn checks whether the column is a virtual generated
// column with a temporal type that contains a date part, such as DATE, DATETIME, or TIMESTAMP.
func (c *ColumnInfo) IsVirtualGeneratedTemporalWithDateColumn() bool {
if !c.IsVirtualGenerated() {
return false
}
switch c.GetType() {
case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp:
return true
default:
return false
}
}

// IsChanging checks if the column is a new column added in modify column.
func (c *ColumnInfo) IsChanging() bool {
return strings.HasPrefix(c.Name.O, changingColumnPrefix)
Expand Down
2 changes: 1 addition & 1 deletion pkg/planner/core/casetest/index/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ go_test(
],
data = glob(["testdata/**"]),
flaky = True,
shard_count = 12,
shard_count = 13,
deps = [
"//pkg/domain",
"//pkg/domain/infosync",
Expand Down
46 changes: 46 additions & 0 deletions pkg/planner/core/casetest/index/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,44 @@ func TestInvisibleIndex(t *testing.T) {
})
}

func TestVirtualGeneratedTemporalWithDateIndex(t *testing.T) {
testkit.RunTestUnderCascades(t, func(t *testing.T, tk *testkit.TestKit, cascades, caller string) {
tk.MustExec("use test")
tk.MustExec("drop table if exists t_issue_52520")
tk.MustExec("create table t_issue_52520 (a varchar(32), b date as (a), key idx_b(b))")
tk.MustExec("set @@sql_mode = ''")
tk.MustExec("insert into t_issue_52520(a) values ('2020-02-31')")
tk.MustExec("set @@sql_mode = 'ALLOW_INVALID_DATES'")

tk.MustNoIndexUsed("select /* issue:52520 */ b from t_issue_52520 use index(idx_b)")
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")
tk.MustQuery("select /* issue:52520 */ b from t_issue_52520 use index(idx_b)").Check(testkit.Rows("2020-02-31"))
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")

tk.MustNoIndexUsed("select /*+ USE_INDEX(t_issue_52520, idx_b) */ /* issue:52520 */ b from t_issue_52520")
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")
tk.MustQuery("select /*+ USE_INDEX(t_issue_52520, idx_b) */ /* issue:52520 */ b from t_issue_52520").Check(testkit.Rows("2020-02-31"))
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")

tk.MustNoIndexUsed("select /* issue:52520 */ b from t_issue_52520 use index(idx_b) where b = '2020-02-31'")
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")
tk.MustQuery("select /* issue:52520 */ b from t_issue_52520 use index(idx_b) where b = '2020-02-31'").Check(testkit.Rows("2020-02-31"))
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")

tk.MustNoIndexUsed("select /*+ USE_INDEX(t_issue_52520, idx_b) */ /* issue:52520 */ b from t_issue_52520 where b = '2020-02-31'")
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")
tk.MustQuery("select /*+ USE_INDEX(t_issue_52520, idx_b) */ /* issue:52520 */ b from t_issue_52520 where b = '2020-02-31'").Check(testkit.Rows("2020-02-31"))
tk.MustQuery("show warnings").CheckContain("virtual generated temporal column")

tk.MustNoIndexUsed("select /* issue:52520 */ b from t_issue_52520 ignore index(idx_b) where b = '2020-02-31'")
tk.MustQuery("show warnings").Check(testkit.Rows())

tk.MustExec("drop table if exists t_issue_52520_prefix")
tk.MustExec("create table t_issue_52520_prefix (a varchar(32), c int, b date as (a), key idx_safe(c), key idx_unsafe(b))")
tk.MustContainErrMsg("select /* issue:52520 */ c from t_issue_52520_prefix use index(idx) where c = 1", "Key 'idx' doesn't exist")
})
}

func TestRangeDerivation(t *testing.T) {
testkit.RunTestUnderCascades(t, func(t *testing.T, testKit *testkit.TestKit, cascades, caller string) {
testKit.MustExec("use test")
Expand Down Expand Up @@ -297,6 +335,14 @@ func TestInvertedIndex(t *testing.T) {
testKit.MustNoIndexUsed("select * from t ignore index(idx_b) where b = 2")
testKit.MustNoIndexUsed("select * from t ignore index(idx_c) where c = 3")
testKit.MustNoIndexUsed("select * from t ignore index(idx_d) where d < 1")

testKit.MustExec("drop table if exists t_issue_52520_columnar")
testKit.MustExec("create table t_issue_52520_columnar (a varchar(32), b date as (a))")
testKit.MustExec("alter table t_issue_52520_columnar set tiflash replica 1")
testKit.MustExec("alter table t_issue_52520_columnar add columnar index idx_b (b) using inverted")
testkit.SetTiFlashReplica(t, dom, "test", "t_issue_52520_columnar")
testKit.MustNoIndexUsed("select /* issue:52520 */ b from t_issue_52520_columnar use index(idx_b)")
testKit.MustQuery("show warnings").CheckContain("virtual generated temporal column")
})
}

Expand Down
89 changes: 70 additions & 19 deletions pkg/planner/core/planbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -1154,32 +1154,71 @@ func (*PlanBuilder) detectSelectWindow(sel *ast.SelectStmt) bool {
return false
}

func getPathByIndexName(paths []*util.AccessPath, idxName ast.CIStr, tblInfo *model.TableInfo) *util.AccessPath {
var indexPrefixPath *util.AccessPath
prefixMatches := 0
for _, path := range paths {
// Only accept tikv's primary key table path.
type indexHintResolveStatus int

const (
indexHintResolveNotFound indexHintResolveStatus = iota
indexHintResolvePublicPath
indexHintResolveFilteredUnsafe
)

func resolveIndexHintByName(publicPaths []*util.AccessPath, filteredUnsafeIndexes []*model.IndexInfo, idxName ast.CIStr, tblInfo *model.TableInfo) (*util.AccessPath, *model.IndexInfo, indexHintResolveStatus) {
// Exact names win over prefixes. Check public paths first so a valid index is
// never shadowed by a filtered index that happens to share the exact name.
for _, path := range publicPaths {
if path.IsTiKVTablePath() && isPrimaryIndex(idxName) && tblInfo.HasClusteredIndex() {
return path
return path, nil, indexHintResolvePublicPath
}
// If it's not a tikv table path and the index is nil, it could not be any index path.
if path.Index == nil {
continue
if path.Index != nil && path.Index.Name.L == idxName.L {
return path, nil, indexHintResolvePublicPath
}
if path.Index.Name.L == idxName.L {
return path
}
for _, index := range filteredUnsafeIndexes {
if index.Name.L == idxName.L {
return nil, index, indexHintResolveFilteredUnsafe
}
}

var foundPath *util.AccessPath
var foundFilteredIndex *model.IndexInfo
prefixMatches := 0
for _, path := range publicPaths {
if path.Index != nil && strings.HasPrefix(path.Index.Name.L, idxName.L) {
foundPath = path
foundFilteredIndex = nil
prefixMatches++
}
if strings.HasPrefix(path.Index.Name.L, idxName.L) {
indexPrefixPath = path
}
for _, index := range filteredUnsafeIndexes {
if strings.HasPrefix(index.Name.L, idxName.L) {
foundPath = nil
foundFilteredIndex = index
prefixMatches++
}
}
if prefixMatches != 1 {
return nil, nil, indexHintResolveNotFound
}
if foundFilteredIndex != nil {
return nil, foundFilteredIndex, indexHintResolveFilteredUnsafe
}
return foundPath, nil, indexHintResolvePublicPath
}

// Return only unique prefix matches
if prefixMatches == 1 {
return indexPrefixPath
func indexContainsVirtualGeneratedTemporalWithDateColumn(tblInfo *model.TableInfo, index *model.IndexInfo) bool {
for _, idxCol := range index.Columns {
if idxCol.Offset < 0 || idxCol.Offset >= len(tblInfo.Columns) {
continue
}
if tblInfo.Columns[idxCol.Offset].IsVirtualGeneratedTemporalWithDateColumn() {
return true
}
}
return nil
return false
}

func genVirtualGeneratedTemporalWithDateIndexHintWarning(index *model.IndexInfo) string {
return fmt.Sprintf("hint is inapplicable, index %s contains a virtual generated temporal column whose values depend on sql_mode", index.Name.O)
}

func isPrimaryIndex(indexName ast.CIStr) bool {
Expand Down Expand Up @@ -1308,6 +1347,7 @@ func getPossibleAccessPaths(ctx base.PlanContext, tableHints *hint.PlanHints, in
var err error
// Inverted Index can not be used as access path index.
invertedIndexes := make(map[string]struct{})
var virtualGeneratedTemporalWithDateIndexes []*model.IndexInfo

// When NO_INDEX_LOOKUP_PUSHDOWN hint is specified, we should set `forceNoIndexLookUpPushDown = true` to avoid
// using index look up push down even if other hint or system variable `tidb_index_lookup_pushdown_policy`
Expand Down Expand Up @@ -1346,6 +1386,10 @@ func getPossibleAccessPaths(ctx base.PlanContext, tableHints *hint.PlanHints, in
continue
}
}
if indexContainsVirtualGeneratedTemporalWithDateColumn(tblInfo, index) {
virtualGeneratedTemporalWithDateIndexes = append(virtualGeneratedTemporalWithDateIndexes, index)
continue
}
Comment thread
expxiaoli marked this conversation as resolved.
Outdated
if index.InvertedInfo != nil {
invertedIndexes[index.Name.L] = struct{}{}
continue
Expand Down Expand Up @@ -1441,8 +1485,15 @@ func getPossibleAccessPaths(ctx base.PlanContext, tableHints *hint.PlanHints, in
if _, ok := invertedIndexes[idxName.L]; ok {
continue
}
path := getPathByIndexName(publicPaths, idxName, tblInfo)
if path == nil {
path, filteredIndex, resolveStatus := resolveIndexHintByName(publicPaths, virtualGeneratedTemporalWithDateIndexes, idxName, tblInfo)
if resolveStatus == indexHintResolveFilteredUnsafe {
if hint.HintType != ast.HintIgnore {
hasUseOrForce = true
ctx.GetSessionVars().StmtCtx.SetHintWarning(genVirtualGeneratedTemporalWithDateIndexHintWarning(filteredIndex))
}
continue
}
if resolveStatus == indexHintResolveNotFound {
err := plannererrors.ErrKeyDoesNotExist.FastGenByArgs(idxName, tblInfo.Name)
// if hint is from comment-style sql hints, we should throw a warning instead of error.
if i < indexHintsLen {
Expand Down
Loading