diff --git a/pkg/executor/BUILD.bazel b/pkg/executor/BUILD.bazel index 061d21e8270ed..d1e485b8c0b33 100644 --- a/pkg/executor/BUILD.bazel +++ b/pkg/executor/BUILD.bazel @@ -438,6 +438,7 @@ go_test( "//pkg/planner/core/resolve", "//pkg/planner/property", "//pkg/planner/util", + "//pkg/privilege", "//pkg/server", "//pkg/session", "//pkg/session/types", diff --git a/pkg/executor/builder.go b/pkg/executor/builder.go index 475a4e1f80767..9be77896e81e4 100644 --- a/pkg/executor/builder.go +++ b/pkg/executor/builder.go @@ -3359,6 +3359,9 @@ func (b *executorBuilder) buildMemTable(v *plannercore.PhysicalMemTable) exec.Ex strings.ToLower(infoschema.TableTiDBCheckConstraints), strings.ToLower(infoschema.TableKeywords), strings.ToLower(infoschema.TableTiDBIndexUsage), + strings.ToLower(infoschema.TableTiDBMViews), + strings.ToLower(infoschema.TableTiDBMLogs), + strings.ToLower(infoschema.TableTiDBTableMViewDependencies), strings.ToLower(infoschema.ClusterTableTiDBIndexUsage): memTracker := memory.NewTracker(v.ID(), -1) memTracker.AttachTo(b.ctx.GetSessionVars().StmtCtx.MemTracker) diff --git a/pkg/executor/infoschema_reader.go b/pkg/executor/infoschema_reader.go index 100de70ec4fc0..878e3f19b9b08 100644 --- a/pkg/executor/infoschema_reader.go +++ b/pkg/executor/infoschema_reader.go @@ -159,6 +159,12 @@ func (e *memtableRetriever) retrieve(ctx context.Context, sctx sessionctx.Contex err = e.setDataFromIndexes(ctx, sctx) case infoschema.TableViews: err = e.setDataFromViews(ctx, sctx) + case infoschema.TableTiDBMViews: + err = e.setDataFromTiDBMViews(ctx, sctx) + case infoschema.TableTiDBMLogs: + err = e.setDataFromTiDBMLogs(ctx, sctx) + case infoschema.TableTiDBTableMViewDependencies: + err = e.setDataFromTiDBTableMViewDependencies(ctx, sctx) case infoschema.TableEngines: e.setDataFromEngines() case infoschema.TableCharacterSets: @@ -1537,6 +1543,418 @@ func (e *memtableRetriever) setDataFromViews(ctx context.Context, sctx sessionct return nil } +func (e *memtableRetriever) setDataFromTiDBMViews(ctx context.Context, sctx sessionctx.Context) error { + checker := privilege.GetPrivilegeManager(sctx) + ex, ok := e.extractor.(*plannercore.InfoSchemaTiDBMViewsExtractor) + if !ok { + return errors.Errorf("wrong extractor type: %T, expected InfoSchemaTiDBMViewsExtractor", e.extractor) + } + + if ex.SkipRequest { + return nil + } + + schemas, tables, err := ex.ListSchemasAndTables(ctx, e.is) + if err != nil { + return errors.Trace(err) + } + + loc := sctx.GetSessionVars().TimeZone + if loc == nil { + loc = time.Local + } + + rows := make([][]types.Datum, 0) + for i, tbl := range tables { + schema := schemas[i] + if tbl.MaterializedView == nil { + continue + } + if checker != nil && !checker.RequestVerification(sctx.GetSessionVars().ActiveRoles, schema.L, tbl.Name.L, "", mysql.AllPrivMask) { + continue + } + modifyTime := types.NewTime(types.FromGoTime(tbl.GetUpdateTime().In(loc)), mysql.TypeDatetime, types.DefaultFsp) + record := types.MakeDatums( + infoschema.CatalogVal, // TABLE_CATALOG + schema.O, // TABLE_SCHEMA + tbl.ID, // MVIEW_ID + tbl.Name.O, // MVIEW_NAME + tbl.MaterializedView.SQLContent, // MVIEW_SQL_CONTENT + tbl.Comment, // MVIEW_COMMENT + modifyTime, // MVIEW_MODIFY_TIME + tbl.MaterializedView.RefreshMethod, // REFRESH_METHOD + tbl.MaterializedView.RefreshStartWith, // REFRESH_START + tbl.MaterializedView.RefreshNext, // REFRESH_NEXT + ) + rows = append(rows, record) + } + e.rows = rows + return nil +} + +type mlogBaseTableInfo struct { + catalog string + schema string + id int64 + idStr string + name string +} + +func newMLogBaseTableInfoFromBaseTable( + baseSchema pmodel.CIStr, + baseTbl *model.TableInfo, +) mlogBaseTableInfo { + baseInfo := mlogBaseTableInfo{ + catalog: infoschema.CatalogVal, + } + + baseInfo.id = baseTbl.ID + baseInfo.idStr = strconv.FormatInt(baseInfo.id, 10) + baseInfo.name = baseTbl.Name.O + if baseSchema.O != "" { + baseInfo.schema = baseSchema.O + } + return baseInfo +} + +func newMLogBaseTableInfos( + baseSchemas []pmodel.CIStr, + baseTables []*model.TableInfo, +) []mlogBaseTableInfo { + baseInfos := make([]mlogBaseTableInfo, 0, len(baseTables)) + for i, baseTbl := range baseTables { + baseInfos = append(baseInfos, newMLogBaseTableInfoFromBaseTable(baseSchemas[i], baseTbl)) + } + return baseInfos +} + +func (e *memtableRetriever) getMLogBaseTableInfo( + ctx context.Context, + mlogSchema pmodel.CIStr, + tbl *model.TableInfo, +) mlogBaseTableInfo { + baseInfo := mlogBaseTableInfo{ + catalog: infoschema.CatalogVal, + schema: mlogSchema.O, + } + if tbl.MaterializedViewLog == nil { + return baseInfo + } + + baseInfo.id = tbl.MaterializedViewLog.BaseTableID + baseInfo.idStr = strconv.FormatInt(baseInfo.id, 10) + if baseInfo.id == 0 { + return baseInfo + } + + baseTbl, ok := e.is.TableByID(ctx, baseInfo.id) + if !ok { + return baseInfo + } + baseInfo.name = baseTbl.Meta().Name.O + if baseSchema, ok := infoschema.SchemaByTable(e.is, baseTbl.Meta()); ok { + baseInfo.schema = baseSchema.Name.O + } + return baseInfo +} + +func (e *memtableRetriever) filterMLogCandidatesByBasePredicates( + ctx context.Context, + ex *plannercore.InfoSchemaTiDBMLogsExtractor, + schemas []pmodel.CIStr, + tables []*model.TableInfo, +) ([]pmodel.CIStr, []*model.TableInfo, []mlogBaseTableInfo) { + filteredSchemas := schemas[:0] + filteredTables := tables[:0] + baseInfos := make([]mlogBaseTableInfo, 0, len(tables)) + for i, tbl := range tables { + if tbl.MaterializedViewLog == nil { + continue + } + baseInfo := e.getMLogBaseTableInfo(ctx, schemas[i], tbl) + if ex.Filter(plannercore.BaseTableSchema, baseInfo.schema) || + ex.Filter(plannercore.BaseTableName, baseInfo.name) || + ex.Filter(plannercore.BaseTableID, baseInfo.idStr) { + continue + } + filteredSchemas = append(filteredSchemas, schemas[i]) + filteredTables = append(filteredTables, tbl) + baseInfos = append(baseInfos, baseInfo) + } + return filteredSchemas, filteredTables, baseInfos +} + +func (e *memtableRetriever) setDataFromTiDBMLogs(ctx context.Context, sctx sessionctx.Context) error { + checker := privilege.GetPrivilegeManager(sctx) + ex, ok := e.extractor.(*plannercore.InfoSchemaTiDBMLogsExtractor) + if !ok { + return errors.Errorf("wrong extractor type: %T, expected InfoSchemaTiDBMLogsExtractor", e.extractor) + } + + if ex.SkipRequest { + return nil + } + + hasMLogPredicates := ex.HasMLogPredicates() + hasBasePredicates := ex.HasBaseTablePredicates() + + var ( + schemas []pmodel.CIStr + tables []*model.TableInfo + baseInfos []mlogBaseTableInfo + err error + ) + switch { + case (!hasMLogPredicates && !hasBasePredicates) || (hasMLogPredicates && !hasBasePredicates): + schemas, tables, err = ex.ListSchemasAndTables(ctx, e.is) + case !hasMLogPredicates && hasBasePredicates: + var ( + baseSchemas []pmodel.CIStr + baseTables []*model.TableInfo + ) + schemas, tables, baseSchemas, baseTables, err = ex.ListSchemasAndTablesByBase(ctx, e.is) + if err == nil { + baseInfos = newMLogBaseTableInfos(baseSchemas, baseTables) + } + default: + schemas, tables, err = ex.ListSchemasAndTables(ctx, e.is) + if err == nil { + schemas, tables, baseInfos = e.filterMLogCandidatesByBasePredicates(ctx, ex, schemas, tables) + } + } + if err != nil { + return errors.Trace(err) + } + + rows := make([][]types.Datum, 0) + for i, tbl := range tables { + schema := schemas[i] + mlogInfo := tbl.MaterializedViewLog + if mlogInfo == nil { + continue + } + if checker != nil && !checker.RequestVerification(sctx.GetSessionVars().ActiveRoles, schema.L, tbl.Name.L, "", mysql.AllPrivMask) { + continue + } + + var baseInfo mlogBaseTableInfo + if i < len(baseInfos) { + baseInfo = baseInfos[i] + } else { + baseInfo = e.getMLogBaseTableInfo(ctx, schema, tbl) + } + + columnNames := make([]string, 0, len(mlogInfo.Columns)) + for _, col := range mlogInfo.Columns { + columnNames = append(columnNames, col.O) + } + + record := types.MakeDatums( + infoschema.CatalogVal, // TABLE_CATALOG + schema.O, // TABLE_SCHEMA + tbl.ID, // MLOG_ID + tbl.Name.O, // MLOG_NAME + strings.Join(columnNames, ","), // MLOG_COLUMNS + baseInfo.catalog, // BASE_TABLE_CATALOG + baseInfo.schema, // BASE_TABLE_SCHEMA + baseInfo.idStr, // BASE_TABLE_ID + baseInfo.name, // BASE_TABLE_NAME + mlogInfo.PurgeMethod, // PURGE_METHOD + mlogInfo.PurgeStartWith, // PURGE_START + mlogInfo.PurgeNext, // PURGE_NEXT + ) + rows = append(rows, record) + } + e.rows = rows + return nil +} + +type mviewDependencyInfo struct { + catalog string + schema pmodel.CIStr + id int64 + idStr string + name string +} + +type mviewDependencyMLogInfo struct { + id int64 + idStr string + name string +} + +func (e *memtableRetriever) getBaseTableMLogInfo( + ctx context.Context, + tbl *model.TableInfo, +) mviewDependencyMLogInfo { + baseInfo := tbl.MaterializedViewBase + if baseInfo == nil || baseInfo.MLogID == 0 { + return mviewDependencyMLogInfo{} + } + + mlogInfo := mviewDependencyMLogInfo{ + id: baseInfo.MLogID, + idStr: strconv.FormatInt(baseInfo.MLogID, 10), + } + mlogTbl, ok := e.is.TableByID(ctx, baseInfo.MLogID) + if !ok { + return mlogInfo + } + mlogMeta := mlogTbl.Meta() + if mlogMeta.MaterializedViewLog == nil { + return mlogInfo + } + mlogInfo.name = mlogMeta.Name.O + return mlogInfo +} + +func (e *memtableRetriever) getBaseTableMViewDependencies( + ctx context.Context, + tbl *model.TableInfo, +) []mviewDependencyInfo { + baseInfo := tbl.MaterializedViewBase + if baseInfo == nil || len(baseInfo.MViewIDs) == 0 { + return nil + } + + deps := make([]mviewDependencyInfo, 0, len(baseInfo.MViewIDs)) + seenMViewIDs := make(map[int64]struct{}, len(baseInfo.MViewIDs)) + for _, mviewID := range baseInfo.MViewIDs { + if _, ok := seenMViewIDs[mviewID]; ok { + continue + } + mviewTbl, ok := e.is.TableByID(ctx, mviewID) + if !ok { + continue + } + mviewMeta := mviewTbl.Meta() + if mviewMeta.MaterializedView == nil { + continue + } + mviewSchema, ok := infoschema.SchemaByTable(e.is, mviewMeta) + if !ok { + continue + } + seenMViewIDs[mviewID] = struct{}{} + deps = append(deps, mviewDependencyInfo{ + catalog: infoschema.CatalogVal, + schema: mviewSchema.Name, + id: mviewMeta.ID, + idStr: strconv.FormatInt(mviewMeta.ID, 10), + name: mviewMeta.Name.O, + }) + } + + slices.SortFunc(deps, func(a, b mviewDependencyInfo) int { + if a.schema.L == b.schema.L { + nameCmp := strings.Compare(strings.ToLower(a.name), strings.ToLower(b.name)) + if nameCmp != 0 { + return nameCmp + } + if a.id < b.id { + return -1 + } + if a.id > b.id { + return 1 + } + return 0 + } + return strings.Compare(a.schema.L, b.schema.L) + }) + return deps +} + +func filterMViewDependencyByMViewPredicates( + ex *plannercore.InfoSchemaTiDBTableMViewDependenciesExtractor, + dep mviewDependencyInfo, +) bool { + return ex.Filter(plannercore.MViewSchema, dep.schema.O) || + ex.Filter(plannercore.MViewName, dep.name) || + ex.Filter(plannercore.MViewID, dep.idStr) +} + +func filterMViewDependencyByMLogPredicates( + ex *plannercore.InfoSchemaTiDBTableMViewDependenciesExtractor, + mlogInfo mviewDependencyMLogInfo, +) bool { + return ex.Filter(plannercore.MLogName, mlogInfo.name) || + ex.Filter(plannercore.MLogID, mlogInfo.idStr) +} + +func (e *memtableRetriever) setDataFromTiDBTableMViewDependencies(ctx context.Context, sctx sessionctx.Context) error { + checker := privilege.GetPrivilegeManager(sctx) + ex, ok := e.extractor.(*plannercore.InfoSchemaTiDBTableMViewDependenciesExtractor) + if !ok { + return errors.Errorf("wrong extractor type: %T, expected InfoSchemaTiDBTableMViewDependenciesExtractor", e.extractor) + } + + if ex.SkipRequest { + return nil + } + + hasTablePredicates := ex.HasTablePredicates() + hasMLogPredicates := ex.HasMLogPredicates() + hasMViewPredicates := ex.HasMViewPredicates() + + var ( + schemas []pmodel.CIStr + tables []*model.TableInfo + err error + ) + switch { + case !hasTablePredicates && hasMLogPredicates: + schemas, tables, err = ex.ListSchemasAndTablesByMLog(ctx, e.is) + case !hasTablePredicates && !hasMLogPredicates && hasMViewPredicates: + schemas, tables, err = ex.ListSchemasAndTablesByMView(ctx, e.is) + default: + schemas, tables, err = ex.ListSchemasAndTables(ctx, e.is) + } + if err != nil { + return errors.Trace(err) + } + + rows := make([][]types.Datum, 0) + for i, tbl := range tables { + schema := schemas[i] + if tbl.MaterializedViewBase == nil { + continue + } + if checker != nil && !checker.RequestVerification(sctx.GetSessionVars().ActiveRoles, schema.L, tbl.Name.L, "", mysql.AllPrivMask) { + continue + } + + mlogInfo := e.getBaseTableMLogInfo(ctx, tbl) + if mlogInfo.id == 0 || mlogInfo.name == "" { + continue + } + if hasMLogPredicates && filterMViewDependencyByMLogPredicates(ex, mlogInfo) { + continue + } + + deps := e.getBaseTableMViewDependencies(ctx, tbl) + for _, dep := range deps { + if hasMViewPredicates && filterMViewDependencyByMViewPredicates(ex, dep) { + continue + } + record := types.MakeDatums( + infoschema.CatalogVal, // TABLE_CATALOG + schema.O, // TABLE_SCHEMA + tbl.ID, // TABLE_ID + tbl.Name.O, // TABLE_NAME + mlogInfo.id, // MLOG_ID + mlogInfo.name, // MLOG_NAME + dep.catalog, // MVIEW_CATALOG + dep.schema.O, // MVIEW_SCHEMA + dep.idStr, // MVIEW_ID + dep.name, // MVIEW_NAME + ) + rows = append(rows, record) + } + } + e.rows = rows + return nil +} + func (e *memtableRetriever) dataForTiKVStoreStatus(ctx context.Context, sctx sessionctx.Context) (err error) { tikvStore, ok := sctx.GetStore().(helper.Storage) if !ok { diff --git a/pkg/executor/infoschema_reader_internal_test.go b/pkg/executor/infoschema_reader_internal_test.go index 30a65a4e856ed..0e8384def185d 100644 --- a/pkg/executor/infoschema_reader_internal_test.go +++ b/pkg/executor/infoschema_reader_internal_test.go @@ -16,17 +16,163 @@ package executor import ( "context" + "errors" + "reflect" + "regexp" "testing" + "time" + "unsafe" "github.com/pingcap/tidb/pkg/infoschema" "github.com/pingcap/tidb/pkg/meta/model" + "github.com/pingcap/tidb/pkg/parser/auth" pmodel "github.com/pingcap/tidb/pkg/parser/model" "github.com/pingcap/tidb/pkg/parser/mysql" plannercore "github.com/pingcap/tidb/pkg/planner/core" + "github.com/pingcap/tidb/pkg/privilege" + "github.com/pingcap/tidb/pkg/table" "github.com/pingcap/tidb/pkg/types" + "github.com/pingcap/tidb/pkg/util/set" "github.com/stretchr/testify/require" ) +type mockInfoSchemaWithItems struct { + infoschema.InfoSchema + items []infoschema.TableItem +} + +func (m *mockInfoSchemaWithItems) IterateAllTableItems(visit func(infoschema.TableItem) bool) { + for _, item := range m.items { + if !visit(item) { + return + } + } +} + +type tableByNameErrorInfoSchema struct { + infoschema.InfoSchema + err error +} + +func (m *tableByNameErrorInfoSchema) TableByName( + _ context.Context, + _, _ pmodel.CIStr, +) (table.Table, error) { + return nil, m.err +} + +type tableByIDCountInfoSchema struct { + infoschema.InfoSchema + calls map[int64]int +} + +func (m *tableByIDCountInfoSchema) TableByID(ctx context.Context, id int64) (table.Table, bool) { + if m.calls == nil { + m.calls = make(map[int64]int) + } + m.calls[id]++ + return m.InfoSchema.TableByID(ctx, id) +} + +type stubPrivilegeManager struct { + privilege.Manager + allow func(db, table string) bool + calls []string +} + +func (m *stubPrivilegeManager) RequestVerification( + _ []*auth.RoleIdentity, + db, table, _ string, + _ mysql.PrivilegeType, +) bool { + m.calls = append(m.calls, db+"."+table) + if m.allow == nil { + return true + } + return m.allow(db, table) +} + +func setExtractorRegexp( + t *testing.T, + ex *plannercore.InfoSchemaTiDBMViewsExtractor, + col string, + patterns ...string, +) { + t.Helper() + + regs := make([]*regexp.Regexp, 0, len(patterns)) + for _, pattern := range patterns { + regs = append(regs, regexp.MustCompile(pattern)) + } + + field := reflect.ValueOf(&ex.InfoSchemaBaseExtractor).Elem().FieldByName("colsRegexp") + require.True(t, field.IsValid()) + field = reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())).Elem() + field.Set(reflect.ValueOf(map[string][]*regexp.Regexp{col: regs})) +} + +func newColumnInfo(name string) *model.ColumnInfo { + return &model.ColumnInfo{Name: pmodel.NewCIStr(name)} +} + +func newMViewTableInfo(id int64, name string, updateTS uint64) *model.TableInfo { + return &model.TableInfo{ + ID: id, + Name: pmodel.NewCIStr(name), + Comment: name + "-comment", + UpdateTS: updateTS, + State: model.StatePublic, + MaterializedView: &model.MaterializedViewInfo{ + SQLContent: "select 1", + RefreshMethod: "FAST", + RefreshStartWith: "CURRENT_TIMESTAMP", + RefreshNext: "CURRENT_TIMESTAMP + INTERVAL 1 HOUR", + }, + } +} + +func newBaseTableInfo(id int64, name string, mlogID int64) *model.TableInfo { + tbl := &model.TableInfo{ + ID: id, + Name: pmodel.NewCIStr(name), + State: model.StatePublic, + } + if mlogID != 0 { + tbl.MaterializedViewBase = &model.MaterializedViewBaseInfo{MLogID: mlogID} + } + return tbl +} + +func newBaseTableInfoWithMViews(id int64, name string, mlogID int64, mviewIDs ...int64) *model.TableInfo { + tbl := newBaseTableInfo(id, name, mlogID) + if tbl.MaterializedViewBase == nil { + tbl.MaterializedViewBase = &model.MaterializedViewBaseInfo{} + } + tbl.MaterializedViewBase.MViewIDs = append([]int64(nil), mviewIDs...) + return tbl +} + +func newMViewTableInfoWithBase(id int64, name string, baseIDs ...int64) *model.TableInfo { + tbl := newMViewTableInfo(id, name, 0) + tbl.MaterializedView.BaseTableIDs = append([]int64(nil), baseIDs...) + return tbl +} + +func newMLogTableInfo(id int64, name string, baseTableID int64) *model.TableInfo { + return &model.TableInfo{ + ID: id, + Name: pmodel.NewCIStr(name), + State: model.StatePublic, + MaterializedViewLog: &model.MaterializedViewLogInfo{ + BaseTableID: baseTableID, + Columns: []pmodel.CIStr{pmodel.NewCIStr("a"), pmodel.NewCIStr("b")}, + PurgeMethod: "DEFERRED", + PurgeStartWith: "CURRENT_TIMESTAMP", + PurgeNext: "CURRENT_TIMESTAMP + INTERVAL 1 HOUR", + }, + } +} + func TestSetDataFromCheckConstraints(t *testing.T) { tblInfos := []*model.TableInfo{ { @@ -162,3 +308,544 @@ func TestSetDataFromKeywords(t *testing.T) { require.Equal(t, types.NewStringDatum("ADD"), mt.rows[0][0]) // Keyword: ADD require.Equal(t, types.NewIntDatum(1), mt.rows[0][1]) // Reserved: true(1) } + +func TestSetDataFromTiDBMViews(t *testing.T) { + t.Run("wrong extractor type", func(t *testing.T) { + mt := memtableRetriever{ + extractor: &plannercore.InfoSchemaTablesExtractor{}, + } + + err := mt.setDataFromTiDBMViews(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "wrong extractor type") + }) + + t.Run("skip request", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMViewsExtractor() + ex.SkipRequest = true + mt := memtableRetriever{extractor: ex} + + err := mt.setDataFromTiDBMViews(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Nil(t, mt.rows) + }) + + t.Run("returns list schemas error", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMViewsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MViewName: set.NewStringSet("mv_err"), + } + mt := memtableRetriever{ + is: &tableByNameErrorInfoSchema{ + InfoSchema: infoschema.MockInfoSchema(nil), + err: errors.New("boom"), + }, + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mview_sql_content"), + }, + } + + err := mt.setDataFromTiDBMViews(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "boom") + }) + + t.Run("columns are not eligible", func(t *testing.T) { + updateTS := uint64(time.Date(2025, 1, 2, 3, 4, 5, 0, time.UTC).UnixMilli()) << 18 + ex := plannercore.NewInfoSchemaTiDBMViewsExtractor() + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + { + ID: 1, + Name: pmodel.NewCIStr("base"), + State: model.StatePublic, + }, + newMViewTableInfo(2, "mv_deny", updateTS), + newMViewTableInfo(3, "mv_keep", updateTS), + }), + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mview_name"), + newColumnInfo("mview_sql_content"), + }, + } + + sctx := defaultCtx() + sctx.GetSessionVars().TimeZone = nil + pm := &stubPrivilegeManager{ + allow: func(db, table string) bool { + return table != "mv_deny" + }, + } + privilege.BindPrivilegeManager(sctx, pm) + + err := mt.setDataFromTiDBMViews(context.Background(), sctx) + require.NoError(t, err) + require.ElementsMatch(t, []string{"test.mv_deny", "test.mv_keep"}, pm.calls) + require.Len(t, mt.rows, 1) + + row := mt.rows[0] + require.Equal(t, types.NewStringDatum(infoschema.CatalogVal), row[0]) + require.Equal(t, types.NewStringDatum("test"), row[1]) + require.Equal(t, types.NewIntDatum(3), row[2]) + require.Equal(t, types.NewStringDatum("mv_keep"), row[3]) + require.Equal(t, types.NewStringDatum("select 1"), row[4]) + require.Equal(t, types.NewStringDatum("mv_keep-comment"), row[5]) + expectedTime := types.NewTime( + types.FromGoTime(model.TSConvert2Time(updateTS).In(time.Local)), + mysql.TypeDatetime, + types.DefaultFsp, + ) + require.Equal(t, expectedTime, row[6].GetMysqlTime()) + require.Equal(t, types.NewStringDatum("FAST"), row[7]) + require.Equal(t, types.NewStringDatum("CURRENT_TIMESTAMP"), row[8]) + require.Equal(t, types.NewStringDatum("CURRENT_TIMESTAMP + INTERVAL 1 HOUR"), row[9]) + }) + + t.Run("predicates are not eligible", func(t *testing.T) { + loc := time.FixedZone("UTC+8", 8*3600) + updateTS := uint64(time.Date(2025, 6, 7, 8, 9, 10, 0, time.UTC).UnixMilli()) << 18 + ex := plannercore.NewInfoSchemaTiDBMViewsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + "refresh_next": set.NewStringSet("ignored"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newMViewTableInfo(1, "mv_keep", updateTS), + }), + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mview_name"), + }, + } + + sctx := defaultCtx() + sctx.GetSessionVars().TimeZone = loc + + err := mt.setDataFromTiDBMViews(context.Background(), sctx) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal( + t, + types.NewTime(types.FromGoTime(model.TSConvert2Time(updateTS).In(loc)), mysql.TypeDatetime, types.DefaultFsp), + mt.rows[0][6].GetMysqlTime(), + ) + }) +} + +func TestSetDataFromTiDBMLogs(t *testing.T) { + t.Run("wrong extractor type", func(t *testing.T) { + mt := memtableRetriever{ + extractor: &plannercore.InfoSchemaTablesExtractor{}, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "wrong extractor type") + }) + + t.Run("skip request", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.SkipRequest = true + mt := memtableRetriever{extractor: ex} + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Nil(t, mt.rows) + }) + + t.Run("returns list schemas error", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MLogName: set.NewStringSet("$mlog$err"), + } + mt := memtableRetriever{ + is: &tableByNameErrorInfoSchema{ + InfoSchema: infoschema.MockInfoSchema(nil), + err: errors.New("boom"), + }, + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_columns"), + }, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "boom") + }) + + t.Run("columns are eligible", func(t *testing.T) { + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + {ID: 1, Name: pmodel.NewCIStr("base"), State: model.StatePublic}, + newMLogTableInfo(2, "$mlog$deny", 1), + newMLogTableInfo(3, "$mlog$keep", 1), + }), + extractor: plannercore.NewInfoSchemaTiDBMLogsExtractor(), + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_name"), + newColumnInfo("base_table_name"), + newColumnInfo("mlog_columns"), + }, + } + + sctx := defaultCtx() + pm := &stubPrivilegeManager{ + allow: func(db, table string) bool { + return table != "$mlog$deny" + }, + } + privilege.BindPrivilegeManager(sctx, pm) + + err := mt.setDataFromTiDBMLogs(context.Background(), sctx) + require.NoError(t, err) + require.ElementsMatch(t, []string{"test.$mlog$deny", "test.$mlog$keep"}, pm.calls) + require.Len(t, mt.rows, 1) + + row := mt.rows[0] + require.Equal(t, types.NewStringDatum(infoschema.CatalogVal), row[0]) + require.Equal(t, types.NewStringDatum("test"), row[1]) + require.Equal(t, types.NewIntDatum(3), row[2]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), row[3]) + require.Equal(t, types.NewStringDatum("a,b"), row[4]) + require.Equal(t, types.NewStringDatum(infoschema.CatalogVal), row[5]) + require.Equal(t, types.NewStringDatum("test"), row[6]) + require.Equal(t, types.NewStringDatum("1"), row[7]) + require.Equal(t, types.NewStringDatum("base"), row[8]) + require.Equal(t, types.NewStringDatum("DEFERRED"), row[9]) + require.Equal(t, types.NewStringDatum("CURRENT_TIMESTAMP"), row[10]) + require.Equal(t, types.NewStringDatum("CURRENT_TIMESTAMP + INTERVAL 1 HOUR"), row[11]) + }) + + t.Run("uses base predicates only", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.BaseTableName: set.NewStringSet("base_keep"), + } + countIS := &tableByIDCountInfoSchema{ + InfoSchema: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfo(1, "base_drop", 3), + newBaseTableInfo(2, "base_keep", 4), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + }), + } + mt := memtableRetriever{ + is: countIS, + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_name"), + }, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][3]) + require.Zero(t, countIS.calls[2]) + }) + + t.Run("uses mlog predicates only", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MLogName: set.NewStringSet("$mlog$keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + {ID: 1, Name: pmodel.NewCIStr("base_drop"), State: model.StatePublic}, + {ID: 2, Name: pmodel.NewCIStr("base_keep"), State: model.StatePublic}, + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + }), + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_name"), + }, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][8]) + }) + + t.Run("uses mlog and base predicates together", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MLogID: set.NewStringSet("3", "4"), + plannercore.BaseTableName: set.NewStringSet("base_keep"), + } + countIS := &tableByIDCountInfoSchema{ + InfoSchema: infoschema.MockInfoSchema([]*model.TableInfo{ + {ID: 1, Name: pmodel.NewCIStr("base_drop"), State: model.StatePublic}, + {ID: 2, Name: pmodel.NewCIStr("base_keep"), State: model.StatePublic}, + newMLogTableInfo(3, "$mlog$keep1", 1), + newMLogTableInfo(4, "$mlog$keep2", 2), + }), + } + mt := memtableRetriever{ + is: countIS, + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_name"), + }, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("$mlog$keep2"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][8]) + require.Equal(t, 1, countIS.calls[2]) + }) + + t.Run("predicates are not eligible", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBMLogsExtractor() + ex.ColPredicates = map[string]set.StringSet{ + "purge_next": set.NewStringSet("ignored"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + {ID: 1, Name: pmodel.NewCIStr("base"), State: model.StatePublic}, + newMLogTableInfo(2, "$mlog$keep", 1), + }), + extractor: ex, + columns: []*model.ColumnInfo{ + newColumnInfo("mlog_name"), + }, + } + + err := mt.setDataFromTiDBMLogs(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("CURRENT_TIMESTAMP + INTERVAL 1 HOUR"), mt.rows[0][11]) + }) +} + +func TestSetDataFromTiDBTableMViewDependencies(t *testing.T) { + t.Run("wrong extractor type", func(t *testing.T) { + mt := memtableRetriever{ + extractor: &plannercore.InfoSchemaTablesExtractor{}, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "wrong extractor type") + }) + + t.Run("skip request", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.SkipRequest = true + mt := memtableRetriever{extractor: ex} + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Nil(t, mt.rows) + }) + + t.Run("returns list schemas error", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.TableName: set.NewStringSet("base_err"), + } + mt := memtableRetriever{ + is: &tableByNameErrorInfoSchema{ + InfoSchema: infoschema.MockInfoSchema(nil), + err: errors.New("boom"), + }, + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.ErrorContains(t, err, "boom") + }) + + t.Run("columns are eligible", func(t *testing.T) { + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_deny", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6), + newMLogTableInfo(3, "$mlog$deny", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_deny", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + }), + extractor: plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor(), + } + + sctx := defaultCtx() + pm := &stubPrivilegeManager{ + allow: func(db, table string) bool { + return table != "base_deny" + }, + } + privilege.BindPrivilegeManager(sctx, pm) + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), sctx) + require.NoError(t, err) + require.ElementsMatch(t, []string{"test.base_deny", "test.base_keep"}, pm.calls) + require.Len(t, mt.rows, 1) + + row := mt.rows[0] + require.Equal(t, types.NewStringDatum(infoschema.CatalogVal), row[0]) + require.Equal(t, types.NewStringDatum("test"), row[1]) + require.Equal(t, types.NewIntDatum(2), row[2]) + require.Equal(t, types.NewStringDatum("base_keep"), row[3]) + require.Equal(t, types.NewIntDatum(4), row[4]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), row[5]) + require.Equal(t, types.NewStringDatum(infoschema.CatalogVal), row[6]) + require.Equal(t, types.NewStringDatum("test"), row[7]) + require.Equal(t, types.NewStringDatum("6"), row[8]) + require.Equal(t, types.NewStringDatum("mv_keep"), row[9]) + }) + + t.Run("uses table predicates only", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.TableName: set.NewStringSet("base_keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_drop", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_drop", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + }), + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][5]) + require.Equal(t, types.NewStringDatum("mv_keep"), mt.rows[0][9]) + }) + + t.Run("uses mlog predicates only", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MLogName: set.NewStringSet("$mlog$keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_drop", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_drop", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + }), + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][5]) + require.Equal(t, types.NewStringDatum("mv_keep"), mt.rows[0][9]) + }) + + t.Run("uses mview predicates only", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MViewName: set.NewStringSet("mv_keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_drop", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_drop", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + }), + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][5]) + require.Equal(t, types.NewStringDatum("mv_keep"), mt.rows[0][9]) + }) + + t.Run("uses table and mview predicates together", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.TableID: set.NewStringSet("1", "2"), + plannercore.MViewName: set.NewStringSet("mv_keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_drop", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6, 7), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_drop", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + newMViewTableInfoWithBase(7, "mv_other", 2), + }), + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][5]) + require.Equal(t, types.NewStringDatum("mv_keep"), mt.rows[0][9]) + }) + + t.Run("uses mlog and mview predicates together", func(t *testing.T) { + ex := plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor() + ex.ColPredicates = map[string]set.StringSet{ + plannercore.MLogID: set.NewStringSet("3", "4"), + plannercore.MViewName: set.NewStringSet("mv_keep"), + } + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base_drop", 3, 5), + newBaseTableInfoWithMViews(2, "base_keep", 4, 6, 7), + newMLogTableInfo(3, "$mlog$drop", 1), + newMLogTableInfo(4, "$mlog$keep", 2), + newMViewTableInfoWithBase(5, "mv_drop", 1), + newMViewTableInfoWithBase(6, "mv_keep", 2), + newMViewTableInfoWithBase(7, "mv_other", 2), + }), + extractor: ex, + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 1) + require.Equal(t, types.NewStringDatum("base_keep"), mt.rows[0][3]) + require.Equal(t, types.NewStringDatum("$mlog$keep"), mt.rows[0][5]) + require.Equal(t, types.NewStringDatum("mv_keep"), mt.rows[0][9]) + }) + + t.Run("uses no predicates", func(t *testing.T) { + mt := memtableRetriever{ + is: infoschema.MockInfoSchema([]*model.TableInfo{ + newBaseTableInfoWithMViews(1, "base1", 3, 5), + newBaseTableInfoWithMViews(2, "base2", 4, 6), + newBaseTableInfoWithMViews(7, "base_no_mlog", 0, 8), + newMLogTableInfo(3, "$mlog$base1", 1), + newMLogTableInfo(4, "$mlog$base2", 2), + newMViewTableInfoWithBase(5, "mv1", 1), + newMViewTableInfoWithBase(6, "mv2", 2), + newMViewTableInfoWithBase(8, "mv_no_mlog", 7), + }), + extractor: plannercore.NewInfoSchemaTiDBTableMViewDependenciesExtractor(), + } + + err := mt.setDataFromTiDBTableMViewDependencies(context.Background(), defaultCtx()) + require.NoError(t, err) + require.Len(t, mt.rows, 2) + require.ElementsMatch(t, []string{"base1", "base2"}, []string{mt.rows[0][3].GetString(), mt.rows[1][3].GetString()}) + require.ElementsMatch(t, []string{"$mlog$base1", "$mlog$base2"}, []string{mt.rows[0][5].GetString(), mt.rows[1][5].GetString()}) + require.ElementsMatch(t, []string{"mv1", "mv2"}, []string{mt.rows[0][9].GetString(), mt.rows[1][9].GetString()}) + }) +} diff --git a/pkg/executor/test/ddl/materialized_view_ddl_test.go b/pkg/executor/test/ddl/materialized_view_ddl_test.go index 673f11aea1056..08c161bd574b1 100644 --- a/pkg/executor/test/ddl/materialized_view_ddl_test.go +++ b/pkg/executor/test/ddl/materialized_view_ddl_test.go @@ -712,6 +712,44 @@ func TestShowMaterializedViewLogWaitPurge(t *testing.T) { require.ErrorContains(t, err, "syntax error: expected WAIT_PURGE") } +func TestInformationSchemaTiDBMViews(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int not null, b int not null)") + tk.MustExec("create materialized view log on t (a, b) purge next date_add(now(), interval 1 hour)") + tk.MustExec("create materialized view mv_meta (a, s, cnt) comment='mv-meta' refresh fast next date_add(now(), interval 1 hour) as select a, sum(b), count(1) from t group by a") + + is := dom.InfoSchema() + mvTable, err := is.TableByName(context.Background(), pmodel.NewCIStr("test"), pmodel.NewCIStr("mv_meta")) + require.NoError(t, err) + mvID := mvTable.Meta().ID + + tk.MustQuery("select table_catalog, table_schema, mview_id, mview_name, mview_comment, refresh_method, refresh_start, refresh_next from information_schema.tidb_mviews where table_schema = 'test' and mview_name = 'mv_meta'"). + Check(testkit.Rows(fmt.Sprintf("def test %d mv_meta mv-meta FAST DATE_ADD(NOW(), INTERVAL 1 HOUR)", mvID))) + tk.MustQuery("select mview_modify_time is not null from information_schema.tidb_mviews where table_schema = 'test' and mview_name = 'mv_meta'"). + Check(testkit.Rows("1")) + tk.MustQuery("select mview_sql_content like 'SELECT%' from information_schema.tidb_mviews where table_schema = 'test' and mview_name = 'mv_meta'"). + Check(testkit.Rows("1")) +} + +func TestInformationSchemaTiDBMLogs(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec("create table t (a int not null, b int not null)") + tk.MustExec("create materialized view log on t (a, b) purge start with now() next date_add(now(), interval 1 hour)") + + is := dom.InfoSchema() + baseTable, err := is.TableByName(context.Background(), pmodel.NewCIStr("test"), pmodel.NewCIStr("t")) + require.NoError(t, err) + mlogID := baseTable.Meta().MaterializedViewBase.MLogID + baseID := baseTable.Meta().ID + + tk.MustQuery("select table_catalog, table_schema, mlog_id, mlog_name, mlog_columns, base_table_catalog, base_table_schema, base_table_id, base_table_name, purge_method, purge_start, purge_next from information_schema.tidb_mlogs where table_schema = 'test' and mlog_name = '$mlog$t'"). + Check(testkit.Rows(fmt.Sprintf("def test %d $mlog$t a,b def test %d t DEFERRED NOW() DATE_ADD(NOW(), INTERVAL 1 HOUR)", mlogID, baseID))) +} + func TestCreateMaterializedViewLogColumnKeyFlag(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) diff --git a/pkg/infoschema/tables.go b/pkg/infoschema/tables.go index 183c1a1accc11..6c244be13f504 100644 --- a/pkg/infoschema/tables.go +++ b/pkg/infoschema/tables.go @@ -219,6 +219,12 @@ const ( TableKeywords = "KEYWORDS" // TableTiDBIndexUsage is a table to show the usage stats of indexes in the current instance. TableTiDBIndexUsage = "TIDB_INDEX_USAGE" + // TableTiDBMViews is a table to show the metadata of materialized views in the current instance. + TableTiDBMViews = "TIDB_MVIEWS" + // TableTiDBMLogs is a table to show the metadata of materialized view logs in the current instance. + TableTiDBMLogs = "TIDB_MLOGS" + // TableTiDBTableMViewDependencies is a table to show dependencies between base tables, mlog and materialized views. + TableTiDBTableMViewDependencies = "TIDB_TABLE_MVIEW_DEPENDENCIES" ) const ( @@ -341,6 +347,9 @@ var tableIDMap = map[string]int64{ TableTiDBIndexUsage: autoid.InformationSchemaDBID + 93, ClusterTableTiDBIndexUsage: autoid.InformationSchemaDBID + 94, TableTiFlashIndexes: autoid.InformationSchemaDBID + 95, + TableTiDBMViews: autoid.InformationSchemaDBID + 96, + TableTiDBMLogs: autoid.InformationSchemaDBID + 97, + TableTiDBTableMViewDependencies: autoid.InformationSchemaDBID + 98, } // columnInfo represents the basic column information of all kinds of INFORMATION_SCHEMA tables @@ -727,6 +736,47 @@ var tableViewsCols = []columnInfo{ {name: "COLLATION_CONNECTION", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, } +var tableTiDBMViewsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MVIEW_ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MVIEW_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MVIEW_SQL_CONTENT", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, + {name: "MVIEW_COMMENT", tp: mysql.TypeVarchar, size: 128}, + {name: "MVIEW_MODIFY_TIME", tp: mysql.TypeDatetime, flag: mysql.NotNullFlag}, + {name: "REFRESH_METHOD", tp: mysql.TypeVarchar, size: 32}, + {name: "REFRESH_START", tp: mysql.TypeVarchar, size: 128}, + {name: "REFRESH_NEXT", tp: mysql.TypeVarchar, size: 128}, +} + +var tableTiDBMLogsCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MLOG_ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MLOG_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MLOG_COLUMNS", tp: mysql.TypeLongBlob, size: types.UnspecifiedLength, flag: mysql.NotNullFlag}, + {name: "BASE_TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "BASE_TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "BASE_TABLE_ID", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "BASE_TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "PURGE_METHOD", tp: mysql.TypeVarchar, size: 32, flag: mysql.NotNullFlag}, + {name: "PURGE_START", tp: mysql.TypeVarchar, size: 128, flag: mysql.NotNullFlag}, + {name: "PURGE_NEXT", tp: mysql.TypeVarchar, size: 128, flag: mysql.NotNullFlag}, +} + +var tableTiDBTableMViewDependenciesCols = []columnInfo{ + {name: "TABLE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "TABLE_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "TABLE_ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "TABLE_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MLOG_ID", tp: mysql.TypeLonglong, size: 21, flag: mysql.NotNullFlag}, + {name: "MLOG_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MVIEW_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, + {name: "MVIEW_SCHEMA", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MVIEW_ID", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, + {name: "MVIEW_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, +} + var tableRoutinesCols = []columnInfo{ {name: "SPECIFIC_NAME", tp: mysql.TypeVarchar, size: 64, flag: mysql.NotNullFlag}, {name: "ROUTINE_CATALOG", tp: mysql.TypeVarchar, size: 512, flag: mysql.NotNullFlag}, @@ -2387,6 +2437,9 @@ var tableNameToColumns = map[string][]columnInfo{ TableTiDBCheckConstraints: tableTiDBCheckConstraintsCols, TableKeywords: tableKeywords, TableTiDBIndexUsage: tableTiDBIndexUsage, + TableTiDBMViews: tableTiDBMViewsCols, + TableTiDBMLogs: tableTiDBMLogsCols, + TableTiDBTableMViewDependencies: tableTiDBTableMViewDependenciesCols, } func createInfoSchemaTable(_ autoid.Allocators, _ func() (pools.Resource, error), meta *model.TableInfo) (table.Table, error) { diff --git a/pkg/planner/core/logical_plan_builder.go b/pkg/planner/core/logical_plan_builder.go index 93fbb076d87c0..615a29615eedc 100644 --- a/pkg/planner/core/logical_plan_builder.go +++ b/pkg/planner/core/logical_plan_builder.go @@ -4963,6 +4963,12 @@ func (b *PlanBuilder) buildMemTable(_ context.Context, dbName pmodel.CIStr, tabl p.Extractor = NewInfoSchemaIndexesExtractor() case infoschema.TableViews: p.Extractor = NewInfoSchemaViewsExtractor() + case infoschema.TableTiDBMViews: + p.Extractor = NewInfoSchemaTiDBMViewsExtractor() + case infoschema.TableTiDBMLogs: + p.Extractor = NewInfoSchemaTiDBMLogsExtractor() + case infoschema.TableTiDBTableMViewDependencies: + p.Extractor = NewInfoSchemaTiDBTableMViewDependenciesExtractor() case infoschema.TableKeyColumn: p.Extractor = NewInfoSchemaKeyColumnUsageExtractor() case infoschema.TableConstraints: diff --git a/pkg/planner/core/memtable_infoschema_extractor.go b/pkg/planner/core/memtable_infoschema_extractor.go index fb21002fc2928..4aad03a0d8579 100644 --- a/pkg/planner/core/memtable_infoschema_extractor.go +++ b/pkg/planner/core/memtable_infoschema_extractor.go @@ -58,6 +58,14 @@ const ( TableSchema = "table_schema" TableName = "table_name" TidbTableID = "tidb_table_id" + MViewSchema = "mview_schema" + MViewName = "mview_name" + MViewID = "mview_id" + MLogName = "mlog_name" + MLogID = "mlog_id" + BaseTableSchema = "base_table_schema" + BaseTableName = "base_table_name" + BaseTableID = "base_table_id" PartitionName = "partition_name" TidbPartitionID = "tidb_partition_id" IndexName = "index_name" @@ -77,6 +85,13 @@ const ( var patternMatchable = map[string]struct{}{ TableSchema: {}, TableName: {}, + MViewSchema: {}, + MViewName: {}, + MViewID: {}, + MLogName: {}, + BaseTableSchema: {}, + BaseTableName: {}, + BaseTableID: {}, IndexName: {}, SchemaName: {}, ConstraintSchema: {}, @@ -999,3 +1014,342 @@ ForLoop: return indexes } + +// InfoSchemaTiDBMViewsExtractor is the predicate extractor for information_schema.tidb_mviews. +type InfoSchemaTiDBMViewsExtractor struct { + InfoSchemaBaseExtractor +} + +// NewInfoSchemaTiDBMViewsExtractor creates a new InfoSchemaTiDBMViewsExtractor. +func NewInfoSchemaTiDBMViewsExtractor() *InfoSchemaTiDBMViewsExtractor { + e := &InfoSchemaTiDBMViewsExtractor{} + e.extractableColumns = extractableCols{ + schema: TableSchema, + table: MViewName, + tableID: MViewID, + } + e.colNames = []string{TableSchema, MViewName, MViewID} + return e +} + +// InfoSchemaTiDBMLogsExtractor is the predicate extractor for information_schema.tidb_mlogs. +type InfoSchemaTiDBMLogsExtractor struct { + InfoSchemaBaseExtractor +} + +// NewInfoSchemaTiDBMLogsExtractor creates a new InfoSchemaTiDBMLogsExtractor. +func NewInfoSchemaTiDBMLogsExtractor() *InfoSchemaTiDBMLogsExtractor { + e := &InfoSchemaTiDBMLogsExtractor{} + e.extractableColumns = extractableCols{ + schema: TableSchema, + table: MLogName, + tableID: MLogID, + } + e.colNames = []string{TableSchema, MLogName, MLogID, BaseTableSchema, BaseTableName, BaseTableID} + return e +} + +// HasMLogPredicates returns true if predicates are extracted for the mlog column set. +func (e *InfoSchemaTiDBMLogsExtractor) HasMLogPredicates() bool { + return e.hasPredicates(TableSchema, MLogName, MLogID) +} + +// HasBaseTablePredicates returns true if predicates are extracted for the base-table column set. +func (e *InfoSchemaTiDBMLogsExtractor) HasBaseTablePredicates() bool { + return e.hasPredicates(BaseTableSchema, BaseTableName, BaseTableID) +} + +func (e *InfoSchemaTiDBMLogsExtractor) hasPredicates(colNames ...string) bool { + for _, colName := range colNames { + if len(e.ColPredicates[colName]) > 0 || len(e.colsRegexp[colName]) > 0 { + return true + } + } + return false +} + +// ListSchemasAndTablesByBase lists candidate mlog tables by base-table predicates. +// The returned base schema/table slices are aligned with the returned mlog schema/table slices. +func (e *InfoSchemaTiDBMLogsExtractor) ListSchemasAndTablesByBase( + ctx context.Context, + is infoschema.InfoSchema, +) ([]pmodel.CIStr, []*model.TableInfo, []pmodel.CIStr, []*model.TableInfo, error) { + baseExtractor := &InfoSchemaBaseExtractor{ + extractHelper: e.extractHelper, + SkipRequest: e.SkipRequest, + ColPredicates: e.ColPredicates, + colsRegexp: e.colsRegexp, + LikePatterns: e.LikePatterns, + colNames: []string{BaseTableSchema, BaseTableName, BaseTableID}, + extractableColumns: extractableCols{ + schema: BaseTableSchema, + table: BaseTableName, + tableID: BaseTableID, + }, + } + candidateBaseSchemas, candidateBaseTables, err := baseExtractor.ListSchemasAndTables(ctx, is) + if err != nil { + return nil, nil, nil, nil, errors.Trace(err) + } + if len(candidateBaseTables) == 0 { + return nil, nil, nil, nil, nil + } + + type schemaAndTable struct { + schema pmodel.CIStr + table *model.TableInfo + baseSchema pmodel.CIStr + baseTable *model.TableInfo + } + + schemaAndTbls := make([]schemaAndTable, 0, len(candidateBaseTables)) + seenMLogIDs := make(map[int64]struct{}, len(candidateBaseTables)) + for i, baseTable := range candidateBaseTables { + baseInfo := baseTable.MaterializedViewBase + if baseInfo == nil || baseInfo.MLogID == 0 { + continue + } + if _, ok := seenMLogIDs[baseInfo.MLogID]; ok { + continue + } + mlogTbl, ok := is.TableByID(ctx, baseInfo.MLogID) + if !ok { + continue + } + mlogMeta := mlogTbl.Meta() + if mlogMeta.MaterializedViewLog == nil { + continue + } + mlogSchema, ok := infoschema.SchemaByTable(is, mlogMeta) + if !ok { + continue + } + seenMLogIDs[baseInfo.MLogID] = struct{}{} + schemaAndTbls = append(schemaAndTbls, schemaAndTable{ + schema: mlogSchema.Name, + table: mlogMeta, + baseSchema: candidateBaseSchemas[i], + baseTable: baseTable, + }) + } + + slices.SortFunc(schemaAndTbls, func(a, b schemaAndTable) int { + if a.schema.L == b.schema.L { + return strings.Compare(a.table.Name.L, b.table.Name.L) + } + return strings.Compare(a.schema.L, b.schema.L) + }) + + schemas := make([]pmodel.CIStr, 0, len(schemaAndTbls)) + tables := make([]*model.TableInfo, 0, len(schemaAndTbls)) + baseSchemas := make([]pmodel.CIStr, 0, len(schemaAndTbls)) + baseTables := make([]*model.TableInfo, 0, len(schemaAndTbls)) + for _, st := range schemaAndTbls { + schemas = append(schemas, st.schema) + tables = append(tables, st.table) + baseSchemas = append(baseSchemas, st.baseSchema) + baseTables = append(baseTables, st.baseTable) + } + return schemas, tables, baseSchemas, baseTables, nil +} + +// Filter reports whether the given row value should be filtered out by the extractor predicates. +func (e *InfoSchemaTiDBMLogsExtractor) Filter(colName, val string) bool { + return e.filter(colName, val) +} + +// InfoSchemaTiDBTableMViewDependenciesExtractor is the predicate extractor for +// information_schema.tidb_table_mview_dependencies. +type InfoSchemaTiDBTableMViewDependenciesExtractor struct { + InfoSchemaBaseExtractor +} + +// NewInfoSchemaTiDBTableMViewDependenciesExtractor creates a new InfoSchemaTiDBTableMViewDependenciesExtractor. +func NewInfoSchemaTiDBTableMViewDependenciesExtractor() *InfoSchemaTiDBTableMViewDependenciesExtractor { + e := &InfoSchemaTiDBTableMViewDependenciesExtractor{} + e.extractableColumns = extractableCols{ + schema: TableSchema, + table: TableName, + tableID: TableID, + } + e.colNames = []string{TableSchema, TableName, TableID, MLogName, MLogID, MViewSchema, MViewName, MViewID} + return e +} + +// HasTablePredicates returns true if predicates are extracted for the base-table column set. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) HasTablePredicates() bool { + return e.hasPredicates(TableSchema, TableName, TableID) +} + +// HasMLogPredicates returns true if predicates are extracted for the mlog column set. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) HasMLogPredicates() bool { + return e.hasPredicates(MLogName, MLogID) +} + +// HasMViewPredicates returns true if predicates are extracted for the mview column set. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) HasMViewPredicates() bool { + return e.hasPredicates(MViewSchema, MViewName, MViewID) +} + +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) hasPredicates(colNames ...string) bool { + for _, colName := range colNames { + if len(e.ColPredicates[colName]) > 0 || len(e.colsRegexp[colName]) > 0 { + return true + } + } + return false +} + +// ListSchemasAndTablesByMLog lists candidate base tables by mlog predicates. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) ListSchemasAndTablesByMLog( + ctx context.Context, + is infoschema.InfoSchema, +) ([]pmodel.CIStr, []*model.TableInfo, error) { + mlogExtractor := &InfoSchemaBaseExtractor{ + extractHelper: e.extractHelper, + SkipRequest: e.SkipRequest, + ColPredicates: e.ColPredicates, + colsRegexp: e.colsRegexp, + LikePatterns: e.LikePatterns, + colNames: []string{MLogName, MLogID}, + extractableColumns: extractableCols{ + table: MLogName, + tableID: MLogID, + }, + } + _, mlogTables, err := mlogExtractor.ListSchemasAndTables(ctx, is) + if err != nil { + return nil, nil, errors.Trace(err) + } + if len(mlogTables) == 0 { + return nil, nil, nil + } + + type schemaAndTable struct { + schema pmodel.CIStr + table *model.TableInfo + } + + schemaAndTbls := make([]schemaAndTable, 0, len(mlogTables)) + seenBaseTableIDs := make(map[int64]struct{}, len(mlogTables)) + for _, mlogTable := range mlogTables { + mlogInfo := mlogTable.MaterializedViewLog + if mlogInfo == nil || mlogInfo.BaseTableID == 0 { + continue + } + if _, ok := seenBaseTableIDs[mlogInfo.BaseTableID]; ok { + continue + } + baseTbl, ok := is.TableByID(ctx, mlogInfo.BaseTableID) + if !ok { + continue + } + baseMeta := baseTbl.Meta() + baseInfo := baseMeta.MaterializedViewBase + if baseInfo == nil || len(baseInfo.MViewIDs) == 0 { + continue + } + baseSchema, ok := infoschema.SchemaByTable(is, baseMeta) + if !ok { + continue + } + seenBaseTableIDs[mlogInfo.BaseTableID] = struct{}{} + schemaAndTbls = append(schemaAndTbls, schemaAndTable{schema: baseSchema.Name, table: baseMeta}) + } + + slices.SortFunc(schemaAndTbls, func(a, b schemaAndTable) int { + if a.schema.L == b.schema.L { + return strings.Compare(a.table.Name.L, b.table.Name.L) + } + return strings.Compare(a.schema.L, b.schema.L) + }) + + schemas := make([]pmodel.CIStr, 0, len(schemaAndTbls)) + tables := make([]*model.TableInfo, 0, len(schemaAndTbls)) + for _, st := range schemaAndTbls { + schemas = append(schemas, st.schema) + tables = append(tables, st.table) + } + return schemas, tables, nil +} + +// ListSchemasAndTablesByMView lists candidate base tables by mview predicates. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) ListSchemasAndTablesByMView( + ctx context.Context, + is infoschema.InfoSchema, +) ([]pmodel.CIStr, []*model.TableInfo, error) { + mviewExtractor := &InfoSchemaBaseExtractor{ + extractHelper: e.extractHelper, + SkipRequest: e.SkipRequest, + ColPredicates: e.ColPredicates, + colsRegexp: e.colsRegexp, + LikePatterns: e.LikePatterns, + colNames: []string{MViewSchema, MViewName, MViewID}, + extractableColumns: extractableCols{ + schema: MViewSchema, + table: MViewName, + tableID: MViewID, + }, + } + _, mviewTables, err := mviewExtractor.ListSchemasAndTables(ctx, is) + if err != nil { + return nil, nil, errors.Trace(err) + } + if len(mviewTables) == 0 { + return nil, nil, nil + } + + type schemaAndTable struct { + schema pmodel.CIStr + table *model.TableInfo + } + + schemaAndTbls := make([]schemaAndTable, 0, len(mviewTables)) + seenBaseTableIDs := make(map[int64]struct{}, len(mviewTables)) + for _, mviewTable := range mviewTables { + mviewInfo := mviewTable.MaterializedView + if mviewInfo == nil { + continue + } + for _, baseTableID := range mviewInfo.BaseTableIDs { + baseTbl, ok := is.TableByID(ctx, baseTableID) + if !ok { + continue + } + baseMeta := baseTbl.Meta() + baseInfo := baseMeta.MaterializedViewBase + if baseInfo == nil || len(baseInfo.MViewIDs) == 0 { + continue + } + if _, ok := seenBaseTableIDs[baseMeta.ID]; ok { + continue + } + baseSchema, ok := infoschema.SchemaByTable(is, baseMeta) + if !ok { + continue + } + seenBaseTableIDs[baseMeta.ID] = struct{}{} + schemaAndTbls = append(schemaAndTbls, schemaAndTable{schema: baseSchema.Name, table: baseMeta}) + } + } + + slices.SortFunc(schemaAndTbls, func(a, b schemaAndTable) int { + if a.schema.L == b.schema.L { + return strings.Compare(a.table.Name.L, b.table.Name.L) + } + return strings.Compare(a.schema.L, b.schema.L) + }) + + schemas := make([]pmodel.CIStr, 0, len(schemaAndTbls)) + tables := make([]*model.TableInfo, 0, len(schemaAndTbls)) + for _, st := range schemaAndTbls { + schemas = append(schemas, st.schema) + tables = append(tables, st.table) + } + return schemas, tables, nil +} + +// Filter reports whether the given row value should be filtered out by the extractor predicates. +func (e *InfoSchemaTiDBTableMViewDependenciesExtractor) Filter(colName, val string) bool { + return e.filter(colName, val) +} diff --git a/pkg/planner/core/operator/logicalop/logicalop_test/BUILD.bazel b/pkg/planner/core/operator/logicalop/logicalop_test/BUILD.bazel index 089caf2a9b400..393b598454d75 100644 --- a/pkg/planner/core/operator/logicalop/logicalop_test/BUILD.bazel +++ b/pkg/planner/core/operator/logicalop/logicalop_test/BUILD.bazel @@ -11,7 +11,7 @@ go_test( ], data = glob(["testdata/**"]), flaky = True, - shard_count = 21, + shard_count = 24, deps = [ "//pkg/domain", "//pkg/expression", diff --git a/pkg/planner/core/operator/logicalop/logicalop_test/logical_mem_table_predicate_extractor_test.go b/pkg/planner/core/operator/logicalop/logicalop_test/logical_mem_table_predicate_extractor_test.go index c554edb357714..8775bfa32ec00 100644 --- a/pkg/planner/core/operator/logicalop/logicalop_test/logical_mem_table_predicate_extractor_test.go +++ b/pkg/planner/core/operator/logicalop/logicalop_test/logical_mem_table_predicate_extractor_test.go @@ -1665,6 +1665,330 @@ func TestColumns(t *testing.T) { } } +func TestTiDBMViews(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + + var cases = []struct { + sql string + tableSchema set.StringSet + mviewName set.StringSet + mviewID set.StringSet + tableSchemaPattern []string + mviewNamePattern []string + skipRequest bool + }{ + { + sql: `select * from information_schema.tidb_mviews where table_schema='TEST';`, + tableSchema: set.NewStringSet("test"), + }, + { + sql: `select * from information_schema.tidb_mviews where mview_name='MV_TEST';`, + mviewName: set.NewStringSet("mv_test"), + }, + { + sql: `select * from information_schema.tidb_mviews where mview_id in (1, 2, 3);`, + mviewID: set.NewStringSet("1", "2", "3"), + }, + { + sql: `select * from information_schema.tidb_mviews where mview_name like 'MV%';`, + mviewNamePattern: []string{"mv%"}, + }, + { + sql: `select * from information_schema.tidb_mviews where table_schema like 'TE%';`, + tableSchemaPattern: []string{"te%"}, + }, + { + sql: `select * from information_schema.tidb_mviews where mview_id=1 and mview_id=2;`, + skipRequest: true, + }, + } + + parser := parser.New() + for _, ca := range cases { + logicalMemTable := getLogicalMemTable(t, dom, se, parser, ca.sql) + require.NotNil(t, logicalMemTable.Extractor) + + mviewExtractor := logicalMemTable.Extractor.(*plannercore.InfoSchemaTiDBMViewsExtractor) + require.Equal(t, ca.skipRequest, mviewExtractor.SkipRequest, "SQL: %v", ca.sql) + + require.Equal(t, ca.tableSchema.Count(), mviewExtractor.ColPredicates["table_schema"].Count()) + if ca.tableSchema.Count() > 0 && mviewExtractor.ColPredicates["table_schema"].Count() > 0 { + require.EqualValues(t, ca.tableSchema, mviewExtractor.ColPredicates["table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mviewName.Count(), mviewExtractor.ColPredicates["mview_name"].Count()) + if ca.mviewName.Count() > 0 && mviewExtractor.ColPredicates["mview_name"].Count() > 0 { + require.EqualValues(t, ca.mviewName, mviewExtractor.ColPredicates["mview_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mviewID.Count(), mviewExtractor.ColPredicates["mview_id"].Count()) + if ca.mviewID.Count() > 0 && mviewExtractor.ColPredicates["mview_id"].Count() > 0 { + require.EqualValues(t, ca.mviewID, mviewExtractor.ColPredicates["mview_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.tableSchemaPattern), len(mviewExtractor.LikePatterns["table_schema"])) + if len(ca.tableSchemaPattern) > 0 && len(mviewExtractor.LikePatterns["table_schema"]) > 0 { + require.EqualValues(t, ca.tableSchemaPattern, mviewExtractor.LikePatterns["table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.mviewNamePattern), len(mviewExtractor.LikePatterns["mview_name"])) + if len(ca.mviewNamePattern) > 0 && len(mviewExtractor.LikePatterns["mview_name"]) > 0 { + require.EqualValues(t, ca.mviewNamePattern, mviewExtractor.LikePatterns["mview_name"], "SQL: %v", ca.sql) + } + } +} + +func TestTiDBMLogs(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + + var cases = []struct { + sql string + tableSchema set.StringSet + mlogName set.StringSet + mlogID set.StringSet + baseTableSchema set.StringSet + baseTableName set.StringSet + baseTableID set.StringSet + tableSchemaPattern []string + mlogNamePattern []string + baseTableNamePattern []string + skipRequest bool + }{ + { + sql: `select * from information_schema.tidb_mlogs where table_schema='TEST';`, + tableSchema: set.NewStringSet("test"), + }, + { + sql: `select * from information_schema.tidb_mlogs where mlog_name='$MLOG$T';`, + mlogName: set.NewStringSet("$mlog$t"), + }, + { + sql: `select * from information_schema.tidb_mlogs where mlog_id in (1, 2, 3);`, + mlogID: set.NewStringSet("1", "2", "3"), + }, + { + sql: `select * from information_schema.tidb_mlogs where base_table_schema='TEST';`, + baseTableSchema: set.NewStringSet("test"), + }, + { + sql: `select * from information_schema.tidb_mlogs where base_table_name='T';`, + baseTableName: set.NewStringSet("t"), + }, + { + sql: `select * from information_schema.tidb_mlogs where base_table_id in ('10', '11');`, + baseTableID: set.NewStringSet("10", "11"), + }, + { + sql: `select * from information_schema.tidb_mlogs where mlog_name like '$MLOG$%';`, + mlogNamePattern: []string{"$mlog$%"}, + }, + { + sql: `select * from information_schema.tidb_mlogs where base_table_name like 'T%';`, + baseTableNamePattern: []string{"t%"}, + }, + { + sql: `select * from information_schema.tidb_mlogs where base_table_id='1' and base_table_id='2';`, + skipRequest: true, + }, + } + + parser := parser.New() + for _, ca := range cases { + logicalMemTable := getLogicalMemTable(t, dom, se, parser, ca.sql) + require.NotNil(t, logicalMemTable.Extractor) + + mlogExtractor := logicalMemTable.Extractor.(*plannercore.InfoSchemaTiDBMLogsExtractor) + require.Equal(t, ca.skipRequest, mlogExtractor.SkipRequest, "SQL: %v", ca.sql) + + require.Equal(t, ca.tableSchema.Count(), mlogExtractor.ColPredicates["table_schema"].Count()) + if ca.tableSchema.Count() > 0 && mlogExtractor.ColPredicates["table_schema"].Count() > 0 { + require.EqualValues(t, ca.tableSchema, mlogExtractor.ColPredicates["table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mlogName.Count(), mlogExtractor.ColPredicates["mlog_name"].Count()) + if ca.mlogName.Count() > 0 && mlogExtractor.ColPredicates["mlog_name"].Count() > 0 { + require.EqualValues(t, ca.mlogName, mlogExtractor.ColPredicates["mlog_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mlogID.Count(), mlogExtractor.ColPredicates["mlog_id"].Count()) + if ca.mlogID.Count() > 0 && mlogExtractor.ColPredicates["mlog_id"].Count() > 0 { + require.EqualValues(t, ca.mlogID, mlogExtractor.ColPredicates["mlog_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.baseTableSchema.Count(), mlogExtractor.ColPredicates["base_table_schema"].Count()) + if ca.baseTableSchema.Count() > 0 && mlogExtractor.ColPredicates["base_table_schema"].Count() > 0 { + require.EqualValues(t, ca.baseTableSchema, mlogExtractor.ColPredicates["base_table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.baseTableName.Count(), mlogExtractor.ColPredicates["base_table_name"].Count()) + if ca.baseTableName.Count() > 0 && mlogExtractor.ColPredicates["base_table_name"].Count() > 0 { + require.EqualValues(t, ca.baseTableName, mlogExtractor.ColPredicates["base_table_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.baseTableID.Count(), mlogExtractor.ColPredicates["base_table_id"].Count()) + if ca.baseTableID.Count() > 0 && mlogExtractor.ColPredicates["base_table_id"].Count() > 0 { + require.EqualValues(t, ca.baseTableID, mlogExtractor.ColPredicates["base_table_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.tableSchemaPattern), len(mlogExtractor.LikePatterns["table_schema"])) + if len(ca.tableSchemaPattern) > 0 && len(mlogExtractor.LikePatterns["table_schema"]) > 0 { + require.EqualValues(t, ca.tableSchemaPattern, mlogExtractor.LikePatterns["table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.mlogNamePattern), len(mlogExtractor.LikePatterns["mlog_name"])) + if len(ca.mlogNamePattern) > 0 && len(mlogExtractor.LikePatterns["mlog_name"]) > 0 { + require.EqualValues(t, ca.mlogNamePattern, mlogExtractor.LikePatterns["mlog_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.baseTableNamePattern), len(mlogExtractor.LikePatterns["base_table_name"])) + if len(ca.baseTableNamePattern) > 0 && len(mlogExtractor.LikePatterns["base_table_name"]) > 0 { + require.EqualValues(t, ca.baseTableNamePattern, mlogExtractor.LikePatterns["base_table_name"], "SQL: %v", ca.sql) + } + } +} + +func TestTiDBTableMViewDependencies(t *testing.T) { + store, dom := testkit.CreateMockStoreAndDomain(t) + + se, err := session.CreateSession4Test(store) + require.NoError(t, err) + + var cases = []struct { + sql string + tableSchema set.StringSet + tableName set.StringSet + tableID set.StringSet + mlogName set.StringSet + mlogID set.StringSet + mviewSchema set.StringSet + mviewName set.StringSet + mviewID set.StringSet + tableNamePattern []string + mlogNamePattern []string + mviewNamePattern []string + skipRequest bool + }{ + { + sql: `select * from information_schema.tidb_table_mview_dependencies where table_schema='TEST';`, + tableSchema: set.NewStringSet("test"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where table_name='BASE_T';`, + tableName: set.NewStringSet("base_t"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where table_id in (1, 2, 3);`, + tableID: set.NewStringSet("1", "2", "3"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mlog_name='$MLOG$T';`, + mlogName: set.NewStringSet("$mlog$t"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mlog_id in (10, 11);`, + mlogID: set.NewStringSet("10", "11"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mview_schema='TEST';`, + mviewSchema: set.NewStringSet("test"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mview_name='MV';`, + mviewName: set.NewStringSet("mv"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mview_id in ('10', '11');`, + mviewID: set.NewStringSet("10", "11"), + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where table_name like 'BASE%';`, + tableNamePattern: []string{"base%"}, + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mlog_name like '$MLOG$%';`, + mlogNamePattern: []string{"$mlog$%"}, + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mview_name like 'MV%';`, + mviewNamePattern: []string{"mv%"}, + }, + { + sql: `select * from information_schema.tidb_table_mview_dependencies where mview_id='1' and mview_id='2';`, + skipRequest: true, + }, + } + + parser := parser.New() + for _, ca := range cases { + logicalMemTable := getLogicalMemTable(t, dom, se, parser, ca.sql) + require.NotNil(t, logicalMemTable.Extractor) + + depExtractor := logicalMemTable.Extractor.(*plannercore.InfoSchemaTiDBTableMViewDependenciesExtractor) + require.Equal(t, ca.skipRequest, depExtractor.SkipRequest, "SQL: %v", ca.sql) + + require.Equal(t, ca.tableSchema.Count(), depExtractor.ColPredicates["table_schema"].Count()) + if ca.tableSchema.Count() > 0 && depExtractor.ColPredicates["table_schema"].Count() > 0 { + require.EqualValues(t, ca.tableSchema, depExtractor.ColPredicates["table_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.tableName.Count(), depExtractor.ColPredicates["table_name"].Count()) + if ca.tableName.Count() > 0 && depExtractor.ColPredicates["table_name"].Count() > 0 { + require.EqualValues(t, ca.tableName, depExtractor.ColPredicates["table_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.tableID.Count(), depExtractor.ColPredicates["table_id"].Count()) + if ca.tableID.Count() > 0 && depExtractor.ColPredicates["table_id"].Count() > 0 { + require.EqualValues(t, ca.tableID, depExtractor.ColPredicates["table_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mlogName.Count(), depExtractor.ColPredicates["mlog_name"].Count()) + if ca.mlogName.Count() > 0 && depExtractor.ColPredicates["mlog_name"].Count() > 0 { + require.EqualValues(t, ca.mlogName, depExtractor.ColPredicates["mlog_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mlogID.Count(), depExtractor.ColPredicates["mlog_id"].Count()) + if ca.mlogID.Count() > 0 && depExtractor.ColPredicates["mlog_id"].Count() > 0 { + require.EqualValues(t, ca.mlogID, depExtractor.ColPredicates["mlog_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mviewSchema.Count(), depExtractor.ColPredicates["mview_schema"].Count()) + if ca.mviewSchema.Count() > 0 && depExtractor.ColPredicates["mview_schema"].Count() > 0 { + require.EqualValues(t, ca.mviewSchema, depExtractor.ColPredicates["mview_schema"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mviewName.Count(), depExtractor.ColPredicates["mview_name"].Count()) + if ca.mviewName.Count() > 0 && depExtractor.ColPredicates["mview_name"].Count() > 0 { + require.EqualValues(t, ca.mviewName, depExtractor.ColPredicates["mview_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, ca.mviewID.Count(), depExtractor.ColPredicates["mview_id"].Count()) + if ca.mviewID.Count() > 0 && depExtractor.ColPredicates["mview_id"].Count() > 0 { + require.EqualValues(t, ca.mviewID, depExtractor.ColPredicates["mview_id"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.tableNamePattern), len(depExtractor.LikePatterns["table_name"])) + if len(ca.tableNamePattern) > 0 && len(depExtractor.LikePatterns["table_name"]) > 0 { + require.EqualValues(t, ca.tableNamePattern, depExtractor.LikePatterns["table_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.mlogNamePattern), len(depExtractor.LikePatterns["mlog_name"])) + if len(ca.mlogNamePattern) > 0 && len(depExtractor.LikePatterns["mlog_name"]) > 0 { + require.EqualValues(t, ca.mlogNamePattern, depExtractor.LikePatterns["mlog_name"], "SQL: %v", ca.sql) + } + + require.Equal(t, len(ca.mviewNamePattern), len(depExtractor.LikePatterns["mview_name"])) + if len(ca.mviewNamePattern) > 0 && len(depExtractor.LikePatterns["mview_name"]) > 0 { + require.EqualValues(t, ca.mviewNamePattern, depExtractor.LikePatterns["mview_name"], "SQL: %v", ca.sql) + } + } +} + func TestTikvRegionStatusExtractor(t *testing.T) { store, dom := testkit.CreateMockStoreAndDomain(t) diff --git a/tests/integrationtest/r/executor/infoschema_mv.result b/tests/integrationtest/r/executor/infoschema_mv.result new file mode 100644 index 0000000000000..cd36d41ee7536 --- /dev/null +++ b/tests/integrationtest/r/executor/infoschema_mv.result @@ -0,0 +1,582 @@ +use test; +drop table if exists t; +create table t ( +c0 int not null, +c1 int unsigned not null, +c2 double not null, +c3 decimal(20, 3) not null, +kcol int +); +select count(*) from information_schema.tidb_mviews; +count(*) +0 +select TABLE_SCHEMA, MVIEW_NAME from information_schema.tidb_mviews; +TABLE_SCHEMA MVIEW_NAME +create materialized view log on t (c0, c1, c2, c3, kcol); +CREATE MATERIALIZED VIEW mv_t (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; +CREATE MATERIALIZED VIEW mv_t1 (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; +CREATE MATERIALIZED VIEW mvv_t1 (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; +select count(*) from information_schema.tidb_mviews; +count(*) +3 +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mv_t1 FAST +test mvv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA = "test" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mv_t1 FAST +test mvv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA != "test" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" or MVIEW_NAME = "mv_t1" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, MVIEW_MODIFY_TIME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" or MVIEW_NAME like "%mvv%" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME MVIEW_MODIFY_TIME REFRESH_METHOD +test mv_t 2026-04-30 15:37:40 FAST +test mvv_t1 2026-04-30 15:37:40 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME in ("mv_t", "mv", "mv_t1") order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME not in ("mv", "mv_t1") order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mvv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA = "test" and MVIEW_NAME like "%mvv%" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mvv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where REFRESH_METHOD = "FAST" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +test mv_t FAST +test mv_t1 FAST +test mvv_t1 FAST +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where REFRESH_METHOD != "FAST" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME REFRESH_METHOD +select TABLE_SCHEMA, MVIEW_NAME, MVIEW_MODIFY_TIME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME in ("mv_t", "mv", "mv_t1") and REFRESH_METHOD = "FAST" order by 1, 2, 3; +TABLE_SCHEMA MVIEW_NAME MVIEW_MODIFY_TIME REFRESH_METHOD +test mv_t 2026-04-30 15:37:40 FAST +test mv_t1 2026-04-30 15:37:40 FAST +drop table if exists tmlogbra, tmlogbrb, tmlog_like_a, tmlog_like_b, tmlog_misc_c; +create table tmlogbra (id int primary key, a int, b int); +create table tmlogbrb (id int primary key, a int, b int); +create table tmlog_like_a (id int primary key, a int, b int); +create table tmlog_like_b (id int primary key, a int, b int); +create table tmlog_misc_c (id int primary key, a int, b int); +create materialized view log on tmlogbra (id, a) purge next date_add(now(), interval 1 hour); +create materialized view log on tmlogbrb (id, a) purge next date_add(now(), interval 1 hour); +create materialized view log on tmlog_like_a (id, a) +purge start with now() next date_add(now(), interval 1 hour); +create materialized view log on tmlog_like_b (id, a) +purge next date_add(now(), interval 2 hour); +create materialized view log on tmlog_misc_c (id, a) +purge start with now() next date_add(now(), interval 3 hour); +select count(*) >= 2 from information_schema.tidb_mlogs; +count(*) >= 2 +1 +select table_schema, mlog_name +from information_schema.tidb_mlogs +where table_schema = 'test' and mlog_name = '$mlog$tmlogbra' +order by 1, 2; +table_schema mlog_name +test $mlog$tmlogbra +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_name = 'tmlogbra' +order by 1, 2; +mlog_name base_table_name +$mlog$tmlogbra tmlogbra +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name like '%tmlogbr%' and base_table_name = 'tmlogbrb' +order by 1, 2; +mlog_name base_table_name +$mlog$tmlogbrb tmlogbrb +select mlog_name, purge_method +from information_schema.tidb_mlogs +where table_catalog like 'de%' +and base_table_catalog like 'de%' +and mlog_columns like '%id%' +and purge_next like 'DATE_ADD%' +order by 1, 2; +mlog_name purge_method +$mlog$tmlog_like_a DEFERRED +$mlog$tmlog_like_b DEFERRED +$mlog$tmlog_misc_c DEFERRED +$mlog$tmlogbra DEFERRED +$mlog$tmlogbrb DEFERRED +select mlog_name, purge_start, purge_next +from information_schema.tidb_mlogs +where mlog_columns like '%a%' +and purge_method = 'DEFERRED' +and purge_start like 'NOW%' +order by 1, 2, 3; +mlog_name purge_start purge_next +$mlog$tmlog_like_a NOW() DATE_ADD(NOW(), INTERVAL 1 HOUR) +$mlog$tmlog_misc_c NOW() DATE_ADD(NOW(), INTERVAL 3 HOUR) +select table_schema, mlog_name, base_table_name +from information_schema.tidb_mlogs +where table_schema like 'te%' +and mlog_name like '%tmlog_like_%' +and purge_method = 'DEFERRED' +order by 1, 2, 3; +table_schema mlog_name base_table_name +test $mlog$tmlog_like_a tmlog_like_a +test $mlog$tmlog_like_b tmlog_like_b +select mlog_name, mlog_columns, purge_start +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_misc_%' +and table_catalog = 'def' +and mlog_columns like '%id%' +and purge_start like 'NOW%' +order by 1, 2, 3; +mlog_name mlog_columns purge_start +$mlog$tmlog_misc_c id,a NOW() +select mlog_name, purge_next +from information_schema.tidb_mlogs +where table_schema like 'te%' +and mlog_name like '%tmlog_%' +and purge_next like 'DATE_ADD(NOW(), INTERVAL 2 HOUR)%' +order by 1, 2; +mlog_name purge_next +$mlog$tmlog_like_b DATE_ADD(NOW(), INTERVAL 2 HOUR) +select mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where base_table_schema like 'te%' +and base_table_name like 'tmlog_like_%' +and purge_next like 'DATE_ADD%' +order by 1, 2, 3; +mlog_name base_table_schema base_table_name +$mlog$tmlog_like_a test tmlog_like_a +$mlog$tmlog_like_b test tmlog_like_b +select mlog_name, base_table_name, purge_start +from information_schema.tidb_mlogs +where base_table_name like 'tmlog_misc_%' +and base_table_catalog like 'de%' +and purge_start like 'NOW%' +order by 1, 2, 3; +mlog_name base_table_name purge_start +$mlog$tmlog_misc_c tmlog_misc_c NOW() +select mlog_name, base_table_name, purge_method +from information_schema.tidb_mlogs +where base_table_schema like 'te%' +and base_table_name like 'tmlog_like_b%' +and mlog_columns like '%a%' +and purge_method = 'DEFERRED' +order by 1, 2, 3; +mlog_name base_table_name purge_method +$mlog$tmlog_like_b tmlog_like_b DEFERRED +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_like_%' +and base_table_name like 'tmlog_like_b%' +and mlog_columns like '%a%' +order by 1, 2; +mlog_name base_table_name +$mlog$tmlog_like_b tmlog_like_b +select table_schema, mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where table_schema like 'te%' +and base_table_schema like 'te%' +and mlog_name like '%tmlog_misc_%' +and purge_next like 'DATE_ADD(NOW(), INTERVAL 3 HOUR)%' +order by 1, 2, 3, 4; +table_schema mlog_name base_table_schema base_table_name +test $mlog$tmlog_misc_c test tmlog_misc_c +select mlog_name, base_table_name, purge_method, purge_start +from information_schema.tidb_mlogs +where table_schema = 'test' +and base_table_name like 'tmlog_like_a%' +and purge_method = 'DEFERRED' +and purge_start like 'NOW%' +order by 1, 2, 3, 4; +mlog_name base_table_name purge_method purge_start +$mlog$tmlog_like_a tmlog_like_a DEFERRED NOW() +select mlog_name, base_table_name, table_catalog, base_table_catalog +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_%' +and base_table_schema like 'te%' +and table_catalog like 'de%' +and base_table_catalog like 'de%' +order by 1, 2, 3, 4; +mlog_name base_table_name table_catalog base_table_catalog +$mlog$tmlog_like_a tmlog_like_a def def +$mlog$tmlog_like_b tmlog_like_b def def +$mlog$tmlog_misc_c tmlog_misc_c def def +$mlog$tmlogbra tmlogbra def def +$mlog$tmlogbrb tmlogbrb def def +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where table_schema in ('test') +and mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_like_b') +and purge_method in ('DEFERRED') +order by 1, 2; +mlog_name base_table_name +$mlog$tmlog_like_a tmlog_like_a +$mlog$tmlog_like_b tmlog_like_b +select mlog_name, purge_next +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_misc_c') +and table_catalog in ('def') +and purge_method in ('DEFERRED') +order by 1, 2; +mlog_name purge_next +$mlog$tmlog_misc_c DATE_ADD(NOW(), INTERVAL 3 HOUR) +select mlog_name, mlog_columns +from information_schema.tidb_mlogs +where table_schema in ('test', 'mysql') +and mlog_name in ('$mlog$tmlog_like_a', '$mlog$not_exist') +and mlog_columns in ('id,a') +order by 1, 2; +mlog_name mlog_columns +$mlog$tmlog_like_a id,a +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_schema in ('test') +and base_table_name in ('tmlog_like_a', 'tmlog_like_b') +and purge_method in ('DEFERRED') +order by 1, 2; +mlog_name base_table_name +$mlog$tmlog_like_a tmlog_like_a +$mlog$tmlog_like_b tmlog_like_b +select mlog_name, base_table_name, purge_next +from information_schema.tidb_mlogs +where base_table_name in ('tmlog_misc_c') +and base_table_catalog in ('def') +and purge_method in ('DEFERRED') +order by 1, 2, 3; +mlog_name base_table_name purge_next +$mlog$tmlog_misc_c tmlog_misc_c DATE_ADD(NOW(), INTERVAL 3 HOUR) +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_schema in ('test', 'mysql') +and base_table_name in ('tmlog_like_b', 'not_exist') +and mlog_columns in ('id,a') +order by 1, 2; +mlog_name base_table_name +$mlog$tmlog_like_b tmlog_like_b +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_like_b') +and base_table_name in ('tmlog_like_b') +and purge_method in ('DEFERRED') +order by 1, 2; +mlog_name base_table_name +$mlog$tmlog_like_b tmlog_like_b +select table_schema, mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where table_schema in ('test') +and mlog_name in ('$mlog$tmlog_misc_c') +and base_table_schema in ('test') +and base_table_name in ('tmlog_misc_c') +and table_catalog in ('def') +order by 1, 2, 3, 4; +table_schema mlog_name base_table_schema base_table_name +test $mlog$tmlog_misc_c test tmlog_misc_c +select mlog_name, base_table_name, purge_next +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_misc_c') +and base_table_name in ('tmlog_like_a', 'tmlog_misc_c') +and purge_method in ('DEFERRED') +order by 1, 2, 3; +mlog_name base_table_name purge_next +$mlog$tmlog_like_a tmlog_like_a DATE_ADD(NOW(), INTERVAL 1 HOUR) +$mlog$tmlog_misc_c tmlog_misc_c DATE_ADD(NOW(), INTERVAL 3 HOUR) +select mlog_name, purge_method +from information_schema.tidb_mlogs +where table_catalog in ('def') +and base_table_catalog in ('def') +and purge_method in ('DEFERRED') +and mlog_columns in ('id,a') +order by 1, 2; +mlog_name purge_method +$mlog$tmlog_like_a DEFERRED +$mlog$tmlog_like_b DEFERRED +$mlog$tmlog_misc_c DEFERRED +$mlog$tmlogbra DEFERRED +$mlog$tmlogbrb DEFERRED +select mlog_name, purge_start, purge_next +from information_schema.tidb_mlogs +where purge_method in ('DEFERRED') +and mlog_columns in ('id,a') +and table_catalog in ('def') +order by 1, 2, 3; +mlog_name purge_start purge_next +$mlog$tmlog_like_a NOW() DATE_ADD(NOW(), INTERVAL 1 HOUR) +$mlog$tmlog_like_b DATE_ADD(NOW(), INTERVAL 2 HOUR) +$mlog$tmlog_misc_c NOW() DATE_ADD(NOW(), INTERVAL 3 HOUR) +$mlog$tmlogbra DATE_ADD(NOW(), INTERVAL 1 HOUR) +$mlog$tmlogbrb DATE_ADD(NOW(), INTERVAL 1 HOUR) +select mlog_name +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a') +and mlog_name in ('$mlog$tmlog_like_b') +order by 1; +mlog_name +drop materialized view log on tmlogbra; +drop materialized view log on tmlogbrb; +drop materialized view log on tmlog_like_a; +drop materialized view log on tmlog_like_b; +drop materialized view log on tmlog_misc_c; +drop table if exists dep_base_alpha, dep_base_beta, dep_base_gamma, dep_base_nomv; +create table dep_base_alpha ( +id int primary key, +a int not null, +b int not null +); +create table dep_base_beta ( +id int primary key, +a int not null, +b int not null +); +create table dep_base_gamma ( +id int primary key, +a int not null, +b int not null +); +create table dep_base_nomv ( +id int primary key, +a int not null, +b int not null +); +create materialized view log on dep_base_alpha (id, a, b) +purge next date_add(now(), interval 1 hour); +create materialized view log on dep_base_beta (id, a, b) +purge next date_add(now(), interval 2 hour); +create materialized view log on dep_base_gamma (id, a, b) +purge next date_add(now(), interval 3 hour); +create materialized view log on dep_base_nomv (id, a, b) +purge next date_add(now(), interval 4 hour); +create materialized view dep_mv_alpha_cnt (a, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select a, count(1) +from dep_base_alpha +group by a; +create materialized view dep_mv_alpha_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select a, sum(b), count(1) +from dep_base_alpha +group by a; +create materialized view dep_mv_alpha_by_b_cnt (b, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select b, count(1) +from dep_base_alpha +group by b; +create materialized view dep_mv_alpha_by_b_sum_a (b, s_a, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select b, sum(a), count(1) +from dep_base_alpha +group by b; +create materialized view dep_mv_beta_cnt (a, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select a, count(1) +from dep_base_beta +group by a; +create materialized view dep_mv_beta_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select a, sum(b), count(1) +from dep_base_beta +group by a; +create materialized view dep_mv_beta_by_b_cnt (b, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select b, count(1) +from dep_base_beta +group by b; +create materialized view dep_mv_gamma_cnt (a, cnt) +refresh fast next date_add(now(), interval 3 hour) +as select a, count(1) +from dep_base_gamma +group by a; +create materialized view dep_mv_gamma_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 3 hour) +as select a, sum(b), count(1) +from dep_base_gamma +group by a; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name = 'dep_base_alpha' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name = 'dep_base_xxx' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_bet%' order by 1, 3; +table_name mlog_name mview_name +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_baaaase_bet%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name like 'dep_base_al%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'information_schema' and table_name like 'dep_base_alpha%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta', 'dep_base_nomv') order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('1111', '2222', '3333') order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_base_alpha' or mlog_name = '$mlog$dep_base_beta' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_11111base_alpha' or mlog_name = '$mlog$dep_11111base_beta' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name like '%dep_base_al%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name like '$mlog$deppppp_base_al%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_base_alpha' and mlog_name like '%dep_base_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_baseeeeeee_alpha' and mlog_name like '$mlog$dep_base_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name in ('$mlog$dep_base_alpha', '$mlog$dep_base_beta', '$mlog$dep_base_nomv') order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name in ('11111', '22222', '33333') order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name = 'dddddep_mv_alpha_cnt' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name like 'dep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name like 'dep_mvvvv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name like 'dep_mv_beta_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'information_schema' and mview_name like 'dep_mv_beta_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name in ('dep_mv_alpha_cnt', 'dep_mv_beta_cnt', 'dep_mv_not_exist') order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name in ('dep_mv_alpha_cntttt', 'dep_mv_beta_cntttt', 'dep_mv_not_existttt') order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name = 'dep_mv_alpha_sum_b' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name = 'deppp_mv_alpha_sum_b' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_basssse_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name like 'dep_mv_aaaaalpha_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta') and mview_name in ('dep_mv_alpha_cnt', 'dep_mv_beta_cnt') order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dddep_base_alpha', 'dep_base_betaaa') and mview_name in ('dddep_mv_alpha_cnt', 'dep_mv_beta_cccnt') order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name = '$mlog$dep_base_alpha' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name = '$mlogggg$dep_base_alpha' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_%' and mlog_name like '%dep_base_%' and mview_name like 'dep_mv_%_cnt' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_by_b_cnt +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +dep_base_gamma $mlog$dep_base_gamma dep_mv_gamma_cnt +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_bbbbase_%' and mlog_name like '%dep_base_%' and mview_name like 'dep_mv_%_cnt' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name like '%dep_base_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_by_b_sum_a +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_cnt +dep_base_alpha $mlog$dep_base_alpha dep_mv_alpha_sum_b +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name like '%dep_base_al%' and mview_name like 'ddddep_mv_alpha_%' order by 1, 3; +table_name mlog_name mview_name +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta', 'dep_base_nomv') and mlog_name in ('$mlog$dep_base_alpha', '$mlog$dep_base_beta', '$mlog$dep_base_nomv') and mview_name in ('dep_mv_alpha_sum', 'dep_mv_beta_cnt') order by 1, 3; +table_name mlog_name mview_name +dep_base_beta $mlog$dep_base_beta dep_mv_beta_cnt +select count(*) from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_nomv'; +count(*) +0 +drop materialized view dep_mv_alpha_by_b_cnt; +drop materialized view dep_mv_alpha_by_b_sum_a; +drop materialized view dep_mv_alpha_cnt; +drop materialized view dep_mv_alpha_sum_b; +drop materialized view dep_mv_beta_by_b_cnt; +drop materialized view dep_mv_beta_cnt; +drop materialized view dep_mv_beta_sum_b; +drop materialized view dep_mv_gamma_cnt; +drop materialized view dep_mv_gamma_sum_b; +drop materialized view log on dep_base_alpha; +drop materialized view log on dep_base_beta; +drop materialized view log on dep_base_gamma; +drop materialized view log on dep_base_nomv; diff --git a/tests/integrationtest/t/executor/infoschema_mv.test b/tests/integrationtest/t/executor/infoschema_mv.test new file mode 100644 index 0000000000000..f17f80cf35b5f --- /dev/null +++ b/tests/integrationtest/t/executor/infoschema_mv.test @@ -0,0 +1,433 @@ +# Start to test tidb_mviews + +use test; +drop table if exists t; +create table t ( + c0 int not null, + c1 int unsigned not null, + c2 double not null, + c3 decimal(20, 3) not null, + kcol int +); + +select count(*) from information_schema.tidb_mviews; +select TABLE_SCHEMA, MVIEW_NAME from information_schema.tidb_mviews; + +create materialized view log on t (c0, c1, c2, c3, kcol); + +CREATE MATERIALIZED VIEW mv_t (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; + +CREATE MATERIALIZED VIEW mv_t1 (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; + +CREATE MATERIALIZED VIEW mvv_t1 (kcol, s0, s1, s2, s3, cnt) +REFRESH FAST NEXT DATE_ADD(now(), interval 1 hour) +AS +SELECT kcol, SUM(c0), SUM(c1), SUM(c2), SUM(c3), COUNT(1) +FROM t +GROUP BY kcol; + +select count(*) from information_schema.tidb_mviews; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA = "test" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA != "test" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" or MVIEW_NAME = "mv_t1" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, MVIEW_MODIFY_TIME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME = "mv_t" or MVIEW_NAME like "%mvv%" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME in ("mv_t", "mv", "mv_t1") order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME not in ("mv", "mv_t1") order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where TABLE_SCHEMA = "test" and MVIEW_NAME like "%mvv%" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where REFRESH_METHOD = "FAST" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, REFRESH_METHOD from information_schema.tidb_mviews where REFRESH_METHOD != "FAST" order by 1, 2, 3; +select TABLE_SCHEMA, MVIEW_NAME, MVIEW_MODIFY_TIME, REFRESH_METHOD from information_schema.tidb_mviews where MVIEW_NAME in ("mv_t", "mv", "mv_t1") and REFRESH_METHOD = "FAST" order by 1, 2, 3; + +# Start to test tidb_mlogs + +drop table if exists tmlogbra, tmlogbrb, tmlog_like_a, tmlog_like_b, tmlog_misc_c; +create table tmlogbra (id int primary key, a int, b int); +create table tmlogbrb (id int primary key, a int, b int); +create table tmlog_like_a (id int primary key, a int, b int); +create table tmlog_like_b (id int primary key, a int, b int); +create table tmlog_misc_c (id int primary key, a int, b int); + +create materialized view log on tmlogbra (id, a) purge next date_add(now(), interval 1 hour); +create materialized view log on tmlogbrb (id, a) purge next date_add(now(), interval 1 hour); +create materialized view log on tmlog_like_a (id, a) + purge start with now() next date_add(now(), interval 1 hour); +create materialized view log on tmlog_like_b (id, a) + purge next date_add(now(), interval 2 hour); +create materialized view log on tmlog_misc_c (id, a) + purge start with now() next date_add(now(), interval 3 hour); + +select count(*) >= 2 from information_schema.tidb_mlogs; + +select table_schema, mlog_name +from information_schema.tidb_mlogs +where table_schema = 'test' and mlog_name = '$mlog$tmlogbra' +order by 1, 2; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_name = 'tmlogbra' +order by 1, 2; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name like '%tmlogbr%' and base_table_name = 'tmlogbrb' +order by 1, 2; + +select mlog_name, purge_method +from information_schema.tidb_mlogs +where table_catalog like 'de%' + and base_table_catalog like 'de%' + and mlog_columns like '%id%' + and purge_next like 'DATE_ADD%' +order by 1, 2; + +select mlog_name, purge_start, purge_next +from information_schema.tidb_mlogs +where mlog_columns like '%a%' + and purge_method = 'DEFERRED' + and purge_start like 'NOW%' +order by 1, 2, 3; + +select table_schema, mlog_name, base_table_name +from information_schema.tidb_mlogs +where table_schema like 'te%' + and mlog_name like '%tmlog_like_%' + and purge_method = 'DEFERRED' +order by 1, 2, 3; + +select mlog_name, mlog_columns, purge_start +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_misc_%' + and table_catalog = 'def' + and mlog_columns like '%id%' + and purge_start like 'NOW%' +order by 1, 2, 3; + + +select mlog_name, purge_next +from information_schema.tidb_mlogs +where table_schema like 'te%' + and mlog_name like '%tmlog_%' + and purge_next like 'DATE_ADD(NOW(), INTERVAL 2 HOUR)%' +order by 1, 2; + +select mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where base_table_schema like 'te%' + and base_table_name like 'tmlog_like_%' + and purge_next like 'DATE_ADD%' +order by 1, 2, 3; + +select mlog_name, base_table_name, purge_start +from information_schema.tidb_mlogs +where base_table_name like 'tmlog_misc_%' + and base_table_catalog like 'de%' + and purge_start like 'NOW%' +order by 1, 2, 3; + +select mlog_name, base_table_name, purge_method +from information_schema.tidb_mlogs +where base_table_schema like 'te%' + and base_table_name like 'tmlog_like_b%' + and mlog_columns like '%a%' + and purge_method = 'DEFERRED' +order by 1, 2, 3; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_like_%' + and base_table_name like 'tmlog_like_b%' + and mlog_columns like '%a%' +order by 1, 2; + +select table_schema, mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where table_schema like 'te%' + and base_table_schema like 'te%' + and mlog_name like '%tmlog_misc_%' + and purge_next like 'DATE_ADD(NOW(), INTERVAL 3 HOUR)%' +order by 1, 2, 3, 4; + +select mlog_name, base_table_name, purge_method, purge_start +from information_schema.tidb_mlogs +where table_schema = 'test' + and base_table_name like 'tmlog_like_a%' + and purge_method = 'DEFERRED' + and purge_start like 'NOW%' +order by 1, 2, 3, 4; + +select mlog_name, base_table_name, table_catalog, base_table_catalog +from information_schema.tidb_mlogs +where mlog_name like '%tmlog_%' + and base_table_schema like 'te%' + and table_catalog like 'de%' + and base_table_catalog like 'de%' +order by 1, 2, 3, 4; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where table_schema in ('test') + and mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_like_b') + and purge_method in ('DEFERRED') +order by 1, 2; + +select mlog_name, purge_next +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_misc_c') + and table_catalog in ('def') + and purge_method in ('DEFERRED') +order by 1, 2; + +select mlog_name, mlog_columns +from information_schema.tidb_mlogs +where table_schema in ('test', 'mysql') + and mlog_name in ('$mlog$tmlog_like_a', '$mlog$not_exist') + and mlog_columns in ('id,a') +order by 1, 2; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_schema in ('test') + and base_table_name in ('tmlog_like_a', 'tmlog_like_b') + and purge_method in ('DEFERRED') +order by 1, 2; + +select mlog_name, base_table_name, purge_next +from information_schema.tidb_mlogs +where base_table_name in ('tmlog_misc_c') + and base_table_catalog in ('def') + and purge_method in ('DEFERRED') +order by 1, 2, 3; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where base_table_schema in ('test', 'mysql') + and base_table_name in ('tmlog_like_b', 'not_exist') + and mlog_columns in ('id,a') +order by 1, 2; + +select mlog_name, base_table_name +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_like_b') + and base_table_name in ('tmlog_like_b') + and purge_method in ('DEFERRED') +order by 1, 2; + +select table_schema, mlog_name, base_table_schema, base_table_name +from information_schema.tidb_mlogs +where table_schema in ('test') + and mlog_name in ('$mlog$tmlog_misc_c') + and base_table_schema in ('test') + and base_table_name in ('tmlog_misc_c') + and table_catalog in ('def') +order by 1, 2, 3, 4; + +select mlog_name, base_table_name, purge_next +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a', '$mlog$tmlog_misc_c') + and base_table_name in ('tmlog_like_a', 'tmlog_misc_c') + and purge_method in ('DEFERRED') +order by 1, 2, 3; + +select mlog_name, purge_method +from information_schema.tidb_mlogs +where table_catalog in ('def') + and base_table_catalog in ('def') + and purge_method in ('DEFERRED') + and mlog_columns in ('id,a') +order by 1, 2; + +select mlog_name, purge_start, purge_next +from information_schema.tidb_mlogs +where purge_method in ('DEFERRED') + and mlog_columns in ('id,a') + and table_catalog in ('def') +order by 1, 2, 3; + +select mlog_name +from information_schema.tidb_mlogs +where mlog_name in ('$mlog$tmlog_like_a') + and mlog_name in ('$mlog$tmlog_like_b') +order by 1; + +drop materialized view log on tmlogbra; +drop materialized view log on tmlogbrb; +drop materialized view log on tmlog_like_a; +drop materialized view log on tmlog_like_b; +drop materialized view log on tmlog_misc_c; + + +drop table if exists dep_base_alpha, dep_base_beta, dep_base_gamma, dep_base_nomv; + +create table dep_base_alpha ( + id int primary key, + a int not null, + b int not null +); + +create table dep_base_beta ( + id int primary key, + a int not null, + b int not null +); + +create table dep_base_gamma ( + id int primary key, + a int not null, + b int not null +); + +create table dep_base_nomv ( + id int primary key, + a int not null, + b int not null +); + +create materialized view log on dep_base_alpha (id, a, b) + purge next date_add(now(), interval 1 hour); + +create materialized view log on dep_base_beta (id, a, b) + purge next date_add(now(), interval 2 hour); + +create materialized view log on dep_base_gamma (id, a, b) + purge next date_add(now(), interval 3 hour); + +create materialized view log on dep_base_nomv (id, a, b) + purge next date_add(now(), interval 4 hour); + +# dep_base_alpha: 4 mviews +create materialized view dep_mv_alpha_cnt (a, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select a, count(1) +from dep_base_alpha +group by a; + +create materialized view dep_mv_alpha_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select a, sum(b), count(1) +from dep_base_alpha +group by a; + +create materialized view dep_mv_alpha_by_b_cnt (b, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select b, count(1) +from dep_base_alpha +group by b; + +create materialized view dep_mv_alpha_by_b_sum_a (b, s_a, cnt) +refresh fast next date_add(now(), interval 1 hour) +as select b, sum(a), count(1) +from dep_base_alpha +group by b; + +# dep_base_beta: 3 mviews +create materialized view dep_mv_beta_cnt (a, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select a, count(1) +from dep_base_beta +group by a; + +create materialized view dep_mv_beta_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select a, sum(b), count(1) +from dep_base_beta +group by a; + +create materialized view dep_mv_beta_by_b_cnt (b, cnt) +refresh fast next date_add(now(), interval 2 hour) +as select b, count(1) +from dep_base_beta +group by b; + +# dep_base_gamma: 2 mviews +create materialized view dep_mv_gamma_cnt (a, cnt) +refresh fast next date_add(now(), interval 3 hour) +as select a, count(1) +from dep_base_gamma +group by a; + +create materialized view dep_mv_gamma_sum_b (a, s_b, cnt) +refresh fast next date_add(now(), interval 3 hour) +as select a, sum(b), count(1) +from dep_base_gamma +group by a; + + +# only base predicates: equal, like, equal+like, in +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name = 'dep_base_alpha' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name = 'dep_base_xxx' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_bet%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_baaaase_bet%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'test' and table_name like 'dep_base_al%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_schema = 'information_schema' and table_name like 'dep_base_alpha%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta', 'dep_base_nomv') order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('1111', '2222', '3333') order by 1, 3; + +# only mlog predicates: equal, like, equal+like, in +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_base_alpha' or mlog_name = '$mlog$dep_base_beta' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_11111base_alpha' or mlog_name = '$mlog$dep_11111base_beta' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name like '%dep_base_al%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name like '$mlog$deppppp_base_al%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_base_alpha' and mlog_name like '%dep_base_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name = '$mlog$dep_baseeeeeee_alpha' and mlog_name like '$mlog$dep_base_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name in ('$mlog$dep_base_alpha', '$mlog$dep_base_beta', '$mlog$dep_base_nomv') order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mlog_name in ('11111', '22222', '33333') order by 1, 3; + +# only mview predicates: equal, like, equal+like, in +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name = 'dddddep_mv_alpha_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name like 'dep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name like 'dep_mvvvv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'test' and mview_name like 'dep_mv_beta_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_schema = 'information_schema' and mview_name like 'dep_mv_beta_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name in ('dep_mv_alpha_cnt', 'dep_mv_beta_cnt', 'dep_mv_not_exist') order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where mview_name in ('dep_mv_alpha_cntttt', 'dep_mv_beta_cntttt', 'dep_mv_not_existttt') order by 1, 3; + +# base + mview predicates: equal, like, equal+like, in +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name = 'dep_mv_alpha_sum_b' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name = 'deppp_mv_alpha_sum_b' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_basssse_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mview_name like 'dep_mv_aaaaalpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta') and mview_name in ('dep_mv_alpha_cnt', 'dep_mv_beta_cnt') order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dddep_base_alpha', 'dep_base_betaaa') and mview_name in ('dddep_mv_alpha_cnt', 'dep_mv_beta_cccnt') order by 1, 3; + +# base + mlog + mview predicates: equal, like, equal+like, in +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name = '$mlog$dep_base_alpha' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name = '$mlogggg$dep_base_alpha' and mview_name = 'dep_mv_alpha_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_base_%' and mlog_name like '%dep_base_%' and mview_name like 'dep_mv_%_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name like 'dep_bbbbase_%' and mlog_name like '%dep_base_%' and mview_name like 'dep_mv_%_cnt' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name like '%dep_base_al%' and mview_name like 'dep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_alpha' and mlog_name like '%dep_base_al%' and mview_name like 'ddddep_mv_alpha_%' order by 1, 3; +select table_name, mlog_name, mview_name from information_schema.tidb_table_mview_dependencies where table_name in ('dep_base_alpha', 'dep_base_beta', 'dep_base_nomv') and mlog_name in ('$mlog$dep_base_alpha', '$mlog$dep_base_beta', '$mlog$dep_base_nomv') and mview_name in ('dep_mv_alpha_sum', 'dep_mv_beta_cnt') order by 1, 3; + +# verify base table with mlog but no mview +select count(*) from information_schema.tidb_table_mview_dependencies where table_name = 'dep_base_nomv'; + +drop materialized view dep_mv_alpha_by_b_cnt; +drop materialized view dep_mv_alpha_by_b_sum_a; +drop materialized view dep_mv_alpha_cnt; +drop materialized view dep_mv_alpha_sum_b; +drop materialized view dep_mv_beta_by_b_cnt; +drop materialized view dep_mv_beta_cnt; +drop materialized view dep_mv_beta_sum_b; +drop materialized view dep_mv_gamma_cnt; +drop materialized view dep_mv_gamma_sum_b; + +drop materialized view log on dep_base_alpha; +drop materialized view log on dep_base_beta; +drop materialized view log on dep_base_gamma; +drop materialized view log on dep_base_nomv;