From 6206b0c924150a0c7b0be3a4fd602d632e8a2bc0 Mon Sep 17 00:00:00 2001 From: jianyilyu <68390752+jianyilyu@users.noreply.github.com> Date: Sat, 10 Oct 2020 13:34:53 +0800 Subject: [PATCH 1/3] cherry pick #19106 to release-4.0 Signed-off-by: ti-srebot --- executor/adapter.go | 13 +++++---- executor/explain.go | 17 ++++++++++- planner/core/integration_test.go | 50 ++++++++++++++++++++++++++++++++ session/session.go | 17 +++++++++++ 4 files changed, 91 insertions(+), 6 deletions(-) diff --git a/executor/adapter.go b/executor/adapter.go index 297fdec04e06c..afa955c8b7ed5 100644 --- a/executor/adapter.go +++ b/executor/adapter.go @@ -384,19 +384,22 @@ func (a *ExecStmt) handleNoDelay(ctx context.Context, e Executor, isPessimistic }() toCheck := e + isExplainAnalyze := false if explain, ok := e.(*ExplainExec); ok { - if explain.analyzeExec != nil { - toCheck = explain.analyzeExec + if analyze := explain.getAnalyzeExecToExecutedNoDelay(); analyze != nil { + toCheck = analyze + isExplainAnalyze = true } } // If the executor doesn't return any result to the client, we execute it without delay. if toCheck.Schema().Len() == 0 { + handled = !isExplainAnalyze if isPessimistic { - return true, nil, a.handlePessimisticDML(ctx, e) + return handled, nil, a.handlePessimisticDML(ctx, toCheck) } - r, err := a.handleNoDelayExecutor(ctx, e) - return true, r, err + r, err := a.handleNoDelayExecutor(ctx, toCheck) + return handled, r, err } else if proj, ok := toCheck.(*ProjectionExec); ok && proj.calculateNoDelay { // Currently this is only for the "DO" statement. Take "DO 1, @a=2;" as an example: // the Projection has two expressions and two columns in the schema, but we should diff --git a/executor/explain.go b/executor/explain.go index 2e12fd9d417b2..ac4c30e699f34 100644 --- a/executor/explain.go +++ b/executor/explain.go @@ -28,6 +28,7 @@ type ExplainExec struct { explain *core.Explain analyzeExec Executor + executed bool rows [][]string cursor int } @@ -79,7 +80,8 @@ func (e *ExplainExec) generateExplainInfo(ctx context.Context) (rows [][]string, closed = true } }() - if e.analyzeExec != nil { + if e.analyzeExec != nil && !e.executed { + e.executed = true chk := newFirstChunk(e.analyzeExec) var nextErr, closeErr error for { @@ -111,3 +113,16 @@ func (e *ExplainExec) generateExplainInfo(ctx context.Context) (rows [][]string, } return e.explain.Rows, nil } + +// getAnalyzeExecToExecutedNoDelay gets the analyze DML executor to execute in handleNoDelay function. +// For explain analyze insert/update/delete statement, the analyze executor should be executed in handleNoDelay +// function and then commit transaction if needed. +// Otherwise, in autocommit transaction, the table record change of analyze executor(insert/update/delete...) +// will not be committed. +func (e *ExplainExec) getAnalyzeExecToExecutedNoDelay() Executor { + if e.analyzeExec != nil && !e.executed && e.analyzeExec.Schema().Len() == 0 { + e.executed = true + return e.analyzeExec + } + return nil +} diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index 901f9f3b6027c..6d450ddb373ba 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -1365,6 +1365,56 @@ func (s *testIntegrationSerialSuite) TestExplainAnalyzePointGet(c *C) { checkExplain("BatchGet") } +<<<<<<< HEAD +======= +func (s *testIntegrationSerialSuite) TestExplainAnalyzeDML(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec("drop table if exists t") + tk.MustExec(" create table t (a int, b int, unique index (a));") + tk.MustExec("insert into t values (1,1)") + + res := tk.MustQuery("explain analyze select * from t where a=1;") + checkExplain := func(rpc string) { + resBuff := bytes.NewBufferString("") + for _, row := range res.Rows() { + fmt.Fprintf(resBuff, "%s\n", row) + } + explain := resBuff.String() + c.Assert(strings.Contains(explain, rpc+":{num_rpc:"), IsTrue, Commentf("%s", explain)) + c.Assert(strings.Contains(explain, "total_time:"), IsTrue, Commentf("%s", explain)) + } + checkExplain("Get") + res = tk.MustQuery("explain analyze insert ignore into t values (1,1),(2,2),(3,3),(4,4);") + checkExplain("BatchGet") +} + +func (s *testIntegrationSuite) TestPartitionExplain(c *C) { + tk := testkit.NewTestKit(c, s.store) + tk.MustExec("use test") + tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( +partition p0 values less than (4), +partition p1 values less than (7), +partition p2 values less than (10))`) + + tk.MustExec("set @@tidb_enable_index_merge = 1;") + + var input []string + var output []struct { + SQL string + Plan []string + } + s.testData.GetTestCases(c, &input, &output) + for i, tt := range input { + s.testData.OnRecord(func() { + output[i].SQL = tt + output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) + }) + tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) + } +} + +>>>>>>> c704b9756... executor: add runtime information for DML statement in explain analyze (#19106) func (s *testIntegrationSuite) TestPartialBatchPointGet(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/session/session.go b/session/session.go index dfd4afcf4f05e..45b94af4636d3 100644 --- a/session/session.go +++ b/session/session.go @@ -1181,7 +1181,24 @@ func (s *session) execute(ctx context.Context, sql string) (recordSets []sqlexec if recordSets, err = s.executeStatement(ctx, stmt, recordSets, multiQuery); err != nil { return nil, err } + err = finishStmt(ctx, se, err, s) } +<<<<<<< HEAD +======= + if rs != nil { + return &execStmtResult{ + RecordSet: rs, + sql: s, + se: se, + }, err + } + + // If it is not a select statement, we record its slow log here, + // then it could include the transaction commit time. + s.(*executor.ExecStmt).FinishExecuteStmt(origTxnCtx.StartTS, err == nil, false) + return nil, err +} +>>>>>>> c704b9756... executor: add runtime information for DML statement in explain analyze (#19106) if s.sessionVars.ClientCapability&mysql.ClientMultiResults == 0 && len(recordSets) > 1 { // return the first recordset if client doesn't support ClientMultiResults. From 6d62ae52f90ea74a6130c7126210996f8b3b97f3 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 16 Nov 2020 11:16:30 +0800 Subject: [PATCH 2/3] init Signed-off-by: crazycs520 --- planner/core/integration_test.go | 28 ---------------------------- session/session.go | 17 ----------------- 2 files changed, 45 deletions(-) diff --git a/planner/core/integration_test.go b/planner/core/integration_test.go index 6d450ddb373ba..2a57b0e8c0346 100644 --- a/planner/core/integration_test.go +++ b/planner/core/integration_test.go @@ -1365,8 +1365,6 @@ func (s *testIntegrationSerialSuite) TestExplainAnalyzePointGet(c *C) { checkExplain("BatchGet") } -<<<<<<< HEAD -======= func (s *testIntegrationSerialSuite) TestExplainAnalyzeDML(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") @@ -1389,32 +1387,6 @@ func (s *testIntegrationSerialSuite) TestExplainAnalyzeDML(c *C) { checkExplain("BatchGet") } -func (s *testIntegrationSuite) TestPartitionExplain(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec(`create table pt (id int, c int, key i_id(id), key i_c(c)) partition by range (c) ( -partition p0 values less than (4), -partition p1 values less than (7), -partition p2 values less than (10))`) - - tk.MustExec("set @@tidb_enable_index_merge = 1;") - - var input []string - var output []struct { - SQL string - Plan []string - } - s.testData.GetTestCases(c, &input, &output) - for i, tt := range input { - s.testData.OnRecord(func() { - output[i].SQL = tt - output[i].Plan = s.testData.ConvertRowsToStrings(tk.MustQuery("explain " + tt).Rows()) - }) - tk.MustQuery("explain " + tt).Check(testkit.Rows(output[i].Plan...)) - } -} - ->>>>>>> c704b9756... executor: add runtime information for DML statement in explain analyze (#19106) func (s *testIntegrationSuite) TestPartialBatchPointGet(c *C) { tk := testkit.NewTestKit(c, s.store) tk.MustExec("use test") diff --git a/session/session.go b/session/session.go index 45b94af4636d3..dfd4afcf4f05e 100644 --- a/session/session.go +++ b/session/session.go @@ -1181,24 +1181,7 @@ func (s *session) execute(ctx context.Context, sql string) (recordSets []sqlexec if recordSets, err = s.executeStatement(ctx, stmt, recordSets, multiQuery); err != nil { return nil, err } - err = finishStmt(ctx, se, err, s) } -<<<<<<< HEAD -======= - if rs != nil { - return &execStmtResult{ - RecordSet: rs, - sql: s, - se: se, - }, err - } - - // If it is not a select statement, we record its slow log here, - // then it could include the transaction commit time. - s.(*executor.ExecStmt).FinishExecuteStmt(origTxnCtx.StartTS, err == nil, false) - return nil, err -} ->>>>>>> c704b9756... executor: add runtime information for DML statement in explain analyze (#19106) if s.sessionVars.ClientCapability&mysql.ClientMultiResults == 0 && len(recordSets) > 1 { // return the first recordset if client doesn't support ClientMultiResults. From 70c526c2e440ae8c258964c1f4f681dcc685b270 Mon Sep 17 00:00:00 2001 From: crazycs520 Date: Mon, 16 Nov 2020 11:19:38 +0800 Subject: [PATCH 3/3] update Signed-off-by: crazycs520 --- session/tidb.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/session/tidb.go b/session/tidb.go index 5e50359956417..9d914407a8eff 100644 --- a/session/tidb.go +++ b/session/tidb.go @@ -325,6 +325,7 @@ func runStmt(ctx context.Context, sctx sessionctx.Context, s sqlexec.Statement) } else { logutil.BgLogger().Error("get txn failed", zap.Error(err1)) } + err = finishStmt(ctx, se, err, s) } if rs != nil { @@ -335,8 +336,6 @@ func runStmt(ctx context.Context, sctx sessionctx.Context, s sqlexec.Statement) }, err } - err = finishStmt(ctx, se, err, s) - if se.hasQuerySpecial() { // The special query will be handled later in handleQuerySpecial, // then should call the ExecStmt.FinishExecuteStmt to finish this statement.