diff --git a/executor/infoschema_reader_test.go b/executor/infoschema_reader_test.go index 400ca848a85c3..9910444f46959 100644 --- a/executor/infoschema_reader_test.go +++ b/executor/infoschema_reader_test.go @@ -981,3 +981,63 @@ func (s *testInfoschemaTableSuite) TestTablesPKType(c *C) { tk.MustQuery("SELECT TIDB_PK_TYPE FROM information_schema.tables where table_schema = 'test' and table_name = 't_common'").Check(testkit.Rows("CLUSTERED")) tk.MustQuery("SELECT TIDB_PK_TYPE FROM information_schema.tables where table_schema = 'INFORMATION_SCHEMA' and table_name = 'TABLES'").Check(testkit.Rows("NONCLUSTERED")) } + +// https://github.com/pingcap/tidb/issues/32459. +func (s *testInfoschemaTableSuite) TestJoinSystemTableContainsView(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t;") + tk.MustExec("drop view if exists v;") + tk.MustExec("create table t (a timestamp, b int);") + tk.MustExec("insert into t values (null, 100);") + tk.MustExec("create view v as select * from t;") + // This is used by grafana when TiDB is specified as the data source. + // See https://github.com/grafana/grafana/blob/e86b6662a187c77656f72bef3b0022bf5ced8b98/public/app/plugins/datasource/mysql/meta_query.ts#L31 + for i := 0; i < 10; i++ { + tk.MustQuery(` +SELECT + table_name as table_name, + ( SELECT + column_name as column_name + FROM information_schema.columns c + WHERE + c.table_schema = t.table_schema AND + c.table_name = t.table_name AND + c.data_type IN ('timestamp', 'datetime') + ORDER BY ordinal_position LIMIT 1 + ) AS time_column, + ( SELECT + column_name AS column_name + FROM information_schema.columns c + WHERE + c.table_schema = t.table_schema AND + c.table_name = t.table_name AND + c.data_type IN('float', 'int', 'bigint') + ORDER BY ordinal_position LIMIT 1 + ) AS value_column + FROM information_schema.tables t + WHERE + t.table_schema = database() AND + EXISTS + ( SELECT 1 + FROM information_schema.columns c + WHERE + c.table_schema = t.table_schema AND + c.table_name = t.table_name AND + c.data_type IN ('timestamp', 'datetime') + ) AND + EXISTS + ( SELECT 1 + FROM information_schema.columns c + WHERE + c.table_schema = t.table_schema AND + c.table_name = t.table_name AND + c.data_type IN('float', 'int', 'bigint') + ) + LIMIT 1 +; +`).Check(testkit.Rows("t a b")) + } + tk.MustExec("drop table if exists t;") + tk.MustExec("drop view if exists v;") +} diff --git a/executor/show.go b/executor/show.go index 473b962d1a78f..61c1c40f983e5 100644 --- a/executor/show.go +++ b/executor/show.go @@ -1761,7 +1761,13 @@ func (e *ShowExec) fetchShowBuiltins() error { // Because view's underlying table's column could change or recreate, so view's column type may change over time. // To avoid this situation we need to generate a logical plan and extract current column types from Schema. func tryFillViewColumnType(ctx context.Context, sctx sessionctx.Context, is infoschema.InfoSchema, dbName model.CIStr, tbl *model.TableInfo) error { - if tbl.IsView() { + if !tbl.IsView() { + return nil + } + // We need to run the build plan process in another session because there may be + // multiple goroutines running at the same time while session is not goroutine-safe. + // Take joining system table as an example, `fetchBuildSideRows` and `fetchProbeSideChunks` can be run concurrently. + return runWithSystemSession(sctx, func(s sessionctx.Context) error { // Retrieve view columns info. planBuilder, _ := plannercore.NewPlanBuilder( plannercore.PlanBuilderOptNoExecution{}).Init(sctx, is, &hint.BlockHintProcessor{}) @@ -1780,6 +1786,23 @@ func tryFillViewColumnType(ctx context.Context, sctx sessionctx.Context, is info } else { return err } + return nil + }) +} + +func runWithSystemSession(sctx sessionctx.Context, fn func(sessionctx.Context) error) error { + b := &baseExecutor{ctx: sctx} + sysCtx, err := b.getSysSession() + if err != nil { + return err } - return nil + // TODO(tangenta): remove the CurrentDB assignment after + // https://github.com/pingcap/tidb/issues/34090 is fixed. + originDB := sysCtx.GetSessionVars().CurrentDB + sysCtx.GetSessionVars().CurrentDB = sctx.GetSessionVars().CurrentDB + defer func() { + sysCtx.GetSessionVars().CurrentDB = originDB + }() + defer b.releaseSysSession(sysCtx) + return fn(sysCtx) }